2678|4

230

帖子

2

TA的资源

纯净的硅(初级)

楼主
 

基于ZX-2型FPGA开发板的串口示波器(二) [复制链接]


串口发送接收模块设计代码分析
1.1Tx_Bps_GenTx_Bps_Gen发送波特率生成模块,每当Byte_En信号到来时,即开始产生发送一个完整字节的数据需要的完整波特率时钟信号
本设计,波特率支持9600bps921600bps例如需要产生的波特率时钟为9600bps波特率时钟频率为9600Hz周期104.17us生成9600Hz波特率时钟的核心思想就是对系统时钟进行计数,这里设定系统时钟为50MHz,则一个时钟的周期为20ns,我们只需要对系统时钟计数5208每计数5208产生一个时钟周期的高电平脉冲,即可实现生成9600Hz波特率时钟的功能。相应代码如下所示:

018     parameter system_clk = 50_000_000; /*输入时钟频率设定,默认50M*/

019

020 /*根据输入时钟频率计算生成各波特率时分频计数器的计数最大值*/        

021     localparam bps9600 = system_clk/9600 - 1;

022     localparam bps19200 = system_clk/19200 - 1;

023     localparam bps38400 = system_clk/38400 - 1;

024     localparam bps57600 = system_clk/57600 - 1;

025     localparam bps115200 = system_clk/115200 - 1;

026     localparam bps230400 = system_clk/230400 - 1;

027     localparam bps460800 = system_clk/460800 - 1;

028     localparam bps921600 = system_clk/921600 - 1;      

029     

030     reg [31:0]BPS_PARA;/*波特率分频计数器的计数最大值*/

031

032     always@(posedge Clk or negedge Rst_n)

033     if(!Rst_n)begin

034         BPS_PARA <= bps9600;/*复位时波特率默认为9600bps*/

035     end

036     else begin

037         case(Baud_Set)/*根据波特率控制信号选择不同的波特率计数器计数最大值*/

038             3'd0: BPS_PARA <= bps9600;

039             3'd1: BPS_PARA <= bps19200;

040             3'd2: BPS_PARA <= bps38400;

041             3'd3: BPS_PARA <= bps57600;

042             3'd4: BPS_PARA <= bps115200;

043             3'd5: BPS_PARA <= bps230400;

044             3'd6: BPS_PARA <= bps460800;

045             3'd7: BPS_PARA <= bps921600;            

046             default: BPS_PARA <= bps9600;

047         endcase

048     end

049     

050 //=========================================================

051     reg[12:0]Count;

052     

053     reg n_state;

054     localparam IDEL_1 = 1'b0,

055                   SEND   = 1'b1;

056                  

057     reg BPS_EN;

058     

059 /*-------波特率时钟生成控制逻辑--------------*/  

060     always@(posedge Clk or negedge Rst_n)

061     if(!Rst_n)begin

062         BPS_EN <= 1'b0;

063         n_state <= IDEL_1;

064     end

065     else begin

066         case(n_state)

067             IDEL_1:

068                 if(Byte_En)begin/*检测到字节发送使能信号,则启动波特率生成进程,同时进入发送状态*/

069                     BPS_EN <= 1'b1;

070                     n_state <= SEND;

071                 end

072                 else begin

073                     n_state <= IDEL_1;

074                     BPS_EN <= 1'b0;

075                 end

076             SEND:

077                 if(Tx_Done == 1)begin/*发送完成,关闭波特率生成进程,回到空闲状态*/

078                     BPS_EN <= 1'b0;

079                     n_state <= IDEL_1;

080                 end

081                 else begin

082                     n_state <= SEND;

083                     BPS_EN <= 1'b1;

084                 end

085             default:n_state <= IDEL_1;

086         endcase

087     end

088

089 /*-------波特率时钟生成定时器--------------*/

090     always@(posedge Clk or negedge Rst_n)

091     if(!Rst_n)

092         Count <= 13'd0;

