我是靠谱客的博主 开心火,最近开发中收集的这篇文章主要介绍板级通信总线之SPI及其Verilog实现,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

打算写几篇专题,系统总结下常用的片上总线、现场总线,就先从最常用的SPI开始吧。

 

1. SPI是干什么的?除了SPI还有那些其它电路板及的通讯总线?有何差别?

相信接触过MCU的同学对SPI都不陌生,详细定义就不罗嗦了。SPI常用的场合包括ADC读写、存储芯片读写、MCU间通讯等等。可以一主多从(通过片选来选择Slave),也可以做成菊花链等等形式的拓扑。与SPI类似的总线还有IIC、UART等,甚至还有很多单根线的总线,原理都是基于简单的串行通信,区别在于收发时序和连接拓扑。要熟练使用这些总线,关键在于理解其时序图,在此基础上创造各种变种的总线形式也不是难事(当然为了设计的通用性不建议这么做)。

以下维基百科的SPI词条介绍非常全面,推荐阅读。

https://en.wikipedia.org/wiki/Serial_Peripheral_Interface_Bus#Data_transmission

 

2. SPI是什么样的?

在此借用一张维基百科上的图,SPI通常有4根线,SS用于选定当前通信的slave,SCLK为通信的基准时钟,采样/发送都在时钟边沿执行,MOSI、MISO为串行的数据线。

以下是一个典型的SPI时序图,Master和Slave均在时钟上升沿采样,下降沿发送数据。数据从最高位(MSB)开始发送。

需要注意图中所有的时序关系都要被满足,包括CS下降沿到第1个时钟上升沿间隔(tsclk_su)、数据的建立时间(tSU)、保持时间(tHD)等等。通常这些参数由具体的器件决定,如果不满足则有通信失败的风险。

 

3. 如何使用SPI?SPI有哪几种配置模式(相位、极性)?

根据SPI时钟信号的空闲状态、是上升沿采样还是下降沿采样,SPI有四种模式。CPOL=0表示时钟空闲时为低电平,反之为高电平;CPHA=0表示时钟信号第一个边沿是采样边沿,反之表示第2个边沿是采样边沿。对于带SPI接口的MCU而言,通常可由软件配置CPOL(Clock Polarity)、CPHA(Clock Phase),以适应和不同类型器件的通信。

 

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,used to generate dataSclk signal
reg dataCS_reg;
reg dataSclk_reg;
reg[7:0] Count1;
always @(posedge clk or negedge rst_n)
if(!rst_n)
Count1 <= 8'd0;
else if(Count1 == SCLK_DIVIDER)
Count1 <= 8'd0;
else
Count1 <= Count1 + 1'b1;
// generate CS and Sclk sequence

