fpga的学习:基于spi协议的flash驱动写操作控制(代码片段)

石小舟 石小舟     2023-04-01     293

关键词:

系统整体框图如图:

顶层模块的设计:

flash页写模块的设计:

时序图:

页写的时序:

各个代码块如下:

`timescale  1ns/1ns
module  key_filter
#(
    parameter CNT_MAX = 20'd999_999 //计数器计数最大值
)
(
    input   wire    sys_clk     ,   //系统时钟50Mhz
    input   wire    sys_rst_n   ,   //全局复位
    input   wire    key_in      ,   //按键输入信号

    output  reg     key_flag        //key_flag为1时表示消抖后检测到按键被按下
                                    //key_flag为0时表示没有检测到按键被按下
);

//********************************************************************//
//****************** Parameter and Internal Signal *******************//
//********************************************************************//
//reg   define
reg     [19:0]  cnt_20ms    ;   //计数器

//********************************************************************//
//***************************** Main Code ****************************//
//********************************************************************//

//cnt_20ms:如果时钟的上升沿检测到外部按键输入的值为低电平时,计数器开始计数
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cnt_20ms <= 20'b0;
    else    if(key_in == 1'b1)
        cnt_20ms <= 20'b0;
    else    if(cnt_20ms == CNT_MAX && key_in == 1'b0)
        cnt_20ms <= cnt_20ms;
    else
        cnt_20ms <= cnt_20ms + 1'b1;

//key_flag:当计数满20ms后产生按键有效标志位
//且key_flag在999_999时拉高,维持一个时钟的高电平
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        key_flag <= 1'b0;
    else    if(cnt_20ms == CNT_MAX - 1'b1)
        key_flag <= 1'b1;
    else
        key_flag <= 1'b0;

endmodule
`timescale  1ns/1ns
module  spi_flash_pp
(
    input   wire    sys_clk     ,   //系统时钟,频率50MHz
    input   wire    sys_rst_n   ,   //复位信号,低电平有效
    input   wire    pi_key      ,   //按键输入信号

    output  wire    cs_n        ,   //片选信号
    output  wire    sck         ,   //串行时钟
    output  wire    mosi            //主输出从输入数据
);

//********************************************************************//
//****************** Parameter and Internal Signal *******************//
//********************************************************************//
//parameter define
parameter   CNT_MAX =   20'd999_999;    //计数器计数最大值

//wire  define
wire    po_key  ;

//********************************************************************//
//*************************** Instantiation **************************//
//********************************************************************//
//------------- key_filter_inst -------------
key_filter
#(
    .CNT_MAX    (CNT_MAX    )   //计数器计数最大值
)
key_filter_inst
(
    .sys_clk    (sys_clk    ),  //系统时钟,频率50MHz
    .sys_rst_n  (sys_rst_n  ),  //复位信号,低电平有效
    .key_in     (pi_key     ),  //按键输入信号

    .key_flag   (po_key     )   //消抖后信号
);

//------------- flash_pp_ctrl_inst -------------
flash_pp_ctrl  flash_pp_ctrl_inst
(
    .sys_clk    (sys_clk    ),  //系统时钟,频率50MHz
    .sys_rst_n  (sys_rst_n  ),  //复位信号,低电平有效
    .key        (po_key     ),  //按键输入信号
                                
    .sck        (sck        ),  //片选信号
    .cs_n       (cs_n       ),  //串行时钟
    .mosi       (mosi       )   //主输出从输入数据
);

