前言
今天学习正点原子领航者开发板RS485串口通信模块,以下是记录的笔记和重写的代码。
1:RS485串口通信
RS-485是针对UART串口的一种接口标准,接口为两线、半双工,信号采用差分传输方式,通过两根线传输数据(差为2 ~ 6v为1,差为-6 ~ -2v为0),RS-432采用单端的传输方式(根据电平来判断0或1)。
差分传输优点在于,抗干扰能力强,单端很容易受影响,但是差分两根线都会受影响,所以差值不变。差分传输相比于单端传输抗干扰能力强,传输距离远,对外界电磁干扰小,时序定位准确;但对布线要求高,适用于高速场景USB/MIPI/PCIE等。
2:波形图——写代码
2.1 按键消抖模块
代码分析:
1:打两拍判断按键是否改变。
2:按键数据有效标志位key_data_valid:当检测到按键数据发生改变时,key_data_valid拉高为1。
3:分别调用key_debounce模块消抖:输入按键key0和按键key1消抖后,输出消抖后的按键key_data。
<font color="#FF8C0" size=3 >
module key_trig(
input [1:0] key ,
input sys_rst_n ,
input sys_clk ,
output [1:0] key_data ,
output reg key_data_valid
);
reg [1:0] key_data_d0;
reg [1:0] key_data_d1;
//打两拍判断按键是否改变。
always @(posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n) begin
key_data_d0 <= 2'b11;
key_data_d1 <= 2'b11;
end
else begin
key_data_d0 <= key_data ;
key_data_d1 <= key_data_d0 ;
end
end
//按键数据有效标志位key_data_valid
always @(posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n)
key_data_valid <= 1'b0;
else if(key_data_d0 != key_data_d1)
key_data_valid <= 1'b1;
else
key_data_valid <= 1'b0;
end
//分别调用key_debounce模块消抖
key_debounce u_key_debounce0(
.sys_clk (sys_clk ),
.sys_rst_n (sys_rst_n ),
.key (key[0] ),
.key_filter (key_data[0])
);
key_debounce u_key_debounce1(
.sys_clk (sys_clk ),
.sys_rst_n (sys_rst_n ),
.key (key[1] ),
.key_filter (key_data[1])
);
endmodule
</font>
2.2 LED灯控制模块
代码分析:
控制led灯的变化:在led_en使能(接收完成信号uart_rx_done)为1,led灯的状态改变为接收到的数据led_data相反(与接收到的按键电平uart_rx_data[1:0] = uart_tx_data[1:0] = key_data 相反),按键按下改变uart_rx_data[1:0],控制led亮灭。
module led_ctrl(
input led_en ,
input sys_clk ,
input sys_rst_n,
input [1:0] led_data ,
output reg [1:0] led
);
//控制led灯的变化
always @(posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n)
led <= 2'b00;
else if(led_en)
led <= ~led_data ;
else
led <= led ;
end
endmodule
2.3 顶层模块
代码分析:
1:串口接收和串口发送模块几乎不变,与之前不同的是,本次发送模块uart_tx是的发送数据uart_tx_data由{6‘b0,key_data[1:0]}两部分组成。
2:led控制模块:led_en对应uart_rx_done,led_data对应uart_rx_data[1:0],led模块不变。
3:按键触发模块:按键有效信号key_data_valid和发送完成信号uart_tx_done(uart_tx_en)信号一致,其他不变。
module rs485_key_led(
input sys_clk ,
input sys_rst_n ,
input [1:0] key ,
output [1:0] led ,
input rs485_rxd ,
output rs485_txd
);
parameter CLK_FREQ = 50000000 ;
parameter UART_BPS = 115200 ;
wire uart_rx_done;
wire [7:0] uart_rx_data;
wire uart_tx_done;
wire [7:0] uart_tx_data;
wire [1:0] key_data ;
assign uart_tx_data = {6'b0,key_data};
uart_rx #(
.CLK_FREQ (CLK_FREQ),
.UART_BPS (UART_BPS))
u_uart_rx(
.sys_clk (sys_clk ),
.sys_rst_n (sys_rst_n ),
.uart_rxd (rs485_rxd ),
.uart_rx_done(uart_rx_done),
.uart_rx_data(uart_rx_data)
);
//led控制模块
led_ctrl u_led_ctrl(
.led_en (uart_rx_done ),
.sys_clk (sys_clk ),
.sys_rst_n (sys_rst_n ),
.led_data (uart_rx_data[1:0] ),
.led (led )
);
uart_tx #(
.CLK_FREQ (CLK_FREQ),
.UART_BPS (UART_BPS))
u_uart_tx(
.sys_clk (sys_clk ),
.sys_rst_n (sys_rst_n ),
.uart_tx_en (uart_tx_done),
.uart_tx_data(uart_tx_data),
.uart_txd (rs485_txd ),
.uart_tx_busy( )
);
//按键触发模块
key_trig u_key_trig(
.key (key ),
.sys_rst_n (sys_rst_n ),
.sys_clk (sys_clk ),
.key_data (key_data ),
.key_data_valid (uart_tx_done )
);
endmodule
2.4 仿真文件
代码分析:
1:按键初始为高电平2'b11,把所有的发送的数据设为0,然后仿真按键按下后抖动。
2:defparam:先是仿真里面的例化模块u_rs485_key_led,寻找u_rs485_key_led中的例化模块u_key_trig,寻找例化模块u_key_trig的例化模块u_key_debounce0,寻找例化模块u_key_debounce0中的常量CNX。简单来说,就是这个CNX是一开始设置的常量,但由于我们连续例化了三到四层子模块,但并没层层例化常量,这里没有办法直接用之前#(.******** (******))这种方式例化的,所以得用defparam来找到这个常量的位置进行例化。
注:defparam不可综合,只适用于仿真(tb文件)
`timescale 1ns/1ns //仿真单位/仿真精读
module tb_uart_loopback();
parameter CLK_PERIOD = 20;
defparam u_rs485_key_led.u_key_trig.u_key_debounce0.CNX=10;
defparam u_rs485_key_led.u_key_trig.u_key_debounce1.CNX=10;
reg sys_clk;
reg sys_rst_n;
reg uart_rxd;
wire uart_txd;
reg [1:0] key ;
wire [1:0] led ;
initial begin
sys_clk <=1'b0;
sys_rst_n <= 1'b0;
uart_rxd <= 1'b1;
key <= 2'b11 ;
#200
sys_rst_n <= 1'b1;
#1000
uart_rxd <= 1'b0; //起始位
#8680
uart_rxd <= 1'b0; //D0
#8680
uart_rxd <= 1'b0; //D1
#8680
uart_rxd <= 1'b0; //D2
#8680
uart_rxd <= 1'b0; //D3
#8680
uart_rxd <= 1'b0; //D4
#8680
uart_rxd <= 1'b0; //D5
#8680
uart_rxd <= 1'b0; //D6
#8680
uart_rxd <= 1'b0; //D7
#8680
uart_rxd <= 1'b1; //停止位
#8680
uart_rxd <= 1'b1; //空闲状态
#1000
key <= 2'b00 ;
#100
key <= 2'b11 ;
#100
key <= 2'b00 ;
end
always #(CLK_PERIOD/2) sys_clk = ~sys_clk;
rs485_key_led u_rs485_key_led(
.sys_clk (sys_clk ),
.sys_rst_n (sys_rst_n ),
.key (key ),
.led (),
.rs485_rxd (uart_rxd ),
.rs485_txd ()
);
endmodule
3:烧录
Flash和SD卡可以固化程序,但这些都是PS端的内容,不能单独固化PL程序比特流,具体操作需看后续Linux部分。
参考内容
- 正点原子 领航者ZYNQ开发板资料盘(http://www.openedv.com/docs/)
- ZYNQ领航者V2开发板