第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
从上图可以看出: 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 上面,如图:
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 数据
}
}