一、基本知識
1、SPI
SPI是串行外設接口(Serial Peripheral Interface)的縮寫。它是一種高速的,全雙工,同步的通信總線,並且在芯片的管腳上只佔用四根線。
SPI的通信原理很簡單,它以主從方式工作,這種模式通常有一個主設備和一個或多個從設備,需要至少4根線,事實上3根也可以(單向傳輸時)。也是所有基於SPI的設備共有的,它們是SDI(數據輸入)、SDO(數據輸出)、SCLK(時鐘)、CS(片選)。
(1)SDI – SerialData In,串行數據輸入;
(2)SDO – SerialDataOut,串行數據輸出;
(3)SCLK – Serial Clock,時鐘信號,由主設備產生;
(4)CS – Chip Select,從設備使能信號,由主設備控制。
2、SPI的工作模式
3、Verilog中的任務
任務是通過調用來執行的,而且只有在調用時才執行。在定義任務時,設計者可以爲其添加輸入和輸出端口,用於在任務調用時傳遞參數。任務可以包含帶時序控制的語句,當調用帶時序控制的任務時,任務返回時的時間和調用時的時間可能不相同。任務可以彼此調用,而且任務還可以調用函數。
(1)任務的定義
task <任務名>
端口及數據類型聲明語句
其他語句
endtask
(2) 任務的特點
- 任務的定義與調用需在一個module模塊內。
- 定義任務時,沒有端口名列表,但需要緊接着進行輸入、輸出端口和數據類型的說明。
- 當任務被調用時,任務被激活。調用時,須列出端口名列表,端口名的排序和類型必須與任務定義中的相一致。
- 一個任務可以調用別的任務和函數,可以調用的任務和函數個數不限。
- 爲了提高任務的模塊化程度,傳遞給任務的參數名通常不使用與任務內部I/O聲明的參數名相同的參數名。
- 任務定義結構中不能出現initial和always過程塊
(3)任務使用
module mult(clk,a,b,out,en_mult)
input clk,en_mult;
input [3:0] a,b;
output [7:0] out;
reg [7:0] out;
task muotme;//任務定義
input [3:0] xme,tome;
output [7:0] result;
wait(en_mult) //電平敏感事件觸發
result=xme*tome;
endtask
always @(posedge clk)
muotme(a,b,out);//任務調用時傳遞給任務的參數與任務I/O聲明時的參數順序相同
endmodule
4、verilog中的函數
函數和任務一樣,也是定義一個可重複調用的模塊,但是函數可以返回一個值,因此可以出現在等號右邊的表達式中,而任務的返回值只能通過任務的輸出端口來獲得。
(1) 函數的定義:
function <返回值位寬或類型說明> 函數名;
輸入端口與類型說明
局部變量說明
塊語句
endfunction
(2) 函數的特點:
- 函數定義不能包含任何時序控制語句。
- 函數必須包含輸入,但不能有輸出或雙向信號。
- 函數中不能使用非阻塞賦值語句。
- 一個函數只能返回一個值,該值的變量名與函數名同名。
- 傳遞給函數參數的順序與函數定義時輸入參數聲明的順序相同。
- 函數定義必須包含在模塊定義之內。
- 函數不能調用任務,但任務可以調用函數。
(3)舉例用函數定義一個8-3編碼器
module code_83(din,dout)
input [7:0] din;
output [2:0] dout;
function [2:0] code; //函數定義
input [7:0] din;//函數只有輸入,輸出爲函數本身
casex(din)
8'b1xxx_xxxx:cade=3'h7;
8'b01xx_xxxx:cade=3'h6;
8'b001x_xxxx:cade=3'h5;
8'b0001_xxxx:cade=3'h4;
8'b0000_1xxx:cade=3'h3;
8'b0000_01xx:cade=3'h2;
8'b0000_001x:cade=3'h1;
8'b0000_0001:cade=3'h0;
default:code=3'hx;
endcase
endfuntion
assign dout=code(din);//函數調用
endmodule
二、實現過程
設計兩段式狀態機,完成SPI的模式1
一個模塊控制讀還是寫,一個完成對SPI引腳控制
1、定義模塊輸入輸出引腳
module tem_rec(
input clk_10m,//輸入10Mhz的時鐘
input rst, //高電平有效
input spidi,//spi數據輸入端
output spiclk,//spi中的時鐘
output spido,//spi的發送端
output spics,//片選信號
output [7:0] data,
);
reg spics;//spi片選信號
reg spiclk;//spi時鐘
reg spido;////spi寫信號
reg [7:0] data;//讀的數據轉換成8位並行數據輸出
2、讀寫控制模塊編寫:
//總狀態控制
parameter ini=4'd0, state_1=4'd1, state_2=4'd2, state_3 =4'd3,state_4=4'd4,state_5=4'd5,
state_6=4'd6,state_7=4'd7,state_8=4'd8,state_9=4'd9,state_10=4'd10,state_11=4'd11,
state_12=4'd12,state_13=4'd13,state_14=4'd14,state_end=4'd15;//使用循環碼
reg wr;
reg rd;//讀寫命令
reg [3:0] next_state;//狀態轉移
always@(posedge clk_10m or posedge rst)
begin
if(rst) begin
next_state<=ini;
wr<=0;
rd<=0;
data<=8'd0;//待發送
end
else begin
case(next_state)
ini://初始化
begin
next_state<=state_1;
end
state_1: //寫
begin
if(send_rdy) begin
wr<=0;
next_state<=state_2;
end
else//寫寄存器
wr<=1;
end
state_2://讀
begin
if(rec_rdy) begin
rd<=0;
end
else begin
rd<=1;
end
end
endcase
end
end
3、SPI引腳控制模塊編寫:
//*spi的數據接收和發送*//
reg send_rdy;//發送完一字節標誌位
reg rec_rdy;//接收完成一字節數據標誌位
always @(posedge clk_10m)
begin
if(rst) begin
spistate<=idle;
spics<=1'b1;
spiclk<=1'b1;
spido<=1'b1;
send_rdy<=1'b0;
rec_rdy<=1'b0;
else
begin
case(spistate)//讀寫狀態控制
idle:
begin
if((wr==1'b1)&&(rd==1'b0)) begin//發送數據轉換
spistate<=send_data;//轉發送狀態
dsend<=datain;//準備待發送數據
send_rdy<=1'b0;
rec_rdy<=1'b0;
end
else if((wr==1'b0)&&(rd==1'b1) begin
spistate<=receive_data;//轉接收轉態
dstate<=8'd0;
rec_rdy<=1'b0;
send_rdy<=1'b0;
end
end
send_data://發送數據狀態
begin
case(dstate)
8'd0://產生片選信號有效
begin
spics<=1'b1;
spiclk<=1'b1;
spido<=1'b1;//發送出去的數據
dstate<=8'd1;
end
8'd1:
begin
spics<=1'b1;
spiclk<=1'b1;
spido<=1'b1;
dstate<=8'd2;
end
8'd2:
begin
spics<=1'b0;
spiclk<=1'b1;
spido<=1'b1;
dstate<=8'd3;
end
8'd3:
begin
spics<=1'b0;
spiclk<=1'b0;
spido<=datain[7];//發送數據最高位
dstate<=8'd4;
end
8'd4:
begin
spics<=1'b0;
spiclk<=1'b1;
spido<=datain[7];
dstate<=8'd5;
end
8'd5:
begin
spics<=1'b0;
spiclk<=1'b0;
spido<=datain[6];
dstate<=8'd6;
end
8'd6:
begin
spics<=1'b0;
spiclk<=1'b1;
spido<=datain[6];
dstate<=8'd7;
end
8'd7:
begin
spics<=1'b0;
spiclk<=1'b0;
spido<=datain[5];
dstate<=8'd8;
end
8'd8:
begin
spics<=1'b0;
spiclk<=1'b1;
spido<=datain[5];
dstate<=8'd9;
end
8'd9:
begin
spics<=1'b0;
spiclk<=1'b0;
spido<=datain[4];
dstate<=8'd10;
end
8'd10:
begin
spics<=1'b0;
spiclk<=1'b1;
spido<=datain[4];
dstate<=8'd11;
end
8'd11:
begin
spics<=1'b0;
spiclk<=1'b0;
spido<=datain[3];
dstate<=8'd12;
end
8'd12:
begin
spics<=1'b0;
spiclk<=1'b1;
spido<=datain[3];
dstate<=8'd13;
end
8'd13:
begin
spics<=1'b0;
spiclk<=1'b0;
spido<=datain[2];
dstate<=8'd14;
end
8'd14:
begin
spics<=1'b0;
spiclk<=1'b1;
spido<=datain[2];
dstate<=8'd15;
end
8'd15:
begin
spics<=1'b0;
spiclk<=1'b0;
spido<=datain[1];
dstate<=8'd16;
end
8'd16:
begin
spics<=1'b0;
spiclk<=1'b1;
spido<=datain[1];
dstate<=8'd17;
end
8'd17:
begin
spics<=1'b0;
spiclk<=1'b0;
spido<=datain[0];
dstate<=8'd18;
end
8'd18:
begin
spics<=1'b0;
spiclk<=1'b1;
spido<=datain[0];
dstate<=8'd19;
end
8'd19:
begin
spics<=1'b1;
spiclk<=1'b1;
spido<=1'b1;
dstate<=8'd20;
// send_rdy<=1'b1;
end
8'd20://一個字節數據發送完成
begin
spics<=1'b1;
spiclk<=1'b1;
spido<=1'b1;
dstate<=8'd0;
spistate<=idle;
send_rdy<=1'b1;//發送完成一幀數據標誌位
end
default
begin
spics<=1'b1;
spiclk<=1'b1;
spido<=1'b1;
spistate<=idle;
end
endcase//對應發送狀態
end
receive_data://接收數據狀態
begin
case (dstate) //片選信號有效
8'd0:
begin
spics <= 1'b1;
spiclk <= 1'b1;
spido <= 1'b1;
dstate <= 8'd1;
end
8'd1:
begin
spics <= 1'b1;
spiclk <= 1'b1;
spido <= 1'b1;
dstate <= 8'd2;
end
8'd2:
begin
spics <= 1'b0;
spiclk <= 1'b1;
spido <= 1'b1;
dstate <= 8'd3;
end
8'd3:
begin
spics <= 1'b0;
spiclk <= 1'b0;
dreceive[7] <= spidi;
dstate <= 8'd4;
end
8'd4:
begin
spics <= 1'b0;
spiclk <= 1'b1; //緊接着上升沿的下降沿數據被讀取
dreceive[7] <= spidi; //接收數據最高位
dstate <= 8'd5;
end
8'd5:
begin
spics <= 1'b0;
spiclk <= 1'b0;
dstate <= 8'd6;
dreceive[6] <= spidi;
end
8'd6:
begin
spics <= 1'b0;
spiclk <= 1'b1;
dreceive[6] <= spidi;
dstate <= 8'd7;
end
8'd7:
begin
spics <= 1'b0;
spiclk <= 1'b0;
dstate <= 8'd8;
dreceive[5] <= spidi;
end
8'd8:
begin
spics <= 1'b0;
spiclk <= 1'b1;
dreceive[5] <= spidi;
dstate <= 8'd9;
end
8'd9:
begin
spics <= 1'b0;
spiclk <= 1'b0;
dstate <= 8'd10;
dreceive[4] <= spidi;
end
8'd10:
begin
spics <= 1'b0;
spiclk <= 1'b1;
dreceive[4] <= spidi;
dstate <= 8'd11;
end
8'd11:
begin
spics <= 1'b0;
spiclk <= 1'b0;
dstate <= 8'd12;
dreceive[3] <= spidi;
end
8'd12:
begin
spics <= 1'b0;
spiclk <= 1'b1;
dreceive[3] <= spidi;
dstate <= 8'd13;
end
8'd13:
begin
spics <= 1'b0;
spiclk <= 1'b0;
dstate <= 8'd14;
dreceive[2] <= spidi;
end
8'd14:
begin
spics <= 1'b0;
spiclk <= 1'b1;
dreceive[2] <= spidi;
dstate <= 8'd15;
end
8'd15:
begin
spics <= 1'b0;
spiclk <= 1'b0;
dstate <= 8'd16;
dreceive[1] <= spidi;
end
8'd16:
begin
spics <= 1'b0;
spiclk <= 1'b1;
dreceive[1] <= spidi;
dstate <= 8'd17;
end
8'd17:
begin
spics <= 1'b0;
spiclk <= 1'b0;
dstate <= 8'd18;
dreceive[0] <= spidi;
end
8'd18:
begin
spics <= 1'b0;
spiclk <= 1'b1;
dreceive[0] <= spidi; //接收數據最低位
dstate <= 8'd19;
end
8'd19:
begin
spics <= 1'b1;
spiclk <= 1'b1;
spido<= 1'b1;
dstate <= 8'd20;
dataout[7:0]<= dreceive[7:0];
//rec_rdy<=1'b1;
end
8'd20:
begin
spics <= 1'b1;//片選信號無效
spiclk <= 1'b1;
spido <= 1'b1;
dstate <= 8'd0;
spistate <= idle;
rec_rdy<=1'b1;//接收完一個字節標誌位有效
end
default
begin
spics<=1'b1;
spiclk<=1'b1;
spido<=1'b1;
end
endcase//對應接收狀態
end
endcase
end
end