前言
UART是一种常用的串行通信协议,RS485则是一种用于长距离和抗干扰的物理层标准。结合UART和RS485可以实现可靠的数据传输,特别是在多点通信和长距离应用中。通过合适的硬件连接、软件配置和验证测试,可以确保这一通信系统的稳定性和数据完整性。
正文
一、UART通信实现与验证(RS485)
1.项目需求
A板通过按键控制B板led灯进行流水灯效果或呼吸灯效果,反过来也可以实现
2.技术介绍
RS-485是双向、半双工通信协议,信号采用差分传输方式,允许多个驱动器和接收器挂接在总线上,其中每个驱动器都能够脱离总线。适合远距离传输(最远1200米,最快10MB/s),RS232适合一主机一从机,RS485允许多主机和多从机的连接,抗干扰能力强。
RS485工作时,通过差分信号线检测传输到来的电平信号,通常检测该电平信号有专用的RS485收发器芯片,该类芯片可以检测到低至200MV的电平信号,不同于RS232收发器芯片的是该芯片需要有一使能信号进行数据传入/传出使能。在帧结构上于RS232相同,1波特起始位,8波特数据位,1波特停止位。
因为帧结构上于RS232相同,故利用RS232工程的收发模块,对其简单调整,即可实现RS485通信。完成实验任务还需要按键消抖,呼吸灯,流水灯模块。对接受到的帧数据另外需用一个模块对接收串并处理后的数据进行抽位判断,并将呼吸灯,流水灯显示效果按照判断结果进行选择输出,以上完成实验任务。
3.顶层架构
为方便仿真,在连线时调用按键消抖,但是按键直接给到flag上,并未使用按键消抖,如果需要实物验证,可将连线自行连接。
4.端口描述
clk | 时钟(50Mhz) |
rst_n | 复位按键(低电平有效) |
rx | 数据接收端口 |
key_w | 按键(流水灯) |
key_y | 按键(呼吸灯) |
tx | 数据发送端 |
re | RS485收发器芯片使能信号 |
led[3:0] | led灯输出 |
二、代码验证
data_led:状态选择模块
module data_led(
input clk ,
input rst_n ,
input flag_w ,//按键有效信号,持续一个时钟
input flag_y ,//按键有效信号,持续一个时钟
input led ,//呼吸灯
input [3:0] led_da ,//流水灯
input [7:0] data ,//收模块处理后的数据
output reg [3:0] led_out ,//LED选择结果
output wire po_flag ,//发模块使能
output wire [7:0] po_data //发数据
);
reg w_en;//流水灯工作输出
reg y_en;//呼吸灯工作输出
always@(posedge clk,negedge rst_n)//按键按下后,划分工作状态
begin
if(rst_n == 0)
w_en <= 1'b0;
else
if(flag_y == 1'b1)//相互清零,避免影响
w_en <= 1'b0;
else
if(flag_w == 1'b1)//按下一次,流水灯,按下2次,led输出清零
w_en <= ~w_en;
else
w_en <= w_en;
end
always@(posedge clk,negedge rst_n)//按键按下后,划分工作状态
begin
if(rst_n == 0)
y_en <= 1'b0;
else
if(flag_w == 1'b1)//相互清零,避免影响
y_en <= 1'b0;
else
if(flag_y == 1'b1)//按下一次,流水灯,按下2次,led输出清零
y_en <= ~y_en;
else
y_en <= y_en;
end
always@(posedge clk,negedge rst_n)//对收到的数据进行检测
begin
if(rst_n == 0)
led_out <= 4'b0000;
else
if(data[0] == 1'b1)
led_out <= led_da;//流水灯
else
if(data[1] == 1'b1)//呼吸灯
led_out <= {led,led,led,led};
else
led_out <= 4'b0000;
end
assign po_data = {6'b000000,y_en,w_en};//输出信号中并入灯状态选择信号
assign po_flag = (flag_w||flag_y);//有按键按下时,输出使能
endmodule
uart_rx:收数据模块
module uart_rx(
input clk ,
input rst_n ,
input rx ,
output reg[7:0]po_data , //接收到的数据
output reg po_flag //数据输出有效
);
parameter uart_btl ='d9600 ;//串口波特率
parameter clk_shuj ='d50_000_000 ;//时钟频率
parameter cnt_max =clk_shuj/uart_btl;
reg reg1,reg2,reg3 ;//打排延迟周期,消除亚稳态
reg flag ;//uart工作信号(uart工作时在工作状态切换后产生一个时钟周期高电平)
reg en ;//uart工作使能标志信号(uart工作时在工作状态切换后的下一个时钟周期开始一直拉高,直到检测到停止信号)
reg [15:0] cnt ;//每比特数据持续时钟周期计数器
reg [3 :0] bit_cnt ;//数据个数计数器
reg bit_flag ;//每bit数据接收有效信号
reg [7 :0] rx_data ;//存储输入数据
reg rx_flag ;//输入数据并位结束信号
always@(posedge clk,negedge rst_n)//数据打排1
begin
if(rst_n == 0)
reg1 <= 1'b1;
else
reg1 <= rx;
end
always@(posedge clk,negedge rst_n)//数据打排2
begin
if(rst_n == 0)
reg2 <= 1'b1;
else
reg2 <= reg1;
end
always@(posedge clk,negedge rst_n)//数据打排3
begin
if(rst_n == 0)
reg3 <= 1'b1;
else
reg3 <= reg2;
end
always@(posedge clk,negedge rst_n)//uart工作信号赋值
begin
if(rst_n == 0)
flag <= 1'b0;
else
if((reg2 == 1'b0)&&(reg3 == 1'b1)&&(en == 1'b0))
flag <= 1'b1;
else
flag <= 1'b0;
end
always@(posedge clk,negedge rst_n)//uart工作使能标志信号赋值
begin
if(rst_n == 0)
en <= 1'b0;
else
if(flag == 1'b1)
en <= 1'b1;
else
if((bit_cnt == 4'd8)&&(bit_flag == 1'b1))
en <= 1'b0;
else
en <= en;
end
always@(posedge clk,negedge rst_n)//每比特数据持续时钟周期计数器驱动逻辑
begin
if(rst_n == 0)
cnt <= 16'd0;
else
if((cnt == cnt_max - 1)||(en == 1'b0))
cnt <= 16'd0;
else
cnt = cnt + 1'b1;
end
always@(posedge clk,negedge rst_n)//每比特数据持续时钟周期计数器驱动逻辑
begin
if(rst_n == 0)
bit_flag <= 1'b0;
else
if(cnt == cnt_max/2 - 1)
bit_flag <= 1'b1;
else
bit_flag <= 1'b0;
end
always@(posedge clk,negedge rst_n)//数据个数计数器驱动逻辑
begin
if(rst_n == 0)
bit_cnt <= 4'd0;
else
if((bit_cnt == 4'd8)&&(bit_flag == 1'b1))
bit_cnt <= 4'd0;
else
if(bit_flag == 1'b1)
bit_cnt <= bit_cnt + 1'b1;
else
bit_cnt <= bit_cnt;
end
always@(posedge clk,negedge rst_n)//接收数据并位
begin
if(rst_n == 0)
rx_data <= 8'd0;
else
if((bit_cnt >= 4'd1)&&(bit_cnt <= 4'd8)&&(bit_flag == 1'b1))
rx_data <= {reg3,rx_data[7:1]};
end
always@(posedge clk,negedge rst_n)//输入数据并位结束信号
begin
if(rst_n == 0)
rx_flag <= 1'b0;
else
if((bit_cnt == 4'd8)&&(bit_flag == 1'b1))
rx_flag <= 1'b1;
else
rx_flag <= 1'b0;
end
always@(posedge clk,negedge rst_n)//输出数据传递
begin
if(rst_n == 0)
po_data <= 8'd0;
else
if(rx_flag == 1'b1)
po_data <= rx_data;
else
po_data <= po_data;
end
always@(posedge clk,negedge rst_n)//输出使能
begin
if(rst_n == 0)
po_flag <= 1'b0;
else
po_flag <= rx_flag;
end
endmodule
uart_tx发数据模块
module uart_tx(
input clk ,
input rst_n ,
input [7:0] pi_data ,
input pi_flag ,
output reg tx ,
output reg en
);
parameter uart_btl ='d9600 ;//串口波特率
parameter clk_shuj ='d50_000_000 ;//时钟频率
parameter cnt_max =clk_shuj/uart_btl;
//reg en ;
reg [15:0] cnt ;//每bit数据传输完成计数器
reg flag ;//
reg [3 :0] bit_cnt ;//bit计数器
always@(posedge clk,negedge rst_n)
begin
if(rst_n == 0)
en <= 1'b0;
else
if(pi_flag == 1'b1)
en <= 1'b1;
else
if((bit_cnt == 4'd9)&&(flag == 1'b1))
en <= 1'b0;
else
en <= en;
end
always@(posedge clk,negedge rst_n)
begin
if(rst_n == 0)
cnt <= 16'd0;
else
if((en == 1'b0)||(cnt == cnt_max - 1'b1))
cnt <= 16'd0;
else
if(en == 1'b1)
cnt <= cnt + 1'b1;
else
cnt <= cnt;
end
always@(posedge clk,negedge rst_n)
begin
if(rst_n == 0)
flag <= 1'b0;
else
if(cnt == cnt_max - 1'b1)
flag <= 1'b1;
else
flag <= 1'b0;
end
always@(posedge clk,negedge rst_n)
begin
if(rst_n == 0)
bit_cnt <= 4'd0;
else
if((bit_cnt == 4'd9)&&(flag == 1'b1))
bit_cnt <= 4'd0;
else
if((en == 1'b1)&&(flag == 1'b1))
bit_cnt <= bit_cnt + 1'b1;
else
bit_cnt <= bit_cnt;
end
always@(posedge clk,negedge rst_n)
begin
if(rst_n == 0)
tx <= 1'b1;
else
if(en == 1'b1)
case(bit_cnt)
0: tx <= 1'b0;
1: tx <= pi_data[0];
2: tx <= pi_data[1];
3: tx <= pi_data[2];
4: tx <= pi_data[3];
5: tx <= pi_data[4];
6: tx <= pi_data[5];
7: tx <= pi_data[6];
8: tx <= pi_data[7];
9: tx <= 1'b1;
default :tx <= 1'b1;
endcase
end
endmodule
huxi_led呼吸灯模块
module huxi_led(
input clk,
input rst_n,
output reg led
);
parameter cnt_1s_max = 10'd999;//999
parameter cnt_1ms_max = 10'd999;//999
parameter cnt_1us_max = 6'd49;//49
reg [9:0]cnt_1s;
reg [9:0]cnt_1ms;
reg [5:0]cnt_1us;
reg cnt_en;
always @(posedge clk,negedge rst_n)
begin
if(rst_n == 0)
cnt_1us <= 6'd0;
else
if(cnt_1us == cnt_1us_max)
cnt_1us <= 6'd0;
else
cnt_1us <= cnt_1us + 6'd1;
end
always @(posedge clk,negedge rst_n)
begin
if(rst_n == 0)
cnt_1ms <= 10'd0;
else
if((cnt_1us == cnt_1us_max)&&(cnt_1ms == cnt_1ms_max))
cnt_1ms <= 10'd0;
else
if(cnt_1us == cnt_1us_max)
cnt_1ms <= cnt_1ms + 10'd1;
else
cnt_1ms <= cnt_1ms;
end
always @(posedge clk,negedge rst_n)
begin
if(rst_n == 0)
cnt_1s <= 10'd0;
else
if((cnt_1s == cnt_1s_max)&&(cnt_1us == cnt_1us_max)&&(cnt_1ms == cnt_1ms_max))
cnt_1s <= 10'd0;
else
if((cnt_1us == cnt_1us_max)&&(cnt_1ms == cnt_1ms_max))
cnt_1s <= cnt_1s + 10'd1;
else
cnt_1s <= cnt_1s;
end
always @(posedge clk,negedge rst_n)
begin
if(rst_n == 0)
cnt_en <= 1'b0;
else
if((cnt_1s == cnt_1s_max)&&(cnt_1us == cnt_1us_max)&&(cnt_1ms == cnt_1ms_max))
cnt_en <= ~cnt_en;
else
cnt_en <= cnt_en;
end
always @(posedge clk,negedge rst_n)
begin
if(rst_n == 0)
led <= 1'b0;
else
if(((cnt_en == 1'b0)&&(cnt_1ms <= cnt_1s))||((cnt_en == 1'b1)&&(cnt_1ms > cnt_1s)))
led <= 1'b1;
else
led <= 1'b0;
end
endmodule
led_state_v1流水灯模块
//二段式
module led_state_v1(
input clk,
input rst_n,
output reg [3:0] led
);
//定义状态变量
reg [1:0] n_state;
reg [1:0] c_state;
//定义状态参数
parameter s0 = 2'd0;//第1个Led灯
parameter s1 = 2'd1;//第2个Led灯
parameter s2 = 2'd2;//第3个Led灯
parameter s3 = 2'd3;//第4个Led灯
reg [25:0] cnt;//1秒钟延迟计数器
parameter MAX = 50_000_000;
//FSM1
always@(posedge clk,negedge rst_n)
begin
if(rst_n == 0)
c_state <= s0;
else
c_state <= n_state;
end
//FSM2
always @(*) begin
if(rst_n == 0)
begin
n_state <= s0;
led <= 4'hf;
end
else
case(c_state)
s0 : begin
led <= 4'b0111;
if(cnt == MAX - 1)
n_state <= s1;
else
n_state <= s0;
end
s1 : begin
led <= 4'b1011;
if(cnt == MAX - 1)
n_state <= s2;
else
n_state <= s1;
end
s2 : begin
led <= 4'b1101;
if(cnt == MAX - 1)
n_state <= s3;
else
n_state <= s2;
end
s3 : begin
led <= 4'b1110;
if(cnt == MAX - 1)
n_state <= s0;
else
n_state <= s3;
end
default : begin led <= 4'hf; n_state <= s0;end
endcase
end
always @(posedge clk,negedge rst_n)
begin
if(rst_n == 0)
cnt <= 26'd0;
else
case(c_state)
s0 : begin
if(cnt < MAX - 1)
cnt <= cnt + 26'd1;
else
cnt <= 26'd0;
end
s1 : begin
if(cnt < MAX - 1)
cnt <= cnt + 26'd1;
else
cnt <= 26'd0;
end
s2 : begin
if(cnt < MAX - 1)
cnt <= cnt + 26'd1;
else
cnt <= 26'd0;
end
s3 : begin
if(cnt < MAX - 1)
cnt <= cnt + 26'd1;
else
cnt <= 26'd0;
end
default : cnt <= 26'd0;
endcase
end
endmodule
jitter_ctrl_v1按键消抖模块
//二段式
module jitter_ctrl_v1(
input clk,
input rst_n,
input key,
output reg flag,
output reg key_en
);
reg [3:0] n_state;
reg [3:0] c_state;
parameter s0 = 4'b0001;//空闲状态
parameter s1 = 4'b0010;//下抖动状态
parameter s2 = 4'b0100;//稳定状态
parameter s3 = 4'b1000;//上抖动状态
reg [3:0] cnt;
//FSM1
always @(posedge clk,negedge rst_n)
begin
if(rst_n == 0)
c_state <= s0;
else
c_state <= n_state;
end
//FSM2
always @(*) begin
if(rst_n == 0)
begin
flag <= 1'b0;
key_en <= 1'b0;
n_state <= s0;
end
else
case(c_state)
s0 : begin
flag <= 1'b0;
key_en <= 1'b0;
if(key == 0)
n_state <= s1;
else
n_state <= s0;
end
s1 : begin
if(cnt == 9)
begin
n_state <= s2;
flag <= 1'b1;
key_en <= 1'b1;
end
else
begin
n_state <= s1;
flag <= 1'b0;
key_en <= 1'b0;
end
end
s2 : begin
flag <= 1'b0;
key_en <= 1'b1;
if(key == 1)
n_state <= s3;
else
n_state <= s2;
end
s3 : begin
flag <= 1'b0;
key_en <= 1'b0;
if(cnt == 9)
n_state <= s0;
else
n_state <= s3;
end
default : begin
flag <= 1'b0;
key_en <= 1'b0;
n_state <= s0;
end
endcase
end
always @(posedge clk,negedge rst_n)
begin
if(rst_n == 0)
cnt <= 4'd0;
else
if((c_state == s1)||(c_state == s3))
if(cnt < 9)
cnt <= cnt + 4'd1;
else
cnt <= 4'd0;
else
cnt <= 4'd0;
end
endmodule
二级顶层模块:将上述模块进行连接
module data_led_top(
input clk ,
input rst_n ,
input flag_w ,
input flag_y ,
input rx ,
output tx ,
output re ,
output [3:0] led_out
);
wire led ;
wire [3:0] led_da;
wire [7:0] po_data;
wire po_flag;
wire [7:0] data;
//呼吸灯
huxi_led #(.cnt_1s_max(5),.cnt_1ms_max(5),.cnt_1us_max(2)) huxi_led_inst(
.clk (clk ) ,
.rst_n(rst_n) ,
.led (led )
);
//流水灯
led_state_v1 #(.MAX(20)) led_state_v1_inst(
.clk (clk ) ,
.rst_n(rst_n) ,
.led (led_da)//[3:0]
);
//按键消抖
jitter_ctrl_v1 jitter_ctrl_v1_inst(
.clk (clk ),
.rst_n (rst_n),
.key (),
.flag (),
.key_en ()
);
//数据发模块
uart_tx uart_tx_inst(
.clk (clk ),
.rst_n (rst_n ),
.pi_data (po_data),
.pi_flag (po_flag),
.tx (tx ),
.en (re )
);
//数据收模块
uart_rx uart_rx_inst(
.clk (clk ),
.rst_n (rst_n ),
.rx (rx ),
.po_data (data ), //接收到的数据
.po_flag () //数据输出有效
);
//led处理模块
data_led data_led_inst(
.clk (clk ),
.rst_n (rst_n ),
.flag_w (flag_w ),
.flag_y (flag_y ),
.led (led ),
.led_da (led_da ),
.data (data ),
.led_out (led_out),
.po_flag (po_flag),
.po_data (po_data)
);
endmodule
一级顶层模块:调用两个二级顶层模块,对一个模块按键进行赋值(发数据),观察另一个模块输出(收数据)
module data_led_top_top(
input clk ,
input rst_n ,
input flag_w ,
input flag_y ,
output [3:0] led_out
);
data_led_top fa(
.clk (clk ),
.rst_n (rst_n ),
.flag_w (flag_w ),
.flag_y (flag_y ),
.rx (rx ),
.tx (tx ),
.re ( ),
.led_out ( )
);
data_led_top sho(
.clk (clk ),
.rst_n (rst_n ),
.flag_w ( ),
.flag_y ( ),
.rx (tx ),
.tx (rx ),
.re ( ),
.led_out (led_out )
);
endmodule
仿真代码
`timescale 1ns/1ps
module data_led_top_tb;
reg clk ;
reg rst_n ;
reg flag_w ;
reg flag_y ;
wire [3:0] led_out ;
data_led_top_top data_led_top_inst1(
.clk (clk ),
.rst_n (rst_n ),
.flag_w (flag_w ),
.flag_y (flag_y ),
.led_out (led_out )
);
initial clk = 1'b1;
always #10 clk=~clk;
initial begin
rst_n = 0;
flag_w = 0;
flag_y = 0;
#20
rst_n = 1;
#200
flag_w = 1;//按键w按下一个时钟周期
#20
flag_w = 0;
#10000000
flag_y = 0;
#20
flag_y = 1;//按键y按下一个时钟周期
#20
flag_y = 0;
#20
#10000000
flag_y = 0;
#20
flag_y = 1;//按键y按下一个时钟周期
#20
flag_y = 0;
#20
#2000000
$stop;
end
endmodule
三、仿真验证
运行仿真,观察输出波形,因工程量大,此处对数据传输不做具体讲解,如有问题参考UART通信实现与验证(RS232)
上图可看出fa模块通过按键,控制sho模块LED灯的一个状态,数据收发正确,
黄线之前,收模块LED以流水灯形式进行点亮,黄线之后,收模块以呼吸灯状态进行点亮,
当第二次Y按键按下后,fa模块发送数据清0,在延迟8位数据之后sho模块收到数据,停止led闪烁。这里通过标线计算,延迟885480000ps,1位数据传输需要1000000000000/9600=104166666ps,即延迟一个数据帧结构后接收模块收到数据。
参考资料
RS485