UART串口通信设计实例



《UART串口通信设计实例》由会员分享,可在线阅读,更多相关《UART串口通信设计实例(8页珍藏版)》请在装配图网上搜索。
1、2.5 UART串口通信设计实例(1) 接下来用刚才采用的方法设计一个典型实例。在一般的嵌入式开发和FPGA设计中,串口UART是使用非常频繁的一种调试手段。下面我们将使用Verilog RTL编程设计一个串口收发模块。这个实例虽然简单,但是在后续的调试开发中,串口使用的次数比较多,这里阐明它的设计方案,不仅仅是为了讲解RTL编程,而且为了后续使用兼容ARM9内核实现嵌入式开发。 串口在一般的台式机上都会有。随着笔记本电脑的使用,一般会采用USB转串口的方案虚拟一个串口供笔记本使用。图2-7为UART串口的结构图。串口具有9个引脚,但是真正连接入FPGA开发板的一般只有两个引脚。这两个
2、引脚是:发送引脚TxD和接收引脚RxD。由于是串行发送数据,因此如果开发板发送数据的话,则要通过TxD线1 bit接着1 bit发送。在接收时,同样通过RxD引脚1 bit接着1 bit接收。 再看看串口发送/接收的数据格式(见图2-8)。在TxD或RxD这样的单线上,是从一个周期的低电平开始,以一个周期的高电平结束的。它中间包含8个周期的数据位和一个周期针对8位数据的奇偶校验位。每次传送一字节数据,它包含的8位是由低位开始传送,最后一位传送的是第7位。 (点击查看大图)图2-8 串口发送串行数据的格式示意图 上述格式只是发送串口数据最通常的格式。打开Windows自带
3、的串口收发软件:超级终端,可以配置相关的选项。比如,每秒位数,可以设定一个周期的长度:如果我们设定为9600,则上图中1位持续的时间是:1/9600s。数据位也可以从5、6、7、8中选一位。奇偶校验可以从偶校验、奇校验、无、标记、空格中任选一个。停止位可以是:1、1.5、2。在这里,我们选择最通常的配置,如图2-9所示。 这个设计有两个目的:一是从串口中接收数据,发送到输出端口。接收的时候是串行的,也就是一个接一个的;但是发送到输出端口时,我们希望是8位放在一起,成为并行状态(见图2-10)。我们知道,串口中出现信号,是没有先兆的。如果出现了串行数据,则如何通知到输出端口呢?我们引
4、入“接收有效”端口。“接收有效”端口在一般情况下都是低电平,一旦有数据到来时,它就变成高电平。下一个模块在得知“接收有效”信号为高电平时,它就明白:新到了一个字节的数据,放在“接收字节”端口里面。 图2-10 串口接收和发送数据时进行串并转换的示意图 二是发送数据到串口。发送数据的时候,我们也希望输入端口能够给出一个简单的形式。我们引入“发送有效”信号,它为高电平,表示我们希望把“发送字节”送入TxD发送出去。但是“发送有效”信号是否生效是有限制的,也就是正在发送的时候,是不能接收新的数据并发送的。所以,我们引入一个“发送状态”信号,它标识当前的“发报机”是否处于忙碌状态。如
5、果“发报机”处于忙碌状态,则它拒绝“发送有效”信号,不予执行。 根据上面的分析,可以确定端口信号如下: 1. module rxtx ( 2. clk, 3. rst, 4. rx, 5. tx_vld, 6. tx_data, 7. 8. rx_vld, 9. rx_data, 1
6、0. tx, 11. txrdy 12. ); 13. input clk; 14. input rst; 15. input rx; 16. input tx_vld; 17. input [7:0] tx_data; 18. 19. output rx_vld; 20. output [7:0] rx_data; 21. o
7、utput tx; 22. output txrdy; rx对应RxD,tx对应TxD。rx_vld就是“接收有效”信号,rx_data则是“接收字节”信号。tx_vld是“发送有效”信号,tx_data是“发送字节”信号。txrdy是“发送状态”信号,它是低电平表示正处于发送状态,不接收新的字节而进行发送。 我们知道,串行数据的频率是9600Hz,而FPGA开发板的频率却是非常高的。这里,我们假定FPGA的工作频率是25MHz,则串口发送1位信息,则FPGA上的clk需要计数:25 000 000/9600=2604次。我们知道rx一旦变化,不论是
8、从0到1,还是从1到0,都表示1位信息的传递开始。因此,我们在设计一个最大计数值为2604的计数器的时候,rx的变化都将导致这个计数器重新开始计数。如果计数到2604附近,rx发生变化,那么又将导致计数器清零;如果rx没有发生变化,没关系,计数器在计数到2604时,自动清零。因此,rx的变化会不断调整计数器的计数。 在这个计数器计算到中间,也就是1302时,是最佳采样时刻,这个时候,rx的电平是我们需要知道的位信息。对于rx的接收,需要2~3个寄存器同步,来消除异步传送的不确定性。下面描述的rx1、rx2、rx3、rxx只是对rx进行延时,消除异步效果。 1. reg rx1,rx2,rx
9、3,rxx; 2. always @ ( posedge clk ) begin 3. rx1 <= rx; 4. rx2 <= rx1; 5. rx3 <= rx2; 6. rxx <= rx3; 7. end 对于rxx,我们将检测它的变化,这个变化将作为置位计数器的标志。rx_change表示rxx发生了改变,它比较了rx_dly和rxx的差别。 1. reg rx_dly; 2. always @ ( posedge clk ) 3. rx_dly <= rxx; 4.
10、 5. wire rx_change; 6. assign rx_change = (rxx != rx_dly ); 下面将实现一个计数器,它将以2604为周期进行计数。如果rx保持长时间不变,比如传送多个1或多个0,则计数器以2604为周期计数,可以计算到底有多少个1或0传送—因为在传送多个1或0时,rx是不会发生变化的,但是计数器会从2604恢复到0,可以计算传递了多少位。rx_en是我们提取rx的标志时刻,这时候计数器位于串行数据的中间—计数到1302时。 1. reg [13:0] rx_cnt; 2. always @ ( posedge clk or po
11、sedge rst ) 3. if ( rst ) 4. rx_cnt <= 0; 5. else if ( rx_change | ( rx_cnt==14'd2603 ) ) 6. rx_cnt <= 0; 7. else 8. rx_cnt <= rx_cnt + 1'b1; 9. 10. wire rx_en; 11. assign rx_en = ( rx_cnt==14'd1301 ); 如果在RxD检测到0,即在rx_en等于1时,检测到rxx等于1'b0,我们知道探测到一个字节的传送开始。这标志着
12、后续将传送8位数据、1个奇偶校验位和1个停止位。因此,在rx_en==1'b1,rxx==1'b0时,启动一个以10为周期的计数器。以10为周期的计数器递进的标志是rx_en==1'b1—这是传送1个位的标志。当计数到9时,计数终止,计数器清0,此时一个字节的数据接收完毕。在第二次探测到rxx在rx_en有效时等于0,又将重复第二次计数,如此周而复始。 1. reg data_vld; 2. always @ ( posedge clk or posedge rst ) 3. if ( rst ) 4. data_vld <= 1'b0; 5. else if
13、 ( rx_en & ~rxx & ~data_vld ) 6. data_vld <= 1'b1; 7. else if ( data_vld & ( data_cnt==4'h9 ) & rx_en ) 8. data_vld <= 1'b0; 9. else; 10. 11. reg [3:0] data_cnt; 12. always @ ( posedge clk or posedge rst ) 13. if ( rst ) 14. data_cnt <= 4'b0; 15. else if ( d
14、ata_vld ) 16. if ( rx_en ) 17. data_cnt <= data_cnt + 1'b1; 18. else; 19. else 20. data_cnt <= 4'b0; 我们在前面已经用到了这个计数器形式。在这里,使用这种类型计数器,就是通过探测到传送开始的0位,启动一个以10为周期的计数器进行对后续位的接收,并在接收完毕后,自动恢复到初态。在data_vld为高电平时,对应8位数据、1个奇偶校验位以及1个停止位的接收。所以data_cnt从0到7计数时,我们可以依次接收rxx的数据。因为r
15、xx是从低位到高位传递,所以向右移位。 1. reg [7:0] rx_data; 2. always @ ( posedge clk or posedge rst ) 3. if ( rst ) 4. rx_data <= 7'b0; 5. else if ( data_vld & rx_en & ~data_cnt[3] ) 6. rx_data <= {rxx,rx_data[7:1]}; 7. else; 同理,在data_vld计数到停止位时,我们认为该字节接收完毕,发送一个周期的高电平信号,通知给其他模块:表示已经接收到1
16、字节,位于rx_data内。其他模块在探测到rx_vld为高电平时,取出rx_data。 1. always @ ( posedge clk or posedge rst ) 2. if ( rst ) 3. rx_vld <= 1'b0; 4. else 5. rx_vld <= data_vld & rx_en & ( data_cnt==4'h9); 以上就是串口接收数据的设计代码。在发送时,我们也希望能够利用rx_en这个定时信息,使用它来发送数据。首先,我们在tx_vld==1'b1时,保存tx_data,用来发送。tx_rdy_data就是用来
17、暂存tx_data的。只有在txrdy等于1的情况下,也就是发送单元处于空闲状态时,tx_data才能保存入tx_rdy_data。 1. reg [7:0] tx_rdy_data; 2. always @ ( posedge clk or posedge rst ) 3. if ( rst ) 4. tx_rdy_data <= 8'b0; 5. else if ( tx_vld & txrdy ) 6. tx_rdy_data <= tx_data; 7. else; 当tx_vld有效时,会触发一个发送过程。在发送时,tx会发送
18、起始0位、8位数据、1个奇偶校验位和1个停止位,总共是11位。因此,tx_vld触发了一个以11为周期的计数器,在每计数到一个数以后,会发送相应的位信息。在发送完毕后,计数器清0。 1. reg tran_vld; 2. always @ ( posedge clk or posedge rst ) 3. if ( rst ) 4. tran_vld <= 1'b0; 5. else if ( tx_vld ) 6. tran_vld <= 1'b1; 7. else if ( tran_vld & rx_en & ( tran_cn
19、t== 4'd10 ) ) 8. tran_vld <= 1'b0; 9. else; 10. 11. reg [3:0] tran_cnt; 12. always @ ( posedge clk or posedge rst ) 13. if ( rst ) 14. tran_cnt <= 4'b0; 15. else if ( tran_vld ) 16. if( rx_en ) 17. tran_cnt <= tran_cnt + 1'b1; 18. else; 19
20、. else 20. tran_cnt <= 4'b0; 在上面,我们用到了同类的计数器。这类计数器通常由一个电平触发,然后持续固定的时间,这样就能做到收发自如,可控性比较好。下面,我们根据计数器计数值发出tx信息。tx的电平会在每次rx_en有效的时候改变。 1. reg tx; 2. always @ ( posedge clk or posedge rst ) 3. if ( rst ) 4. tx <= 1'b1; 5. else if ( tran_vld ) 6. if ( rx_en ) 7. cas
21、e ( tran_cnt ) 8. 4'd0 : tx <= 1'b0; 9. 4'd1 : tx <= tx_rdy_data[0]; 10. 4'd2 : tx <= tx_rdy_data[1]; 11. 4'd3 : tx <= tx_rdy_data[2]; 12. 4'd4 : tx <= tx_rdy_data[3]; 13. 4'd5 : tx <= tx_rdy_data[4]; 14. 4'd6 : tx <= t
22、x_rdy_data[5]; 15. 4'd7 : tx <= tx_rdy_data[6]; 16. 4'd8 : tx <= tx_rdy_data[7]; 17. 4'd9: tx <= ^tx_rdy_data; 18. 4'd10: tx <= 1'b1; 19. default: tx <= 1'b1; 20. endcase 21. else; 22. else 23. tx<= 1'b1; 最后,给出txrdy的描述。txrdy对外界提供一个信息:不能在txrdy为低电平时传送新的数据,因为发送单元正处于传送数据的过程中,不能接收新的数据了。 1. assign txrdy = ~tran_vld; 如果在上面的代码的基础上再加上endmodule,就是一个完整的Verilog RTL设计了。
- 温馨提示:
1: 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
2: 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
3.本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
5. 装配图网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。