第26章 RS485实验

第二十六章 RS485实验

1. 导入

本章,我们通过该芯片连接 STM32F4 的串口 2,实现两个开发板之间的 485 通信。本章将实现这样的功能:通过连接两个探索者 STM32F4 开发板的 RS485 接口,然后由 KEY0 控制发送,当按下一个开发板的 KEY0 的时候,就发送 5 个数据给另外一个开发板,并在两个开发板上分别显示发送的值和接收到的值。

本章,我们只需要配置好串口 2,就可以实现正常的 485 通信了,串口 2 的配置和串口 1基本类似,只是串口的时钟来自 APB1,最大频率为 42Mhz。

2. 硬件设计

本章要用到的硬件资源如下:

  • 指示灯DS0

  • KEY0按键

  • TFTLCD模块

  • 串口2

  • RS485收发芯片SP3485

屏幕截图 20241021 121818png

从上图可以看出: STM32F4 的串口 2 通过 P9 端口设置,连接到 SP3485,通过 STM32F4的 PG8 控制 SP3485 的收发,当 PG8=0 的时候,为接收模式;当 PG8=1 的时候,为发送模式。

这里需要注意, PA2, PA3 和 ETH_MDIO 和 PWM_DAC 有共用 IO,所以在使用的时候,注意分时复用,不能同时使用。另外 RS485_RE 信号,也和 NRF_IRQ 共用 PG8,所以他们也不可以同时使用,只能分时复用。

另外,图中的 R38 和 R40 是两个偏置电阻,用来保证总线空闲时, A、 B 之间的电压差都会大于 200mV(逻辑 1)。从而避免因总线空闲时, A、 B 压差不定,引起逻辑错乱,可能出现的乱码。

然后,我们要设置好开发板上P9排针的连接,通过跳线帽将PA2和PA3分别连接到485_TX和 485_RX 上面,如图:

屏幕截图 20241021 122419png

3. 软件设计

3.1 中断服务程序

void USART2_IRQHandler(void) // USART2接收中断服务程序
{
    u8 res;	  
    if((__HAL_UART_GET_FLAG(&USART2_RS485Handler,UART_FLAG_RXNE)!=RESET))  //接收中断
	{	 	
       HAL_UART_Receive(&USART2_RS485Handler,&res,1,1000);
		  if(RS485_RX_CNT<64)
		  {
		   	RS485_RX_BUF[RS485_RX_CNT]=res; // 记录接收到的值
			  RS485_RX_CNT++;				// 接收数据增加1 
		  } 
	} 
} 

3.2 初始化串口2

// 初始化IO 串口2
// bound:波特率
void RS485_Init(u32 bound)
{
    //GPIO端口设置
	GPIO_InitTypeDef GPIO_Initure;
	__HAL_RCC_GPIOA_CLK_ENABLE();		   // 使能GPIOA时钟
	__HAL_RCC_USART2_CLK_ENABLE();		   // 使能USART2时钟
	GPIO_Initure.Pin=GPIO_PIN_2|GPIO_PIN_3;// PA2,3
	GPIO_Initure.Mode=GPIO_MODE_AF_PP;	   // 复用推挽输出
	GPIO_Initure.Pull=GPIO_PULLUP;		   // 上拉
	GPIO_Initure.Speed=GPIO_SPEED_HIGH;	   // 高速
	GPIO_Initure.Alternate=GPIO_AF7_USART2;// 复用为USART2
	HAL_GPIO_Init(GPIOA,&GPIO_Initure);	   // 初始化PA2,3
	// PG8推挽输出,485模式控制  
    GPIO_Initure.Pin=GPIO_PIN_8; 		   // PG8
    GPIO_Initure.Mode=GPIO_MODE_OUTPUT_PP; // 推挽输出
    GPIO_Initure.Pull=GPIO_PULLUP;         // 上拉
    GPIO_Initure.Speed=GPIO_SPEED_HIGH;    // 高速
    HAL_GPIO_Init(GPIOG,&GPIO_Initure);
    //USART 初始化设置
	USART2_RS485Handler.Instance=USART2;			       // USART2
	USART2_RS485Handler.Init.BaudRate=bound;		       // 波特率
	USART2_RS485Handler.Init.WordLength=UART_WORDLENGTH_8B;// 字长为8位数据格式
	USART2_RS485Handler.Init.StopBits=UART_STOPBITS_1;	   // 一个停止位
	USART2_RS485Handler.Init.Parity=UART_PARITY_NONE;	   // 无奇偶校验位
	USART2_RS485Handler.Init.HwFlowCtl=UART_HWCONTROL_NONE;// 无硬件流控
	USART2_RS485Handler.Init.Mode=UART_MODE_TX_RX;		   // 收发模式
	HAL_UART_Init(&USART2_RS485Handler);			       // HAL_UART_Init()会使能USART2
  __HAL_UART_DISABLE_IT(&USART2_RS485Handler,UART_IT_TC);  // 关闭接收中断

#if EN_USART2_RX // 如果使能了接收
	__HAL_UART_ENABLE_IT(&USART2_RS485Handler,UART_IT_RXNE);// 开启接收中断
	HAL_NVIC_EnableIRQ(USART2_IRQn);				        // 使能USART1中断
	HAL_NVIC_SetPriority(USART2_IRQn,3,3);			        // 抢占优先级3,子优先级3
#endif	
	RS485_TX_EN = 0; // 默认为接收模式		
}

