【程序】Xilinx FPGA将DDR3内存转换为W25Q256的SPI接口,供STM32单片机调用,产生容量为256MB的USB U盘设备
发布日期:2021-06-29 10:17:21 浏览次数:2 分类:技术文章

本文共 29650 字,大约阅读时间需要 98 分钟。

本文以XC7A35TFGG484-2这款芯片为例,采用米联客FPGA开发板,用MIG核驱动DDR3内存。FPGA外接的晶振大小为50MHz,DDR3内存的驱动频率(ddr3_ck_p和ddr3_ck_n)为400MHz。选用的DDR3内存型号为MT41K128M16,内存容量为256MB。然后,FPGA通过SPI接口和STM32单片机连接,SPI的通信协议和W25Q256一模一样。FPGA是SPI从机,单片机是SPI主机。单片机的型号为STM32F103C8,利用USB接口产生一个256MB的USB U盘设备。

程序的关键是要正确计算DDR3内存地址。DDR3内存是一次性写16字节内存,每个DDR3地址访问两个字节内存,所以一次性写的是8个地址。而SPI协议里面每个地址访问的是1个字节内存。连续读写DDR3内存时,地址通常为8的倍数。
在这里插入图片描述

程序下载地址: (提取码:bix1 )

程序的运行效果如下:
(1)往DDR3磁盘里面复制文件
文件复制测试(2)复制的文件装满整个DDR3磁盘
装满整个磁盘(3)格式化DDR3磁盘为FAT文件系统
格式化磁盘(4)ILA抓包分析SPI从机信号
ILA抓包分析
【Verilog代码】
main.v:

