板级通信总线之SPI及其Verilog实现
打算写⼏篇专题,系统总结下常⽤的⽚上总线、现场总线,就先从最常⽤的SPI开始吧。
1. SPI是⼲什么的?除了SPI还有那些其它电路板及的通讯总线?有何差别?
相信接触过MCU的同学对SPI都不陌⽣,详细定义就不罗嗦了。SPI常⽤的场合包括ADC读写、存储芯⽚读写、MCU间通讯等等。可以⼀主多从(通过⽚选来选择Slave),也可以做成菊花链等等形式的拓扑。与SPI类似的总线还有IIC、UART等,甚⾄还有很多单根线的总线,原理都是基于简单的串⾏通信,区别在于收发时序和连接拓扑。要熟练使⽤这些总线,关键在于理解其时序图,在此基础上创造各种变种的总线形式也不是难事(当然为了设计的通⽤性不建议这么做)。
以下维基百科的SPI词条介绍⾮常全⾯,推荐阅读。
2. SPI是什么样的?
在此借⽤⼀张维基百科上的图,SPI通常有4根线,SS⽤于选定当前通信的slave,SCLK为通信的基准时钟,采样/发送都在时钟边沿执⾏,MOSI、MISO为串⾏的数据线。
以下是⼀个典型的SPI时序图,Master和Slave均在时钟上升沿采样,下降沿发送数据。数据从最⾼位(MSB)开始发送。
需要注意图中所有的时序关系都要被满⾜,包括CS下降沿到第1个时钟上升沿间隔(t sclk_su)、数据的建⽴时间(t SU)、保持时间(t HD)等等。通常这些参数由具体的器件决定,如果不满⾜则有通信失败的风险。
3. 如何使⽤SPI?SPI有哪⼏种配置模式(相位、极性)?
根据SPI时钟信号的空闲状态、是上升沿采样还是下降沿采样,SPI有四种模式。CPOL=0表⽰时钟空闲时为低电平,反之为⾼电平;CPHA=0表⽰时钟信号第⼀个边沿是采样边沿,反之表⽰第2个边沿是采样边沿。对于带SPI接⼝的MCU⽽⾔,通常可由软件配置
CPOL(Clock Polarity)、CPHA(Clock Pha),以适应和不同类型器件的通信。
4. 如何⽤verilog 编写SPI协议?
以下是使⽤verilog写的SPI主从通信代码,经过实测通信OK,可供参考。主从都在下降沿置数,同时在下降沿采样上⼀次发送的数据。为了尽量在主时钟较慢的情况下提⾼通信速度,采⽤的是⾮同步采样⽅式(即直接⽤SCLK边沿触发采样/发送,⽽不⽤主时钟对SCLK进⾏同步)。
module Serial2Parallel_Master #(
parameter SCLK_DIVIDER = 8'd0 //Sclk Freq = Clk/2 / (SCLK_DIVIDER + 1)
)(
input rst_n,
input clk,
input sDataRd,
input [15:0] pDataWr,
output dataCS,
output dataSclk,
output sDataWr,
output [15:0] pDataRd
);
// counter,ud to generate dataSclk signal
reg dataCS_reg;
clockworkmodreg dataSclk_reg;
reg[7:0] Count1;
always @(podge clk or negedge rst_n)
if(!rst_n)
Count1 <= 8'd0;
el if(Count1 == SCLK_DIVIDER)
Count1 <= 8'd0;
el
Count1 <= Count1 + 1'b1;
// generate CS and Sclk quence
reg [5:0] i;//Step number
always @(podge clk or negedge rst_n)
if(!rst_n)begin
i <= 6'd0;
dataSclk_reg <= 1'b1;//Sclk high at ret
dataCS_reg <= 1'b1; //CS high at ret
end
el begin
ca(i)
//pull down CS at the beginning
6'd0:
if(Count1 == SCLK_DIVIDER) begin
i <= i + 1'b1;
dataSclk_reg <= 1'b1;
dataCS_reg <= 1'b0;
end
el;
//generate 1st to 17th Sclk falling edge
6'd1,6'd3,6'd5,6'd7,6'd9,6'd11,6'd13,6'd15,6'd17,6'd19,6'd21,6'd23,6'd25,5'd27,6'd29,6'd31,6'd33:
if(Count1 == SCLK_DIVIDER) begin
dataSclk_reg <= 1'b0;
dataCS_reg <= 1'b0;
i <= i + 1'b1;
end
el;
//generate 1st to 16th Sclk rising edge
6'd2,6'd4,6'd6,6'd8,6'd10,6'd12,6'd14,6'd16,6'd18,6'd20,6'd22,6'd24,6'd26,6'd28,6'd30,6'd32:
if(Count1 == SCLK_DIVIDER) begin
dataSclk_reg <= 1'b1;
dataCS_reg <= 1'b0;
i <= i + 1'b1;
i <= i + 1'b1;
end
el;
6'd34://CS and Sclk go high
if(Count1 == SCLK_DIVIDER) begin
dataSclk_reg <= 1'b1;
dataCS_reg <= 1'b1;
i <= i + 1'b1;
end
el;
6'd35://CS keep high, Sclk go low
if(Count1 == SCLK_DIVIDER) begin
dataSclk_reg <= 1'b0;
dataCS_reg <= 1'b1;
i <= 6'd0;
end
el ;
default ;
endca
end ;
// - receive and nd SPI data
reg sDataWr_reg;
reg [15:0] pDataRd_reg;
reg rxDone_reg;
reg [5:0] j;
always @(negedge dataSclk or negedge rst_n)
if(!rst_n) begin
j <= 6'd0;
sDataWr_reg <= 1'b0;
pDataRd_reg <= 16'd0;
rxDone_reg <= 1'b0;
end
// - CS high,clear j & AD data
el if(dataCS) begin
j <= 6'd0;
sDataWr_reg <= 1'b0;
pDataRd_reg <= 16'd0;
rxDone_reg <= 1'b0;
end
el begin
// - first falling of Sclk, nd MSB of nd databeryllium
if(j == 6'd0) begin
j <= j + 1'b1;
sDataWr_reg <= pDataWr[15];//nd data
pDataRd_reg <= 16'd0;//receive data clear
rxDone_reg <= 1'b0;
end
// - 2nd to 16th falling of Sclk
2011高考试卷el if(j <= 6'd15) begin
j <= j + 1'b1;
sDataWr_reg <= pDataWr[15-j];//nd data
pDataRd_reg[16-j] <= sDataRd;//receive data
rxDone_reg <= 1'b0;
end
// - at 17th falling of sclk_fbk, CS is still low, receive LSB of receive data el if(j == 6'd16) begin
j <= j + 1'b1;
sDataWr_reg <= 1'b0;//nd data clear
pDataRd_reg[0] <= sDataRd;//receive data
rxDone_reg <= 1'b1;//receive done
end
el begin
el begin
j <= j;
sDataWr_reg <= sDataWr_reg; pDataRd_reg <= pDataRd_reg; rxDone_reg <= rxDone_reg;
end
end
// - data latch for pDataRd
reg [15:0] pDataRd_l;
always @(podge clk or negedge rst_n)
if(!rst_n)
pDataRd_l <= 16'd0;
el if(rxDone_reg) begin
pDataRd_l <= pDataRd_reg;
end
el begin
pDataRd_l <= pDataRd_l;
end
// - delay sDataWr for 1 main clk(10ns)
reg sDataWr_dly;
always @(podge clk or negedge rst_n)
if(!rst_n)
sDataWr_dly <= 1'b0;
el if(sDataWr_reg) begin
sDataWr_dly <= 1'b1;
end
el begin
sDataWr_dly <= 1'b0;
ordinalend
// - output assignment
assign dataCS = dataCS_reg;
assign dataSclk = dataSclk_reg;
assign sDataWr = sDataWr_dly;
assign pDataRd = pDataRd_l; endmodule
View Code:SPI Master
1module Serial2Parallel_Slave (
2
3input rst_n,
4input clk,
5
6input dataCS,
7input dataSclk,
8
9input sDataRd,
10input [15:0] pDataWr,
11
12output sDataWr,
13output [15:0] pDataRd
14 );
15
16// - SPI read and write
17reg sDataWr_reg;
18reg [15:0] pDataRd_reg;
19reg rxDone_reg;
20reg [5:0] j;//operation steps
20reg [5:0] j;//operation steps
21always @(negedge dataSclk or negedge rst_n)
22if(!rst_n) begin
23 j <= 6'd0;
24 sDataWr_reg <= 1'b0;
25 pDataRd_reg <= 16'd0;
26 rxDone_reg <= 1'b0;
27end
28// - CS high,clear j & AD data
29el if(dataCS) begin
30 j <= 6'd0;
31 sDataWr_reg <= 1'b0;
32 pDataRd_reg <= 16'd0;
33 rxDone_reg <= 1'b0;
34end
刘邦崛起之谜35el begin
36// - first falling of Sclk, nd MSB of nd data
泰语我爱你怎么说37if(j == 6'd0) begin
38 j <= j + 1'b1;
39 sDataWr_reg <= pDataWr[15];//nd data
40 pDataRd_reg <= 16'd0;//receive data clear
41 rxDone_reg <= 1'b0;
42end
43
44// - 2nd to 16th falling of Sclk
45el if(j <= 6'd15) begin
46 j <= j + 1'b1;
47 sDataWr_reg <= pDataWr[15-j];//nd data
48 pDataRd_reg[16-j] <= sDataRd;//receive data
49 rxDone_reg <= 1'b0;
50end
51
52// - at 17th falling of sclk_fbk, CS is still low, receive LSB of receive data 53el if(j == 6'd16) begin
54 j <= j + 1'b1;
55 sDataWr_reg <= 1'b0;//nd data clear
56 pDataRd_reg[0] <= sDataRd;//receive data
57 rxDone_reg <= 1'b1;//receive done
英语新年祝福语58end
59el begin
60 j <= j;
61 sDataWr_reg <= sDataWr_reg;
62 pDataRd_reg <= pDataRd_reg;
63 rxDone_reg <= rxDone_reg;
64end
65end
海伦多兰少儿英语66
67// - data latch for pDataRd
68reg [15:0] pDataRd_l;
69always @(podge dataCS or negedge rst_n)
70if(!rst_n)prospective
71 pDataRd_l <= 16'd0;
72el if(rxDone_reg) begin
73 pDataRd_l <= pDataRd_reg;
74end
75el begin
76 pDataRd_l <= pDataRd_l;
77end
78
79// - output assignment
80assign sDataWr = sDataWr_reg;
81assign pDataRd = pDataRd_l;
82
销售二八法则83
84endmodule