串口RS485

发布于:2024-04-15 ⋅ 阅读:(186) ⋅ 点赞:(0)

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

看控制模块

看被控模块


网站公告

今日签到

点亮在社区的每一天
去签到