目录
通信是指是消息的传递或者交换,分为模拟通信和数字通信两大类。
数字通信是指以数字信号载荷消息的通信方式,分为基带传输和载波传输两种类型。
基带传输是指不对数字信号进行频谱搬移,直接在信道上进行传输。
载波传输是指将数字信号调制到载波上,以频带信号的形式在信道上进行传输。
数字信号调制有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建立工程,经编辑、综合与适配后封装成图形符号,并建立图所示的数字信号传输收发机顶层测试电路。