【程序】Xilinx FPGA将DDR3内存转换为W25Q256的SPI接口,供STM32单片机调用,产生容量为256MB的USB U盘设备

40 篇文章 47 订阅

本文以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从机信号
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 <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>&copy; 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****/


  • 8
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

巨大八爪鱼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值