学习过单片机的朋友估计对SPI不陌生吧。想象一下74HC595 的传输方式相似与SPI传输方式,不同之点就是多了一个STCP(锁存信号)出来。
上面的示意图表示了SPI Module是一个主设备,而SMG Display是一个从设备,而且SPI Module有8位数据的输入,串行时钟和串行数据输出,串行数据传出是一个字节。在示意图中还表示了,除了数据的传输是上升沿有效之外,SPI Module 还有 Start Signal 和 Done Signal. Start Signal 是用来启动 SPI Module ,而 Done Signal 是反馈给更上一层的“东西”。
1.module spi_module
2.(
3. RST, CLK, Send_Sig, Done_Sig, DI, SDO, SCL
4.);
5. input RST;
6. input CLK;
7. input [7:0]DI;
8. input Send_Sig;
9. output SDO;
10. output SCL;
11. output Done_Sig;
12.
13. /*****************************************/
14. wire isStart = Send_Sig;
15. /*****************************************/
16.
17. reg [9:0]Counter;
18.
19. always @ ( posedge CLK or negedge RST )
20. if( !RST )
21. Counter <= 10'd0;
22. else if( Counter == 500 )
23. Counter <= 10'd0;
24. else if( isStart )
25. Counter <= Counter + 1'd1;
26. else
27. Counter <= 10'd0;
28.
29. /*****************************************/
30. reg [4:0]j;
31.
32. always @ ( posedge CLK or negedge RST )
33. if( !RST )
34. j <= 5'd0;
35. else if( Counter == 500 )
36. j <= j + 1'd1;
37. else if( j == 18 )
38. j <= 5'd0;
39. else if( !isStart )
40. j <= 5'd0;
41. /*****************************************/
42. reg rSCL;
43.
44. always @ ( posedge CLK or negedge RST )
45. if( !RST )
46. rSCL <= 1'b1;
47. else if( isStart )
48. case ( j )
49.
50. 0:
51. rSCL <= 1'b1;
52.
53. 1, 3, 5, 7, 9, 11, 13, 15:
54. rSCL <= 1'b0;
55. 2, 4, 6, 8, 10, 12, 14, 16:
56. rSCL <= 1'b1;
57.
58. endcase
59.
60. assign SCL = rSCL;
61.
62. /*****************************************/
63.
64. reg rDO;
65. reg isDone;
66. reg [7:0]rData;
67.
68. always @ ( posedge CLK or negedge RST )
69. if( !RST )
70. begin
71. rDO <= 1'b0;
72. rData <= 8'd0;
73. isDone <= 1'b0;
74. end
75. else if( isStart )
76. case ( j )
77. 8'd0: rData <= DI;
78. 8'd1: rDO <= rData[7];
79. 8'd3: rDO <= rData[6];
80. 8'd5: rDO <= rData[5];
81. 8'd7: rDO <= rData[4];
82. 8'd9: rDO <= rData[3];
83. 8'd11: rDO <= rData[2];
84. 8'd13: rDO <= rData[1];
85. 8'd15: rDO <= rData[0];
86. 8'd17: isDone <= 1'b1;
87. endcase
88. else
89. begin
90. rData <= 8'd0;
91. rDO <= 1'b0;
92. isDone <= 1'b0;
93. end
94.
95. assign SDO = rDO;
96. assign Done_Sig = isDone;
97.
98. /*****************************************/
99.endmodule
(为了使代码更整洁,我不加注释了。Quartes II 对中文的支持不够友好... ╮(╯▽╰)╭)
1. 第1行到11行是一如既往熟悉的定义,自己看着明白吧。
2. 第14行声明了Start_Sig 是 Send_Sig 同一个连接的。
3. 第15行到29行定义了对SCL(串行时钟信号)的延迟。“Counter == 500” 中的 500是随
意取的。目的就是为了延迟SCL的时钟周期。
注意点:"Counter == 500" 应该更改为 “ Counter == 10 'd500”。
4. 第30行到41行定义了j计数器。j计数器它就伟大了,它的工作就是记录SCL信号的时间周
期。
5. 第42行到60行这个就重要了。rSCL是用来驱动SCL的触发器。
在j 等于 0 的时候,用来初始化rData数据寄存器(77行)。
而j >= 1 的奇数,都是SCL信号半时钟周期的低电平(53行)。凡是j的1~15(奇数),都是
用来设置rDO 寄存器。
在这里我们先暂停一下,回顾一下SPI一些概念: SPI的传输都是从MSB开始。 当SPI主设备为从设备写入数据设计到几个概念就是“设置”和“采样”。 设置:发生在低电平,主设备将输出数据更新。 采样:发生在高电平,从设备读取数据信号的值。 |
对于主设备的设计,我们只关系到“设置”,而j的1~15(奇数)都是“设置”的动作。(78行到86行)
j等于1的时候:将rDO设置为rData[7]的值。
......
j等于15的时候:将rDO设置为rData[0]的值。
6. 第55行与地53行的动作悄悄是相反,凡是j的2~16(偶数),都是将SCL信号的半时钟周
期设置为高电平。
7. j如此将SCL信号一上一下,就完成了8个有效的上升沿的SCL信号,传输8位的数据。
8. 当j等于17时使isDone寄存器置一(86行),为了就是反馈更高一层的“东西”,表示完
成一个字节的数据传输( 96行)。
9. 最后就是isStart 这个wire型数据,它的定义是等价于 Start_Sig ( 14行)的。当isStart为高电
平的时候,(24行)Counter计数器开始奇数,rSCL和 rDO寄存器才开始工作(47行,75
行)。相反的当isStart为低电平,rSCL和rDO寄存器就不工作,而且j计数器也一直清零
(39行),为了就是双层保障。
10. 再谈谈初始化的故。我们知道SPI的时钟信号为上升沿有效,所以rSCL寄存器都必须初始
化为高电平(46行)。有一个现象很微妙,当j奇数到18的时候(37行),rSCL的值会是高
电平。啊哈!除此之外rData 和rDO的初始化都是0值(71~72行),而且一旦它们使用
完后都必须恢复为0值(90~91行)。
(故事还没有结束O(∩_∩)O)
11. isDone寄存器,是用于驱动Done_Sig(96行)。它的地位很重要,因为它是唯一联系上
一层“东西”的信号。isDone寄存器初始化值为0(73行),而j计数器为17的时候被设
置为高电平(86行),而使用过后isDone寄存器恢复为0值(92行)。
Testbench代码:
1.`timescale 1 ns/ 1 ns
2.module spi_module_vlg_tst();
3.
4. reg CLK;
5. reg [7:0] DI;
6. reg RST;
7. reg Send_Sig;
8.
9. wire SCL;
10. wire SDO;
11. wire Done_Sig;
12.
13. spi_module i1
14. (
15. .CLK(CLK), .DI(DI), .RST(RST), .SCL(SCL), .SDO(SDO),
16. .Send_Sig(Send_Sig), .Done_Sig( Done_Sig )
17. );
18.
19. initial
20. begin RST = 0; #100; RST = 1; end
21.
22. initial
23. begin CLK = 0; forever #10 CLK = ~CLK; end
24.
25. initial
26. begin DI = 8'h8e; Send_Sig = 1; end
27.
28.endmodule
Modelsim仿真载图:
问1. 在Modelsim仿真的时候,当传输一个字节完毕后,为什么Done_Sig 一直居高不下?
答:这是因为isStart的缘故,在Verilog 代码中 39行,87行到93行中,isDone寄存器被置零是
发生在isStart == 0 的条件下,而Testbench中,Send_Sig 由始至终都是置一的关系。多
以才会产生以上的现象。
问2. 继问题1, 这样做有什么好处?
答:你要知道Send_Sig和Done_Sig 是给予上一层作于通行使用,好处就是简化了底层模块
的设计。又或者说,底层的模块和上一层的模块之间“你做你的,我做我的”。
问3. “Counter == 500”的特别意义?
答:这也没什么特别意义,为了就是降低SCL的频率。SCL的频率必须小于源时钟频率。
这是最底层的模块了,所以在某种程度上是最重要的。在结论上是没有什么话好所,或许还有很多地方我没有详细说明清楚,故都是一些小细节,自己看着办吧。
源码下载:
https://static.assets-stash.eet-china.com/album/old-resources/2010/5/14/5e84a716-5648-4932-9ef4-9ba4f3b06b1d.rar
用户410992 2011-10-26 18:29
用户1373959 2010-5-18 21:27
gujunyi1_407560534 2010-4-28 21:05