093     else if(BPS_EN == 1'b0)

094         Count <= 13'd0;

095     else begin

096         if(Count == BPS_PARA)

097             Count <= 13'd0;

098         else

099             Count <= Count + 1'b1;

100     end

101     

102 /*输出数据接收采样时钟*/  

103 //-----------------------------------------------

104     always @(posedge Clk or negedge Rst_n)

105     if(!Rst_n)

106         Bps_Clk <= 1'b0;

107     else if(Count== 1)

108         Bps_Clk <= 1'b1;

109     else

110         Bps_Clk <= 1'b0;


18行“parameter system_clk = 50_000_000;,这里用一个全局参数定义了系统时钟,暂时设定为50M,可根据实际使用板卡上的工作时钟进行修改。
所谓波特率生成,就是用一个定时器来定时,产生频率与对应波特率时钟频率相同的时钟信号。例如,我们使用波特率为115200bps,则我们需要产生一个频率为115200Hz的时钟信号。那么如何产生这样一个115200Hz的时钟信号呢?这里,我们首先将115200Hz时钟信号的周期计算出来,1秒钟为1000_000_000ns,因此波特率时钟的周期Tb= 1000000000/115200 =8680.6ns,即115200信号的一个周期为8680.6ns,那么,我们只需要设定我们的定时器定时时间为8680.6ns,每当定时时间到,产生一个系统时钟周期长度高脉冲信号即可系统时钟频率为50MHz,即周期为20ns,那么我们只需要计数8680/20个系统时钟,就可获得8680ns的定时,bps115200=Tb/Tclk - 1=Tb*fclk - 1=fclk/115200-1。相应的,其它波特率定时值的计算与此类似,这里小梅哥就不再一一分析。2028为波特率定时器定时值的计算部分
为了能够通过外部控制波特率,设计中使用了一个3的波特率选择端口Baud_Set。通过此端口不同的,就能选择不同的波特率,此端口控制不同波特率的原理很简单,就是一个多路选择器,32至第48即为此多路选择器的控制代码, Baud_Set波特率的对应关系如下:

000 9600bps

001 19200bps;

010 38400bps;

011 57600bps

100 115200bps

101 230400bps

110 460800bps

111 921600bps


1.2Uart_Byte_TxUart_Byte_Tx字节发送模块该模块在波特率时钟的节拍下,依UART通信协议发送一个完整的字节的数据。当一个字节发送完毕后,Tx_Done产生一个高脉冲信号,以告知其它模块或逻辑一个字节的数据已经传输完成,可以开始下一个字节的发送了。发送一个字节数据实现代码如下:

33  /*计数波特率时钟,11个波特率时钟为一次完整的数据发送过程*/    

34      always@(posedge Clk or negedge Rst_n)

35      if(!Rst_n)

36          Bps_Clk_Cnt <= 4'b0;

37      else if(Bps_Clk_Cnt == 4'd11)

38          Bps_Clk_Cnt <= 4'b0;

39      else if(Bps_Clk)

40          Bps_Clk_Cnt <= Bps_Clk_Cnt + 1'b1;

41      else

42          Bps_Clk_Cnt <= Bps_Clk_Cnt;

43

44  /*生成数据发送完成标志信号*/        

45      always@(posedge Clk or negedge Rst_n)

46      if(!Rst_n)

47          Tx_Done <= 1'b0;

48      else if(Bps_Clk_Cnt == 4'd11)

49          Tx_Done <= 1'b1;

50      else

51          Tx_Done <= 1'b0;

52

53  /*在开始发送起始位的时候就读取并寄存Data_Byte,以免Data_Byte变化导致数据的丢失*/      

54      always@(posedge Clk or negedge Rst_n)

55      if(!Rst_n)

56          Data = 8'd0;

57      else if(Bps_Clk & Bps_Clk_Cnt == 4'd1)

58          Data <= Data_Byte;

59      else

60          Data <= Data;

61

62  /*发送数据序列机*/      

63      always@(posedge Clk or negedge Rst_n)

64      if(!Rst_n)  

65          Rs232_Tx <= 1'b1;

66      else begin

67          case(Bps_Clk_Cnt)

68              4'd1: Rs232_Tx <= 1'b0;

69              4'd2: Rs232_Tx <= Data[0];

70              4'd3: Rs232_Tx <= Data[1];

71              4'd4: Rs232_Tx <= Data[2];  

72              4'd5: Rs232_Tx <= Data[3];

73              4'd6: Rs232_Tx <= Data[4];

74              4'd7: Rs232_Tx <= Data[5];

75              4'd8: Rs232_Tx <= Data[6];

76              4'd9: Rs232_Tx <= Data[7];

77              4'd10: Rs232_Tx <= 1'b1;

78              default:Rs232_Tx <= 1'b1;

79          endcase

80      end


UART协议中,一个完整的字节包括一位起始8数据位一位停止位总共十位数据,那么,要完整的实现这十位数据的发送,就需要11波特率时钟脉冲,如下所示




BPS_CLK信号的第一个上升沿到来时,字节发送模块开始发送起始位,接下来的29上升沿,发送8数据位,第10上升沿到第11个上升沿为停止位的发送。
单个串口接收模块中实现串口数据接收的主要代码如下所示:
025     always @ (posedge Clk or negedge Rst_n)
026     if(!Rst_n) begin
027         Rs232_Rx0 <= 1'b0;
028         Rs232_Rx1 <= 1'b0;
029         Rs232_Rx2 <= 1'b0;
030         Rs232_Rx3 <= 1'b0;
031     end
032     else begin
033         Rs232_Rx0 <= Rs232_Rx;
034         Rs232_Rx1 <= Rs232_Rx0;
035         Rs232_Rx2 <= Rs232_Rx1;
036         Rs232_Rx3 <= Rs232_Rx2;
037     end
038     
039     wire neg_Rs232_Rx= Rs232_Rx3 & Rs232_Rx2 & ~Rs232_Rx1 & ~Rs232_Rx0;
040     
041     assign Byte_En = neg_Rs232_Rx;
042
043 /*----------计数采样时钟--------------*/
044 /*9倍波特率采样时钟,故一个完整的接收过程有90个波特率时钟*/
045     reg[6:0]Sample_Clk_Cnt;
046     always @ (posedge Clk or negedge Rst_n)
047     if(!Rst_n)
048         Sample_Clk_Cnt <= 7'd0;
049     else if(Sample_Clk)begin
050         if(Sample_Clk_Cnt == 7'd89)
051             Sample_Clk_Cnt <= 7'd0;
052         else
053             Sample_Clk_Cnt <= Sample_Clk_Cnt + 1'b1;
054     end
055     else
056         Sample_Clk_Cnt <= Sample_Clk_Cnt;
057
058     reg [1:0]Start_Bit; /*起始位,这里虽然定义,但并未使用该位来判断接收数据的正确性,即默认接收都是成功的*/
059     reg [1:0]Stop_Bit;  /*停止位,这里虽然定义,但并未使用该位来判断接收数据的正确性,即默认接收都是成功的*/
060     reg [1:0] Data_Tmp[7:0];/*此部分较为复杂,请参看说明文档中相关解释*/
061     
062     always @ (posedge Clk or negedge Rst_n)
063     if(!Rst_n)begin
064         Data_Tmp[0] <= 2'd0;
065         Data_Tmp[1] <= 2'd0;
066         Data_Tmp[2] <= 2'd0;
067         Data_Tmp[3] <= 2'd0;
068         Data_Tmp[4] <= 2'd0;
069         Data_Tmp[5] <= 2'd0;
070         Data_Tmp[6] <= 2'd0;
071         Data_Tmp[7] <= 2'd0;
072         Start_Bit <= 2'd0;
073         Stop_Bit <= 2'd0;      
074     end
075     else if(Sample_Clk)begin
076         case(Sample_Clk_Cnt)
077             7'd0:
078                 begin
079                     Data_Tmp[0] <= 2'd0;
080                     Data_Tmp[1] <= 2'd0;
081                     Data_Tmp[2] <= 2'd0;
082                     Data_Tmp[3] <= 2'd0;
083                     Data_Tmp[4] <= 2'd0;
084                     Data_Tmp[5] <= 2'd0;
085                     Data_Tmp[6] <= 2'd0;
086                     Data_Tmp[7] <= 2'd0;
087                     Start_Bit <= 2'd0;
088                     Stop_Bit <= 2'd0;   
089                 end
090             7'd3,7'd4,7'd5: Start_Bit <= Start_Bit + Rs232_Rx;
091             7'd12,7'd13,7'd14:Data_Tmp[0] <= Data_Tmp[0] + Rs232_Rx;
092             7'd21,7'd22,7'd23:Data_Tmp[1] <= Data_Tmp[1] + Rs232_Rx;
093             7'd30,7'd31,7'd32:Data_Tmp[2] <= Data_Tmp[2] + Rs232_Rx;
094             7'd39,7'd40,7'd41:Data_Tmp[3] <= Data_Tmp[3] + Rs232_Rx;
095             7'd48,7'd49,7'd50:Data_Tmp[4] <= Data_Tmp[4] + Rs232_Rx;
096             7'd57,7'd58,7'd59:Data_Tmp[5] <= Data_Tmp[5] + Rs232_Rx;   
097             7'd66,7'd67,7'd68:Data_Tmp[6] <= Data_Tmp[6] + Rs232_Rx;
098             7'd75,7'd76,7'd77:Data_Tmp[7] <= Data_Tmp[7] + Rs232_Rx;   
099             7'd84,7'd85,7'd86:Stop_Bit <= Stop_Bit + Rs232_Rx;
100             default:;
101         endcase
102     end
103     else ;
根据串口发送协议,一个字节的数据传输是以一个波特率周期的低电平作为起始位的,因此,成功接收UART串口数据的核心就是准确检测起始位。由于外部串口发送过来的数据与接收系统不在同一个时钟域,因此不能直接使用该信号的下降沿来作为检测标志,我们需要在fpga中,采用专用的边沿检测电路来实现,第25至37通过四个移位寄存器,存储连续四个时钟上升沿时外部发送数据线的状态,第39通过比较前两个时钟时数据线的状态与后两个时钟时数据线的状态,来得到该数据线的准确下降沿,以此保证起始位的准确检测。
在简单的串口接收中,我们通常选取一位数据的中间时刻进行采样,因为此时数据最稳定,但是在工业环境中,存在着各种干扰,在干扰存在的情况下,如果采用传统的中间时刻采样一次的方式,采样结果就有可能受到干扰而出错。为了滤除这种干扰,这里采用多次采样求概率的方式。如下图,将一位数据平均分成9时间段,对位于中间的三个时间段进行采样。然后对三个采样结果进行统计判断,如果某种电平状态在三次采样结果中占到了两次及以上,则可以判定此电平状态即为正确的数据电平。例如456时刻采样结果分别为110那么就取此位解码结果为1否则,若三次采样结果为010则解码结果就为0
因为采样一位需要9时钟上升沿,因此,采样一个完整的数据需要10*9,即90时钟上升沿,这里,采样时钟为波特率时钟的9。产生采样时钟的部分代码如下所示:
089 /*-------波特率时钟生成定时器--------------*/
090     always@(posedge Clk or negedge Rst_n)
091     if(!Rst_n)
092         Count <= 10'd0;
093     else if(BPS_EN == 1'b0)
094         Count <= 10'd0;
095     else begin
096         if(Count == BPS_PARA)
097             Count <= 10'd0;
098         else
099             Count <= Count + 1'b1;
100     end
101     
102 //=====================================================
103 /*输出数据接收采样时钟*/
104     always @(posedge Clk or negedge Rst_n)
105     if(!Rst_n)
106         Sample_Clk <= 1'b0;
107     else if(Count== 1)
108         Sample_Clk <= 1'b1;
109     else
110         Sample_Clk <= 1'b0;
这里,BPS_PARA的计算原理和前面Tx_Bps_Gen模块中的BPS_PARA的计算原理一致,不过这里,因为采样时钟为波特率时钟的9,所以,BPS_PARA为Tx_Bps_Gen模块中的BPS_PARA1/9。计算BPS_PARA的相关代码如下:
018     parameter system_clk = 50_000_000;  /*输入时钟频率设定,默认50M*/
019
020 /*根据输入时钟频率计算生成各波特率时分频计数器的计数最大值*/   
021     localparam bps9600 = system_clk/9600/9 - 1;
022     localparam bps19200 = system_clk/19200/9 - 1;
023     localparam bps38400 = system_clk/38400/9 - 1;
024     localparam bps57600 = system_clk/57600/9 - 1;
025     localparam bps115200 = system_clk/115200/9 - 1;
026     localparam bps230400 = system_clk/230400/9 - 1;
027     localparam bps460800 = system_clk/460800/9 - 1;
028     localparam bps921600 = system_clk/921600/9 - 1;     
029     
030     reg [31:0]BPS_PARA;/*波特率分频计数器的计数最大值*/
031
032     always@(posedge Clk or negedge Rst_n)
033     if(!Rst_n)begin
034         BPS_PARA <= bps9600;    /*复位时波特率默认为9600bps*/
035     end
036     else begin
037         case(Baud_Set)  /*根据波特率控制信号选择不同的波特率计数器计数最大值*/
038             3'd0: BPS_PARA <= bps9600;
039             3'd1: BPS_PARA <= bps19200;
040             3'd2: BPS_PARA <= bps38400;
041             3'd3: BPS_PARA <= bps57600;
042             3'd4: BPS_PARA <= bps115200;
043             3'd5: BPS_PARA <= bps230400;
044             3'd6: BPS_PARA <= bps460800;
045             3'd7: BPS_PARA <= bps921600;            
046             default: BPS_PARA <= bps9600;/*异常情况,恢复到9600的波特率*/
047         endcase
048     end
小梅哥
2015年4月8日 于至芯科技



此帖出自FPGA/CPLD论坛

最新回复

小梅哥,太厉害了,求分享  详情 回复 发表于 2015-4-13 15:06
点赞 关注
 

回复
举报

6423

帖子

17

TA的资源

版主

沙发
 
论坛的帖子支持插入代码,效果看起来比这个要好一些,可以试一下
此帖出自FPGA/CPLD论坛
个人签名training
 
 

回复

132

帖子

0

TA的资源

一粒金砂(中级)

板凳
 
太牛逼了!
工程分享一下呗
此帖出自FPGA/CPLD论坛
 
 
 

回复

1891

帖子

2

TA的资源

纯净的硅(中级)

4
 
有点厉害。。。。
此帖出自FPGA/CPLD论坛
个人签名
分享铸就美好未来。。。




 
 
 

回复

28

帖子

0

TA的资源

一粒金砂(初级)

5
 
小梅哥,太厉害了,求分享
此帖出自FPGA/CPLD论坛
 
 
 

回复
您需要登录后才可以回帖 登录 | 注册

随便看看
查找数据手册?

EEWorld Datasheet 技术支持

相关文章 更多>>
关闭
站长推荐上一条 1/6 下一条

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

About Us 关于我们 客户服务 联系方式 器件索引 网站地图 最新更新 手机版

站点相关: 国产芯 安防电子 汽车电子 手机便携 工业控制 家用电子 医疗电子 测试测量 网络通信 物联网

北京市海淀区中关村大街18号B座15层1530室 电话:(010)82350740 邮编:100190

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2025 EEWORLD.com.cn, Inc. All rights reserved
快速回复 返回顶部 返回列表