Files
DTU-LCD/Drivers/BSP/RS485/rs485.c
2026-01-24 20:03:14 +08:00

209 lines
10 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/******************************************************************************
* @file rs485.c
* @brief rs485串口配置主要配置两种模式 中断接收模式 和 DMA 接收模式
* @details
* 1. DMA模式是主要工作模式
* 2. 中断模式用于错误恢复和接收第一个短帧
* 3. 中断处理完后自动切换回DMA模式
* 4. 中断接收主要用于异常情况处理
* @author 阜阳师范大学物电学院
* @version V0.01
* @date 2026.1.24
* @note 通信协议Modbus RTU
* 通信接口RS485
* 主站地址0x01
******************************************************************************/
#include "rs485.h"
#define RS485_UX USART2
#define RS485_UX_IRQn USART2_IRQn
#define RS485_UX_IRQHandler USART2_IRQHandler
#define RS485_UX_CLK_ENABLE() do{ __HAL_RCC_USART2_CLK_ENABLE(); }while(0) /* 时钟使能 */
#define RS485_SEND_ENABLE() do{HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_RESET);}while(0)
#define RS485_RECEIVE_ENABLE() do{HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_SET);}while(0)
#define RS485_ENABLE_GPIO_PORT GPIOA
#define RS485_ENABLE_GPIO_PIN GPIO_PIN_1
#define RS485_ENABLE_GPIO_CLK_ENABLE() do{ __HAL_RCC_GPIOA_CLK_ENABLE(); }while(0) /* PA口时钟使能 */
#define RS485_TX_GPIO_PORT GPIOA
#define RS485_TX_GPIO_PIN GPIO_PIN_2
#define RS485_TX_GPIO_CLK_ENABLE() do{ __HAL_RCC_GPIOA_CLK_ENABLE(); }while(0) /* PA口时钟使能 */
#define RS485_RX_GPIO_PORT GPIOA
#define RS485_RX_GPIO_PIN GPIO_PIN_3
#define RS485_RX_GPIO_CLK_ENABLE() do{ __HAL_RCC_GPIOA_CLK_ENABLE(); }while(0) /* PA口时钟使能 */
RS485_REGISTER_TYPE RS485REG = {RESET, {0}, {0}}; /* 零初始化定义 */
// HAL库UART句柄
UART_HandleTypeDef rs485_handle; /* UART句柄 */
// HAL库DMA句柄接收
DMA_HandleTypeDef hdma_rs485_rx;
// HAL库DMA句柄发送
DMA_HandleTypeDef hdma_rs485_tx;
#define BAUDRATE (700000)
/**
* @brief 串口X初始化函数
* @param baudrate: 波特率, 根据自己需要设置波特率值
* @note 注意: 必须设置正确的时钟源, 否则串口波特率就会设置异常.
* 这里的USART的时钟源在sys_stm32_clock_init()函数中已经设置过了.
* @retval 无
*/
void RS485_DMA_init()
{
/* 开启时钟 */
RS485_TX_GPIO_CLK_ENABLE(); /* 使能串口TX脚时钟 */
RS485_RX_GPIO_CLK_ENABLE(); /* 使能串口RX脚时钟 */
RS485_ENABLE_GPIO_CLK_ENABLE(); /* 使能串口RX脚时钟 */
RS485_UX_CLK_ENABLE(); /* 使能串口时钟 */
/*GPIO 初始化设置*/
GPIO_InitTypeDef gpio_init_struct = {0};
gpio_init_struct.Pin = RS485_TX_GPIO_PIN; /* 串口发送引脚号 */
gpio_init_struct.Mode = GPIO_MODE_AF_PP; /* 复用推挽输出 */
gpio_init_struct.Pull = GPIO_PULLUP; /* 上拉 */
gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH; /* IO速度设置为高速 */
HAL_GPIO_Init(RS485_TX_GPIO_PORT, &gpio_init_struct);
gpio_init_struct.Pin = RS485_RX_GPIO_PIN; /* 串口RX脚 模式设置 */
gpio_init_struct.Mode = GPIO_MODE_AF_INPUT;
HAL_GPIO_Init(RS485_RX_GPIO_PORT, &gpio_init_struct); /* 串口RX脚 必须设置成输入模式 */
gpio_init_struct.Pin = RS485_ENABLE_GPIO_PIN;
gpio_init_struct.Mode = GPIO_MODE_OUTPUT_PP;
HAL_GPIO_Init(RS485_ENABLE_GPIO_PORT, &gpio_init_struct);
/* 配置DMA接收句柄 */
hdma_rs485_rx.Instance = DMA1_Channel6;
hdma_rs485_rx.Init.Direction = DMA_PERIPH_TO_MEMORY; // 外设到内存
hdma_rs485_rx.Init.PeriphInc = DMA_PINC_DISABLE; // 外设地址不递增
hdma_rs485_rx.Init.MemInc = DMA_MINC_ENABLE; // 内存地址递增
hdma_rs485_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE; // 外设数据对齐:字节
hdma_rs485_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE; // 内存数据对齐:字节
hdma_rs485_rx.Init.Mode = DMA_NORMAL; // 正常模式
hdma_rs485_rx.Init.Priority = DMA_PRIORITY_VERY_HIGH; // 最高优先级
HAL_DMA_Init(&hdma_rs485_rx); /*初始化DMA 接收*/
/* 关联 DMA 到 UART重要*/
__HAL_LINKDMA(&rs485_handle, hdmarx, hdma_rs485_rx);
/* 使能DMA传输完成中断 */
HAL_NVIC_EnableIRQ(DMA1_Channel6_IRQn);
HAL_NVIC_SetPriority(DMA1_Channel6_IRQn, 0, 0);
/* UART 初始化设置 */
rs485_handle.Instance = RS485_UX; /* USART_UX */
rs485_handle.Init.BaudRate = BAUDRATE; /* 波特率 */
rs485_handle.Init.WordLength = UART_WORDLENGTH_8B; /* 字长为8位数据格式 */
rs485_handle.Init.StopBits = UART_STOPBITS_1; /* 一个停止位 */
rs485_handle.Init.Parity = UART_PARITY_NONE; /* 无奇偶校验位 */
rs485_handle.Init.HwFlowCtl = UART_HWCONTROL_NONE; /* 无硬件流控 */
rs485_handle.Init.Mode = UART_MODE_TX_RX; /* 收发模式 */
rs485_handle.Init.OverSampling = UART_OVERSAMPLING_16;
HAL_UART_Init(&rs485_handle); /* 初始化UART */
HAL_NVIC_EnableIRQ(RS485_UX_IRQn); /* 使能USART中断通道 */
HAL_NVIC_SetPriority(RS485_UX_IRQn, 1, 0); /* 组2抢占优先级1子优先级0 */
/* 启动DMA接收 */
RS485_RECEIVE_ENABLE();
HAL_UARTEx_ReceiveToIdle_DMA(&rs485_handle, (uint8_t *)RS485REG.DR, UART_RX_LEN);
}
/**
* @brief 串口X初始化函数
* @note 注意: 必须设置正确的时钟源, 否则串口波特率就会设置异常.
*
* @retval 无
*/
void RS485_init()
{
/* 开启时钟 */
RS485_TX_GPIO_CLK_ENABLE(); /* 使能串口TX脚时钟 */
RS485_RX_GPIO_CLK_ENABLE(); /* 使能串口RX脚时钟 */
RS485_ENABLE_GPIO_CLK_ENABLE(); /* 使能串口RX脚时钟 */
RS485_UX_CLK_ENABLE(); /* 使能串口时钟 */
/*GPIO 初始化设置*/
GPIO_InitTypeDef gpio_init_struct;
gpio_init_struct.Pin = RS485_TX_GPIO_PIN; /* 串口发送引脚号 */
gpio_init_struct.Mode = GPIO_MODE_AF_PP; /* 复用推挽输出 */
gpio_init_struct.Pull = GPIO_PULLUP; /* 上拉 */
gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH; /* IO速度设置为高速 */
HAL_GPIO_Init(RS485_TX_GPIO_PORT, &gpio_init_struct);
gpio_init_struct.Pin = RS485_RX_GPIO_PIN; /* 串口RX脚 模式设置 */
gpio_init_struct.Mode = GPIO_MODE_AF_INPUT;
HAL_GPIO_Init(RS485_RX_GPIO_PORT, &gpio_init_struct); /* 串口RX脚 必须设置成输入模式 */
gpio_init_struct.Pin = RS485_ENABLE_GPIO_PIN;
gpio_init_struct.Mode = GPIO_MODE_OUTPUT_PP;
HAL_GPIO_Init(RS485_ENABLE_GPIO_PORT, &gpio_init_struct);
/*UART 初始化设置*/
rs485_handle.Instance = RS485_UX; /* USART_UX */
rs485_handle.Init.BaudRate = BAUDRATE; /* 波特率 */
rs485_handle.Init.WordLength = UART_WORDLENGTH_8B; /* 字长为8位数据格式 */
rs485_handle.Init.StopBits = UART_STOPBITS_1; /* 一个停止位 */
rs485_handle.Init.Parity = UART_PARITY_NONE; /* 无奇偶校验位 */
rs485_handle.Init.HwFlowCtl = UART_HWCONTROL_NONE; /* 无硬件流控 */
rs485_handle.Init.Mode = UART_MODE_TX_RX; /* 收发模式 */
HAL_UART_Init(&rs485_handle); /* 使能UART */
/* 该函数会开启接收中断标志位UART_IT_RXNE并且设置接收缓冲以及接收缓冲接收最大数据量 */
__HAL_UART_ENABLE_IT(&rs485_handle, UART_IT_RXNE);
HAL_NVIC_EnableIRQ(RS485_UX_IRQn); /* 使能USART中断通道 */
HAL_NVIC_SetPriority(RS485_UX_IRQn, 1, 0); /* 组2抢占优先级1子优先级0 */
RS485_RECEIVE_ENABLE();
}
void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size)
{
if(huart->Instance == RS485_UX)
{
RS485REG.NewMessageFlag = SET;
HAL_UARTEx_ReceiveToIdle_DMA(&rs485_handle, (uint8_t *)RS485REG.DR, UART_RX_LEN);
}
}
/**
* @brief 485串口中断服务函数
* @param 无
* @retval 无
*/
void RS485_UX_IRQHandler(void)
{
HAL_UART_IRQHandler(&rs485_handle);
}
/*******************************************************************************
* FunctionName : DMA1_Channel6_IRQHandler()
* Description : 串口接收中断
* EntryParameter : none
* ReturnValue : none
********************************************************************************/
void DMA1_Channel6_IRQHandler(void)
{
HAL_DMA_IRQHandler(&hdma_rs485_rx);
}
/***********************************************************************
* FunctionName : RS485_SendBuff()
* Description : 485发送数组
* EntryParameter : *ptr:待发送的字符串, len:待发送的数据个数
* ReturnValue : ptrLen:发送的数据个数
**************************************************************************/
void RS485_SendBuff(uint8_t *ptr, uint32_t len)
{
RS485_SEND_ENABLE();
HAL_UART_Transmit(&rs485_handle, ptr,len, 1000);
RS485_RECEIVE_ENABLE();
}