3.3 RS485发送字节

// RS485发送len个字节.
// buf:发送区首地址
// len:发送的字节数(为了和本代码的接收匹配,这里建议不要超过64个字节)
void RS485_Send_Data(u8 *buf,u8 len)
{
	RS485_TX_EN = 1; // 设置为发送模式
	HAL_UART_Transmit(&USART2_RS485Handler,buf,len,1000);//串口2发送数据
	RS485_RX_CNT = 0;// 清空接收缓存	  
	RS485_TX_EN=0;   // 设置为接收模式	
}

3.4 RS485接收数据

// RS485查询接收到的数据
// buf:接收缓存首地址
// len:读到的数据长度
void RS485_Receive_Data(u8 *buf,u8 *len)
{
	u8 rxlen=RS485_RX_CNT;
	u8 i=0;
	*len=0;				// 默认为0
	delay_ms(10);		// 等待10ms,连续超过10ms没有接收到一个数据,则认为接收结束
	if(rxlen==RS485_RX_CNT&&rxlen) // 接收到了数据,且接收完成了
	{
		for(i=0;i<rxlen;i++)
		{
			buf[i]=RS485_RX_BUF[i];	
		}		
		*len=RS485_RX_CNT;	//记录本次数据长度
		RS485_RX_CNT=0;		//清零
	}
} 

3.5 主函数

int main(void)
{
    u8 key;
	u8 i=0,t=0;
	u8 cnt=0;
	u8 rs485buf[5]; 
	
    HAL_Init();                 // 初始化HAL库    
    Stm32_Clock_Init(336,8,2,7);// 设置时钟,168Mhz
	delay_init(168);            // 初始化延时函数
	uart_init(115200);          // 初始化USART
	usmart_dev.init(84); 		// 初始化USMART
	LED_Init();				    // 初始化LED	
	KEY_Init();					// 初始化KEY
 	LCD_Init();           		// 初始化LCD
	RS485_Init(9600);		    // 初始化RS485
  	POINT_COLOR=RED;
	LCD_ShowString(30,50,200,16,16,"Explorer STM32F4");	
	LCD_ShowString(30,70,200,16,16,"RS485 TEST");		
	LCD_ShowString(30,130,200,16,16,"KEY0:Send");    	//显示提示信息		
	POINT_COLOR=BLUE;//设置字体为蓝色	  
	LCD_ShowString(30,150,200,16,16,"Count:");			// 显示当前计数值	
	LCD_ShowString(30,170,200,16,16,"Send Data:");		// 提示发送的数据	
	LCD_ShowString(30,210,200,16,16,"Receive Data:");	// 提示接收到的数据		
	while(1)
  	{
	  	key=KEY_Scan(0);
		  if(key==KEY0_PRES)//KEY0按下,发送一次数据
	  	{
		  	for(i=0;i<5;i++)
			  {
			  	rs485buf[i]=cnt+i;//填充发送缓冲区
				  LCD_ShowxNum(30+i*32,190,rs485buf[i],3,16,0X80);	//显示数据
 		  	}
			  RS485_Send_Data(rs485buf,5);//发送5个字节 									   
		  }		 
		 RS485_Receive_Data(rs485buf,&key);
		 if(key)//接收到有数据
		  {
			   if(key>5)key=5;//最大是5个数据.
 			   for(i=0;i<key;i++)LCD_ShowxNum(30+i*32,230,rs485buf[i],3,16,0X80);	//显示数据
 	   	}
	 	 t++; 
		 delay_ms(10);
		 if(t==20)
		 {
			 LED0=!LED0;//提示系统正在运行	
			 t=0; 
			 cnt++;
			 LCD_ShowxNum(30+48,150,cnt,3,16,0X80);	//显示数据
		 }		   
	 }	
}

