1.原理
全双工:在同一时刻可以同时进行数据的接收和数据的发送,两者互不影响
半双工:在同一时刻只能进行数据的接收或者数据的发送,两者不能同时进行
差分信号幅值相同,相位相反,有更强的抗干扰能力。
干扰对差分信号的影响都是相同的,所以差分信号的干扰一相减就没有了
RS485的优点,采用差分信号有更强的抗干扰能力;相比RS232能能进行长距离传输(RS485要用到收发器芯片,收发器的灵敏度是很高的,可以检测到低至200mv的电压,表示传输信号在千米之外都可以恢复,最远的通信距离可以达到1200米左右,速度最快10MB/s,速度和距离是成反比的,速度越小,传输距离越长,长距离的通信可以增加RS485的中继器);缺点就是只支持半双工。
RE是低电平有效,表示数据的收,当接收时,RE=0,DE=0;然后芯片将差分信号转换为单端信号。当RE=1,DE=1时,数据发送,将单端信号转换为差分信号。
485和232使用相同的传输协议
2.代码
以上是控制板的波形图
以上是被控板的时序图
2.1 led_ctrl.v
module led_ctrl(
input wire sys_clk ,
input wire sys_rst_n ,
input wire [3:0] led_out_w ,//流水灯
input wire led_out_b ,//呼吸灯
input wire [7:0] pi_data ,
input wire key_flag_w ,
input wire key_flag_b ,
output reg [3:0] led ,
output wire [7:0] po_data ,
output wire po_flag
);
reg w_en;
reg b_en;
assign po_data={6'b000_000,b_en,w_en};
assign po_flag=key_flag_b||key_flag_w;
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n==1'b0)
w_en<=1'b0;
else if(key_flag_b==1'b1)
w_en<=1'b0;
else if(key_flag_w==1'b1)
w_en<=~w_en;
else
w_en<=w_en;
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n==1'b0)
b_en<=1'b0;
else if(key_flag_w==1'b1)
b_en<=1'b0;
else if(key_flag_b==1'b1)
b_en<=~b_en;
else
b_en<=b_en;
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n==1'b0)
led<=4'b1111;
else if(po_data[0]==1'b1)
led<=led_out_w;
else if(po_data[1]==1'b1)
led<=led_out_b;
else
led<=4'b1111;
endmodule
2.2 uart_tx.v
闲杂输出的赋值条件不再是bit_flag信号,而是使能信号,rx下降沿延迟work_en一个周期,因为是时序逻辑,而且只延迟一个时钟周期,一个Bit传输有5208个周期。若是想对齐可以把work_en打一拍
module uart_tx
#(
parameter UART_BPS='d9600,
parameter CLK_FREQ='d50_000_000
)(
input wire sys_clk ,
input wire sys_rst_n ,
input wire [7:0] pi_data ,
input wire pi_flag ,
output reg work_en ,
output reg tx
);
parameter BAUD_CNT_MAX=CLK_FREQ/UART_BPS;
reg [15:0] baud_cnt;
reg bit_flag;
reg [3:0]bit_cnt;
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n==1'b0)
work_en<=1'b0;
else if ((bit_cnt==4'd9)&&(bit_flag==1'b1))
work_en<=1'b0;
else if(pi_flag==1'b1)
work_en<=1'b1;
else
work_en<=work_en;
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n==1'b0)
baud_cnt<=16'd0;
else if((baud_cnt==BAUD_CNT_MAX-1'b1)||(work_en==1'b0))
baud_cnt<=16'd0;
else if(work_en==1'b1)
baud_cnt<=baud_cnt+1'b1;
else
baud_cnt<=baud_cnt;
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n==1'b0)
bit_flag<=1'b0;
else if (baud_cnt==BAUD_CNT_MAX-1'b1) //因为只有使能信号为高电平时,使能信号才进行计数,使能信号为低电平,波特计数器为0。所以不适合用计数值为0来作为条件
bit_flag<=1'b1;
else
bit_flag<=1'b0;
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n==1'b0)
bit_cnt<=4'd0;
else if((bit_cnt==4'd9)&&(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;
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n==1'b0)
tx<=1'b1;
else if(work_en==1'b1)
case(bit_cnt)
4'd0: tx<=1'b0;
4'd1: tx<=pi_data[0];
4'd2: tx<=pi_data[1];
4'd3: tx<=pi_data[2];
4'd4: tx<=pi_data[3];
4'd5: tx<=pi_data[4];
4'd6: tx<=pi_data[5];
4'd7: tx<=pi_data[6];
4'd8: tx<=pi_data[7];
4'd9: tx<=1'b1;
default:tx<=1'b1;
endcase
endmodule
2.3 rs485.v
module rs485(
input wire sys_clk ,
input wire sys_rst_n ,
input wire key_in_w ,
input wire key_in_b ,
input wire rx ,
output wire tx ,
output wire re ,
output wire[3:0]led
);
parameter KEY_CNT_MAX=20'd999_999;
parameter WATER_LED_CNT_MAX=25'd24_999_999;
parameter CNT_1US_MAX = 6'd49 ,
CNT_1MS_MAX = 10'd999 ,
CNT_1S_MAX = 10'd999 ;
parameter UART_BPS=9600,
CLK_FREQ=50_000_000;
wire w_flag;
wire b_flag;
wire [3:0]w_led;
wire b_led;
wire [7:0]rx_data;
wire [7:0]po_data;
wire po_flag;
key_filter
#(
.CNT_MAX(KEY_CNT_MAX)
)
key_filter_inst_w
(
.sys_clk (sys_clk ) ,
.sys_rst_n (sys_rst_n ) ,
.key_in (key_in_w ) ,
.key_flag (w_flag)
);
key_filter
#(
.CNT_MAX(KEY_CNT_MAX)
)
key_filter_inst_b
(
.sys_clk (sys_clk ) ,
.sys_rst_n (sys_rst_n ) ,
.key_in (key_in_b ) ,
.key_flag (b_flag )
);
water_led
#(
.CNT_MAX(WATER_LED_CNT_MAX)
)
water_led_inst
(
.sys_clk (sys_clk ),
.sys_rst_n (sys_rst_n ),
.led_out (w_led )
);
breath_led#(
.CNT_1US_MAX(CNT_1US_MAX ),
.CNT_1MS_MAX(CNT_1MS_MAX ),
.CNT_1S_MAX (CNT_1S_MAX )
)
breath_led_inst
(
.sys_clk (sys_clk ) ,
.sys_rst_n (sys_rst_n),
.led_out (b_led )
);
uart_rx
#(
.UART_BPS(UART_BPS),
.CLK_FREQ( CLK_FREQ )
)
uart_rx_inst
(
.sys_clk (sys_clk ) ,
.sys_rst_n (sys_rst_n ) ,
.rx (rx ) ,
.po_data (rx_data ) ,
.po_flag ()
);
led_ctrl led_ctrl_inst(
.sys_clk (sys_clk) ,
.sys_rst_n (sys_rst_n),
.led_out_w (w_led),
.led_out_b (b_led),
.pi_data (rx_data) ,
.key_flag_w (w_flag),
.key_flag_b (b_flag),
.led (led) ,
.po_data (po_data) ,
.po_flag (po_flag)
);
uart_tx
#(
.UART_BPS(UART_BPS ),
.CLK_FREQ(CLK_FREQ )
)
uart_tx_inst
(
.sys_clk (sys_clk) ,
.sys_rst_n (sys_rst_n) ,
.pi_data (po_data) ,
.pi_flag (po_flag) ,
.work_en (re) ,
.tx (tx)
);
endmodule
2.4 tb_rs485.v
`timescale 1ns/1ns
module tb_rs485();
reg sys_clk;
reg sys_rst_n;
reg key_in_w ;
reg key_in_b ;
reg rx ;
wire tx;
wire re;
wire [3:0]led;
initial
begin
sys_clk=1'b1;
sys_rst_n<=1'b0;
key_in_b<=1'b1;
key_in_w<=1'b1;
#20
sys_rst_n<=1'b1;
//流水灯
#2000000 key_in_w<=1'b0; //按下流水灯的按键
#20 key_in_w<=1'b1;
#20 key_in_w<=1'b0;
#20 key_in_w<=1'b1; //模拟前抖动
#20 key_in_w<=1'b0; //模拟稳定状态
#200 key_in_w<=1'b1; //模拟后抖动
#20 key_in_w<=1'b0;
#20 key_in_w<=1'b1;
#20 key_in_w<=1'b0;
#20 key_in_w<=1'b1;
//呼吸灯
#2000000 key_in_b<=1'b0;
#20 key_in_b<=1'b1;
#20 key_in_b<=1'b0;
#20 key_in_b<=1'b1;
#20 key_in_b<=1'b0;
#200 key_in_b<=1'b1;
#20 key_in_b<=1'b0;
#20 key_in_b<=1'b1;
#20 key_in_b<=1'b0;
#20 key_in_b<=1'b1;
//呼吸灯
#2000000 key_in_b<=1'b0;
#20 key_in_b<=1'b1;
#20 key_in_b<=1'b0;
#20 key_in_b<=1'b1;
#20 key_in_b<=1'b0;
#200 key_in_b<=1'b1;
#20 key_in_b<=1'b0;
#20 key_in_b<=1'b1;
#20 key_in_b<=1'b0;
#20 key_in_b<=1'b1;
//流水灯
#2000000 key_in_w<=1'b0;
#20 key_in_w<=1'b1;
#20 key_in_w<=1'b0;
#20 key_in_w<=1'b1;
#20 key_in_w<=1'b0;
#200 key_in_w<=1'b1;
#20 key_in_w<=1'b0;
#20 key_in_w<=1'b1;
#20 key_in_w<=1'b0;
#20 key_in_w<=1'b1;
end
always #10 sys_clk=~sys_clk;
defparam rs485_inst0.KEY_CNT_MAX=5;
defparam rs485_inst0.WATER_LED_CNT_MAX=4000;
defparam rs485_inst1.WATER_LED_CNT_MAX=4000;
defparam rs485_inst0.CNT_1US_MAX=4;
defparam rs485_inst1.CNT_1US_MAX=4;
defparam rs485_inst0.CNT_1MS_MAX=9;
defparam rs485_inst1.CNT_1MS_MAX=9;
defparam rs485_inst0.CNT_1S_MAX=9;
defparam rs485_inst1.CNT_1S_MAX=9;
defparam rs485_inst0.UART_BPS=1000_000;
defparam rs485_inst1.UART_BPS=1000_000;//越大越快
//控制板不用rx信号
rs485 rs485_inst0(
.sys_clk (sys_clk) ,
.sys_rst_n (sys_rst_n),
.key_in_w (key_in_w ),
.key_in_b (key_in_b ),
.rx (),//对于控制板来说,rx是无效的,因为我们只需要两路按键
.tx (tx),
.re (re),
.led ()//控制板的led灯一直是熄灭状态不需要引出来
);
//被控板
rs485 rs485_inst1(
.sys_clk (sys_clk) ,
.sys_rst_n (sys_rst_n),
.key_in_w ( ),//对于被控板来说,按键信号是无效的
.key_in_b ( ),
.rx (tx),
.tx (),//tx和re没有用到不需要引出
.re (),
.led (led)
);
endmodule
看控制模块
看被控模块