reg [5:0] i;//Step number 
always @(posedge clk or negedge rst_n)
if(!rst_n)begin
i <= 6'd0;
dataSclk_reg <= 1'b1;//Sclk high at reset
dataCS_reg <= 1'b1;
//CS high at reset
end
else begin
case(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
else;
//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
else;
//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;
end
else;
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
else;
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
else ;
default ;
endcase
end ;
// - receive and send 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
else if(dataCS) begin
j <= 6'd0;
sDataWr_reg <= 1'b0;
pDataRd_reg <= 16'd0;
rxDone_reg <= 1'b0;
end
else begin
// - first falling of Sclk, send MSB of send data
if(j == 6'd0) begin

j <= j + 1'b1;
sDataWr_reg <= pDataWr[15];//send data 
pDataRd_reg <= 16'd0;//receive data clear
rxDone_reg <= 1'b0;
end
// - 2nd to 16th falling of Sclk
else if(j <= 6'd15) begin

j <= j + 1'b1;
sDataWr_reg <= pDataWr[15-j];//send 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
else if(j == 6'd16) begin 
j <= j + 1'b1;
sDataWr_reg <= 1'b0;//send data clear
pDataRd_reg[0] <= sDataRd;//receive data
rxDone_reg <= 1'b1;//receive done
end
else 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 @(posedge clk or negedge rst_n)
if(!rst_n)
pDataRd_l <= 16'd0;
else if(rxDone_reg) begin
pDataRd_l <= pDataRd_reg;
end
else begin
pDataRd_l <= pDataRd_l;
end
// - delay sDataWr for 1 main clk(10ns)
reg sDataWr_dly;
always @(posedge clk or negedge rst_n)
if(!rst_n)
sDataWr_dly <= 1'b0;
else if(sDataWr_reg) begin
sDataWr_dly <= 1'b1;
end
else begin
sDataWr_dly <= 1'b0;
end
// - output assignment
assign dataCS = dataCS_reg;
assign dataSclk = dataSclk_reg;
assign sDataWr = sDataWr_dly;
assign pDataRd = pDataRd_l;
endmodule
View Code:SPI Master
 1 module Serial2Parallel_Slave (
 2
 3
input rst_n,
 4
input clk,
 5
 6
input dataCS,
 7
input dataSclk,
 8
 9
input sDataRd,
10
input [15:0] pDataWr,
11
12
output sDataWr,
13
output [15:0] pDataRd
14 );
15
16
// - SPI read and write
17
reg sDataWr_reg;
18
reg [15:0] pDataRd_reg;
19
reg rxDone_reg;
20
reg [5:0] j;//operation steps
21
always @(negedge dataSclk or negedge rst_n)
22
if(!rst_n) begin
23
j <= 6'd0;
24
sDataWr_reg <= 1'b0;
25
pDataRd_reg <= 16'd0;
26
rxDone_reg <= 1'b0;
27
end
28
// - CS high,clear j & AD data
29
else if(dataCS) begin
30
j <= 6'd0;
31
sDataWr_reg <= 1'b0;
32
pDataRd_reg <= 16'd0;
33
rxDone_reg <= 1'b0;
34
end
35
else begin
36
// - first falling of Sclk, send MSB of send data
37
if(j == 6'd0) begin

38
j <= j + 1'b1;
39
sDataWr_reg <= pDataWr[15];//send data 
40
pDataRd_reg <= 16'd0;//receive data clear
41
rxDone_reg <= 1'b0;
42
end
43
44
// - 2nd to 16th falling of Sclk
45
else if(j <= 6'd15) begin

46
j <= j + 1'b1;
47
sDataWr_reg <= pDataWr[15-j];//send data 
48
pDataRd_reg[16-j] <= sDataRd;//receive data
49
rxDone_reg <= 1'b0;
50
end
51
52
// - at 17th falling of sclk_fbk, CS is still low, receive LSB of receive data
53
else if(j == 6'd16) begin 
54
j <= j + 1'b1;
55
sDataWr_reg <= 1'b0;//send data clear
56
pDataRd_reg[0] <= sDataRd;//receive data
57
rxDone_reg <= 1'b1;//receive done
58
end
59
else begin
60
j <= j;
61
sDataWr_reg <= sDataWr_reg;
62
pDataRd_reg <= pDataRd_reg;
63
rxDone_reg <= rxDone_reg;
64
end
65
end
66
67
// - data latch for pDataRd
68
reg [15:0] pDataRd_l;
69
always @(posedge dataCS or negedge rst_n)
70
if(!rst_n)
71
pDataRd_l <= 16'd0;
72
else if(rxDone_reg) begin
73
pDataRd_l <= pDataRd_reg;
74
end
75
else begin
76
pDataRd_l <= pDataRd_l;
77
end
78
79
// - output assignment
80
assign sDataWr = sDataWr_reg;
81
assign pDataRd = pDataRd_l;
82
83
84 endmodule
View Code:SPI Slave

 

转载于:https://www.cnblogs.com/lobster89/p/5197266.html

最后

以上就是开心火为你收集整理的板级通信总线之SPI及其Verilog实现的全部内容,希望文章能够帮你解决板级通信总线之SPI及其Verilog实现所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(38)

评论列表共有 0 条评论

立即
投稿
返回
顶部