FPGA-SPI通信之FLASH擦除

1、前言

此博客只为了记录自己的FPGA的学习过程,不完全正确,仅供参考!!!

2、SPI协议的介绍

        SPI是串行外设接口(Serial Peripheral Interface)的缩写,是一种高速的,全双工,同步的通信总线,并且在芯片的管脚上只占用四根线,节约了芯片的管脚,同时为PCB的布局上节省空间,提供方便。SPI的四根线是指cs(片选信号)、clk(时钟信号)、mosi(主出从入)、miso(主入从出),因为SPI通信是同步通讯协议,所以这边的是时钟信号是由主机产生,通过时钟线传输给从机,在通信时可以通过拉低片选引脚来选中相应的外设,所以SPI总线上可以接上不同的外设,通过拉低片选引脚进行片选。

在使用SPI通的时候,我们需要注意SPI通信有四种工作模式,我们通过设定CPOL和CPHA来选择工作模式

        CPOL:时钟极性,决定时钟信号空闲时的电平状态。

        CPHA:时钟相位,决定是奇数沿还是偶数沿进行采样,如果奇数沿进行采样,那么就是偶数沿进行数据更新。

这边两者共同决定工作模式,如果时钟空闲状态是低电平,那么时钟的第一个上升沿也就是奇数沿就是上升沿,那么数据会在上升沿进行采样,这种就是模式0.最常用的就是模式0和模式3,这边选用的就是模式0.

3、片区擦除的介绍及工作时序

        flash的擦除可分为全部擦除和部分擦除,前者比后者更为简单,不需要传输地址给flash,只需要将全擦除指令写进flash芯片即可,但后者不仅需要写入擦除指令之外还需要写入扇区地址、页地址、字节地址,但两者的工作时序是相同的,所以这边可以放在一起学习一下,只要会了部分扇区的擦除地址,那么全擦除你肯定会了,那么我们先来看一下两种的工作时序。

全擦除时序

 部分擦除时序

        从上图我们可以看到全擦除和部分扇区的擦除区别就在于在指令写入之后,需要继续写入擦除的地址,这个地址是24位的,分别为8位扇区地址,8位页地址,8位字节地址。

 时间参数表

        那么我们需要对上面的时序进行一个理解,因为fpga的程序就是严格按照时序来进行的,SPI通信的时候时钟我们可以从上面的表格可以得到,那么上图中读操作的最大时钟频率为20MHZ,进行其他操作的最大时钟频率为50MHZ,为了方便,我们这边定义下所有的时钟频率都不超过20MHZ,那么就选定时钟评率为12.5MHZ,FPGA系统的时钟频率为50MHZ,所以我们进行操作时,只需要进行一个四分频的操作就可以了。所以我们肯定需要提供一个时钟首先进行第一步就是拉低片选信号,,然后我们需要等待一个Tslch的时间(Tsclh的最小时间是5ns),然后写入写使能命令,写使能命令写入时,最后一个bit数据写入到片选信号拉高需要保持一个Tchsh时间(Tchsh时间最小位5ns),然后需要进行一个延时Tshsl时间(最小为100ns)。因为SPI的通信时钟选择为12.5MHZ,时钟周期为80ns,这边传输一个字节需要640ns,那就可以用这个640ns来代替之前的所有的时间,这样代码写起来也比较方便。那么在写代码之前我们需要知道几个操作的指令,写使能指令(0000_0110)、全擦除指令BE(1100_0111)、部分擦除指令SE(1101_1000).

4、波形图绘制

        越到稍微大一点的工程,越感觉到波形图的重要,因为参数很多,很多的计数器,如果没有一个流程图,那么代码有时真的是非常困难去编写,光靠大脑的逻辑去写真的很容易出错,所以这边劝大家一定要去画。

        

         我这边放的SE的波形图,状态SE的中包括四个数据的写入,第一个是SE指令,然后加上3个字节的地址写入,加上一个Tslch时间和Tchsh时间。正好对应5x640ns,这些都是根据时序图来进行操作的。

5、代码

        

module  spi_flash_se
(
    input   wire    sys_clk,
    input   wire    sys_rst_n,
    input   wire    key_flag,
    output  reg     spi_clk,
    output  reg     spi_csn,
    output  reg     spi_mosi
);

