整理了main 函数

This commit is contained in:
2026-08-29 09:45:54 +08:00
parent 9c990ead43
commit 7bc616cf9b
22 changed files with 311 additions and 235 deletions
+64 -33
View File
@@ -14,27 +14,10 @@
#include "rs485.h"
/* ======================== 内部辅助函数 ======================== */
/* ======================== 全局实例 ======================== */
/*
* 函数功能:DWT 微秒级延时
* 入口参数:us - 延时微秒数 uint32_t 1 - N
* 返回值:无
* 限定条件:无(首次调用自动初始化 DWT CYCCNT)
* 函数说明:利用 Cortex-M4 DWT CYCCNT 实现精确微秒延时,168MHz 下 1us = 168 计数。
* 首次调用时自动使能 DWT 和 CYCCNT,后续只读 CYCCNT 寄存器,不影响其他模块。
*/
static void delay_us(uint32_t us)
{
if (!(CoreDebug->DEMCR & CoreDebug_DEMCR_TRCENA_Msk)) {
CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;
DWT->CYCCNT = 0;
DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk;
}
uint32_t start = DWT->CYCCNT;
uint32_t ticks = us * (SystemCoreClock / 1000000uL);
while ((DWT->CYCCNT - start) < ticks) { ; }
}
/* RS-485 全局实例(接收缓冲内聚于句柄的 rx_buf 字段) */
rs485_handle_t g_rs485;
/* ======================== 方向控制宏 ======================== */
@@ -51,7 +34,8 @@ static void delay_us(uint32_t us)
* dir_pin - 方向控制 GPIO 引脚号(如 GPIO_PIN_0) uint16_t
* 返回值:无
* 限定条件:UART 和 GPIO 须先由 CubeMX 完成初始化
* 函数说明:初始化后默认处于接收状态(DE 引脚拉低)
* 函数说明:初始化后默认处于接收状态(DE 引脚拉低),
* 并自动启动 IDLE 中断接收,无需再手动调用 rs485_receive_start。
*/
void rs485_init(rs485_handle_t *p_handle,
UART_HandleTypeDef *p_huart,
@@ -64,6 +48,9 @@ void rs485_init(rs485_handle_t *p_handle,
p_handle->rx_size = 0;
RS485_DIR_RX(p_handle);
/* 自动开启 IDLE 中断接收(后续在回调中重新开启) */
(void)rs485_receive_start(p_handle);
}
/*
@@ -76,14 +63,15 @@ void rs485_init(rs485_handle_t *p_handle,
* HAL_TIMEOUT - 发送超时
* HAL_ERROR - 发送错误
* 限定条件:须在主循环或 FreeRTOS 任务中调用,不可在 UART 中断中调用
* 函数说明:发送前拉高 DE,等待 50us 确保 RS485 收发器驱动使能稳定后再启动 UART 发送。
* 函数说明:发送前拉高 DE,等待 HAL 时基延时确保 RS485 收发器驱动使能稳定后再启动
* UART 发送(HAL 时基最小粒度 1ms,此处延时 1ms,远大于 50us 的 DE 建立时间)。
* HAL_UART_Transmit 内部等待 TC(发送完成)标志,确保最后一字节完全移出后
* 再拉低 DE 切回接收状态。
*/
HAL_StatusTypeDef rs485_transmit(rs485_handle_t *p_handle,
const uint8_t *p_data,
uint16_t len,
uint32_t timeout)
const uint8_t *p_data,
uint16_t len,
uint32_t timeout)
{
HAL_StatusTypeDef status = HAL_OK;
@@ -93,7 +81,8 @@ HAL_StatusTypeDef rs485_transmit(rs485_handle_t *p_handle,
RS485_DIR_TX(p_handle);
delay_us(50);
/* DE 建立延时:HAL 时基为 1ms 粒度,1ms 已充分覆盖收发器驱动使能稳定时间 */
HAL_Delay(1);
status = HAL_UART_Transmit(p_handle->huart,
(uint8_t *)p_data,
@@ -108,22 +97,19 @@ HAL_StatusTypeDef rs485_transmit(rs485_handle_t *p_handle,
/*
* 函数功能:启动中断方式接收(IDLE 空闲帧检测)
* 入口参数:p_handle - RS-485 句柄指针 rs485_handle_t*(非空)
* p_buf - 接收缓冲区 uint8_t*(非空)
* buf_size - 缓冲区大小 uint16_t
* 返回值:HAL_OK - 启动成功 HAL_StatusTypeDef
* HAL_ERROR - 启动失败
* 限定条件:UART 须已开启全局中断(NVIC 使能)
* 函数说明:使用 HAL_UARTEx_ReceiveToIdle_IT 实现变长帧接收。
* 函数说明:使用 HAL_UARTEx_ReceiveToIdle_IT 实现变长帧接收,
* 接收缓冲固定使用句柄内部的 rx_buf(RS485_RX_BUF_SIZE)。
* 当 UART 总线空闲超过 1 个字符时间后硬件触发 IDLE 中断,
* HAL 自动调用 HAL_UARTEx_RxEventCallback 并返回已接收字节数。
* 用户须在回调中重新调用本函数重新开启接收。
*/
HAL_StatusTypeDef rs485_receive_start(rs485_handle_t *p_handle,
uint8_t *p_buf,
uint16_t buf_size)
HAL_StatusTypeDef rs485_receive_start(rs485_handle_t *p_handle)
{
p_handle->rx_size = 0;
return HAL_UARTEx_ReceiveToIdle_IT(p_handle->huart, p_buf, buf_size);
return HAL_UARTEx_ReceiveToIdle_IT(p_handle->huart, p_handle->rx_buf, RS485_RX_BUF_SIZE);
}
/*
@@ -150,3 +136,48 @@ uint16_t rs485_rx_get_size(const rs485_handle_t *p_handle)
{
return p_handle->rx_size;
}
/* ======================== HAL 回调(UART5 / RS-485 专属) ======================== */
/*
* 应用层接收钩子默认实现(弱符号,可被应用重定义)。
* 在中断上下文调用,仅做占位,业务解析由应用提供。
*/
__weak void rs485_on_rx(uint8_t *buf, uint16_t size)
{
(void)buf;
(void)size;
}
/*
* HAL UART 扩展接收回调(IDLE 空闲帧中断触发)。
* 当 RS-485 总线空闲超过 1 个字符时间后触发,由 HAL 自动调用。
* 注意:回调按实例分发,仅处理本驱动绑定的 UART,不写死 UART5。
*/
void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size)
{
if (huart == g_rs485.huart) {
rs485_rx_set_size(&g_rs485, Size);
/* 通知应用处理本次接收(中断上下文,应尽快返回) */
rs485_on_rx(g_rs485.rx_buf, Size);
/* 重新开启接收 */
rs485_receive_start(&g_rs485);
}
}
/*
* HAL UART 错误回调(ORE/NE/FE/PE 等)。
* 读 SR + DR 清除错误标志后重新开启接收,避免中断锁死。
*/
void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart)
{
if (huart == g_rs485.huart) {
/* STM32F4 通过读 SR + DR 清除 ORE/NE/FE/PE 错误标志 */
(void)huart->Instance->SR;
(void)huart->Instance->DR;
/* 重新开启接收 */
rs485_receive_start(&g_rs485);
}
}
+25 -9
View File
@@ -24,6 +24,7 @@ extern "C" {
* 常量定义
*/
#define RS485_TIMEOUT_DEFAULT 1000U /* 默认发送超时(ms) */
#define RS485_RX_BUF_SIZE 256U /* 接收缓冲区大小 */
/*
* RS-485 句柄结构体
@@ -34,6 +35,7 @@ typedef struct {
GPIO_TypeDef *dir_port; /* 方向控制 GPIO 端口 */
uint16_t dir_pin; /* 方向控制 GPIO 引脚号 */
volatile uint16_t rx_size; /* 最近一次接收的字节数 */
uint8_t rx_buf[RS485_RX_BUF_SIZE]; /* 接收缓冲区 */
} rs485_handle_t;
/*
@@ -48,7 +50,8 @@ typedef struct {
* dir_pin - 方向控制 GPIO 引脚号(如 GPIO_PIN_0)
* 返回值:无
* 限定条件:UART 和 GPIO 须先由 CubeMX 完成初始化
* 函数说明:初始化后默认处于接收状态(DE 引脚拉低)
* 函数说明:初始化后默认处于接收状态(DE 引脚拉低),并自动启动 IDLE
* 中断接收,无需再手动调用 rs485_receive_start。
*/
void rs485_init(rs485_handle_t *p_handle,
UART_HandleTypeDef *p_huart,
@@ -73,18 +76,16 @@ HAL_StatusTypeDef rs485_transmit(rs485_handle_t *p_handle,
/*
* 函数功能:启动中断方式接收(IDLE 空闲帧检测)
* 入口参数:handle - RS-485 句柄指针
* buf - 接收缓冲区
* buf_size - 缓冲区大小
* 入口参数:handle - RS-485 句柄指针
* 返回值:HAL_OK / HAL_ERROR
* 限定条件:UART 须已开启全局中断,NVIC 中须使能对应 UART 中断
* 函数说明:使用 HAL_UARTEx_ReceiveToIdle_IT 实现变长帧接收,
* 收到完整帧后在 RxEventCallback 中通知用户。
* 用户须在回调中重新调用本函数重新开启接收
* 接收缓冲固定使用句柄内部的 rx_buf(RS485_RX_BUF_SIZE)。
* 当 UART 总线空闲超过 1 个字符时间后硬件触发 IDLE 中断,
* HAL 自动调用 HAL_UARTEx_RxEventCallback 并返回已接收字节数。
* 用户须在回调中重新调用本函数重新开启接收。
*/
HAL_StatusTypeDef rs485_receive_start(rs485_handle_t *p_handle,
uint8_t *p_buf,
uint16_t buf_size);
HAL_StatusTypeDef rs485_receive_start(rs485_handle_t *p_handle);
/*
* 函数功能:将 RS-485 句柄与 HAL 回调关联
@@ -103,6 +104,21 @@ void rs485_rx_set_size(rs485_handle_t *p_handle, uint16_t size);
*/
uint16_t rs485_rx_get_size(const rs485_handle_t *p_handle);
/*
* 全局 RS-485 实例(由 rs485.c 定义,应用层通过 extern 引用)。
* 接收缓冲已内聚到 rs485_handle_t 的 rx_buf 字段,随实例统一管理。
*/
extern rs485_handle_t g_rs485;
/*
* 应用层接收钩子:RS-485 收到完整帧(IDLE 空闲帧)后由驱动回调调用,
* 传入本次接收缓冲区与有效字节数。默认弱实现为空,应用可重定义此函数
* 处理业务数据。
* 注意:此函数在 UART 中断上下文执行,应尽快拷贝数据后返回,不要在内部
* 调用阻塞式 API(如 rs485_transmit 会切换 DE 并等待 TC)。
*/
void rs485_on_rx(uint8_t *buf, uint16_t size);
#ifdef __cplusplus
}
#endif