module main(    input clock, // 50MHz外部晶振        // DDR3引脚    inout [15:0] ddr3_dq,    inout [1:0] ddr3_dqs_n,    inout [1:0] ddr3_dqs_p,    output [13:0] ddr3_addr,    output [2:0] ddr3_ba,    output ddr3_ras_n,    output ddr3_cas_n,    output ddr3_we_n,    output ddr3_reset_n,    output ddr3_ck_p,    output ddr3_ck_n,    output ddr3_cke,    output ddr3_cs_n,    output [1:0] ddr3_dm,    output ddr3_odt,        input [3:0] keys,    output [3:0] leds, // 4个LED灯        input spi_nss,    input spi_sck,    output spi_miso,    input spi_mosi    );        parameter SYSCLK = 50000000;    wire nrst;    Reset #(SYSCLK) reset(clock, !keys[0], nrst);        wire clock200;    wire locked;    clk_wiz_0 clk_wiz_0(        .reset(!nrst),        .clk_in1(clock), // 输入50MHz时钟        .clk_out1(clock200), // 输出200MHz时钟        .locked(locked) // 该信号表示输出时钟是否已稳定    );        reg [27:0] ddr3_app_addr;    reg [2:0] ddr3_app_cmd;    reg ddr3_app_en;    reg [127:0] ddr3_app_wdf_data; // 因为burst=8, data_width=16, 所以wdf_data的宽度为8*16=128    reg ddr3_app_wdf_wren;    reg [15:0] ddr3_app_wdf_mask;    wire [127:0] ddr3_app_rd_data; // 这个也是128位    wire ddr3_app_rd_data_end;    wire ddr3_app_rd_data_valid;    wire ddr3_app_rdy;    wire ddr3_app_wdf_rdy;    wire ddr3_app_sr_active;    wire ddr3_app_ref_ack;    wire ddr3_app_zq_ack;    wire ddr3_ui_clk;    wire ddr3_ui_clk_sync_rst;    wire ddr3_init_calib_complete;    wire [11:0] ddr3_device_temp;    mig_7series_0 mig_7series_0(        .ddr3_dq(ddr3_dq),        .ddr3_dqs_n(ddr3_dqs_n),        .ddr3_dqs_p(ddr3_dqs_p),        .ddr3_addr(ddr3_addr),        .ddr3_ba(ddr3_ba),        .ddr3_ras_n(ddr3_ras_n),        .ddr3_cas_n(ddr3_cas_n),        .ddr3_we_n(ddr3_we_n),        .ddr3_reset_n(ddr3_reset_n),        .ddr3_ck_p(ddr3_ck_p), // DDR3内存时钟输出: 400MHz        .ddr3_ck_n(ddr3_ck_n),        .ddr3_cke(ddr3_cke),        .ddr3_cs_n(ddr3_cs_n),        .ddr3_dm(ddr3_dm),        .ddr3_odt(ddr3_odt),        .sys_clk_i(clock200), // 系统时钟输入: 200MHz        .clk_ref_i(clock200), // 参考时钟输入: 200MHz        .app_addr(ddr3_app_addr),        .app_cmd(ddr3_app_cmd),        .app_en(ddr3_app_en),        .app_wdf_data(ddr3_app_wdf_data),        .app_wdf_end(1'b1),        .app_wdf_mask(ddr3_app_wdf_mask), // 8突发*每个数据2字节=16字节, 所以mask有16位        .app_wdf_wren(ddr3_app_wdf_wren),        .app_rd_data(ddr3_app_rd_data),        .app_rd_data_end(ddr3_app_rd_data_end),        .app_rd_data_valid(ddr3_app_rd_data_valid),        .app_rdy(ddr3_app_rdy),        .app_wdf_rdy(ddr3_app_wdf_rdy),        .app_sr_req(1'b0),        .app_ref_req(1'b0),        .app_zq_req(1'b0),        .app_sr_active(ddr3_app_sr_active),        .app_ref_ack(ddr3_app_ref_ack),        .app_zq_ack(ddr3_app_zq_ack),        .ui_clk(ddr3_ui_clk), // 用户时钟输出: 因为选的是4:1, 所以ddr3_ck_p:ddr3_ui_clk=4:1, ddr3_ui_clk是100MHz        .ui_clk_sync_rst(ddr3_ui_clk_sync_rst), // 用户程序复位输出        .init_calib_complete(ddr3_init_calib_complete),        .device_temp(ddr3_device_temp),        .sys_rst(locked) // 复位输入: 当倍频器时钟未稳定时, 使MIG处于复位状态    );        reg [127:0] spi_data_in;    reg [15:0] spi_next_bits_cnt;    wire [127:0] spi_data_out;    wire spi_selected;    wire spi_received;    wire spi_error;    wire [15:0] spi_remaining;    SPISlave #(128) spi_slave(ddr3_ui_clk, !ddr3_ui_clk_sync_rst, spi_nss, spi_sck, spi_miso, spi_mosi, spi_data_in, spi_data_out, spi_next_bits_cnt, spi_selected, spi_received, spi_error, spi_remaining);        localparam SPI_IDLE = 0;    localparam SPI_REQUESTED = 1;    localparam SPI_PROCESS = 2;    localparam SPI_REQUESTED_AGAIN = 3;    localparam SPI_DATAERROR = 4;        reg [7:0] cmd;    reg [3:0] i;    reg [2:0] spi_state;    assign leds = i;    always @(posedge ddr3_ui_clk) begin        if (ddr3_ui_clk_sync_rst) begin            ddr3_app_en <= 0;            ddr3_app_wdf_wren <= 0;            spi_next_bits_cnt <= 0;            i <= 0;            spi_state <= SPI_IDLE;        end        else begin            case (spi_state)                SPI_IDLE: begin                    // 开始接收命令号                    spi_data_in <= 0;                    spi_next_bits_cnt <= 8;                    spi_state <= SPI_REQUESTED;                    i <= 0;                end                SPI_REQUESTED: begin                    // 等待收到数据                    if (spi_received) begin                        if (!spi_error) begin                            // 收到数据                            spi_next_bits_cnt <= 0; // 数据处理完之前, 暂停接收, 防止spi_data_in中的数据改变                            spi_state <= SPI_PROCESS;                        end                        else                            spi_state <= SPI_DATAERROR; // 接收出错                    end                end                SPI_PROCESS: begin                    // 处理数据                    if (i == 0) begin                        // 收到命令号                        cmd <= spi_data_out;                        i <= 1;                    end                    else begin                        // 处理各种命令                        case (cmd)                            8'h02: begin                                // 写内存                                case (i)                                    1: begin                                        // 接收4个字节的地址                                        spi_next_bits_cnt <= 32;                                        spi_state <= SPI_REQUESTED_AGAIN;                                        i <= 2;                                    end                                    2, 5: begin                                        // 保存地址                                        if (i == 2) begin                                            ddr3_app_addr <= {
spi_data_out[27:4], 3'b0}; // 从收到的地址开始写 ddr3_app_wdf_mask <= ~(16'h8000 >> spi_data_out[3:0]); end else begin if (ddr3_app_wdf_mask == 16'hfffe) begin ddr3_app_addr[26:3] <= ddr3_app_addr[26:3] + 1'b1; ddr3_app_wdf_mask <= 16'h7fff; end else ddr3_app_wdf_mask <= {
1'b1, ddr3_app_wdf_mask[15:1]}; end // 接收数据内容 spi_next_bits_cnt <= 8; spi_state <= SPI_REQUESTED_AGAIN; i <= i + 1'b1; end 3, 6: begin // 写DDR3内存 ddr3_app_cmd <= 0; ddr3_app_en <= 1; ddr3_app_wdf_data <= {
16{
spi_data_out[7:0]}}; ddr3_app_wdf_wren <= 1; i <= i + 1'b1; end 4, 7: begin // 检查是否写入成功 case ({
ddr3_app_rdy, ddr3_app_wdf_rdy}) 2'b01: ddr3_app_wdf_wren <= 0; 2'b10: ddr3_app_en <= 0; 2'b11: begin ddr3_app_wdf_wren <= 0; ddr3_app_en <= 0; i <= 5; end endcase end endcase end 8'h03: begin // 读内存 case (i) 1: begin // 接收4个字节的地址 spi_next_bits_cnt <= 32; spi_state <= SPI_REQUESTED_AGAIN; i <= 2; end 2, 4: begin // 读DDR3内存 // 每个DDR3内存地址访问2字节内存 // 0号地址访问第0~1字节, 1号地址访问第2~3字节, 8号地址访问第16~17字节 // MIG核一次性突发读取16字节内存, 也就是8个地址 // 所以这里ddr3_app_addr始终是8的倍数 ddr3_app_cmd <= 1; if (i == 2) ddr3_app_addr <= {
spi_data_out[27:4], 3'b0}; // 从收到的地址开始读 else ddr3_app_addr[26:3] <= ddr3_app_addr[26:3] + 1'b1; // 读后续地址 ddr3_app_en <= 1; i <= i + 1'b1; // DDR3内存容量为256MB // SPI端的地址位宽为[27:0], 范围为0~0xfffffff, 每个地址访问1字节内存 // DDR3端的地址位宽为[26:0], 范围为0~0x7ffffff, 每个地址访问2字节内存 // 所以, spi_data_out[27:4]对应ddr3_app_addr[26:3] // spi_data_out[3:1]对应ddr3_app_addr[2:0] end 3, 5: begin if (ddr3_app_rdy) // 读命令发送成功 ddr3_app_en <= 0; if (ddr3_app_rd_data_valid) begin // 数据读取成功 spi_data_in <= ddr3_app_rd_data; // 要发送的SPI数据 if (i == 3) spi_next_bits_cnt <= {
5'd16 - spi_data_out[3:0], 3'd0}; // 16-n字节 else spi_next_bits_cnt <= 128; // 16字节 spi_state <= SPI_REQUESTED_AGAIN; i <= 4; end end endcase end 8'h90: begin // 读ID号 if (i == 1) begin // 模仿W25Q256的ID号 spi_data_in <= 16'hef18; spi_next_bits_cnt <= 40; i <= 2; end spi_state <= SPI_REQUESTED_AGAIN; end 8'h9f: begin // 读JEDEC ID号 if (i == 1) begin spi_data_in <= 24'hef4019; spi_next_bits_cnt <= 24; i <= 2; end spi_state <= SPI_REQUESTED_AGAIN; end default: spi_state <= SPI_REQUESTED_AGAIN; // 无效命令 endcase end end SPI_REQUESTED_AGAIN: begin if (!spi_received) begin // 等待之前数据的received脉冲结束 if (spi_next_bits_cnt != 0) // 有新数据要发送和接收 spi_state <= SPI_REQUESTED; // 等待新一批数据出现received脉冲 else if (!spi_selected) // 没有新数据要发送和接收, 则等待主机拉高NSS spi_state <= SPI_IDLE; // 回到空闲模式 end end SPI_DATAERROR: begin // SPI未接收完指定数量的数据, 主机就把NSS拉高了 // 等待received和error信号结束后, 回到空闲模式 if (!spi_received && !spi_error) spi_state <= SPI_IDLE; end endcase end end wire _spi_miso = spi_miso; ila_0 ila_0( .clk(ddr3_ui_clk), .probe0(ddr3_ui_clk_sync_rst), .probe1(ddr3_app_addr), // [27:0] .probe2(ddr3_app_cmd[0]), .probe3(ddr3_app_en), .probe4(ddr3_app_wdf_data[15:0]), // [15:0] .probe5(ddr3_app_wdf_wren), .probe6(ddr3_app_wdf_mask), // [15:0] .probe7(ddr3_app_rd_data_valid), .probe8(ddr3_app_rd_data[15:0]), // [15:0] .probe9(ddr3_app_rdy), .probe10(ddr3_app_wdf_rdy), .probe11(spi_next_bits_cnt), // [15:0] .probe12(spi_received), .probe13(spi_error), .probe14(spi_remaining), // [15:0] .probe15(spi_state), // [2:0] .probe16(cmd), // [7:0] .probe17(i), // [3:0] .probe18(spi_nss), .probe19(spi_sck), .probe20(_spi_miso), .probe21(spi_mosi), .probe22(spi_data_in[127:96]), // [32:0] .probe23(spi_data_in[15:0]), // [15:0] .probe24(spi_data_out[15:0]), // [15:0] .probe25(spi_selected) ); endmodule

