0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

分析一下串口发送与接收模块的设计代码

电子工程师 来源:电子工程世界 作者:小梅哥 2021-04-19 11:22 次阅读

串口发送与接收模块设计代码分析

1.1Tx_Bps_Gen

Tx_Bps_Gen为发送波特率生成模块,每当有Byte_En信号到来时,即开始产生发送一个完整字节的数据需要的完整波特率时钟信号。

本设计,波特率支持9600bps到921600bps。例如,需要产生的波特率时钟为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。相应的,其它波特率定时值的计算与此类似,这里小梅哥就不再一一分析。20行至28行为波特率定时器定时值的计算部分。

为了能够通过外部控制波特率,设计中使用了一个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_Tx

Uart_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信号的第一个上升沿到来时,字节发送模块开始发送起始位,接下来的2到9个上升沿,发送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个时间段,对位于中间的三个时间段进行采样。然后对三个采样结果进行统计判断,如果某种电平状态在三次采样结果中占到了两次及以上,则可以判定此电平状态即为正确的数据电平。例如4、5、6时刻采样结果分别为1、1、0,那么就取此位解码结果为1,否则,若三次采样结果为0、1、0,则解码结果就为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_PARA的1/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

责任编辑:lq6

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 接收模块
    +关注

    关注

    1

    文章

    17

    浏览量

    10413
  • 时钟信号
    +关注

    关注

    4

    文章

    374

    浏览量

    28068
  • 分频计数器
    +关注

    关注

    0

    文章

    4

    浏览量

    7905

原文标题:基于ZX-2型FPGA开发板的串口示波器(二)

文章出处:【微信号:gh_9d70b445f494,微信公众号:FPGA设计论坛】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    单片机串口通信的接收发送

    的原理。串口通信是通过发送接收两根线来实现的,分别为发送线(Tx)和接收线(Rx)。当单片机发送
    的头像 发表于 12-20 14:03 1583次阅读

    单片机串口通信的接收发送程序

    单片机串口通信的接收发送程序 一、引言 单片机串口通信是一种常见的通信方式,广泛应用于各种嵌入式系统和工业控制领域。通过串口通信,单片机可
    的头像 发表于 12-19 13:57 1006次阅读

    ROS与STM32串口通信代码

    Stm32_Serial; // 声明串口对象 并且在类的定义中,声明两个结构体,用来存储接收和要发送的数据 RECEIVE_DATA Receive_Data ; //The serial port
    的头像 发表于 11-17 18:10 494次阅读

    STM32串口发送接收

    好USART的电路之后,直接读取数据寄存器,就可以自动发送数据和接收数据了。在发送接收模块有4个重要的寄存器
    的头像 发表于 11-10 16:04 654次阅读
    STM32<b class='flag-5'>串口</b>的<b class='flag-5'>发送</b>和<b class='flag-5'>接收</b>

    LPC5500_SDK例程:串口DMA发送+中断接收

    LPC5500_SDK例程:串口DMA发送+中断接收
    的头像 发表于 10-30 16:59 528次阅读
    LPC5500_SDK例程:<b class='flag-5'>串口</b>DMA<b class='flag-5'>发送</b>+中断<b class='flag-5'>接收</b>

    STM32串口发送数据和接收数据方式总结

    STM32串口发送数据和接收数据方式总结
    的头像 发表于 09-19 09:14 5698次阅读
    STM32<b class='flag-5'>串口</b><b class='flag-5'>发送</b>数据和<b class='flag-5'>接收</b>数据方式总结

    12_串口接收模块设计与验证 - 第7节

    电路串口代码发送模块
    充八万
    发布于 :2023年08月18日 12:03:29

    12 11_串口发送模块与验证 - 第5节 #硬声创作季

    电路串口代码发送模块
    充八万
    发布于 :2023年08月18日 11:17:29

    12 11_串口发送模块与验证 - 第4节

    电路串口代码发送模块
    充八万
    发布于 :2023年08月18日 11:16:38

    12 11_串口发送模块与验证 - 第3节 #硬声创作季

    电路串口代码发送模块
    充八万
    发布于 :2023年08月18日 11:15:47

    12 11_串口发送模块与验证 - 第2节 #硬声创作季

    电路串口代码发送模块
    充八万
    发布于 :2023年08月18日 11:14:56

    11A_串口发送模块与验证 音频修复版 - 第4节 #硬声创作季

    电路串口代码发送模块
    充八万
    发布于 :2023年08月18日 10:29:02

    11A_串口发送模块与验证 音频修复版 - 第3节 #硬声创作季

    电路串口代码发送模块
    充八万
    发布于 :2023年08月18日 10:28:11

    STM32F407 串口配置步骤

    介绍STM32F407串口配置步骤,完成串口的数据发送与接收、实现中断接收,支持printf重定向。
    的头像 发表于 07-06 14:29 1894次阅读
    STM32F407 <b class='flag-5'>串口</b>配置步骤

    C51与PC机串口通信中为何出现发送与接收数据长度不致?

    C51与PC机串口通信中为何出现发送与接收数据长度不致?比如我用单片机做下位机,无任何校验,发送1或2,3等几个数据,
    发表于 05-15 15:02