/****************************************************************************** * @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(); }