Reset.v:

module Reset #(        parameter CLK = 50000000,        parameter KEYTIME = 10 // ms    )(    input clock,    input key, // 高电平有效    output reg nrst = 0    );        localparam MAXCNT = KEYTIME * CLK / 1000 - 1;    integer counter = 5;    always @(posedge clock, posedge key) begin        if (key || counter == 0) begin            counter <= MAXCNT;            nrst <= !key;        end        else            counter <= counter - 1;    end    endmodule

SPISlave.v:

`define SPIS_MAXBIT (BITCNT - 1)module SPISlave #(    parameter BITCNT = 200    )(    input clock,    input nrst,    input nss,    input sck, // SPI时钟引脚 (上升沿采样数据)               // 若想要下降沿采样数据, 请在模块输入端用!或~符号取反    output miso,    input mosi,        input [`SPIS_MAXBIT:0] data_in,    output reg [`SPIS_MAXBIT:0] data_out,    input [15:0] next_bits_cnt, // 接下来要发送/接收多少位数据    output reg selected, // 缓冲后的NSS    output received, // 收到了指定长度的数据    output reg error, // 未收完指定长度的数据, NSS就拉高了    output reg [15:0] remaining    );        reg _miso;    reg [1:0] _nss;    reg [1:0] _sck; // 采用两级缓冲检测SCK边沿    reg data_out_reset;    reg [2:0] pulse;    assign miso = (!nss) ? _miso : 1'bz;    assign received = (pulse[1:0] != 0);    always @(posedge clock, negedge nrst) begin        if (!nrst) begin            selected <= 0;            error <= 0;            remaining <= 0;            _miso <= 0;            _nss <= 2'b11;            _sck <= 2'b11;            data_out_reset <= 1;            pulse <= 0;        end        else begin            // 芯片外输入信号可以放到赋值语句的右端            // 但如果要放到if语句的括号内, 则必须用寄存器缓冲一下            _nss <= {
_nss[0], nss}; case (_nss) 2'b00: selected <= 1; 2'b11: selected <= 0; endcase _sck <= {
_sck[0], selected ? sck : 1'b1}; if (pulse != 0) begin if (pulse == 1) error <= 0; pulse <= pulse - 1'b1; end else if (!selected) begin if ((!_sck[1] || remaining != 0) && !data_out_reset) begin // 若NSS出现上升沿时数据还没接收完, 则认为出错 // _sck和nss同时出现上升沿, 也认定为出错 error <= 1; pulse <= 3; end data_out_reset <= 1; end else begin case (_sck) 2'b00, 2'b10: begin // 下降沿改变数据 if (data_out_reset || remaining == 0) begin // data_out_reset==1: NSS下降沿产生后发送第一位数据, remaining==0: 数据已发送完毕 if (next_bits_cnt >= 1 && next_bits_cnt <= BITCNT) begin // 发送新一批数据 remaining <= next_bits_cnt; _miso <= data_in[next_bits_cnt - 1'b1]; end else begin // 无数据发送 remaining <= 0; _miso <= 0; end end else if (remaining != 0) _miso <= data_in[remaining - 1'b1]; // 继续发送数据 // NSS下降沿产生后, 将数据寄存器清零 if (data_out_reset) begin data_out_reset <= 0; data_out <= 0; end end 2'b01: begin // 上升沿采样数据 if (remaining != 0) begin data_out <= {
data_out[`SPIS_MAXBIT - 1:0], mosi}; if (remaining == 1) pulse <= 4; // 收到数据 remaining <= remaining - 1'b1; end end endcase end end end endmodule