parameter   IDLE    = 4'b0001,
            WREN    = 4'B0010,
            DELAY   = 4'b0100,
            SE      = 4'b1000;
parameter   wren_data = 8'b0000_0110,
            se_data   = 8'b1101_1000,
            s_addr    = 8'b0000_0000,
            p_addr    = 8'b0000_0100,
            b_addr    = 8'b0010_0101;
            
reg [3:0]   state;
reg [4:0]   time_cnt;
reg [3:0]   bite_cnt;
reg [2:0]   bit_cnt;
reg [1:0]   clk_cnt;

//状态机
always@(posedge sys_clk)
begin
    if(sys_rst_n ==1'b0)
        state <= IDLE;
    else
        case(state)
        IDLE:   if(key_flag == 1'b1)
                    state <= WREN;
        WREN:   if((bite_cnt == 4'd3)&&(bit_cnt == 5'd31))
                    state <= DELAY;
        DELAY:  if((bite_cnt == 4'd4)&&(bit_cnt == 5'd31))
                    state <= SE;
        SE:     if((bite_cnt == 4'd10)&&(bit_cnt == 5'd31))
                    state <= IDLE;
           default: state <= IDLE;
        endcase
end


always@(posedge sys_clk)
begin
    if(sys_rst_n == 1'b0)
        time_cnt <= 5'd0;
    else if((state = IDLE)&&(time_cnt == 5'd31))
        time_cnt <= 5'd0;
    else
        time_cnt <= time_cnt + 5'd1;
end

always@(posedge sys_clk)
begin  
    if(sys_rst_n == 1'b0)
        bite_cnt <= 4'd0;
    else if(bite_cnt == 4'd10)&&(time_cnt == 5'd31))
        bite_cnt <= 4'd0;
    else if(time_cnt == 5'd31)
        bite_cnt <= bite_cnt + 5'd1;
end

always@(posedge sys_clk)
begin   
    if(sys_rst_n == 1'b0)
        clk_cnt <= 2'd0;
    else if(((bite_cnt != 4'd2)&&(bite_cnt >= 4'd3)&&(bite_cnt <= 4'd5)&&(state == IDLE)&&(bite_cnt == 2'd3))
        clk_cnt <= 2'd0;
    else    
        clk_cnt <= clk_cnt + 2'd1;
end

always@(posedge sys_clk)
begin
    if(sys_rst_n == 1'b0)
        bit_cnt <= 3'd0;
    else if(clk_cnt == 2'd1)
        bit_cnt <= bit_cnt + 3'd1;
    else
        bit_cnt <= bit_cnt;
end
     
always@(posedge sys_clk)     
begin
    if(sys_rst_n == 1'b0)
        spi_clk <= 1'b0;
    else if(clk_cnt == 2'b0)
        spi_clk <= 1'b0;
    else if(clk_cnt == 2'b2)
        spi_clk <= 1'b1;
    else 
        spi_clk <= spi_clk;
end

always@(posedge sys_clk)
begin
    if(sys_rst_n == 1'b0)
        spi_csn <=1'b1;
    else if((key_flag == 1'b1)||((bite_cnt == 4'd4)&&(time_cnt == 5'd31)))
        spi_csn <= 1'b0;
    else if(((bite_cnt == 4'd3)&&(time_cnt == 5'd31))||((bite_cnt == 4'd10)&&(time_cnt == 5'd31)))
        spi_csn <= 1'b1;
    else 
        spi_csn <= spi_csn;
end

always@(posedge sys_clk)
begin
    if(sys_rst_n == 1'b0)
        spi_mosi <= 1'b0;
    else if((bite_cnt == 4'd2)&&(time_cnt == 5'd31))
        spi_mosi <= wren_data[7 - bit_cnt];
    else if((bite_cnt == 4'd5)&&(time_cnt == 5'd31))
        spi_mosi <= se_data[7 - bite_cnt];
    else if((bite_cnt == 4'd6)&&(time_cnt == 5'd31))
        spi_mosi <= s_addr[7 - bite_cnt];   
    else if((bite_cnt == 4'd7)&&(time_cnt == 5'd31))
        spi_mosi <= p_addr[7 - bite_cnt];
    else if((bite_cnt == 4'd8)&&(time_cnt == 5'd31))
        spi_mosi <= b_addr[7 - bite_cnt];
    else
        spi_mosi <= 1'b0;
end
end module  
        

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值