串口通信收发机的设计

 

目录

1. UART发送模块的设计

2. UART接收模块的设计

3. 收发模块的功能测试

4.发送机的设计

5.接收机的设计


 通信是指是消息的传递或者交换,分为模拟通信和数字通信两大类。  

   数字通信是指以数字信号载荷消息的通信方式,分为基带传输和载波传输两种类型。

  基带传输是指不对数字信号进行频谱搬移,直接在信道上进行传输。

载波传输是指将数字信号调制到载波上,以频带信号的形式在信道上进行传输。

   数字信号调制有ASK(Amplitude Shift Keying,幅值键控)、FSK(Frequency Shift Keying,频移键控)和PSK(Phase Shift Keying,相移键控)三种方式,其中ASK用数字信号调制载波的幅度。FSK用数字信号调制载波的频率,而PSK则用数字信号调制载波的相位。

  应用二进制序列调制载波幅度称为2ASK,最简单的实现方法是用数字信号控制载波的通断(On-Off Keying,简称OOK)。2ASK传输带宽为基带信号带宽的2倍。

   设计任务:设计下图所示的数字-模拟混合信号传输收发机,能够无线传输数模混合信号。其中对数字信号的传输要求如下:(1)在发送端,输入4个0~9的数字后存储并显示,按下发送键后对数字信号连续循环传输;(2)在接收端,解调出数字信号后应用4个数码管显示发送的数字。要求开始发送到数码管显示的响应时间不大于2秒;(3)发送端停止数字信号传输后,接收端数码显示延迟 5 秒自动熄灭。

 分析:(1)竞赛题中定义了模拟信号占用的带宽为50Hz~10kHz,并且要求收发机的信道带宽不大于25kHz。因此,应用2ASK传输数字信号时,应将频谱限制在10~12.5kHz之间,以满足信道带宽不大于25kHz的设计要求。  

  (2)为避免传输数字-模拟混合信号时发生频谱混叠,同时有利于分离数字信号和模拟混合信号,应降低数字基带信号的频率,同时尽量提高载波频率。定义基带信号的最高频率为50Hz时,以能够传输10次谐波进行计算,则载波频率最高可设置为12kHz。

  (3)只要求传输4个数字,而且从开始发送到数码管显示的响应时间不大于2秒,因此对数字信号的传输速率要求不高,可以应用串口的通信原理来设计传输收发机。

 UART为通用异步收发器(Universal Asynchronous Receiver/Transmitter),通常称为串口,其特点是通信线路简单,成本低,适用于对数字信号传输速率要求不高的应用场合。

   UART使用DB-9物理接口,应用两条信号线和地线就能够进行全双工通信,其中TxD为数据发送口,RxD为数据接收端。单向传输数字信号时,只需要一条信号线和地线。

   UART支持50、100、300、600、1200、9600、38400、19200和115200 bits/s等多种传输速率,以适应不同的设备接口。在嵌入式系统设计中,UART常用于主机与设备之间,或者上位机与下位机之间的字符通信。

1. UART发送模块的设计

  设置传输波特率为50,并定义每帧传输8位数据、1位奇偶校验位和2比特停止位时,描述UART发送模块的Verilog代码参考如下:

module UART_transmitter(clk, TXstart, TX_data, Parity, TxD, TxD_busy);
   input         clk;                      // UART时钟
   input         TXstart;                // 发送启动脉冲
   input [7:0]  TX_data;                // 需要传输的数据
   input         Parity;                  // 奇偶校验位
   output reg   TxD;                    // 串行发送端
   output wire TxD_busy;               // 状态标志
   // 波特率参数和变量定义
   parameter ClkFreq = 50000000;      // 时钟频率,50MHz
   parameter BaudRate = 50;            // 波特率
   parameter BaudGenAccWidth = 24;   // 分频计数位宽:224>(50*106/50)=106
   parameter BaudGenInc = ((BaudRate<<(BaudGenAccWidth-4))+
                                (ClkFreq>>5))/(ClkFreq>>4); // 计数增量
   reg [BaudGenAccWidth:0] BaudGenAcc;   // 分频计数变量
   // 波特率脉冲定义及生成逻辑
   wire BaudTick = BaudGenAcc[BaudGenAccWidth];   
   // 状态变量定义及发送忙标志逻辑
   reg [3:0] state;
   assign TxD_busy = ( state!=0 );   
   // 波特率分频计数过程                                                          
   always @( posedge clk ) 
      if ( TxD_busy ) 
         BaudGenAcc <= BaudGenAcc[BaudGenAccWidth-1:0] + BaudGenInc;
   // 发送状态机描述过程
   always @( posedge clk )
      case ( state )        
         4'b0000:  if( TXstart )   state <= 4'b0100;
         4'b0100:  if( BaudTick )  state <= 4'b1000;   // start
         4'b1000:  if( BaudTick )  state <= 4'b1001;   // bit 0
         4'b1001:  if( BaudTick )  state <= 4'b1010;   // bit 1
         4'b1010:  if( BaudTick )  state <= 4'b1011;   // bit 2
         4'b1011:  if( BaudTick )  state <= 4'b1100;   // bit 3
         4'b1100:  if( BaudTick )  state <= 4'b1101;   // bit 4
         4'b1101:  if( BaudTick )  state <= 4'b1110;   // bit 5
         4'b1110:  if( BaudTick )  state <= 4'b1111;   // bit 6
         4'b1111:  if( BaudTick )  state <= 4'b0011;   // bit 7
         4'b0011:  if( BaudTick )  state <= 4'b0010;   // parity
         4'b0010:  if( BaudTick )  state <= 4'b0001;   // stop1
         4'b0001:  if( BaudTick )  state <= 4'b0000;   // stop2
         default:  if( BaudTick )  state <= 4'b0000;
      endcase   
   // 发送数据位定义及选择过程
   reg TxD_bit;
   always @ (state or TX_data or Parity )
     case ( state )
        4'b0000: TxD_bit = 1'b1;          // idle
        4'b0100: TxD_bit = 1'b0;          // start
        4'b1000: TxD_bit = TX_data[0];
        4'b1001: TxD_bit = TX_data[1];
        4'b1010: TxD_bit = TX_data[2];
        4'b1011: TxD_bit = TX_data[3];
        4'b1100: TxD_bit = TX_data[4];
        4'b1101: TxD_bit = TX_data[5];
        4'b1110: TxD_bit = TX_data[6];
        4'b1111: TxD_bit = TX_data[7];
        4'b0011: TxD_bit = Parity;       
        4'b0010: TxD_bit = 1'b1;           // stop1
        4'b0001: TxD_bit = 1'b1;           // stop2
        default: TxD_bit = 1'b1;
     endcase
   // 帧数据位同步输出过程
   always @( posedge clk )
      TxD <=  TxD_bit;  
endmodule

