SPI从机程序

/*SPI 即为:serial peripheral interface,串行外围设备接口。是一种全双工同步通信总线。
 通信是通过数据传输来完成的,SPI是串行通信协议,也就是说,数据时一位一位传输的。也就是时钟线存在的原因,
 由于时钟线提供的时钟脉冲,数据发送和数据接收都是基于这个时钟脉冲完成数据传输的,数据通过数据输出线输出,
 数据在时钟上升沿或者下降沿时改变,在紧接着的下降沿或者上升沿被读取,完成一次数据传输,输入原理和输出一样。
 也就是说数据输入和输出是在同一个时钟完成的,而区别就是在时钟的上升沿输入那么在时钟下降沿就输出,或者相反。

 为了实现这一点,一下设计就是通过一个计数器,在计数器数到偶数的时候则输出,计数器数到奇数的时候则输入。代码如下:*/

module spi_ctrl(
    clk,rst_n,
    spi_miso,spi_mosi,spi_clk,
    spi_tx_en,spi_tx_rdy,spi_rx_en,spi_rx_rdy,spi_tx_db,spi_rx_db
    );
 input  clk;            //FPAG输入时钟信号50MHz
 input  rst_n;     //FPGA输入复位信号

 input  spi_miso;          //SPI主机输入从机输出数据信号
 output spi_mosi;          //SPI主机输出从机输入数据信号
 output spi_clk;           //SPI时钟信号,由主机产生

 input  spi_tx_en;         //SPI数据发送使能信号,高有效
 output spi_tx_rdy;        //SPI数据发送完成标志位,高有效
 input  spi_rx_en;         //SPI数据接收使能信号,高有效
 output spi_rx_rdy;        //SPI数据接收完成标志位,高有效
 input  [7:0]spi_tx_db;    //SPI数据发送寄存器
 output [7:0]spi_rx_db;    //SPI数据接收寄存器

//模拟SPI的时序模式为CPOL=1, CPHA=1,模拟速率为50Mbit

//------------------------------------------------------
//SPI时序控制计数器,所有SPI时序由该计数器值控制
 reg [4:0]cnt8;  //SPI时序控制计数器,计数范围在0-18

always @(posedge clk or negedge rst_n)
    if(!rst_n)
        cnt8 <= 5'd0;
    else if(spi_tx_en || spi_rx_en) begin    //SPI工作使能
        if(cnt8 < 5'd18)
            cnt8 <= cnt8 + 1'b1;
        else cnt8<=cnt8;                     //计数到18停止,等待撤销spi使能
    end
    else cnt8 <= 5'd0;                       //SPI关闭,计数停止

//--------------------------------------------------------
//SPI时钟信号产生
 reg spi_clkr;  //SPI时钟信号,由主机产生

always @(posedge clk or negedge rst_n)
    if(!rst_n)
        spi_clkr <= 1'b1;                           //spi时钟只有在spi启用过程中才有效
    else if(cnt8>5'd1 && cnt8<5'd18)
        spi_clkr <= ~spi_clkr;//在cnt8处于2-17时SPI时钟有效翻转

 assign spi_clk = spi_clkr;

//-------------------------------------------------------
//SPI主机输出数据控制
 reg spi_mosir; //SPI主机输出从机输入数据信号    

always @(posedge clk or negedge rst_n)
    if(!rst_n) spi_mosir <= 1'b1;
    else if(spi_tx_en) begin
        case(cnt8[4:1])      //不判断最低位,则数据在cnt8两次加一后才有一次变化,这里刚好和接收的时钟分开
            4'd1: spi_mosir <= spi_tx_db[7];    //发送bit7  相当于cnt8=2
            4'd2: spi_mosir <= spi_tx_db[6]; //发送bit6  相当于cnt8=4
            4'd3: spi_mosir <= spi_tx_db[5]; //发送bit5  相当于cnt8=6
            4'd4: spi_mosir <= spi_tx_db[4]; //发送bit4  相当于cnt8=8
            4'd5: spi_mosir <= spi_tx_db[3]; //发送bit3  相当于cnt8=10
            4'd6: spi_mosir <= spi_tx_db[2]; //发送bit2  相当于cnt8=12
            4'd7: spi_mosir <= spi_tx_db[1]; //发送bit1  相当于cnt8=14
            4'd8: spi_mosir <= spi_tx_db[0]; //发送bit0  相当于cnt8=16
            default: spi_mosir <= 1'b1;            //spi_mosi没有输出时应保持高电平
            //如果这里只是单纯地使用cnt8的2、4、6..在default里面有所不同,如果是246之类的那么在357的时候default会有效,而本例中是无效的
        endcase
    end
   else spi_mosir <= 1'b1; //spi_mosi没有输出时应保持高电平

 assign spi_mosi = spi_mosir;

//---------------------------------------------------------------
//SPI主机输入数据控制
 reg [7:0]spi_rx_dbr;  //SPI主机输入从机输出数据总线寄存器

always @(posedge clk or negedge rst_n)
    if(!rst_n) spi_rx_dbr <= 8'hff;
    else if(spi_rx_en) begin
        case(cnt8)           //取cnt8的奇数,这样可以和发送时钟错开
            5'd3 : spi_rx_dbr[7] <= spi_miso;   //接收bit7
            5'd5 : spi_rx_dbr[6] <= spi_miso; //接收bit6
            5'd7 : spi_rx_dbr[5] <= spi_miso; //接收bit5
            5'd9 : spi_rx_dbr[4] <= spi_miso; //接收bit4
            5'd11: spi_rx_dbr[3] <= spi_miso; //接收bit3
            5'd13: spi_rx_dbr[2] <= spi_miso; //接收bit2
            5'd15: spi_rx_dbr[1] <= spi_miso; //接收bit1
            5'd17: spi_rx_dbr[0] <= spi_miso; //接收bit0
        default: ; //区别上个always里面的default,这里也是无操作
        endcase
    end

 assign spi_rx_db = spi_rx_dbr;

//----------------------------------------------------------------- 
//SPI数据发送完成标志位,高有效
 assign spi_tx_rdy = (cnt8 == 5'd18);

//------------------------------------------------------------------
//SPI数据接收完成标志位,高有效
 assign spi_rx_rdy = (cnt8 == 5'd18);

endmodule

SPI从机程序_第1张图片

你可能感兴趣的:(FPGA,SPI)