Verilog: 按键控制基于SPI协议M25P16读写操作系统设计(二)spi 接口模块

1、模块概述

      SPI接口模块用于连接flash芯片(从机)与FPGA(主机),实现主从机之间的串口通信。

考虑到分别于主机和从机连接所以端口可分为两部:

(1)与flash_control模块连接端口(主机部分)

input/outputnamedescription
inputreq读写请求信号
inputdin[7:0]输入的命令、地址、写入数据等
outputdone一字节(8bit)传输完成标志
outputdout[7:0]

接收m25p16反馈数据后发送回主机(串转并

 (2)与m25p16连接端口(从机部分)

input/outputnamedescription
input

miso

m25p16反馈的串行数据
output

cs_n

片选信号(低有效),拉低时才能进行读写等操作
output

mosi

输入的命令、地址、写入数据等(并转串
output

sclk

主机产生时钟信号(16分频)

2. 主要完成任务

(1)产生时钟分频驱动主从设备

      由上片文章可知,需要20MHz及以下时钟频率才能同时满足读写操作,且m25p16支持SPI工作模式0和3。

      本设计采用16分频和SPI工作模式3,使用计数器实现分频代码如下:

//****************************************************************
//--cnt_freq
//****************************************************************
    always @(posedge clk or negedge rst_n)begin 
       if(!rst_n)begin
            cnt_freq <= 5'd0;
        end 
        else if(add_cnt_freq)begin 
            if(end_cnt_freq)begin 
                cnt_freq <= 5'd0;
            end
            else begin 
                cnt_freq <= cnt_freq + 1'b1;
            end 
        end
    end 
    
    assign add_cnt_freq = req;
    assign end_cnt_freq = add_cnt_freq && cnt_freq == `FREQUENCY;

//****************************************************************
//--sclk_reg 采用SPI通信模式3  下降沿发送,上升沿采样
//****************************************************************
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            sclk_reg <= 1'b1;                 //初始高电平
        end 
        else if(cnt_freq == `SCLK_FALL)begin 
            sclk_reg <= 1'b0;                 //flash m25p16 下降沿 数据改变
        end 
        else if(cnt_freq == `SCLK_RISE)begin 
            sclk_reg <= 1'b1;                 //flash m25p16 上升沿 数据稳定
        end 
    end

(二)并串互转

a. 将主机发送的并行数据(指令、地址、写入数据等),转换成串行数据发送给m25p16;

b. 将m25p16反馈的串行数据(ID、状态寄存器、读出数据等),转换成并行数据发送给主机

使用bit计数器实现代码如下:

//****************************************************************
//--cnt_bit
//****************************************************************   
    always @(posedge clk or negedge rst_n)begin 
       if(!rst_n)begin
            cnt_bit <= 'd0;
        end 
        else if(add_cnt_bit)begin 
            if(end_cnt_bit)begin 
                cnt_bit <= 'd0;
            end
            else begin 
                cnt_bit <= cnt_bit + 1'b1;
            end 
        end
    end 
    
    assign add_cnt_bit = end_cnt_freq;
    assign end_cnt_bit = add_cnt_bit && cnt_bit == `MUX_BIT;

//****************************************************************
//--tx_data
//****************************************************************
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            tx_data <= 8'b0;
        end 
        else if(req && cnt_freq == `SCLK_FALL)begin 
            tx_data <= din;
        end 
    end

    assign  mosi = tx_data[4'd7-cnt_bit];

//****************************************************************
//--rx_data
//****************************************************************
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            rx_data <= 8'b0;
        end 
        else if(req && cnt_freq == `SCLK_RISE)begin 
            rx_data[4'd7-cnt_bit] <= miso;
        end 
    end

(三)片选信号(cs_n)及一字节传输完成信号(done)

使用比特计数器计数传输的bit数,当传输完8bit后done信号拉高一周期,当读写请求信号有效状态下,cs_n拉低,代码实现如下:

//****************************************************************
//--cnt_bit
//****************************************************************   
    always @(posedge clk or negedge rst_n)begin 
       if(!rst_n)begin
            cnt_bit <= 'd0;
        end 
        else if(add_cnt_bit)begin 
            if(end_cnt_bit)begin 
                cnt_bit <= 'd0;
            end
            else begin 
                cnt_bit <= cnt_bit + 1'b1;
            end 
        end
    end 
    
    assign add_cnt_bit = end_cnt_freq;
    assign end_cnt_bit = add_cnt_bit && cnt_bit == `MUX_BIT;

    assign  cs_n = ~req       ;
    assign  done = end_cnt_bit;

3. 模块代码

/**************************************功能介绍***********************************
Description: SPI协议接口模块		
Change history: 2023年7月13日   
*********************************************************************************/
 
`include "param.v"
     
//---------<模块及端口声名>------------------------------------------------------
module spi_interface( 
    input				clk		    ,
    input				rst_n	    ,
    //flash_control接口    
    input               req         ,//操作请求信号
    input      [7:0]    din         ,//输入数据(命令 地址  ID 写数据)
    output              done        ,//传输完成一个字节数据标志信号  
    output     [7:0]    dout        ,//接受到的flash数据串转并        
    //flash芯片 接口     
    input				miso	    ,//从机(flash)信号
    output		     	cs_n	    ,//主机发送片选信号
    output		     	mosi	    ,//主机发送数据  并转串
    output		    	sclk	     //主机产生时钟信号
);								 
//---------<内部信号定义>-----------------------------------------------------
    //分频计数器
    reg			[4:0]	cnt_freq	;
    wire				add_cnt_freq;
    wire				end_cnt_freq;
    //spi输出信号寄存                   
    reg                 sclk_reg    ;//串行同步时钟
    //寄存flash发送过来的数据         
    reg         [7:0]   rx_data     ;//寄存dout
    //寄存控制模块发送过来的数据
    reg         [7:0]   tx_data     ;
    //字节计数器                      
    reg			[3:0]	cnt_bit	   	;
    wire				add_cnt_bit	;
    wire				end_cnt_bit	;

//****************************************************************
//--cnt_freq
//****************************************************************
    always @(posedge clk or negedge rst_n)begin 
       if(!rst_n)begin
            cnt_freq <= 5'd0;
        end 
        else if(add_cnt_freq)begin 
            if(end_cnt_freq)begin 
                cnt_freq <= 5'd0;
            end
            else begin 
                cnt_freq <= cnt_freq + 1'b1;
            end 
        end
    end 
    
    assign add_cnt_freq = req;
    assign end_cnt_freq = add_cnt_freq && cnt_freq == `FREQUENCY;

//****************************************************************
//--cnt_bit
//****************************************************************   
    always @(posedge clk or negedge rst_n)begin 
       if(!rst_n)begin
            cnt_bit <= 'd0;
        end 
        else if(add_cnt_bit)begin 
            if(end_cnt_bit)begin 
                cnt_bit <= 'd0;
            end
            else begin 
                cnt_bit <= cnt_bit + 1'b1;
            end 
        end
    end 
    
    assign add_cnt_bit = end_cnt_freq;
    assign end_cnt_bit = add_cnt_bit && cnt_bit == `MUX_BIT;

//****************************************************************
//--sclk_reg 采用SPI通信模式3  下降沿发送,上升沿采样
//****************************************************************
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            sclk_reg <= 1'b1;                 //初始高电平
        end 
        else if(cnt_freq == `SCLK_FALL)begin 
            sclk_reg <= 1'b0;                 //flash m25p16 下降沿 数据改变
        end 
        else if(cnt_freq == `SCLK_RISE)begin 
            sclk_reg <= 1'b1;                 //flash m25p16 上升沿 数据稳定
        end 
    end

//****************************************************************
//--tx_data
//****************************************************************
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            tx_data <= 8'b0;
        end 
        else if(req && cnt_freq == `SCLK_FALL)begin 
            tx_data <= din;
        end 
    end

//****************************************************************
//--rx_data
//****************************************************************
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            rx_data <= 8'b0;
        end 
        else if(req && cnt_freq == `SCLK_RISE)begin 
            rx_data[4'd7-cnt_bit] <= miso;
        end 
    end

//****************************************************************
//--sclk  cs_n  mosi  done  dout
//****************************************************************
    assign  sclk = sclk_reg   ;
    assign  cs_n = ~req       ;
    assign  mosi = tx_data[4'd7-cnt_bit];
    assign  done = end_cnt_bit;
    assign  dout = rx_data    ;


endmodule

4. 模块仿真

调用m25p16从机模型进行命令发送机数据接收仿真。

仿真代码如下:

`timescale 1ns/1ns
    
module tb_spi_interface();

    reg				tb_clk	;
    reg				tb_rst_n;
    //flash_control接口
    reg             req     ;
    reg      [7:0]  din     ;
    wire            done    ;
    wire     [7:0]  dout    ;
    //flash芯片 接口 
    wire			miso	;
    wire		    cs_n	;
    wire		    mosi	;
    wire		    sclk	;

    pullup(miso);
    //pulldown(miso);
 
//时钟周期参数定义	
    parameter		CLOCK_CYCLE = 20;   

//模块例化
    spi_interface spi_interface_inst( 
    .clk  	(tb_clk  ) ,
    .rst_n	(tb_rst_n) ,
    .req    (req     ) ,//操作请求信号
    .miso   (miso    ) ,
    .din    (din     ) ,//输入数据(命令 地址  ID 写数据)
    .done   (done    ) ,//传输完成一个字节数据标志信号  
    .dout   (dout    ) ,//接受到的flash数据串转并        
    .cs_n	(cs_n	 ) ,//主机发送片选信号
    .mosi	(mosi	 ) ,//主机发送数据  并转串
    .sclk	(sclk	 )  //主机产生时钟信号
);

    m25p16 m25p16_inst(
   /*input */.c       (sclk    ),
   /*input */.data_in (mosi    ),
   /*input */.s       (cs_n    ),
   /*input */.w       (        ),
   /*input */.hold    (        ),
   /*output*/.data_out(miso    )
    );

//产生时钟
    initial 		tb_clk = 1'b0;
    always #(CLOCK_CYCLE/2) tb_clk = ~tb_clk;

//产生激励
    initial  begin 
        tb_rst_n = 1'b1;
        req  = 1'b0;
        din  = 8'b0;
        #(CLOCK_CYCLE*2);
        tb_rst_n = 1'b0;
        #(CLOCK_CYCLE*20);
        tb_rst_n = 1'b1;
        #3;

        //模拟输入指令
        req = 1'b1 ;
        din = 8'h03;//输入读数据命令
        @(negedge done);
        din = 8'h00;//高位地址
         @(negedge done);
        din = 8'h00;//中位地址
         @(negedge done);
        din = 8'h01;//低位地址
        #(CLOCK_CYCLE*1000);
        req = 1'b0;
        #(CLOCK_CYCLE*100);
        $stop;


    end

endmodule 

仿真结果:

从机初始地址内容全为1,即读出内容为FF 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值