pins.xdc:

set_property CFGBVS VCCO [current_design]set_property CONFIG_VOLTAGE 3.3 [current_design]set_property BITSTREAM.GENERAL.COMPRESS true [current_design]set_property BITSTREAM.CONFIG.CONFIGRATE 50 [current_design]set_property BITSTREAM.CONFIG.SPI_BUSWIDTH 4 [current_design]set_property BITSTREAM.CONFIG.SPI_FALL_EDGE Yes [current_design]set_property PACKAGE_PIN V4 [get_ports clock]set_property IOSTANDARD LVCMOS15 [get_ports clock]set_property PACKAGE_PIN R14 [get_ports {
keys[3]}]set_property PACKAGE_PIN P14 [get_ports {
keys[2]}]set_property PACKAGE_PIN N14 [get_ports {
keys[1]}]set_property PACKAGE_PIN N13 [get_ports {
keys[0]}]set_property PACKAGE_PIN D22 [get_ports {
leds[3]}]set_property PACKAGE_PIN E22 [get_ports {
leds[2]}]set_property PACKAGE_PIN D21 [get_ports {
leds[1]}]set_property PACKAGE_PIN E21 [get_ports {
leds[0]}]set_property IOSTANDARD LVCMOS33 [get_ports {
keys[3]}]set_property IOSTANDARD LVCMOS33 [get_ports {
keys[2]}]set_property IOSTANDARD LVCMOS33 [get_ports {
keys[1]}]set_property IOSTANDARD LVCMOS33 [get_ports {
keys[0]}]set_property IOSTANDARD LVCMOS33 [get_ports {
leds[3]}]set_property IOSTANDARD LVCMOS33 [get_ports {
leds[2]}]set_property IOSTANDARD LVCMOS33 [get_ports {
leds[1]}]set_property IOSTANDARD LVCMOS33 [get_ports {
leds[0]}]set_property PACKAGE_PIN H14 [get_ports spi_nss]set_property PACKAGE_PIN J14 [get_ports spi_sck]set_property PACKAGE_PIN L13 [get_ports spi_miso]set_property PACKAGE_PIN M13 [get_ports spi_mosi]set_property IOSTANDARD LVCMOS33 [get_ports spi_miso]set_property IOSTANDARD LVCMOS33 [get_ports spi_mosi]set_property IOSTANDARD LVCMOS33 [get_ports spi_nss]set_property IOSTANDARD LVCMOS33 [get_ports spi_sck]set_property C_CLK_INPUT_FREQ_HZ 300000000 [get_debug_cores dbg_hub]set_property C_ENABLE_CLK_DIVIDER false [get_debug_cores dbg_hub]set_property C_USER_SCAN_CHAIN 1 [get_debug_cores dbg_hub]connect_debug_port dbg_hub/clk [get_nets ddr3_ui_clk]

