FPGA实现SPI 协议
SPI 協議是由摩托羅拉公司提出的通訊協議(Serial Peripheral Interface),即串行外圍設備接口,是一種高速全雙工的通信總線。它被廣泛地使用在 ADC、LCD 等設備與 MCU 間,要求通訊速率較高的場合。
SPI 通訊設備之間的常用連接方式見圖 1。
SPI 通訊使用3 條總線及片選線,3 條總線分別為SCK、MOSI、MISO,片選線為 SS
,它們的作用介紹如下:
(1) SS ( Slave Select):從設備選擇信號線,常稱為片選信號線,也稱為NSS、CS,以下用NSS 表示。當有多個SPI 從設備與SPI 主機相連時,設備的其它信號線SCK、MOSI及MISO 同時并聯到相同的SPI 總線上,即無論有多少個從設備,都共同只使用這3 條總線;而每個從設備都有獨立的這一條NSS 信號線,本信號線獨占主機的一個引腳,即有多少個從設備,就有多少條片選信號線。I2C 協議中通過設備地址來尋址、選中總線上的某個設備并與其進行通訊;而SPI 協議中沒有設備地址,它使用NSS 信號線
來尋址,當主機要選擇從設備時,把該從設備的NSS 信號線設置為低電平,該從設備即被選中,即片選有效,接著主機開始與被選中的從設備進行SPI 通訊。所以SPI 通訊以NSS 線置低電平為開始信號,以NSS 線被拉高作為結束信號。
(2) SCK (Serial Clock):時鐘信號線,用于通訊數據同步。它由通訊主機產生,決定了通訊的速率,不同的設備支持的最高時鐘頻率不一樣,如STM32 的SPI 時鐘頻率最大為fpclk/2,兩個設備之間通訊時,通訊速率受限于低速設備。
(3) MOSI (Master Output, Slave Input):主設備輸出/從設備輸入引腳。主機的數據從這條信號線輸出,從機由這條信號線讀入主機發送的數據,即這條線上數據的方向為主機到從機。
(4) MISO(Master Input,,Slave Output):主設備輸入/從設備輸出引腳。主機從這條信號線讀入數據,從機的數據由這條信號線輸出到主機,即在這條線上數據的方向為從機到主機。
協議層
與I2C 的類似,SPI 協議定義了通訊的起始和停止信號、數據有效性、時鐘同步等環節。
SPI 基本通訊過程先看看SPI 通訊的通訊時序,見圖 2。
這是一個主機的通訊時序。NSS、SCK、MOSI 信號都由主機控制產生,而MISO 的信號由從機產生,主機通過該信號線讀取從機的數據。MOSI 與MISO 的信號只在NSS 為低電平的時候才有效,在SCK 的每個時鐘周期MOSI 和MISO 傳輸一位數據。以上通訊流程中包含的各個信號分解如下:
3. 通訊的起始和停止信號
在圖 2 中的標號處,NSS 信號線由高變低,是SPI 通訊的起始信號。NSS 是每個從機各自獨占的信號線,當從機在自己的NSS 線檢測到起始信號后,就知道自己被主機選中了,開始準備與主機通訊。在圖中的標號?處,NSS 信號由低變高,是SPI 通訊的停止信號,表示本次通訊結束,從機的選中狀態被取消。
4. 數據有效性
SPI 使用MOSI 及MISO 信號線來傳輸數據,使用SCK信號線進行數據同步。MOSI 及MISO 數據線在SCK 的每個時鐘周期傳輸一位數據,且數據輸入輸出是同時進行的。數據傳輸時,MSB 先行或LSB 先行并沒有作硬性規定,但要保證兩個SPI 通訊設備之間使用同樣的協定,一般都會采用圖 2 中的MSB 先行模式。MOSI 及MISO 的數據在SCK的上升沿期間變化輸出,在SCK 的下降沿時被采樣。即在SCK 的下降沿時刻,MOSI 及MISO 的數據有效,高電平時表示數據“1”,為低電平時表示數據“0”。在其它時刻,數據無效,MOSI 及MISO 為下一次表示數據做準備。SPI 每次數據傳輸可以8 位或16 位為單位,每次傳輸的單位數不受限制。
5. CPOL/CPHA 及通訊模式
上面講述的圖 25-2 中的時序只是SPI 中的其中一種通訊模式,SPI 一共有四種通訊模式,它們的主要區別是總線空閑時SCK 的時鐘狀態以及數據采樣時刻。為方便說明,在此引入“時鐘極性CPOL”和“時鐘相位CPHA”的概念。時鐘極性CPOL 是指SPI 通訊設備處于空閑狀態時,SCK 信號線的電平信號(即SPI 通
訊開始前、 NSS 線為高電平時SCK 的狀態)。CPOL=0 時, SCK 在空閑狀態時為低電平,CPOL=1 時,則相反。時鐘相位CPHA 是指數據的采樣的時刻,當CPHA=0 時,MOSI 或MISO 數據線上的信號將會在SCK 時鐘線的“奇數邊沿”被采樣。當CPHA=1 時,數據線在SCK 的“偶數邊沿”采樣。見圖 3 及圖 4。
我們來分析這個CPHA=0 的時序圖。首先,根據SCK在空閑狀態時的電平,分為兩種情況。SCK 信號線在空閑狀態為低電平時,CPOL=0;空閑狀態為高電平時,CPOL=1。無論CPOL=0 還是=1,因為我們配置的時鐘相位CPHA=0,在圖中可以看到,采樣時刻都是在SCK 的奇數邊沿。注意當CPOL=0 的時候,時鐘的奇數邊沿是上升沿,而CPOL=1 的時候,時鐘的奇數邊沿是下降沿。所以SPI 的采樣時刻不是由上升/下降沿決定的。MOSI 和MISO 數據線的有效信號在SCK 的奇數邊沿保持不變,數據信號將在SCK 奇數邊沿時被采樣,在非采樣時刻,MOSI 和MISO 的有效信號才發生切換。類似地,當CPHA=1 時,不受CPOL 的影響,數據信號在SCK的偶數邊沿被采樣,見圖 4。
圖 4 CPHA=1 時的SPI 通訊模式
由CPOL 及CPHA 的不同狀態,SPI 分成了四種模式,見表 1,主機與從機需要工
作在相同的模式下才可以正常通訊,實際中采用較多的是“模式0”與“模式3”。
表 1 SPI 的四種模式
SPI 模式 CPOL CPHA 空閑時SCK 時鐘 采樣時刻
0 0 0 低電平 奇數邊沿
1 0 1 低電平 偶數邊沿
2 1 0 高電平 奇數邊沿
3 1 1 高電平 偶數邊沿
3 FPGA實現從機收發SPI數據
在實現SPI時需要完成通信協議和數據長度的約定,這個代碼實現的協議如下圖所示
代碼如下所示,代碼通過多時鐘上升沿的變化來讀寫數據的
`timescale 1ns / 1ps // // Company: // Engineer: // // module SPI_data( input wire clk_100m , input wire clk_125m , input wire rst_n ,input wire spi_sclk0 , //mast input wire spi_cs0 , input wire spi_din0 , output reg spi_dout0 ,output reg spi_wr_flag , output reg spi_rd_flag , output reg [1:0] spi_rd_data_id ,input wire data_in_en , input wire [15:0] data_in , output reg data_out_en , output reg [15:0] data_out );// //SPI1parameter spi_data_dlc = 14 ; parameter spi_data_dlc_t = 13 ; parameter spi_dlc = 16 ; parameter spi_half_dlc = 8 ;reg spi_sclk0_t ; reg spi_cs0_t ; reg spi_din0_t ; reg spi_sclk0_t_t ; reg spi_cs0_t_t ; reg spi_din0_t_t ; reg spi_cs0_t_t_t ;reg data_din_flag ; reg one_data_flag ; reg one_data_flag_t ;reg data_dout_flag ;reg [6:0] spi_bit_cont ;reg [4:0] spi_cs0_cont ; reg [7:0] spi_first_data0 ; reg [7:0] spi_last_data0 ;reg [15:0] spi_sum_in ; reg [15:0] spi_sum_out ;always @(posedge clk_100m or negedge rst_n )begin if(rst_n==1'b0) begin spi_sclk0_t <= 'b0 ; spi_cs0_t <= 'b0 ; spi_din0_t <= 'b0 ; spi_sclk0_t_t <= 'b0 ; spi_cs0_t_t <= 'b0 ; spi_din0_t_t <= 'b0 ; spi_cs0_t_t_t <= 'b0 ; end else begin spi_sclk0_t <= spi_sclk0 ; spi_cs0_t <= spi_cs0 ; spi_din0_t <= spi_din0 ; spi_sclk0_t_t <= spi_sclk0_t ; spi_cs0_t_t <= spi_cs0_t ; spi_cs0_t_t_t <= spi_cs0_t_t ; spi_din0_t_t <= spi_din0_t ; end end SPI modle 0 16 bitalways @(posedge clk_100m or negedge rst_n )begin if(rst_n==1'b0) begin spi_cs0_cont <= 'b0 ; end else if ((spi_cs0_t_t==1'b0)&&((spi_sclk0_t_t==1'b0)&&(spi_sclk0_t==1'b1))) begin spi_cs0_cont <= spi_cs0_cont + 1'b1 ; end else if ((spi_cs0_t_t==1'b1)||(spi_cs0_cont>=spi_half_dlc)) begin spi_cs0_cont <= 'b0 ; end end always @(posedge clk_100m or negedge rst_n )begin if(rst_n==1'b0) begin one_data_flag <= 'b0 ; end else if ((spi_cs0_t_t==1'b0)&&((spi_sclk0_t_t==1'b0)&&(spi_sclk0_t==1'b1))&&(spi_cs0_cont==5'b00111)) begin one_data_flag <= 1'b1 ; end else begin one_data_flag <= 'b0 ; end end always @(posedge clk_100m or negedge rst_n )begin if(rst_n==1'b0) begin one_data_flag_t <= 'b0 ; end else begin one_data_flag_t <= one_data_flag ; end end always @(posedge clk_100m or negedge rst_n )begin if(rst_n==1'b0) begin spi_bit_cont <= 'b0 ; end else if ((spi_cs0_t_t_t==1'b0)&&(spi_cs0_cont==spi_half_dlc)) begin spi_bit_cont <= spi_bit_cont+ 1'b1 ; end else if ((spi_cs0_t_t_t==1'b1)) begin spi_bit_cont <= 'b0 ; end end always @(posedge clk_100m or negedge rst_n )begin if(rst_n==1'b0) begin spi_first_data0 <= 'b0 ; end else if ((spi_cs0_t_t==1'b0)&&((spi_sclk0_t_t==1'b0)&&(spi_sclk0_t==1'b1))&&(spi_cs0_cont<spi_half_dlc)) begin spi_first_data0 <= {spi_first_data0[6:0],spi_din0_t} ; end else if ((spi_cs0_t_t_t==1'b1)||(spi_cs0_cont==spi_half_dlc)) begin spi_first_data0 <= 'b0 ; end end always @(posedge clk_100m or negedge rst_n )begin if(rst_n==1'b0) begin spi_wr_flag <= 'b0 ; spi_rd_flag <= 'b0 ; spi_rd_data_id<= 'b0 ; end else if ((spi_cs0_t_t==1'b0)&&(spi_bit_cont==1'b0)&&(spi_cs0_cont==3)&&((spi_first_data0[2:1]==2'b10))&&((spi_sclk0_t_t==1'b0)&&(spi_sclk0_t==1'b1))) begin spi_wr_flag <= 'b1 ; spi_rd_flag <= 'b0 ; spi_rd_data_id<= 'b0 ; end else if ((spi_cs0_t_t==1'b0)&&(spi_bit_cont==1'b0)&&(spi_cs0_cont==3)&&((spi_first_data0[2:1]==2'b01))&&((spi_sclk0_t_t==1'b0)&&(spi_sclk0_t==1'b1))) begin spi_wr_flag <= 'b0 ; spi_rd_flag <= 'b1 ; spi_rd_data_id<= {spi_first_data0[0],spi_din0_t} ; end else if ((spi_cs0_t_t==1'b0)&&(spi_bit_cont==1'b0)&&(spi_cs0_cont==3)&&((spi_first_data0[2:1]==2'b00))&&((spi_sclk0_t_t==1'b0)&&(spi_sclk0_t==1'b1))) begin spi_wr_flag <= 'b1 ; spi_rd_flag <= 'b1 ; spi_rd_data_id<= {spi_first_data0[0],spi_din0_t} ; end else if ((spi_cs0_t_t_t==1'b1)) begin spi_wr_flag <= 'b0 ; spi_rd_flag <= 'b0 ; spi_rd_data_id<= 'b0 ; end end //spi_rd_data_idalways @(posedge clk_100m or negedge rst_n )begin if(rst_n==1'b0) begin data_out_en <= 'b0 ; end else if ((spi_cs0_cont==spi_half_dlc)&&(spi_bit_cont==spi_data_dlc_t)&&(spi_wr_flag==1'b1)&&(spi_rd_flag==1'b0)) begin data_out_en <= 1'b1 ; end else if ((spi_cs0_cont==spi_half_dlc)&&(spi_bit_cont==spi_data_dlc_t)&&(spi_wr_flag==1'b1)&&(spi_rd_flag==1'b1)) begin data_out_en <= 1'b1 ; end else begin data_out_en <= 'b0 ; end end always @(posedge clk_100m or negedge rst_n )begin if(rst_n==1'b0) begin data_out <= 'b0 ; end else if ((spi_cs0_cont==spi_half_dlc)&&(spi_wr_flag==1'b1)&&(spi_rd_flag==1'b0)) begin data_out <= {data_out[199:0],spi_first_data0} ; end else if ((spi_cs0_cont==spi_half_dlc)&&(spi_wr_flag==1'b1)&&(spi_rd_flag==1'b1)) begin data_out <= {data_out[199:0],spi_first_data0} ; end else if ((spi_cs0_t_t_t==1'b1)) begin data_out <= 'b0 ; end end always @(posedge clk_100m or negedge rst_n )begin if(rst_n==1'b0) begin spi_last_data0 <= 'b0 ; end else if ((spi_cs0_t_t==1'b0)&&((spi_sclk0_t_t==1'b0)&&(spi_sclk0_t==1'b1))&&(spi_cs0_cont<spi_half_dlc)&&(data_dout_flag==1'b1)) begin spi_last_data0 <= {spi_last_data0[6:0],spi_dout0} ; end else if ((spi_cs0_t_t_t==1'b1)||(spi_cs0_cont==spi_half_dlc)) begin spi_last_data0 <= 'b0 ; end end //data_dout_flagalways @(posedge clk_100m or negedge rst_n )begin if(rst_n==1'b0) begin data_dout_flag <= 'b0 ; end else if ((spi_cs0_t_t==1'b0)&&(spi_bit_cont==1'b0)&&(one_data_flag==1'b1)&&(spi_rd_flag==1'b1)&&(spi_wr_flag==1'b0)) begin data_dout_flag <= 1'b1 ; end else if ((spi_cs0_t_t==1'b0)&&(spi_bit_cont==1'b0)&&(one_data_flag==1'b1)&&(spi_rd_flag==1'b1)&&(spi_wr_flag==1'b1)) begin data_dout_flag <= 1'b1 ; end else if ((spi_cs0_t_t_t==1'b1)) begin data_dout_flag <= 'b0 ; end end reg [191:0] spi_data_in ; reg [7:0] last_sum ;always @(posedge clk_100m or negedge rst_n )begin if(rst_n==1'b0) begin last_sum <= 'b0 ; end else if ((spi_cs0_t_t==1'b0)&&(spi_bit_cont==spi_data_dlc_t)&&(one_data_flag_t==1'b1)&&(spi_rd_flag==1'b1)&&(spi_wr_flag==1'b0)) begin last_sum <= spi_sum_out[7:0] ; end else if ((spi_cs0_t_t==1'b0)&&(spi_bit_cont==spi_data_dlc_t)&&(one_data_flag_t==1'b1)&&(spi_rd_flag==1'b1)&&(spi_wr_flag==1'b1)) begin last_sum <= spi_sum_out[7:0] ; end else if ((spi_cs0_t_t==1'b0)&&((spi_sclk0_t_t==1'b1)&&(spi_sclk0_t==1'b0))&&(spi_bit_cont==spi_data_dlc_t)&&(spi_rd_flag==1'b1)&&(spi_wr_flag==1'b0)) begin last_sum <= {last_sum[6:0],1'b0} ; end else if ((spi_cs0_t_t==1'b0)&&((spi_sclk0_t_t==1'b1)&&(spi_sclk0_t==1'b0))&&(spi_bit_cont==spi_data_dlc_t)&&(spi_rd_flag==1'b1)&&(spi_wr_flag==1'b1)) begin last_sum <= {last_sum[6:0],1'b0} ; end else if ((spi_cs0_t_t_t==1'b1)) begin last_sum <= 'b0 ; end end always @(posedge clk_100m or negedge rst_n )begin if(rst_n==1'b0) begin spi_data_in <= 'b0 ; end else if (data_in_en==1'b1) begin spi_data_in <= data_in ; end else if ((spi_cs0_t_t==1'b0)&&((spi_sclk0_t_t==1'b1)&&(spi_sclk0_t==1'b0))&&(data_dout_flag==1'b1)&&(spi_rd_flag==1'b1)&&(spi_wr_flag==1'b0)) begin spi_data_in <= {spi_data_in[190:0],1'b0} ; end else if ((spi_cs0_t_t==1'b0)&&((spi_sclk0_t_t==1'b1)&&(spi_sclk0_t==1'b0))&&(data_dout_flag==1'b1)&&(spi_rd_flag==1'b1)&&(spi_wr_flag==1'b1)) begin spi_data_in <= {spi_data_in[190:0],1'b0} ; end else if ((spi_cs0_t_t_t==1'b1)) begin spi_data_in <= 'b0 ; end end always @(posedge clk_100m or negedge rst_n )begin if(rst_n==1'b0) begin spi_dout0 <= 'b0 ; end else if ((spi_cs0_t_t==1'b0)&&((spi_sclk0_t_t==1'b1)&&(spi_sclk0_t==1'b0))&&(spi_bit_cont<1)) begin //spi_dout0 <= spi_data_in[191] ; spi_dout0 <= spi_din0_t ; end else if ((spi_cs0_t_t==1'b0)&&((spi_sclk0_t_t==1'b1)&&(spi_sclk0_t==1'b0))&&(spi_rd_flag==1'b1)&&(spi_wr_flag==1'b0)&&(spi_bit_cont<spi_data_dlc_t)&&(spi_bit_cont>0)) begin spi_dout0 <= spi_data_in[191] ; end else if ((spi_cs0_t_t==1'b0)&&((spi_sclk0_t_t==1'b1)&&(spi_sclk0_t==1'b0))&&(spi_rd_flag==1'b1)&&(spi_wr_flag==1'b1)&&(spi_bit_cont<spi_data_dlc_t)&&(spi_bit_cont>0)) begin spi_dout0 <= spi_data_in[191] ; end else if ((spi_cs0_t_t==1'b0)&&((spi_sclk0_t_t==1'b1)&&(spi_sclk0_t==1'b0))&&(spi_rd_flag==1'b1)&&(spi_wr_flag==1'b0)&&(spi_bit_cont==spi_data_dlc_t)&&(spi_bit_cont>0)) begin spi_dout0 <= last_sum[7] ; end else if ((spi_cs0_t_t==1'b0)&&((spi_sclk0_t_t==1'b1)&&(spi_sclk0_t==1'b0))&&(spi_rd_flag==1'b1)&&(spi_wr_flag==1'b1)&&(spi_bit_cont==spi_data_dlc_t)&&(spi_bit_cont>0)) begin spi_dout0 <= last_sum[7] ; end else if ((spi_cs0_t_t==1'b1)) begin spi_dout0 <= 'b0 ; end end /always @(posedge clk_100m or negedge rst_n )begin if(rst_n==1'b0) begin spi_sum_in <= 'b0 ; end else if (((spi_bit_cont<spi_data_dlc)&&(spi_bit_cont>1'b0))&&(spi_cs0_cont==spi_half_dlc)&&(spi_wr_flag==1'b1)&&(spi_rd_flag==1'b0)) begin spi_sum_in <= spi_sum_in + spi_first_data0 ; end else if (((spi_bit_cont<spi_data_dlc)&&(spi_bit_cont>1'b0))&&(spi_cs0_cont==spi_half_dlc)&&(spi_wr_flag==1'b1)&&(spi_rd_flag==1'b1)) begin spi_sum_in <= spi_sum_in + spi_first_data0 ; end else if ((spi_cs0_t_t==1'b1)) begin spi_sum_in <= 'b0 ; end end always @(posedge clk_100m or negedge rst_n )begin if(rst_n==1'b0) begin spi_sum_out <= 'b0 ; end else if (((spi_bit_cont<spi_data_dlc)&&(spi_bit_cont>1'b0))&&(spi_cs0_cont==spi_half_dlc)&&(spi_wr_flag==1'b0)&&(spi_rd_flag==1'b1)) begin spi_sum_out <= spi_sum_out + spi_last_data0 ; end else if (((spi_bit_cont<spi_data_dlc)&&(spi_bit_cont>1'b0))&&(spi_cs0_cont==spi_half_dlc)&&(spi_wr_flag==1'b1)&&(spi_rd_flag==1'b1)) begin spi_sum_out <= spi_sum_out + spi_last_data0 ; end else if ((spi_cs0_t_t==1'b1)) begin spi_sum_out <= 'b0 ; end end endmodule4、FPGA實現主機SPI數據的收發
作為主機時實現時鐘的發送和數據傳輸的控制。
`timescale 1ns / 1ps // // Company: // Engineer: // module SPI_slv_data( input wire clk_100m , input wire clk_125m , input wire rst_n ,output wire spi_sclk1 , //slv output reg spi_cs1 , input wire spi_din1 , output reg spi_dout1 ,///input wire data_fram_sof , input wire data_fram_eof ,input wire data_in_en , input wire [15:0] data_in ,output reg data_one_flag );/ reg [3:0] spi_div ; reg spi_clk ; reg bit_flag ; reg [4:0] bit_cont ; reg [15:0] spi_data_in ;always @(posedge clk_100m or negedge rst_n )begin if(rst_n==1'b0) begin spi_cs1 <= 'b1 ; end else if(data_fram_sof==1'b1) begin spi_cs1 <= 1'b0 ; end else begin spi_cs1 <= 'b1 ; end end always @(posedge clk_100m or negedge rst_n )begin if(rst_n==1'b0) begin spi_div <= 'b0 ; end else if((spi_div<12)&&(data_fram_eof==1'b1)) begin spi_div <= spi_div + 1'b1 ; end else begin spi_div <= 'b0 ; end end always @(posedge clk_100m or negedge rst_n )begin if(rst_n==1'b0) begin spi_clk <= 'b1 ; end else if (spi_div<=5) begin spi_clk <= 1'b1 ; end else spi_clk <= 'b0 ; end 時鐘經過BUFG緩沖 wire spi_clk_o ; BUFG spi_clk_obuf (.I(spi_clk), .O(spi_sclk1)); // always @(posedge clk_100m or negedge rst_n )begin if(rst_n==1'b0) begin spi_data_in <= 'b1 ; end else if (data_in_en==1'b1) begin spi_data_in <= data_in ; end else if (bit_flag==1'b1) begin spi_data_in <= {spi_data_in[14:0],1'b1} ; end end always @(posedge clk_100m or negedge rst_n )begin if(rst_n==1'b0) begin bit_flag <= 'b0 ; end else if (spi_div==6) begin bit_flag <= 1'b1 ; end else bit_flag <= 'b0 ; end always @(posedge clk_100m or negedge rst_n )begin if(rst_n==1'b0) begin spi_dout1 <= 'b0 ; end else if (bit_flag==1'b1) begin spi_dout1 <= spi_data_in[15] ; end end always @(posedge clk_100m or negedge rst_n )begin if(rst_n==1'b0) begin bit_cont <= 'b0 ; end else if (bit_flag==1'b1) begin bit_cont <= bit_cont + 1'b1 ; end else if (bit_cont>=16) begin bit_cont <= 'b0 ; end end always @(posedge clk_100m or negedge rst_n )begin if(rst_n==1'b0) begin data_one_flag <= 'b0 ; end else if (bit_cont==16) begin data_one_flag <= 1'b1 ; end else data_one_flag <= 'b0 ; end endmodu總體來說FPGA實現SPI相對簡單。
總結
以上是生活随笔為你收集整理的FPGA实现SPI 协议的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 如何发现优秀的开源项目?
- 下一篇: 乌班图系统部署jdk