赞
踩
本文以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的倍数。
程序下载地址:https://pan.baidu.com/s/1Ck8NcoT4W-Vx2ggXRKfVRw (提取码:bix1 )
程序的运行效果如下:
(1)往DDR3磁盘里面复制文件
(2)复制的文件装满整个DDR3磁盘
(3)格式化DDR3磁盘为FAT文件系统
(4)ILA抓包分析SPI从机信号
【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 <stdio.h> #include <stm32f1xx.h> #include <usbd_core.h> #include <usbd_desc.h> #include <usbd_msc.h> #include <usbd_msc_storage.h> #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 <stdio.h> #include <stm32f1xx.h> #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 * * <h2><center>© Copyright (c) 2015 STMicroelectronics. * All rights reserved.</center></h2> * * 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 4096 int8_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****/
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。