【STM32代码】

main.c:

#include 
#include
#include
#include
#include
#include
#include "common.h"#include "W25Qxx.h"USBD_HandleTypeDef husbd;static void usb_init(void){ USBD_Init(&husbd, &Class_Desc, 0); USBD_RegisterClass(&husbd, USBD_MSC_CLASS); USBD_MSC_RegisterStorage(&husbd, &USBD_MSC_Template_fops); USBD_Start(&husbd);}int main(void){ HAL_Init(); clock_init(); usart_init(921600); printf("STM32F103C8 USB\n"); printf("SystemCoreClock=%u\n", SystemCoreClock); W25Qxx_Init(); usb_init(); while (1) { }}

W25Qxx.c:

#include 
#include
#include "W25Qxx.h"#define CS_0 HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET)#define CS_1 HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET)static void W25Qxx_EnableWrite(void);static void W25Qxx_Enter4ByteMode(void);static uint8_t W25Qxx_Send(uint8_t data);static void W25Qxx_SendAddress(uint32_t addr);SPI_HandleTypeDef hspi1;static void W25Qxx_EnableWrite(void){
CS_0; W25Qxx_Send(0x06); CS_1;}static void W25Qxx_Enter4ByteMode(void){
CS_0; W25Qxx_Send(0xb7); CS_1;}void W25Qxx_EraseSector(uint16_t sector){
W25Qxx_EnableWrite(); CS_0; W25Qxx_Send(0x20); W25Qxx_SendAddress(sector << 12); CS_1; while (W25Qxx_ReadStatus() & W25Qxx_STATUS_BUSY);}void W25Qxx_Init(void){
uint8_t m; uint16_t id; GPIO_InitTypeDef gpio; __HAL_RCC_GPIOA_CLK_ENABLE(); __HAL_RCC_SPI1_CLK_ENABLE(); CS_1; gpio.Mode = GPIO_MODE_OUTPUT_PP; gpio.Pin = GPIO_PIN_4; gpio.Speed = GPIO_SPEED_FREQ_HIGH; HAL_GPIO_Init(GPIOA, &gpio); gpio.Mode = GPIO_MODE_AF_PP; gpio.Pin = GPIO_PIN_5 | GPIO_PIN_7; gpio.Speed = GPIO_SPEED_FREQ_HIGH; HAL_GPIO_Init(GPIOA, &gpio); hspi1.Instance = SPI1; hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_16; hspi1.Init.Mode = SPI_MODE_MASTER; hspi1.Init.NSS = SPI_NSS_SOFT; HAL_SPI_Init(&hspi1); W25Qxx_Enter4ByteMode(); id = W25Qxx_ReadID(&m); printf("SPI Flash: M=0x%02x, ID=0x%02x\n", m, id); id = W25Qxx_ReadJEDECID(&m); printf("SPI Flash: M=0x%02x, ID=0x%04x\n", m, id);}void W25Qxx_ProgramPage(uint32_t addr, const void *data, int len){
W25Qxx_EnableWrite(); CS_0; W25Qxx_Send(0x02); W25Qxx_SendAddress(addr); HAL_SPI_Transmit(&hspi1, (uint8_t *)data, len, HAL_MAX_DELAY); CS_1; while (W25Qxx_ReadStatus() & W25Qxx_STATUS_BUSY);}void W25Qxx_Read(uint32_t addr, void *data, int len){
CS_0; W25Qxx_Send(0x03); W25Qxx_SendAddress(addr); HAL_SPI_Receive(&hspi1, data, len, HAL_MAX_DELAY); CS_1;}uint8_t W25Qxx_ReadID(uint8_t *pm){
uint8_t m, id; CS_0; W25Qxx_Send(0x90); W25Qxx_Send(0x00); W25Qxx_Send(0x00); W25Qxx_Send(0x00); m = W25Qxx_Send(0x00); if (pm != NULL) *pm = m; id = W25Qxx_Send(0x00); CS_1; return id;}uint16_t W25Qxx_ReadJEDECID(uint8_t *pm){
uint8_t m; uint16_t id; CS_0; W25Qxx_Send(0x9f); m = W25Qxx_Send(0x00); if (pm != NULL) *pm = m; id = W25Qxx_Send(0x00) << 8; id |= W25Qxx_Send(0x00); CS_1; return id;}uint8_t W25Qxx_ReadStatus(void){
uint8_t status; CS_0; W25Qxx_Send(0x05); status = W25Qxx_Send(0x00); CS_1; return status;}static uint8_t W25Qxx_Send(uint8_t data){
HAL_SPI_TransmitReceive(&hspi1, &data, &data, 1, HAL_MAX_DELAY); return data;}static void W25Qxx_SendAddress(uint32_t addr){
W25Qxx_Send((addr >> 24) & 0xff); W25Qxx_Send((addr >> 16) & 0xff); W25Qxx_Send((addr >> 8) & 0xff); W25Qxx_Send(addr & 0xff);}

