利用SPI驅動12864液晶

SPI 發送模塊
在這裏,我們要在主機上建立,一個向從機寫入數據的SPI 發送模塊,首先我們先從C語言上了解幾個主機在SPI 寫操作上容易被疏忽的小細節:我們知道SPI 設備在傳輸都有一個規則,SCL 時鐘信號在“上升沿”的時候是“鎖存數據”,SCL 時鐘信號在“下降沿”是“設置數據”。在這裏我們SPI 主機(FPGA),寫操作要乾的工作就是在“拉高SCL 時鐘信號之前”設置數據(移位數據),設置數據之後,再拉高時鐘信號。但是我們常常會忽略了一些具體的細節。
利用SPI驅動12864液晶
上面有兩個主機的SPI_Send 函數(寫函數),左邊的寫法是最常用,但是也是最容易忽略小細節。相比右邊的寫法比較謹慎,在最低的程度上符合一寫小細節。
利用SPI驅動12864液晶
再來,我們繼續引用ST7565P 芯片的寫入時序圖,分析並且區分上邊的兩個寫法。關於SCL 信號在空閒的時候總是處於高電平。當主機開始向從機寫入數據,主機會先拉低CS 信號,再拉低SCL 信號,然後“設置”數據,亦即主機(FPGA)更新SI 的數據(主機數據移位操作),最後再拉高SCL 信號。同一時間,從機會因爲SCL 的上升沿變化,從機(液晶資源)“鎖存”(從機讀取數據操作)SI 上的數據。很明顯左邊的寫法沒有符合這些細節,然而右邊的寫法卻符合這些細節。無論是左邊的寫法還是右邊的寫法,都忽略了一個致命的細節,兩種寫法都無法確定SPI 時鐘信號的時鐘頻率。
module spi_write_module
(
CLK,RSTn,Start_Sig,SPI_Data,Done_Sig,SPI_Out
);
input CLK;
input RSTn;
input Start_Sig;
output [9:0] SPI_Data;
output Done_Sig;
output [3:0]SPI_Out;//[3]CS,[2]A0 [1]CLK [0]D0
parameter T0P5US=4'd9;//0.5us
always@(posedge CLK or negedge RSTn)
if(!RSTn)
Count1<=4'd0;
else if(Count1==T0P5US)
Count1<4'd0;
else if(Start_Sig)
Count1<=Count1+1'b1;
else
Count1<=4'd0;
reg[4:0]i;
reg rCLK;
reg rDO;
reg isDone;
always@(posedge CLK or negedge RSTn)
if(!RSTn)
begin
i<=5'd0;
rCLK<=1'b1;
rDO<=1'b0;
isDone<=1'b0;
end
else if(Start_Sig)
case(i)
5'd0,5'd2,5'd4,5'd6,5'd8,5'd10,5'd12,5'd14:
if(Count1==T0P5US)begin rCLK<=1'b0;rDO<=SPI_Data[7-(i>>1)];i<=1+1'b1;end
5'd1,5'd3,5'd5,5'd7,5'd9,5'd11,5'd13,5'd15:
if(Count==T0P5US)begin rCLK<=1'b1;i<=i+1'b1;end
5'd16:
begin isDone<=1'b1;i<=i+1'b1;end
5'd17:
begin isDone<=1'b0;i<=5'd0;end
endcase
assign Done_Sig=isDone;
assign SPI_Out={SPI_Data[9],SPI_Data[8],rCLK,rDO};
endmodule
SCL 的時鐘頻率定義爲1Mhz , 也就是說週期時間是1us ,半週期就是0.5us 。如果以20Mhz 來定時,那麼計數的結果是10。在19 行定義了0.5us 的常量,第23~33 行是0.5us的定時器。但是比較不同的是,這個定時器平時不工作,當Start_Sig 拉高的時候纔開始計數(第30 行)。第37~65 行是spi_write_module.v 的核心功能。i 寄存器表示操作步驟,rCLK 寄存器表示SCL 然而rDO 寄存器表示SI 。如同前面所述那樣,SCL 時鐘信號,處於空閒狀態
時是出於高電平,所以rCLK 復位值是邏輯1(46 行)。
SPI_Data : 第9 位表示CS,第8 位表示A0,第7 .. 0 位表示一字節數據。
SPI_Out : 第3 位表示CS,第2 位表示A0,第1 位表示SCL,第0 位表示SI。
當Start_Sig 拉高的同時,定時器開始計數(30 行),該模塊也開始執行(50 行)。
當第一個0.5us 定時產生的時候(54 行),也就是第一個時鐘的前半週期,亦即下降沿,
rCLK 設置爲邏輯0。根據SPI 傳輸的規則,下降沿的時候主機設置數據,rDO 賦予SPI_Data 信號的第7 位(SPI 傳輸是從最高位開始,最低位結束),最後i 遞增以示下一個步驟。當i 等於1 的時候並且定時產生(56 行),這表示第一個時鐘的後半週期,亦即上升沿,rCLK 設置爲邏輯1。在SPI 傳輸的規則中上升沿的時候,從機鎖存數據。然後i 遞增以示下一個步驟。
上述的步驟會一直重複到第八次,直到一字節的數據發送完畢。最後會產生一個完成信號(59~63 行)。
這裏有一個表達式需要說明一下:
i >> 1 : 表示i / 2。右移操作也代表了“i / j2”(j 是右移次數)。
假設8 >> 2 ,亦即8 / 22 等於2。8 >> 3, 亦即8 / 23 等於1。
最後還有一個重點就是SPI_Out 的驅動(70 行)。在上面我已經重複過SPI_Out 是佔4 位的輸出。而且每一個位都有意義。
SPI_Out 第3 位:表示了CS,所以直接由SPI_Data 的第9 位驅動。
SPI_Out 第2 位:表示了A0,同樣也是直接由SPI_Data 的第8 位驅動。
SPI_Out 第1 位:表示了SCL,以寄存器rCLK 來驅動。
SPI_Out 第0 位:表示了SI,以寄存器rDO 來驅動。
這樣的目的是簡化連線的複雜度。我們知道Verilog HDL 語言的位操作是很強大。懂得善用,會對建模提到很大的幫助。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章