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


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章