本文以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****/