@@ -12,6 +12,7 @@
******************************************************************************/
# include "rs485.h"
# include "160160D.h"
/* ============================================================================
* USART 与引脚宏定义
@@ -37,7 +38,6 @@
# define RS485_RX_GPIO_CLK_ENABLE() do{ __HAL_RCC_GPIOA_CLK_ENABLE(); }while(0)
# define BAUDRATE (700000) /**< 波特率 */
/* ============================================================================
* 全局变量定义
* ============================================================================ */
@@ -45,7 +45,6 @@ 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 发送句柄(未使用,保留) */
/* ============================================================================
* 初始化函数
@@ -65,61 +64,201 @@ DMA_HandleTypeDef hdma_rs485_tx; /**< HAL DMA 发送句柄(未使用,保留
*/
void RS485_DMA_init ( void )
{
/* 使能时钟 */
RS485_TX_GPIO_CLK_ENABLE ( ) ;
RS485_RX_GPIO_CLK_ENABLE ( ) ;
RS485_ENABLE_GPIO_CLK_ENABLE ( ) ;
RS485_UX_CLK_ENABLE ( ) ;
/* ========================================================================
* 第一步:使能相关外设时钟
* ========================================================================
* 在 STM32 中,所有外设在使用前必须先使能其时钟,以降低功耗。
* 这里需要使能:
* - GPIOA 时钟:用于 TX(PA2)、RX(PA3)、DE(PA1) 引脚
* - USART2 时钟:用于串口通信功能
* - DMA1 时钟:用于 DMA 数据传输功能(必须!)
*/
RS485_TX_GPIO_CLK_ENABLE ( ) ; /* 使能 GPIOA 时钟( TX 引脚 PA2) */
RS485_RX_GPIO_CLK_ENABLE ( ) ; /* 使能 GPIOA 时钟( RX 引脚 PA3) */
RS485_ENABLE_GPIO_CLK_ENABLE ( ) ; /* 使能 GPIOA 时钟( DE 引脚 PA1) */
RS485_UX_CLK_ENABLE ( ) ; /* 使能 USART2 时钟 */
__HAL_RCC_DMA1_CLK_ENABLE ( ) ; /* 使能 DMA1 时钟(必须!否则 DMA 无法工作) */
/* 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 ;
/* ========================================================================
* 第二步:配置 GPIO 引脚功能
* ========================================================================
* RS485 需要三个 GPIO 引脚:
* 1. TX 引脚( PA2) : 发送数据, 配置为复用推挽输出
* 2. RX 引脚( PA3) : 接收数据, 配置为复用输入
* 3. DE 引脚( PA1) : 收发方向控制, 配置为普通推挽输出
*/
GPIO_InitTypeDef gpio_init_struct = { 0 } ; /* 初始化 GPIO 配置结构体 */
/* 配置 TX 引脚( PA2) 为复用推挽输出模式 */
gpio_init_struct . Pin = RS485_TX_GPIO_PIN ; /* 选择 PA2 引脚 */
gpio_init_struct . Mode = GPIO_MODE_AF_PP ; /* 复用推挽输出: USART2_TX 功能 */
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 ;
/* 配置 RX 引脚( PA3) 为复用输入模式 */
gpio_init_struct . Pin = RS485_RX_GPIO_PIN ; /* 选择 PA3 引脚 */
gpio_init_struct . Mode = GPIO_MODE_AF_INPUT ; /* 复用输入: USART2_RX 功能 */
/* 注意: 复用输入模式下, Pull 和 Speed 参数通常被忽略,但保持一致性 */
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 ;
/* 配置 DE 引脚( PA1) 为普通推挽输出模式 */
gpio_init_struct . Pin = RS485_ENABLE_GPIO_PIN ; /* 选择 PA1 引脚 */
gpio_init_struct . Mode = GPIO_MODE_OUTPUT_PP ; /* 推挽输出:用于控制 RS485 收发方向 */
/* DE 引脚控制逻辑:
* - 低电平( RESET) : 使能发送模式, 允许数据从 TX 发送到总线
* - 高电平( SET) : 使能接收模式, 允许从总线接收数据到 RX
*/
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( Direct Memory Access) 用于在 USART 接收数据时自动将数据从
* 外设寄存器传输到内存缓冲区,无需 CPU 干预,提高效率。
*
* STM32F103 中, USART2_RX 对应 DMA1_Channel6
*
* 时钟配置分析(系统时钟 72MHz) :
* - AHB 时钟: 72MHz( DMA 在 AHB 总线上)
* - APB1 时钟: 36MHz( USART2 在 APB1 总线上)
* - 波特率: 700000 bps
* - 每个字节时间:约 14.3 微秒( 10 位: 1 起始 + 8 数据 + 1 停止)
*
* DMA 传输速度要求:
* - DMA 需要从 APB1 外设( USART2) 读取数据到 AHB 内存
* - 需要通过 AHB-APB1 桥,可能有 1-2 个时钟周期延迟
* - 理论上 DMA 时钟 72MHz 足够快,但总线仲裁可能导致延迟
*
* 溢出错误可能原因:
* 1. 总线竞争: CPU 访问 APB1 外设时与 DMA 竞争总线
* 2. AHB-APB1 桥延迟: DMA 访问 APB1 外设需要经过桥接器
* 3. DMA 优先级:虽然设置为最高,但总线仲裁是轮询的
* 4. 接收速度过快: 3208 字节连续接收, DMA 可能来不及处理
*/
hdma_rs485_rx . Instance = DMA1_Channel6 ; /* 使用 DMA1 通道 6 */
hdma_rs485_rx . Init . Direction = DMA_PERIPH_TO_MEMORY ; /* 传输方向:外设到内存 */
hdma_rs485_rx . Init . PeriphInc = DMA_PINC_DISABLE ; /* 外设地址不递增: USART 数据寄存器地址固定 */
hdma_rs485_rx . Init . MemInc = DMA_MINC_ENABLE ; /* 内存地址递增:数据依次存入缓冲区 */
hdma_rs485_rx . Init . PeriphDataAlignment = DMA_PDATAALIGN_BYTE ; /* 外设数据宽度: 字节( 8位) */
hdma_rs485_rx . Init . MemDataAlignment = DMA_MDATAALIGN_BYTE ; /* 内存数据宽度: 字节( 8位) */
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 库能够自动管理 DMA 传输 */
__HAL_LINKDMA ( & rs485_handle , hdmarx , hdma_rs485_rx ) ;
HAL_NVIC_EnableIRQ ( DMA1_Channel6_IRQn ) ;
HAL_NVIC_SetPriority ( DMA1_Channel6_IRQn , 0 , 0 ) ;
/* 使能 DMA 中断,用于接收完成等事件的通知 */
HAL_NVIC_EnableIRQ ( DMA1_Channel6_IRQn ) ; /* 使能 DMA1_Channel6 中断 */
HAL_NVIC_SetPriority ( DMA1_Channel6_IRQn , 0 , 0 ) ; /* 设置中断优先级为最高( 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 ) ;
/* ========================================================================
* 第四步:配置 UART 串口参数
* ========================================================================
* 配置 USART2 的通信参数,包括波特率、数据位、停止位、校验位等
*/
rs485_handle . Instance = RS485_UX ; /* 使用 USART2 */
rs485_handle . Init . BaudRate = BAUDRATE ; /* 波特率: 700000 bps( 高速通信) */
rs485_handle . Init . WordLength = UART_WORDLENGTH_8B ; /* 数据位: 8 位 */
rs485_handle . Init . StopBits = UART_STOPBITS _1; /* 停止位: 1 位 */
rs485_handle . Init . Parity = UART_PARITY_NONE ; /* 校验位:无校验 */
rs485_handle . Init . HwFlowCtl = UART_HWCONTROL_NONE ; /* 硬件流控: 无( RS485 不需要 RTS/CTS) */
rs485_handle . Init . Mode = UART_MODE_TX_RX ; /* 工作模式:同时支持发送和接收 */
rs485_handle . Init . OverSampling = UART_OVERSAMPLING_16 ; /* 过采样: 16 倍( STM32F103 仅支持 16 倍过采样) */
HAL_UART_Init ( & rs485_handle ) ; /* 初始化 UART, 应用上述配置 */
HAL_NVIC_EnableIRQ ( RS485_UX_IRQn ) ;
HAL_NVIC_SetPriority ( RS485_UX_IRQn , 1 , 0 ) ;
/* 手动精确设置 BRR 寄存器以获得准确的 700000 波特率
* 计算: PCLK1 = 36MHz, 16倍过采样
* USARTDIV = 36000000 / (16 * 700000) = 3.2142857...
* BRR = (整数部分 << 4) | 小数部分
*/
/* 启动 DMA 接收 */
RS485_RECEIVE_ENABLE ( ) ;
HAL_UARTEx_ReceiveToIdle_DMA ( & rs485_handle , ( uint8_t * ) RS485REG . DR , UART_RX_LEN ) ;
/* 使能 USART2 中断,用于处理接收完成、错误等事件 */
HAL_NVIC_EnableIRQ ( RS485_UX_IRQn ) ; /* 使能 USART2 中断 */
HAL_NVIC_SetPriority ( RS485_UX_IRQn , 1 , 0 ) ; /* 设置中断优先级为 1( 低于 DMA 中断) */
/* ========================================================================
* 第五步:启动 DMA 接收
* ========================================================================
* 将 RS485 切换到接收模式,并启动 ReceiveToIdle DMA 接收
*
* ReceiveToIdle 模式特点:
* - 当检测到总线空闲( IDLE) 时, 自动触发接收完成回调
* - 适用于接收不定长数据帧(如 Modbus 协议)
* - 无需预先知道数据长度,总线空闲即表示一帧数据接收完成
*/
RS485_RECEIVE_ENABLE ( ) ; /* DE 引脚置高,切换到接收模式 */
// HAL_UARTEx_ReceiveToIdle_DMA(&rs485_handle, /* UART 句柄 */
// (uint8_t *)RS485REG.DR, /* 接收缓冲区: RS485REG.DR( 3208 字节) */
// UART_RX_LEN); /* 最大接收长度: 3208 字节 */
HAL_UARTEx_ReceiveToIdle_IT ( & rs485_handle , /* UART 句柄 */
( uint8_t * ) RS485REG . DR , /* 接收缓冲区: RS485REG.DR( 3208 字节) */
UART_RX_LEN ) ; /* 最大接收长度: 3208 字节 */
/* 注意:接收完成后,会在 HAL_UARTEx_RxEventCallback 回调函数中
* 自动重新启动接收,实现连续接收 */
}
/**
* @brief RS485 反初始化( DMA 接收模式)
* @note 按照与初始化相反的顺序清理资源:
* 1. 停止 DMA 接收
* 2. 禁用中断
* 3. 反初始化 DMA
* 4. 反初始化 UART
* 5. 反初始化 GPIO( 可选)
* 注意:时钟通常不禁用,因为可能被其他外设使用
* @retval 无
*/
void RS485_DMA_DeInit ( void )
{
/* ========================================================================
* 第一步:停止 DMA 接收
* ========================================================================
* 必须先停止正在进行的 DMA 传输,避免数据损坏
*/
HAL_UART_DMAStop ( & rs485_handle ) ; /* 停止 UART DMA 接收 */
/* ========================================================================
* 第二步:禁用中断
* ========================================================================
* 禁用 USART2 和 DMA1_Channel6 的中断,避免在反初始化过程中触发中断
*/
HAL_NVIC_DisableIRQ ( RS485_UX_IRQn ) ; /* 禁用 USART2 中断 */
HAL_NVIC_DisableIRQ ( DMA1_Channel6_IRQn ) ; /* 禁用 DMA1_Channel6 中断 */
/* ========================================================================
* 第三步:反初始化 DMA
* ========================================================================
* 断开 DMA 与 UART 的关联,然后反初始化 DMA 通道
* 注意: HAL 库没有提供 __HAL_UNLINK_DMA 宏,需要手动将指针设为 NULL
*/
rs485_handle . hdmarx = NULL ; /* 断开 DMA 与 UART 的关联 */
HAL_DMA_DeInit ( & hdma_rs485_rx ) ; /* 反初始化 DMA 通道 */
/* ========================================================================
* 第四步:反初始化 UART
* ========================================================================
* 反初始化 USART2, 这会调用 HAL_UART_MspDeInit 来清理底层硬件
*/
HAL_UART_DeInit ( & rs485_handle ) ; /* 反初始化 UART */
/* ========================================================================
* 第五步:反初始化 GPIO( 可选)
* ========================================================================
* 将 GPIO 引脚恢复为默认状态(模拟输入,高阻态)
* 注意:如果这些引脚可能被其他外设使用,可以跳过此步骤
*/
HAL_GPIO_DeInit ( RS485_TX_GPIO_PORT , RS485_TX_GPIO_PIN ) ; /* 反初始化 TX 引脚( PA2) */
HAL_GPIO_DeInit ( RS485_RX_GPIO_PORT , RS485_RX_GPIO_PIN ) ; /* 反初始化 RX 引脚( PA3) */
HAL_GPIO_DeInit ( RS485_ENABLE_GPIO_PORT , RS485_ENABLE_GPIO_PIN ) ; /* 反初始化 DE 引脚( PA1) */
/* 注意:时钟通常不禁用,因为:
* - GPIOA 时钟可能被其他外设使用
* - USART2 时钟可能被其他功能使用
* - DMA1 时钟可能被其他 DMA 通道使用
* 如果需要完全禁用时钟以节省功耗,可以在确认没有其他外设使用时禁用
*/
}
/**
@@ -127,46 +266,96 @@ void RS485_DMA_init(void)
* @note 配置 GPIO、UART, 使能 RXNE 中断。不启用 DMA。
* 用于错误恢复或接收短帧。USART 时钟源需已配置。
* @retval 无
*
* @details 中断接收模式与 DMA 模式的区别:
* - 中断模式: 每接收一个字节触发一次中断, CPU 需要逐字节处理
* - DMA 模式:自动将数据从 USART 传输到内存, CPU 负担小
* 中断模式适用于:
* - DMA 故障时的错误恢复
* - 接收短帧(数据量小,中断开销可接受)
* - 调试和测试场景
*/
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 ( ) ; /* 使能串口时钟 */
/* ========================================================================
* 第一步:使能相关外设时钟
* ========================================================================
* 与 DMA 模式相同,需要使能 GPIOA 和 USART2 的时钟
*/
RS485_TX_GPIO_CLK_ENABLE ( ) ; /* 使能 GPIOA 时钟( TX 引脚 PA2) */
RS485_RX_GPIO_CLK_ENABLE ( ) ; /* 使能 GPIOA 时钟( RX 引脚 PA3) */
RS485_ENABLE_GPIO_CLK_ENABLE ( ) ; /* 使能 GPIOA 时钟( DE 引脚 PA1) */
RS485_UX_CLK_ENABLE ( ) ; /* 使能 USART2 时钟 */
/* 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 ;
/* ========================================================================
* 第二步:配置 GPIO 引脚功能
* ========================================================================
* GPIO 配置与 DMA 模式完全相同:
* - TX(PA2):复用推挽输出,用于发送数据
* - RX(PA3):复用输入,用于接收数据
* - DE(PA1):普通推挽输出,用于控制收发方向
*/
GPIO_InitTypeDef gpio_init_struct = { 0 } ; /* 初始化 GPIO 配置结构体 */
/* 配置 TX 引脚( PA2) 为复用推挽输出模式 */
gpio_init_struct . Pin = RS485_TX_GPIO_PIN ; /* 选择 PA2 引脚 */
gpio_init_struct . Mode = GPIO_MODE_AF_PP ; /* 复用推挽输出: USART2_TX 功能 */
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 ;
/* 配置 RX 引脚( PA3) 为复用输入模式 */
gpio_init_struct . Pin = RS485_RX_GPIO_PIN ; /* 选择 PA3 引脚 */
gpio_init_struct . Mode = GPIO_MODE_AF_INPUT ; /* 复用输入: USART2_RX 功能 */
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 ;
/* 配置 DE 引脚( PA1) 为普通推挽输出模式 */
gpio_init_struct . Pin = RS485_ENABLE_GPIO_PIN ; /* 选择 PA1 引脚 */
gpio_init_struct . Mode = GPIO_MODE_OUTPUT_PP ; /* 推挽输出:控制 RS485 收发方向 */
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 ) ;
/* ========================================================================
* 第三步:配置 UART 串口参数
* ========================================================================
* UART 参数配置与 DMA 模式完全相同:
* - 波特率: 700000 bps
* - 数据格式: 8 位数据位, 1 位停止位,无校验位
* - 无硬件流控
* - 16 倍过采样
*/
rs485_handle . Instance = RS485_UX ; /* 使用 USART2 */
rs485_handle . Init . BaudRate = BAUDRATE ; /* 波特率: 700000 bps */
rs485_handle . Init . WordLength = UART_WORDLENGTH_8B ; /* 数据位: 8 位 */
rs485_handle . Init . StopBits = UART_STOPBITS_1 ; /* 停止位: 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 ; /* 过采样: 16 倍 */
HAL_UART_Init ( & rs485_handle ) ; /* 初始化 UART */
__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 ( ) ;
/* ========================================================================
* 第四步: 使能接收中断( RXNE)
* ========================================================================
* RXNE( Receive Not Empty) 中断:
* - 当 USART 接收数据寄存器( RDR) 中有新数据时触发
* - 每接收一个字节触发一次中断
* - 在中断服务函数中需要手动读取数据并存入缓冲区
*
* 注意:此模式不使用 DMA, 需要 CPU 在中断中逐字节处理数据
*/
__HAL_UART_ENABLE_IT ( & rs485_handle , UART_IT_RXNE ) ; /* 使能 RXNE( 接收数据寄存器非空) 中断 */
HAL_NVIC_EnableIRQ ( RS485_UX_IRQn ) ; /* 使能 USART2 中断 */
HAL_NVIC_SetPriority ( RS485_UX_IRQn , 1 , 0 ) ; /* 设置中断优先级为 1 */
/* ========================================================================
* 第五步:切换到接收模式
* ========================================================================
* 将 DE 引脚置高,使 RS485 处于接收状态,准备接收数据
*/
RS485_RECEIVE_ENABLE ( ) ; /* DE 引脚置高,切换到接收模式 */
/* 注意:中断模式下,需要在 USART2_IRQHandler 中断服务函数中
* 手动调用 HAL_UART_IRQHandler, 并在回调函数中处理接收到的数据 */
}
/* ============================================================================
@@ -178,14 +367,70 @@ void RS485_init(void)
* @param huart UART 句柄
* @param Size 本帧接收到的字节数
* @note 当为 RS485 所用 UART 时:置位 NewMessageFlag, 并重新启动 ReceiveToIdle DMA。
*
* @details HAL 库在调用此回调前已经完成以下操作:
* - DMA 状态已设置为 READY( 在 DMA IRQ 处理中)
* - UART 状态已设置为 READY( 在 UART_DMAReceiveCplt 中)
* - DMA 请求已禁用(清除 DMAR 位)
* - 相关中断已禁用
* 因此可以直接调用 HAL_UARTEx_ReceiveToIdle_DMA 重新启动接收。
*
* @warning 重要:当接收数据长度 = 缓冲区大小( 3208字节) 时:
* - DMA 传输完成中断触发,而不是 IDLE 中断
* - 如果发生溢出错误( ORE) , HAL 库会中止 DMA 并重置 ReceptionType 为 STANDARD
* - 这会导致回调函数不会被调用
* - 解决方案:添加错误回调函数来处理这种情况
*
* @retval 无
*/
void HAL_UARTEx_RxEventCallback ( UART_HandleTypeDef * huart , uint16_t Size )
{
if ( huart - > Instance = = RS485_UX )
{
/* 调试信息:显示接收到的字节数 */
// HZ12AndChar_Printf(0,32, (uint8_t*)"RxEvent:", RESET);
// IntValue_Printf(32,32, Size, RESET);
/* 设置新消息标志,通知主程序处理数据 */
RS485REG . NewMessageFlag = SET ;
HAL_UARTEx_ReceiveToIdle_DMA ( & rs485_handle , ( uint8_t * ) RS485REG . DR , UART_RX_LEN ) ;
/* 重新启动 IT 接收*/
HAL_UARTEx_ReceiveToIdle_IT ( & rs485_handle , /* UART 句柄 */
( uint8_t * ) RS485REG . DR , /* 接收缓冲区: RS485REG.DR( 3208 字节) */
UART_RX_LEN ) ; /* 最大接收长度: 3208 字节 */
}
}
/**
* @brief UART 错误回调函数
* @param huart UART 句柄
* @note 当发生 UART 错误( 如溢出、帧错误等) 时, HAL 库会调用此函数
*
* @details 重要: 当接收3208字节时, 如果发生溢出错误( ORE) , HAL 库会:
* 1. 中止 DMA 传输
* 2. 重置 ReceptionType 为 STANDARD
* 3. 调用错误回调函数(而不是 RxEventCallback)
* 因此,需要在错误回调中处理这种情况,并重新启动接收
*
* @details 错误代码说明:
* - HAL_UART_ERROR_NONE (0x00): 无错误
* - HAL_UART_ERROR_PE (0x01): 奇偶校验错误
* - HAL_UART_ERROR_NE (0x02): 噪声错误(信号干扰、波特率不匹配等)
* - HAL_UART_ERROR_FE (0x04): 帧错误(停止位检测失败)
* - HAL_UART_ERROR_ORE (0x08): 溢出错误( 数据接收过快, DMA 来不及处理)
* - HAL_UART_ERROR_DMA (0x10): DMA 传输错误
*
* @retval 无
*/
void HAL_UART_ErrorCallback ( UART_HandleTypeDef * huart )
{
if ( huart - > Instance = = RS485_UX )
{
// /* 调试信息:显示错误代码 */
// HZ12AndChar_Printf(0,64, (uint8_t*)"Error:", RESET);
// IntValue_Printf(32,64, huart->ErrorCode, RESET);
//HAL_UARTEx_ReceiveToIdle_DMA(&rs485_handle, (uint8_t *)RS485REG.DR, UART_RX_LEN);
}
}