3. 小结

实验目标

通过两个微控制器(如 STM32)实现 RS485 通信。一个微控制器作为主设备,另一个作为从设备,进行数据的发送和接收。

硬件连接

组件 引脚连接
STM32 (主设备) RXD -> DI
STM32 (主设备) TXD -> RO
STM32 (主设备) GND -> GND
STM32 (从设备) RXD -> DI
STM32 (从设备) TXD -> RO
STM32 (从设备) GND -> GND
RS485 转换器 A -> A
RS485 转换器 B -> B
RS485 转换器 VCC -> VCC
RS485 转换器 GND -> GND

软件设计

UART 初始化

在 STM32 项目中,需要配置 UART 接口用于 RS485 通信。以下是一个简单的 UART 初始化代码示例:

#include "main.h"

UART_HandleTypeDef huart1; // UART 句柄

void MX_USART1_UART_Init(void) {
    huart1.Instance = USART1;
    huart1.Init.BaudRate = 9600; // 波特率
    huart1.Init.WordLength = UART_WORDLENGTH_8B; // 数据位
    huart1.Init.StopBits = UART_STOPBITS_1; // 停止位
    huart1.Init.Parity = UART_PARITY_NONE; // 校验位
    huart1.Init.Mode = UART_MODE_TX_RX; // 收发模式
    huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE; // 流控制
    huart1.Init.OverSampling = UART_OVERSAMPLING_16; // 过采样
    HAL_UART_Init(&huart1); // 初始化 UART
}

RS485 数据发送与接收函数

数据发送

使用 RS485 通信时,需要控制驱动的方向引脚,以切换发送和接收模式。下面是一个简单的发送函数:

#define RS485_TX_ENABLE()  HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_SET)  // TX 控制引脚
#define RS485_TX_DISABLE() HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_RESET) // RX 控制引脚

void RS485_Send(uint8_t* data, uint16_t length) {
    RS485_TX_ENABLE(); // 使能发送模式
    HAL_Delay(1); // 延迟确保控制引脚稳定
    HAL_UART_Transmit(&huart1, data, length, HAL_MAX_DELAY); // 发送数据
    RS485_TX_DISABLE(); // 使能接收模式
}
数据接收

以下是接收数据的简单实现

void RS485_Receive(uint8_t* buffer, uint16_t length) {
    HAL_UART_Receive(&huart1, buffer, length, HAL_MAX_DELAY); // 接收数据
}

主函数示例

int main(void) {
    HAL_Init(); // 初始化 HAL 库
    SystemClock_Config(); // 配置系统时钟
    MX_GPIO_Init(); // 初始化 GPIO
    MX_USART1_UART_Init(); // 初始化 UART
    uint8_t send_data[] = "Hello, RS485!";
    uint8_t receive_data[20]; // 用于接收的数据
    while (1) {
        RS485_Send(send_data, sizeof(send_data)); // 发送数据
        HAL_Delay(1000); // 延迟 1 秒
        RS485_Receive(receive_data, sizeof(receive_data)); // 接收数据
        // 可以在这里添加代码来处理 receive_data 数据
    }
}
posted @ 2024-10-21 12:46  hazy1k  阅读(25)  评论(0编辑  收藏  举报