Files
DTU-LCD/Drivers/BSP/RS485/rs485.c

230 lines
9.0 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 本文件实现 RS485 串口通信驱动,支持两种模式:
* 1. DMA 模式:主工作模式,使用 ReceiveToIdle 接收不定长帧
* 2. 中断模式用于错误恢复或短帧接收RXNE 逐字节接收
* 收发通过 DE 引脚PA1切换低电平发送高电平接收。
* @author 阜阳师范大学物电学院
* @version V0.01
* @date 2026.1.24
* @note USART2TX=PA2RX=PA3DE=PA1波特率 700000
******************************************************************************/
#include "rs485.h"
/* ============================================================================
* USART 与引脚宏定义
* ============================================================================ */
#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) /**< USART2 时钟使能 */
#define RS485_SEND_ENABLE() do{HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_RESET);}while(0) /**< DE 低:发送 */
#define RS485_RECEIVE_ENABLE() do{HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_SET);}while(0) /**< DE 高:接收 */
#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)
#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)
#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)
#define BAUDRATE (700000) /**< 波特率 */
/* ============================================================================
* 全局变量定义
* ============================================================================ */
RS485_REGISTER_TYPE RS485REG = {RESET, {0}, {0}}; /**< RS485 收发寄存器,零初始化 */
UART_HandleTypeDef rs485_handle; /**< HAL UART 句柄 */
DMA_HandleTypeDef hdma_rs485_rx; /**< HAL DMA 接收句柄DMA1 Channel6 */
DMA_HandleTypeDef hdma_rs485_tx; /**< HAL DMA 发送句柄(未使用,保留) */
/* ============================================================================
* 初始化函数
* ============================================================================ */
/**
* @brief RS485 初始化DMA 接收模式)
* @note 配置流程:
* 1. 使能 GPIO、USART 时钟
* 2. 配置 TX(PA2)、RX(PA3)、DE(PA1)
* 3. 配置 DMA1 Channel6 接收,关联 UART
* 4. 使能 DMA、USART 中断
* 5. 初始化 UART8N1无流控波特率 BAUDRATE
* 6. 切换为接收,启动 ReceiveToIdle DMA
* USART 时钟源需在 sys_stm32_clock_init 中已配置。
* @retval 无
*/
void RS485_DMA_init(void)
{
/* 使能时钟 */
RS485_TX_GPIO_CLK_ENABLE();
RS485_RX_GPIO_CLK_ENABLE();
RS485_ENABLE_GPIO_CLK_ENABLE();
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;
HAL_GPIO_Init(RS485_TX_GPIO_PORT, &gpio_init_struct);
gpio_init_struct.Pin = RS485_RX_GPIO_PIN;
gpio_init_struct.Mode = GPIO_MODE_AF_INPUT;
HAL_GPIO_Init(RS485_RX_GPIO_PORT, &gpio_init_struct);
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);
__HAL_LINKDMA(&rs485_handle, hdmarx, hdma_rs485_rx);
HAL_NVIC_EnableIRQ(DMA1_Channel6_IRQn);
HAL_NVIC_SetPriority(DMA1_Channel6_IRQn, 0, 0);
/* UART 初始化 */
rs485_handle.Instance = RS485_UX;
rs485_handle.Init.BaudRate = BAUDRATE;
rs485_handle.Init.WordLength = UART_WORDLENGTH_8B;
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);
HAL_NVIC_EnableIRQ(RS485_UX_IRQn);
HAL_NVIC_SetPriority(RS485_UX_IRQn, 1, 0);
/* 启动 DMA 接收 */
RS485_RECEIVE_ENABLE();
HAL_UARTEx_ReceiveToIdle_DMA(&rs485_handle, (uint8_t *)RS485REG.DR, UART_RX_LEN);
}
/**
* @brief RS485 初始化(中断接收模式)
* @note 配置 GPIO、UART使能 RXNE 中断。不启用 DMA。
* 用于错误恢复或接收短帧。USART 时钟源需已配置。
* @retval 无
*/
void RS485_init(void)
{
/* 开启时钟 */
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;
HAL_GPIO_Init(RS485_TX_GPIO_PORT, &gpio_init_struct);
gpio_init_struct.Pin = RS485_RX_GPIO_PIN;
gpio_init_struct.Mode = GPIO_MODE_AF_INPUT;
HAL_GPIO_Init(RS485_RX_GPIO_PORT, &gpio_init_struct);
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;
rs485_handle.Init.BaudRate = BAUDRATE;
rs485_handle.Init.WordLength = UART_WORDLENGTH_8B;
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);
__HAL_UART_ENABLE_IT(&rs485_handle, UART_IT_RXNE);
HAL_NVIC_EnableIRQ(RS485_UX_IRQn);
HAL_NVIC_SetPriority(RS485_UX_IRQn, 1, 0);
RS485_RECEIVE_ENABLE();
}
/* ============================================================================
* 回调与中断服务函数
* ============================================================================ */
/**
* @brief UART 接收事件回调ReceiveToIdle 完成时由 HAL 调用)
* @param huart UART 句柄
* @param Size 本帧接收到的字节数
* @note 当为 RS485 所用 UART 时:置位 NewMessageFlag并重新启动 ReceiveToIdle DMA。
* @retval 无
*/
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 RS485 所用 USART 中断服务函数
* @note 调用 HAL_UART_IRQHandler 处理 UART 及关联 DMA 中断。
* @retval 无
*/
void RS485_UX_IRQHandler(void)
{
HAL_UART_IRQHandler(&rs485_handle);
}
/**
* @brief DMA1 Channel6 中断服务函数RS485 接收 DMA
* @note 调用 HAL_DMA_IRQHandler在 ReceiveToIdle 完成等事件时触发。
* @retval 无
*/
void DMA1_Channel6_IRQHandler(void)
{
HAL_DMA_IRQHandler(&hdma_rs485_rx);
}
/* ============================================================================
* 发送接口
* ============================================================================ */
/**
* @brief RS485 发送数据
* @param ptr 待发送数据指针
* @param len 待发送字节数
* @note 切换为发送DE 低)→ 阻塞发送 → 切换回接收DE 高)。超时 1000ms。
* @retval 无
*/
void RS485_SendBuff(uint8_t *ptr, uint32_t len)
{
RS485_SEND_ENABLE();
HAL_UART_Transmit(&rs485_handle, ptr, len, 1000);
RS485_RECEIVE_ENABLE();
}