2. UART接收模块的设计

   UART为异步通信协议,发送端可能随时发送数据帧,因此接收端也需要随时准备接收帧数据。两个收发帧之间的空闲时间不确定,但同一数据帧内相邻比特位之间的时间间隔由波特率决定。

 描述传输波特率为50,每个数据位采样8次的接收模块Verilog代码参考如下:

 module UART_receiver (
    input               clk,                // UART时钟
    input               RxD,               // 串行接收端
    output reg [7:0]  RX_data,          // 接收数据输出
    output reg    	 RX_data_vld,        // 数据有效标
    output reg  		 Parity,     // 奇偶标志
    output reg         RxD_eop,          // 数据包结束标志          
    output wire        RxD_idle           // RxD空闲标志
    );   
    // 波特率参数定义
   parameter ClkFreq = 50000000;             // 时钟频率,50MHz   
    parameter BaudRate = 50;                   // 波特率
    parameter Baud8 = BaudRate*8;             // 8次采样
    // 分频计数参数及变量定义
    parameter Baud8GenAccWidth = 21;          // 分频计数位宽
    parameter Baud8GenInc = ((Baud8<<(Baud8GenAccWidth-4))+
                                   (ClkFreq>>5))/(ClkFreq>>4); // 计数增量
    reg [Baud8GenAccWidth:0] Baud8GenAcc;    // 分频计数变量
    // Baud8分频计数过程  
    always @( posedge clk ) 
       Baud8GenAcc <= Baud8GenAcc[Baud8GenAccWidth-1:0] +    
                           Baud8GenInc;
    // 采样脉冲定义及生成逻辑
    wire Baud8Tick = Baud8GenAcc[Baud8GenAccWidth];
    // 接收同步器定义。将RxD取反使空闲状态为0以防止启动时接收到无效数据。
    reg [1:0] RxD_sync_inv;    
    always @( posedge clk )    // 同步接收过程
        if ( Baud8Tick ) 
           RxD_sync_inv <= { RxD_sync_inv[0], ~RxD };  // 左移存入
    // 采样次数以及接收数据位定义
    reg [1:0] RxD_cnt_inv;
    reg RxD_bit_inv;
    // 数据采样及判决过程
    always @ ( posedge clk )
       if ( Baud8Tick ) begin
           // 如果采样值为1,并且采样次数不等于3
           if ( RxD_sync_inv[1] && RxD_cnt_inv != 2'b11 )
                RxD_cnt_inv <= RxD_cnt_inv + 1;      // 采样次数加1
           // 否则如果采样值为0,并且采样次数不等于0
           else if ( ~RxD_sync_inv[1] && RxD_cnt_inv != 2'b00 )
                RxD_cnt_inv <= RxD_cnt_inv - 1;      // 采样次数减1          
           // 数值判决逻辑
           if ( RxD_cnt_inv == 2'b00 )                // 如果采样计数值=0
                RxD_bit_inv <= 0;                       // 则接收数据为0
           else if ( RxD_cnt_inv == 2'b11 )          // 如果采样计数值=3
                RxD_bit_inv <= 1;                       // 则接收数据为1
       end
    // 状态变量及位间隔变量定义
    reg [3:0] state, bit_space;
    // 位间隔计数过程
    always @ ( posedge clk )
       if  ( state==0 ) 
            bit_space <= 0;
        else if ( Baud8Tick )
                bit_space <= {bit_space[2:0] + 1} | {bit_space[3],3'b000};
    // next_bit用于控制数据采样时刻,取决于RxD噪声大小。噪声很小时,取8到11。   
    wire next_bit = ( bit_space==10 );
    // 接收状态机描述
    always @( posedge clk )
        if ( Baud8Tick )
            case ( state )
                // 如果RXD_sync_inv为1则检测到起始位,开始接收数据
                4'b0000:  if( RxD_bit_inv ) state <= 4'b1000;   
                4'b1000:  if( next_bit )     state <= 4'b1001;  // bit 0
                4'b1001:  if( next_bit )     state <= 4'b1010;  // bit 1
                4'b1010:  if( next_bit )     state <= 4'b1011;  // bit 2
                4'b1011:  if( next_bit )     state <= 4'b1100;  // bit 3
                4'b1100:  if( next_bit )     state <= 4'b1101;  // bit 4
                4'b1101:  if( next_bit )     state <= 4'b1110;  // bit 5
                4'b1110:  if( next_bit )     state <= 4'b1111;  // bit 6
                4'b1111:  if( next_bit )     state <= 4'b0011;  // bit 7              
                4'b0011:  if( next_bit )     state <= 4'b0010;  // parity
                4'b0010:  if( next_bit )     state <= 4'b0001;  // stop 1
                4'b0001:  if( next_bit )     state <= 4'b0000;  // stop 2
                default:                        state <= 4'b0000;
            endcase
    // 接收数据变量定义及存储过程
    always @( posedge clk ) begin
       if ( Baud8Tick && next_bit && state[3] )
            RX_data <= { ~RxD_bit_inv, RX_data[7:1] };  // 右移存入
       if ( Baud8Tick && next_bit && state==3 )
            Parity <=  ~RxD_bit_inv;
       end
    // 数据有效标志生成过程。接收到停止位,数据有效。
    always @( posedge clk )
       RX_data_vld <= ( Baud8Tick && next_bit && state==1 && ~RxD_bit_inv);    
    // 间隙计数变量及过程
    reg [4:0] gap_count;  
    always @( posedge clk ) 
        if ( state!=0 )  
            gap_count<=0;
        else if ( Baud8Tick & ~gap_count[4] )
            gap_count <= gap_count + 1;
    // RxD空闲标志逻辑,一个时钟脉冲内不再接收数据时有效。
    assign RxD_idle = gap_count[4];
    // 数据包结束逻辑,以突发方式发送多个字符时视为一个“数据包”。
    always @( posedge clk ) 
        RxD_eop <= Baud8Tick & ( gap_count==15 );
endmodule

3. 收发模块的功能测试

  将发送模块UART_transmitter.v和接收模块UART_receiver分别封装为图形符号,并建立下图所示的收发测试电路:

应用模块例化方式用Verilog HDL描述顶层测试电路,参考代码如下:

module transceiver_tst (
   input 	        iclk,
   input 	        iTXstart,
   input [7:0]    iData,   
   input 	        iParity,
   output [7:0]  oDATA,
   output wire   oDATAvld,
   output wire   oParity,  
   output wire   oRxEOP,
   output wire   oRxD_idle,
   output wire   oTxBusy  );
   // 内部线网定义
   wire txd2rxd;
   // 发送模块例化
   UART_transmitter UART_transmitter_inst(
                     .clk(iclk),.TXstart(iTXstart),.TX_data(iData),
                     .Parity(iParity),.TxD(txd2rxd),.TxD_busy(oTxBusy));
   // 接收模块例化
   UART_receiver UART_receiver_inst(
 		.clk(iclk),.RxD(txd2rxd),.RX_data(oDATA),         
    		.RX_data_vld(oDATAvld),.Parity(oParity),          
  		.RxD_eop(oRxEOP),.RxD_idle(oRxD_idle));   
endmodule

4.发送机的设计

   将4个数字分别用d0、d1、d2和d3表示,并将d0和d1合并为一个8位数据data0发送、将d2和d3合并为一个8位数据data1发送,同时定义帧校验位为0时表示发送的数据为data0,为1时表示发送的数据为data1。定义数据帧格式中含有2比特停止位时,则每个数据帧共有(1+8+1+2=)12个比特位。  选取UART的波特率为50时,则完成两帧数据的发送和接收共需要(2×12×2×20ms=) 960ms,即使考虑到发送机和接收机的时序裕量,仍然能够满足从发送数据到数码管显示数据的响应时间不大于 2 秒的设计要求。

  发送状态机用于控制模块UART_transmitter的发送数据和发送过程,载波发生器用于产生12kHz的方波,ASK调制电路根据TxD产生2ASK信号。

  根据上述设计方案,描述发送机的Verilog代码参考如下:

module transmitter_fsm (
input clk,                                   // 时钟,50MHz
input rst_n,                                 // 复位信号,低电平有效
input start,                                 // 发送启动脉冲
input [3:0] d0,d1,d2,d3,                  // 4个数字输入
output wire [6:0] Td0,Td1,Td2,Td3,      // 数码管段驱动信号
output wire ASKout                         // 2ASK序列输出
// output wire TxD                         // 调试用
); 
// 内部线网和变量定义
wire [7:0] data0 = { d0, d1 }; 
wire [7:0] data1 = { d2, d3 };  
wire [7:0] data;                  // 发送数据
reg  [0:7] start_reg;            // 同步寄存器
   wire sync_start,TXstart;        // 同步发送信号及发送脉冲
   wire TxD;                          // 发送数据口,调试时注销 
   // 启动脉冲同步过程
   always @ ( posedge clk )
      start_reg <= { start, start_reg[0:6] };  // 右移存入
   // 启动脉冲生成逻辑
   assign sync_start = &start_reg;
   // 波特率参数、变量和线网定义
   parameter ClkFreq = 50000000;          // 时钟频率,50MHz
   parameter BaudRate = 50;                // 波特率
   parameter BaudGenAccWidth = 24;        // 分频计数位宽
   parameter BaudGenInc = ((BaudRate<<(BaudGenAccWidth-4))+
                                (ClkFreq>>5))/(ClkFreq>>4); // 计数增量
    reg [BaudGenAccWidth:0] BaudGenAcc;   // 分频计数变量定义
    // 波特率脉冲信号定义及产生逻辑    
    wire BaudTick = BaudGenAcc[BaudGenAccWidth];   
    // 波特率分频计数过程                                                          
   always @( posedge clk ) 
      if ( sync_start ) 
         BaudGenAcc <= BaudGenAcc[BaudGenAccWidth-1:0] + BaudGenInc;
    // 状态变量定义及状态机描述
    reg [4:0] TXstate;     // 5位,32个状态
    always @( posedge clk or negedge rst_n )
       if ( !rst_n ) TXstate <= 5'd0;
         else if ( TXstate==0 && sync_start ) TXstate <= 5'd1;
           else if( TXstate!=0 && BaudTick ) TXstate <= TXstate + 1'b1;
        // 发送数据及启动脉冲逻辑
        assign data = ~TXstate[4]? data0 : data1;
        assign TXstart = ( TXstate==5'd1 || TXstate==5'd17 )? 1:0;
    // 发送模块例化
  UART_transmitter UART_transmitter_inst (
    .clk(clk),.TXstart(TXstart),
    .TX_data(data),.Parity(TXstate[4]),
    .TxD(TxD),.TxD_busy());
  // 译码显示,例化CD4511s模块实现
  CD4511s U1 (.bcd(data0[7:4]),.le(1'b0),.seg7(Td0));
  CD4511s U2 (.bcd(data0[3:0]),.le(1'b0),.seg7(Td1));
  CD4511s U3 (.bcd(data1[7:4]),.le(1'b0),.seg7(Td2));
  CD4511s U4 (.bcd(data1[3:0]),.le(1'b0),.seg7(Td3));
  // 载波生成及ASK输出
  parameter fpN = 4167;
  reg [12:0] fp_cnt;
  wire carrier;
  always @ ( posedge clk or negedge rst_n )
    if ( !rst_n )      
      fp_cnt <= 0;
   else if ( fp_cnt < fpN-1 )
           fp_cnt <= fp_cnt + 1;
         else
           fp_cnt <= 0;
   assign carrier = ( fp_cnt < fpN/2 )? 0:1;
   assign ASKout =  TXD ? carrier : 0; 
endmodule

5.接收机的设计

   接收机的设计方案如图所示。无线传输时,首先需要从接收到的高频调制信号中解调并滤波出2ASK信号,然后再从2ASK中解调出RxD送给模块UART_receiver以提取传输数据,接收及译码显示电路用于将接收到的两个8位数据RX_data分解为四个BCD码进行译码显示。

  包络检波法不需要相干载波,解调原理如图所示。首先对接收到的2ASK信号进行整流,然后应用低通滤波器提取ASK信号的包络,最后在位定时脉冲的作用下,经过判决输出RxD序列,从而完成对2ASK的解调。

  根据上述设计方案,描述接收机数字部分的Verilog代码参考如下:\

module receiver_fsm (
   input clk,                              // 时钟,50MHz
   input rst_n,                            // 复位信号,低电平有效
   input RxD_bit,                         // RxD输入
   output wire [6:0] Rd0,Rd1,Rd2,Rd3  // 数码管段驱动信号  ); 
    // 内部线网和变量定义
    wire [7:0] RX_data;
    wire RX_data_vld,Parity,RxD_idle; 
    wire blank_n = 1'b1;    // 灭灯信号,低电平有效 
    reg [7:0] data0,data1;
    // UART接收模块例化
    UART_receiver UART_receiver_inst ( 
           .clk(clk),.RxD(RxD_bit),.RX_data(RX_data),
           .RX_data_vld(RX_data_vld),.Parity(Parity),
          .RxD_eop(),.RxD_idle(RxD_idle));        
         // 数据接收及存储过程
         always @ ( posedge RX_data_vld or negedge rst_n )
            if ( !rst_n ) begin
                 data0 <= 8'b0; data1 <= 8'b0; end
              else if ( Parity )  
                       data1 <= RX_data;
                    else
                       data0 <= RX_data;
   // 译码显示,例化CD4511模块实现
   CD4511 U1 (.bcd(data0[7:4]),lt_n(1),.bi_n(blank_n),.le(0),.seg7(Rd0));
   CD4511 U2 (.bcd(data0[3:0]),lt_n(1),.bi_n(blank_n),.le(0),.seg7(Rd1));
   CD4511 U3 (.bcd(data1[7:4]),lt_n(1),.bi_n(blank_n),.le(0),.seg7(Rd2));
   CD4511 U4 (.bcd(data1[3:0]),lt_n(1),.bi_n(blank_n),.le(0),.seg7(Rd3));
endmodule

  分别以发送机transmitter_fsm和接收机receiver_fsm建立工程,经编辑、综合与适配后封装成图形符号,并建立图所示的数字信号传输收发机顶层测试电路。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

三十度角阳光的问候

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值