串口RS485

本文介绍了RS485通信技术,包括其全双工和半双工的工作模式,以及差分信号的抗干扰优势。还详细展示了两个VHDL模块(led_ctrl.v和uart_tx.v)的代码,涉及LED控制和UART串口传输的实现。最后,通过tb_rs485.v给出了一个测试bench的例子,用于模拟控制板和被控板的交互。
摘要由CSDN通过智能技术生成

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

看控制模块

看被控模块

  • 5
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
下面是使用STM32的HAL库,通过串口RS485输出字符串的代码示例: 1. 首先需要在CubeMX中配置串口和RS485控制脚的GPIO引脚。 2. 在代码中添加头文件和全局变量: ```c #include "stm32f4xx_hal.h" #include <string.h> UART_HandleTypeDef huart2; // 串口句柄 #define RS485_DIR_Pin GPIO_PIN_1 #define RS485_DIR_GPIO_Port GPIOA ``` 3. 在main函数中初始化串口和GPIO引脚: ```c int main(void) { HAL_Init(); /* Configure the system clock */ SystemClock_Config(); /* Initialize all configured peripherals */ MX_GPIO_Init(); MX_USART2_UART_Init(); /* Infinite loop */ while (1) { } } /* USART2 init function */ static void MX_USART2_UART_Init(void) { huart2.Instance = USART2; huart2.Init.BaudRate = 115200; huart2.Init.WordLength = UART_WORDLENGTH_8B; huart2.Init.StopBits = UART_STOPBITS_1; huart2.Init.Parity = UART_PARITY_NONE; huart2.Init.Mode = UART_MODE_TX_RX; huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE; huart2.Init.OverSampling = UART_OVERSAMPLING_16; if (HAL_UART_Init(&huart2) != HAL_OK) { Error_Handler(); } // 配置RS485控制脚为输出模式 GPIO_InitTypeDef GPIO_InitStruct = {0}; GPIO_InitStruct.Pin = RS485_DIR_Pin; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; HAL_GPIO_Init(RS485_DIR_GPIO_Port, &GPIO_InitStruct); // 初始状态下关闭RS485发送使能 HAL_GPIO_WritePin(RS485_DIR_GPIO_Port, RS485_DIR_Pin, GPIO_PIN_RESET); } ``` 4. 添加发送字符串的函数: ```c void USART_RS485_SendString(UART_HandleTypeDef *huart, char *str) { // 启用RS485发送使能 HAL_GPIO_WritePin(RS485_DIR_GPIO_Port, RS485_DIR_Pin, GPIO_PIN_SET); // 发送字符串 HAL_UART_Transmit(huart, (uint8_t*)str, strlen(str), HAL_MAX_DELAY); // 等待串口发送完成 HAL_UART_Transmit(huart, (uint8_t*)"\r\n", strlen("\r\n"), HAL_MAX_DELAY); HAL_UART_Transmit(huart, (uint8_t*)"\r\n", strlen("\r\n"), HAL_MAX_DELAY); HAL_UART_Transmit(huart, (uint8_t*)"\r\n", strlen("\r\n"), HAL_MAX_DELAY); // 禁用RS485发送使能 HAL_GPIO_WritePin(RS485_DIR_GPIO_Port, RS485_DIR_Pin, GPIO_PIN_RESET); } ``` 5. 在main函数中调用发送字符串函数: ```c int main(void) { HAL_Init(); /* Configure the system clock */ SystemClock_Config(); /* Initialize all configured peripherals */ MX_GPIO_Init(); MX_USART2_UART_Init(); /* Infinite loop */ while (1) { USART_RS485_SendString(&huart2, "Hello, RS485!"); HAL_Delay(1000); } } ``` 以上就是使用STM32的HAL库,通过串口RS485输出字符串的代码示例。注意在实际应用中,需要根据具体的硬件连接和通信协议进行相应的修改。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值