endmodule
`timescale  1ns/1ns
module  flash_pp_ctrl(

    input   wire            sys_clk     ,   //系统时钟,频率50MHz
    input   wire            sys_rst_n   ,   //复位信号,低电平有效
    input   wire            key         ,   //按键输入信号

    output  reg             cs_n        ,   //片选信号
    output  reg             sck         ,   //串行时钟
    output  reg             mosi            //主输出从输入数据

);

//********************************************************************//
//****************** Parameter and Internal Signal *******************//
//********************************************************************//

//parameter define
parameter   IDLE    =   4'b0001 ,   //初始状态
            WR_EN   =   4'b0010 ,   //写状态
            DELAY   =   4'b0100 ,   //等待状态
            PP      =   4'b1000 ;   //页写状态
parameter   WR_EN_INST      =   8'b0000_0110,   //写使能指令
            PP_INST         =   8'b0000_0010;   //页写指令
parameter   SECTOR_ADDR     =   8'b0000_0000,   //扇区地址
            PAGE_ADDR       =   8'b0000_0100,   //页地址
            BYTE_ADDR       =   8'b0010_0101;   //字节地址
parameter   NUM_DATA        =   8'd100      ;   //页写数据个数(0-99)

//reg   define
reg     [7:0]   cnt_byte        ;   //字节计数器
reg     [3:0]   state           ;   //状态机状态
reg     [4:0]   cnt_clk         ;   //系统时钟计数器
reg     [1:0]   cnt_sck         ;   //串行时钟计数器
reg     [2:0]   cnt_bit         ;   //比特计数器
reg     [7:0]   data            ;   //页写入数据

//********************************************************************//
//***************************** Main Code ****************************//
//********************************************************************//

//cnt_clk:系统时钟计数器,用以记录单个字节
always@(posedge sys_clk or  negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cnt_clk  <=  5'd0;
    else    if(state != IDLE)
        cnt_clk  <=  cnt_clk + 1'b1;

//cnt_byte:记录输出字节个数和等待时间
always@(posedge sys_clk or  negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cnt_byte    <=  8'd0;
    else    if((cnt_clk == 5'd31) && (cnt_byte == NUM_DATA + 8'd9))
        cnt_byte    <=  8'd0;
    else    if(cnt_clk == 5'd31)
        cnt_byte    <=  cnt_byte + 1'b1;

//cnt_sck:串行时钟计数器,用以生成串行时钟
always@(posedge sys_clk or  negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cnt_sck <=  2'd0;
    else    if((state == WR_EN) && (cnt_byte == 8'd1))
        cnt_sck <=  cnt_sck + 1'b1;
    else    if((state == PP) && (cnt_byte >= 8'd5)
                && (cnt_byte <= NUM_DATA + 8'd9 - 1'b1))
        cnt_sck <=  cnt_sck + 1'b1;

//cs_n:片选信号
always@(posedge sys_clk or  negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cs_n    <=  1'b1;
    else    if(key == 1'b1)
        cs_n    <=  1'b0;
    else    if((cnt_byte == 8'd2) && (cnt_clk == 5'd31) && (state == WR_EN))
        cs_n    <=  1'b1;
    else    if((cnt_byte == 8'd3) && (cnt_clk == 5'd31) && (state == DELAY))
        cs_n    <=  1'b0;
    else    if((cnt_byte == NUM_DATA + 8'd9) && (cnt_clk == 5'd31) && (state == PP))
        cs_n    <=  1'b1;

//sck:输出串行时钟
always@(posedge sys_clk or  negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        sck <=  1'b0;
    else    if(cnt_sck == 2'd0)
        sck <=  1'b0;
    else    if(cnt_sck == 2'd2)
        sck <=  1'b1;

//cnt_bit:高低位对调,控制mosi输出
always@(posedge sys_clk or  negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cnt_bit <=  3'd0;
    else    if(cnt_sck == 2'd2)
        cnt_bit <=  cnt_bit + 1'b1;

//data:页写入数据
always@(posedge sys_clk or  negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        data <=  8'd0;
    else    if((cnt_clk == 5'd31) && ((cnt_byte >= 8'd9)
                && (cnt_byte < NUM_DATA + 8'd9 - 1'b1)))
        data <=  data + 1'b1;

//state:两段式状态机第一段,状态跳转
always@(posedge sys_clk or  negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        state   <=  IDLE;
    else
    case(state)
        IDLE:   if(key == 1'b1)
                state   <=  WR_EN;
        WR_EN:  if((cnt_byte == 8'd2) && (cnt_clk == 5'd31))
                state   <=  DELAY;
        DELAY:  if((cnt_byte == 8'd3) && (cnt_clk == 5'd31))
                state   <=  PP;
        PP:     if((cnt_byte == NUM_DATA + 8'd9) && (cnt_clk == 5'd31))
                state   <=  IDLE;
        default:    state   <=  IDLE;
    endcase

//mosi:两段式状态机第二段,逻辑输出
always@(posedge sys_clk or  negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        mosi    <=  1'b0;
    else    if((state == WR_EN) && (cnt_byte== 8'd2))
        mosi    <=  1'b0;
    else    if((state == PP) && (cnt_byte == NUM_DATA + 8'd9))
        mosi    <=  1'b0;
    else    if((state == WR_EN) && (cnt_byte == 8'd1) && (cnt_sck == 5'd0))
        mosi    <=  WR_EN_INST[7 - cnt_bit];  //写使能指令
    else    if((state == PP) && (cnt_byte == 8'd5) && (cnt_sck == 5'd0))
        mosi    <=  PP_INST[7 - cnt_bit];    //页写指令
    else    if((state == PP) && (cnt_byte == 8'd6) && (cnt_sck == 5'd0))
        mosi    <=  SECTOR_ADDR[7 - cnt_bit];  //扇区地址
    else    if((state == PP) && (cnt_byte == 8'd7) && (cnt_sck == 5'd0))
        mosi    <=  PAGE_ADDR[7 - cnt_bit];    //页地址
    else    if((state == PP) && (cnt_byte == 8'd8) && (cnt_sck == 5'd0))
        mosi    <=  BYTE_ADDR[7 - cnt_bit];    //字节地址
    else    if((state == PP) && ((cnt_byte >= 8'd9)
                && (cnt_byte <= NUM_DATA + 8'd9 - 1'b1)) && (cnt_sck == 5'd0))
        mosi    <=  data[7 - cnt_bit];  //页写入数据

endmodule

flash学习笔记

    最近升级FPGA程序遇到校验失败的问题,进一步接触了flash,升级的过程是上位机软件将hex文件通过usb发到RK3188,RK3188通过串口一帧一帧(每帧1kb的数据)地将数据发到fpga,由fpga最终将程序通过SPI写到flash中。升... 查看详情

基于fpga的spi协议接口的verilog设计(代码片段)

1.简介与仿真结论    SPI是一种三线同步接口,分别为同步时钟信号、数据输入信号和数据输出信号。另外每个扩展芯片还需要一个片选信号,主器件通过片选信号选通与其通信的从器件。它允许处理器与各种外围设备... 查看详情

fpga教程案例81接口案例1——基于fpga的spi接口实现

FPGA教程目录MATLAB教程目录---------------------------------------------------------------------------------------目录1.软件版本2.SPI原理概述3.SPI的FPGA实现  查看详情

fpga作为从机与stm32进行spi协议通信---verilog实现

一.SPI协议简要介绍SPI,是英语Serial Peripheral Interface的缩写,顾名思义就是串行外围设备接口。SPI,是一种高速的,全双工,同步的通信总线,并且在芯片的管脚上只占用四根线,节约了芯片的管脚,同时为PCB的布局上... 查看详情

spi—读写串行flash

SPI协议简介SPI协议是由摩托罗拉公司提出的通讯协议(SerialPeripheralInterface),即串行外围设备接口,是一种高速全双工的通信总线。它被广泛地使用在ADC、LCD等设备与MCU间,要求通讯速率较高的场合。  ?标号1处,NSS信号线... 查看详情

fpga/数字ic手撕代码11——基于pwm驱动的蜂鸣器verilog开发

深度学习/机器视觉/数字IC/FPGA/算法手撕代码目录总汇目录基于PWM驱动的蜂鸣器verilog开发1.程序2.测试3.仿真结果4.分析 查看详情

基于fpga的以太网tcp/ip协议实现过程记录

基于FPGA的以太网的TCP/IP协议的学习笔记一、完整的以太网数据部分包括以下几部分:前导码帧起始界定符以太网帧头IP首部UDP首部UDP数据(有效数据)CRC校验字节二、针对每一部分的格式如下:MAC数据包格式:... 查看详情

基于fpga的以太网tcp/ip协议实现过程记录

基于FPGA的以太网的TCP/IP协议的学习笔记一、完整的以太网数据部分包括以下几部分:前导码帧起始界定符以太网帧头IP首部UDP首部UDP数据(有效数据)CRC校验字节二、针对每一部分的格式如下:MAC数据包格式:... 查看详情

fpga--spi通信(代码片段)

...控制,没有应答机制确认是否接收到数据,所以跟IIC总线协议比较在数据可靠性上有一定的缺陷。4、特点1):高速、同步、全双工 查看详情

zynq之fpga学习----iic协议驱动模块仿真实验(代码片段)

1IIC通信协议简介IIC通信协议基础知识学习:硬件设计基础----通信协议IIC2实验任务设计IIC驱动模块,并进行仿真验证,观察仿真波形3实验设计3.1创建工程新建工程,操作如图所示:输入工程名和路径,如... 查看详情

spi通信协议(spi总线)学习

1、什么是SPI?SPI是串行外设接口(SerialPeripheralInterface)的缩写。是Motorola公司推出的一 种同步串行接口技术,是一种高速的,全双工,同步的通信总线。2、SPI优点支持全双工通信通信简单数据传输速率块3、缺点没有指定的流... 查看详情

基于fpga驱动vga显示图片的小问题

     学习VGA显示图片的过程中,遇到了一个小问题,我在显示屏上开了一个60x60的框,放了一张图片进去显示,但是最终的结果如下图所示。  出现了一个竖黑边,看了看代码,分析了一下逻辑没问题,然而... 查看详情

用于非基于 I2C/SPI 的传感器的 Linux 内核驱动程序(用于模拟传感器)

...【发布时间】:2019-12-1222:31:57【问题描述】:我最近开始学习IIO子系统,现在对IIO子系统中基于SPI/I2C的传感器非常熟悉(也使用RegmapAPI)。不过,今天我遇到了一个电位器传感器:http://w 查看详情

固件远程更新之startupe2原语(fpga控制flash)

...CCLK_0连接,当使用远程更新时,首先fpga内部有控制flash的驱动(即逻辑控制flash时序),当然flash时钟也需要控制了,但这时时钟管脚已经连接到CCLK_0,那该如何操作啊,你直接约束分配管脚试试,是通不过的,这时STARTUPE2就派... 查看详情

spimaster接口的fpga实现

...serialperipheralinterface串行外围接口大致了解:spi是个同步协议,数据在master和slaver间交换通过时钟sck,由于它是同步协议,时钟速率就可以各种变换。sck:主机提供,从机不能操控,从器件由主机产生的时钟控制。数据只有在sck 查看详情

spimaster接口的fpga实现

...serialperipheralinterface串行外围接口大致了解:spi是个同步协议,数据在master和slaver间交换通过时钟sck,由于它是同步协议,时钟速率就可以各种变换。sck:主机提供,从机不能操控,从器件由主机产生的时钟控制。数据只有在sck 查看详情

基于basys2驱动lcdqc12864b的verilog设计图片显示

...动LCD显示,确实用单片机驱动是要简单不少,记得在FPGA学习交流群里问问题的时候,被前辈指教,说给我最好的指教便是别在玩这个了,多看看关于FPGA方面的书籍,比做这个单片机做的东西价值强多了。现在想来确实,自从学... 查看详情

stm32学习笔记(15)——spi协议(代码片段)

STM32学习笔记(15)——SPI协议一、SPI协议简介1.物理层2.协议层(1)通讯的开始与停止(2)时钟极性CPOL、时钟相位CPHA二、STM32的SPI外设1.通讯引脚2.时钟控制逻辑3.数据控制逻辑4.整体控制逻辑5.STM32的SPI通... 查看详情