usbd_msc_storage.c:

/**  ******************************************************************************  * @file    usbd_msc_storage_template.c  * @author  MCD Application Team  * @brief   Memory management layer  ******************************************************************************  * @attention  *  * 

© Copyright (c) 2015 STMicroelectronics. * All rights reserved.

* * This software component is licensed by ST under Ultimate Liberty license * SLA0044, the "License"; You may not use this file except in compliance with * the License. You may obtain a copy of the License at: * www.st.com/SLA0044 * ****************************************************************************** *//* BSPDependencies- "stm32xxxxx_{eval}{discovery}{nucleo_144}.c"- "stm32xxxxx_{eval}{discovery}_io.c"- "stm32xxxxx_{eval}{discovery}{adafruit}_sd.c"EndBSPDependencies *//* Includes ------------------------------------------------------------------*/#include "usbd_msc_storage.h"#include "../W25Qxx.h"/* Private typedef -----------------------------------------------------------*//* Private define ------------------------------------------------------------*//* Private macro -------------------------------------------------------------*//* Private variables ---------------------------------------------------------*//* Private function prototypes -----------------------------------------------*//* Extern function prototypes ------------------------------------------------*//* Private functions ---------------------------------------------------------*/#define STORAGE_LUN_NBR 1U#define STORAGE_BLK_NBR 65536#define STORAGE_BLK_SIZ 4096int8_t STORAGE_Init(uint8_t lun);int8_t STORAGE_GetCapacity(uint8_t lun, uint32_t *block_num, uint16_t *block_size);int8_t STORAGE_IsReady(uint8_t lun);int8_t STORAGE_IsWriteProtected(uint8_t lun);int8_t STORAGE_Read(uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint16_t blk_len);int8_t STORAGE_Write(uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint16_t blk_len);int8_t STORAGE_GetMaxLun(void);/* USB Mass storage Standard Inquiry Data */int8_t STORAGE_Inquirydata[] = /* 36 */{
/* LUN 0 */ 0x00, 0x80, 0x02, 0x02, (STANDARD_INQUIRY_DATA_LEN - 5), 0x00, 0x00, 0x00, 'S', 'T', 'M', ' ', ' ', ' ', ' ', ' ', /* Manufacturer : 8 bytes */ 'P', 'r', 'o', 'd', 'u', 'c', 't', ' ', /* Product : 16 Bytes */ ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '0', '.', '0', '1', /* Version : 4 Bytes */};USBD_StorageTypeDef USBD_MSC_Template_fops ={
STORAGE_Init, STORAGE_GetCapacity, STORAGE_IsReady, STORAGE_IsWriteProtected, STORAGE_Read, STORAGE_Write, STORAGE_GetMaxLun, STORAGE_Inquirydata,};/******************************************************************************** Function Name : Read_Memory* Description : Handle the Read operation from the microSD card.* Input : None.* Output : None.* Return : None.*******************************************************************************/int8_t STORAGE_Init(uint8_t lun){
return (0);}/******************************************************************************** Function Name : Read_Memory* Description : Handle the Read operation from the STORAGE card.* Input : None.* Output : None.* Return : None.*******************************************************************************/int8_t STORAGE_GetCapacity(uint8_t lun, uint32_t *block_num, uint16_t *block_size){
*block_num = STORAGE_BLK_NBR; *block_size = STORAGE_BLK_SIZ; return (0);}/******************************************************************************** Function Name : Read_Memory* Description : Handle the Read operation from the STORAGE card.* Input : None.* Output : None.* Return : None.*******************************************************************************/int8_t STORAGE_IsReady(uint8_t lun){
return (0);}/******************************************************************************** Function Name : Read_Memory* Description : Handle the Read operation from the STORAGE card.* Input : None.* Output : None.* Return : None.*******************************************************************************/int8_t STORAGE_IsWriteProtected(uint8_t lun){
return 0;}/******************************************************************************** Function Name : Read_Memory* Description : Handle the Read operation from the STORAGE card.* Input : None.* Output : None.* Return : None.*******************************************************************************/int8_t STORAGE_Read(uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint16_t blk_len){
W25Qxx_Read(blk_addr * STORAGE_BLK_SIZ, buf, blk_len * STORAGE_BLK_SIZ); return 0;}/******************************************************************************** Function Name : Write_Memory* Description : Handle the Write operation to the STORAGE card.* Input : None.* Output : None.* Return : None.*******************************************************************************/int8_t STORAGE_Write(uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint16_t blk_len){
int i; uint32_t addr = blk_addr * STORAGE_BLK_SIZ; printf("W%d,%d\n", blk_addr, blk_len); while (blk_len--) {
W25Qxx_EraseSector(blk_addr); blk_addr++; for (i = 0; i < 16; i++) {
W25Qxx_ProgramPage(addr, buf, 256); addr += 256; buf += 256; } } return (0);}/******************************************************************************** Function Name : Write_Memory* Description : Handle the Write operation to the STORAGE card.* Input : None.* Output : None.* Return : None.*******************************************************************************/int8_t STORAGE_GetMaxLun(void){
return (STORAGE_LUN_NBR - 1);}/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

转载地址:https://blog.csdn.net/ZLK1214/article/details/111416038 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:Xilinx Vitis 2020.1修改工程占用的BRAM内存大小的方法
下一篇:Xilinx Vitis 2020.1无法创建SREC SPI Bootloader工程,提示缺少xilisf库的解决办法

发表评论

最新留言

哈哈,博客排版真的漂亮呢~
[***.90.31.176]2024年04月08日 03时59分17秒