STM32从零到量产开发:四路继电器工业控制模块开发-RS485 半双工通信底层驱动模块(bsp_res485)设计说明
·
STM32从零到量产开发:四路继电器工业控制模块开发-RS485 半双工通信底层驱动模块(bsp_res485)设计说明
1. 模块概述
本模块(bsp_rs485)是基于 STM32 HAL 库开发的 RS485 半双工通信底层驱动,主要基于 USART1 实现。模块采用“单字节接收中断 + IDLE 总线空闲中断”的机制进行 Modbus 帧的分割与接收,并使用阻塞式发送配合 DE 方向引脚自动控制完成半双工换向。支持运行时动态切换波特率,并提供配套的字符串查询接口供 Shell 调试打印。
2. 硬件接口配置
- TX (发送引脚): PA9
- RX (接收引脚): PA10
- DE (发送使能引脚): PA8(高电平有效,拉高进入发送模式,拉低进入接收模式)
3. 核心数据流
3.1 接收数据流
- RS485 总线数据到达 USART1,触发单字节接收中断。
- 进入
BSP_RS485_OnRxByte(),将字节线性写入s_rx_buf,并使s_rx_index递增。 - 总线空闲触发 IDLE 中断,进入
BSP_RS485_OnIdleIRQ()。 - 置位
s_frame_ready并记录s_frame_len,标记整帧接收完成。 - 主循环通过
BSP_RS485_IsFrameReady()查询到就绪标志后,调用BSP_RS485_GetFrame()取走完整数据。 - 上层协议栈(如
modbus_rtu)对取出的数据进行解析。
3.2 发送数据流
- 上层协议栈调用
BSP_RS485_Send()发起发送。 - 模块自动拉高 DE 引脚,切换为发送模式,并关闭接收中断(防止总线回环干扰)。
- 执行阻塞式发送,轮询等待 TC(发送完成)标志。
- 发送完成后拉低 DE 引脚,恢复接收模式。
- 清空接收缓存中的脏数据,重新开启单字节接收中断,恢复总线监听。
4. 关键变量与缓冲区
| 变量/宏名称 | 类型/大小 | 说明 |
|---|---|---|
RS485_RX_BUF_SIZE |
宏 (256u) | RS485 接收线性缓冲区最大字节数(适配 Modbus 最大帧长) |
RS485_TX_BUF_SIZE |
宏 (256u) | RS485 发送缓冲区最大字节数 |
s_rx_buf |
静态数组 | 线性接收缓存,存放当前正在接收的一帧原始报文 |
s_rx_index |
静态变量 | 接收缓存写入下标 |
s_frame_ready |
volatile 静态变量 | 完整帧就绪标志(中断置位,主循环清零) |
s_frame_len |
volatile 静态变量 | 完整帧字节长度 |
s_baud_tbl |
静态常量数组 | 波特率索引到硬件寄存器数值的映射表 |
g_baud_str |
静态常量数组 | 波特率索引到打印字符串的映射表(仅本文件私有) |
5. API 接口说明
5.1 初始化与配置
-
void BSP_RS485_Init(void)- 功能: RS485 外设初始化。
- 说明: 初始化串口,DE 引脚默认拉低进入接收模式,开启 IDLE 空闲中断与单字节接收中断。
-
int8_t BSP_RS485_SetBaudrate(uint8_t baud_index)- 功能: 动态重新配置 RS485 串口波特率。
- 参数:
baud_index(波特率索引,范围BAUD_INDEX_9600~BAUD_INDEX_115200) - 返回值:
0配置成功;-1索引参数非法或初始化失败。 - 说明: 会重新初始化串口,清空接收缓存,重启中断接收。
5.2 数据收发
-
uint8_t BSP_RS485_Send(const uint8_t *data, uint16_t len)- 功能: RS485 阻塞发送一帧数据,自动控制 DE 发送引脚。
- 参数:
data(待发送二进制数据指针),len(发送字节长度) - 返回值:
0发送成功;1空指针/长度超限/发送超时失败。 - 注意: 阻塞式发送,禁止在中断上下文调用。
-
uint8_t BSP_RS485_IsFrameReady(void)- 功能: 查询是否收到完整一帧数据。
- 返回值:
1存在完整帧;0无完整接收帧。
-
void BSP_RS485_GetFrame(uint8_t *buf, uint16_t *len)- 功能: 读取已接收完整 Modbus 帧,读取后自动清空帧标志并重启接收。
- 参数:
buf(接收数据存储缓冲区),len(实际接收字节长度输出)
5.3 状态查询与辅助
-
uint8_t BSP_RS485_GetBaudIndex(void)- 功能: 获取当前生效的波特率索引。
-
const char* RS485_GetBaudStr(uint8_t index)- 功能: 根据波特率索引获取对应打印字符串(供 RS232 shell 调试输出)。
- 参数:
index(波特率索引 0~4) - 返回值: 波特率文本,索引越界返回
"?"。
5.4 底层中断回调(内部使用)
void BSP_RS485_OnIdleIRQ(void): USART IDLE 空闲中断回调,标记一帧接收完成。仅由stm32f1xx_it.c内部调用。void BSP_RS485_OnRxByte(void): 单字节接收完成 HAL 回调分发函数。由HAL_UART_RxCpltCallback统一入口调用。
6. 架构设计与解耦说明
- HAL 回调统一分发: 本文件承载了 HAL 串口回调的总分发入口(
RxCplt/Error),通过判断Instance区分 USART1(RS485) 与 UART4(RS232),分别转交各自模块处理,避免回调冲突。 - DE 换向时序封装: DE 方向引脚的换向时序完全封装在
BSP_RS485_Send内部,上层业务只管发数据,无需关心底层硬件时序。 - 中断与主循环解耦: 中断上下文仅做“存字节”与“打帧标记”等极简操作,所有复杂的帧解析与业务处理全部放在主循环执行。
- 字符串映射解耦: 波特名字符串映射统一存放在本模块内部,对外仅提供
RS485_GetBaudStr只读接口供 Shell 调用,消除了rs232_shell内部重复定义波特表的耦合问题。 - 线性缓冲设计: 针对 Modbus RS485 通信特性,未采用标准环形 FIFO,而是采用线性缓冲 +
s_rx_index设计,配合 IDLE 中断实现精准的帧分割。
/**
******************************************************************************
* @file bsp_rs485.h
* @brief RS485 半双工通信底层驱动 - USART1
* @details 硬件:PA9(TX) PA10(RX) PA8(DE发送使能高有效)
* 接收:单字节中断 + IDLE帧空闲中断检测一帧结束
* 发送:阻塞发送,自动拉高/拉低DE方向引脚
* 波特率配置可运行时动态切换,配套字符串查询接口供shell打印
******************************************************************************
*/
#ifndef __BSP_RS485_H
#define __BSP_RS485_H
#ifdef __cplusplus
extern "C" {
#endif
#include "main.h"
#include "usart.h"
/* RS485接收环形缓冲区最大字节数 Modbus最大帧256 */
#define RS485_RX_BUF_SIZE 256u
/* RS485发送缓冲区最大字节数 */
#define RS485_TX_BUF_SIZE 256u
/* 波特率索引枚举宏,与Flash参数保持寄存器一一对应 */
#define BAUD_INDEX_9600 0u
#define BAUD_INDEX_19200 1u
#define BAUD_INDEX_38400 2u
#define BAUD_INDEX_57600 3u
#define BAUD_INDEX_115200 4u
#define BAUD_INDEX_NUM 5u
/** 波特率索引最大值 */
#define BAUD_INDEX_MAX (BAUD_INDEX_NUM - 1u)
/**
* @brief RS485外设初始化
* @note 初始化串口、DE引脚默认接收模式,开启IDLE空闲中断与单字节接收中断
*/
void BSP_RS485_Init(void);
/**
* @brief 动态重新配置RS485串口波特率
* @param[in] baud_index 波特率索引 BAUD_INDEX_9600~BAUD_INDEX_115200
* @retval int8_t 0:配置成功;-1:索引参数非法
* @note 会重新初始化串口,清空接收缓存,重启中断接收
*/
int8_t BSP_RS485_SetBaudrate(uint8_t baud_index);
/**
* @brief 获取当前生效波特率索引
* @retval uint8_t 当前baud_index
*/
uint8_t BSP_RS485_GetBaudIndex(void);
/**
* @brief 根据波特率索引获取对应打印字符串(供RS232 shell调试输出)
* @param[in] index 波特率索引0~4
* @retval const char* 波特率文本,索引越界返回"?"
* @note 字符串映射表仅存在bsp_rs485.c内部,上层仅通过本接口读取,消除跨文件耦合
*/
const char* RS485_GetBaudStr(uint8_t index);
/**
* @brief RS485阻塞发送一帧数据,自动控制DE发送引脚
* @param[in] data 待发送二进制数据指针
* @param[in] len 发送字节长度
* @retval uint8_t 0:发送成功;1:空指针/长度超限/发送超时失败
*/
uint8_t BSP_RS485_Send(const uint8_t *data, uint16_t len);
/**
* @brief 查询是否收到完整一帧数据
* @retval uint8_t 1:存在完整帧;0:无完整接收帧
*/
uint8_t BSP_RS485_IsFrameReady(void);
/**
* @brief 读取已接收完整Modbus帧,读取后自动清空帧标志重启接收
* @param[out] buf 接收数据存储缓冲区,长度≥RS485_RX_BUF_SIZE
* @param[out] len 实际接收字节长度输出
*/
void BSP_RS485_GetFrame(uint8_t *buf, uint16_t *len);
/**
* @brief USART IDLE空闲中断回调函数
* @note 仅stm32f1xx_it.c中断服务函数内部调用,业务层禁止直接调用
* 总线空闲时标记一帧接收完成,置帧就绪标志
*/
void BSP_RS485_OnIdleIRQ(void);
/**
* @brief 单字节接收完成HAL回调分发函数
* @note 由HAL_UART_RxCpltCallback统一入口调用,仅底层中断文件使用
*/
void BSP_RS485_OnRxByte(void);
#ifdef __cplusplus
}
#endif
#endif /* __BSP_RS485_H */
/**
******************************************************************************
* @file bsp_rs485.c
* @brief RS485 半双工通信底层驱动实现 (基于 USART1)
*
* 模块功能:
* 1. 接收: 单字节接收中断 + IDLE 总线空闲中断做 Modbus 帧分割。
* 2. 发送: 阻塞式发送, 自动拉高 / 拉低 DE 方向引脚完成半双工换向。
* 3. 波特率运行时动态切换, 并提供波特字符串只读查询接口。
*
* 数据流:
* 接收: RS485 总线 -> USART1 接收中断 -> BSP_RS485_OnRxByte()
* -> 线性写入 s_rx_buf (s_rx_index 递增)
* -> 总线空闲触发 IDLE 中断 -> BSP_RS485_OnIdleIRQ()
* -> 置 s_frame_ready / s_frame_len 标记整帧完成
* -> 主循环 BSP_RS485_IsFrameReady() 查询, BSP_RS485_GetFrame() 取走
* -> 上层 modbus_rtu 解析
* 发送: modbus_rtu -> BSP_RS485_Send() -> 拉高 DE -> 阻塞发送
* -> 轮询等待 TC 发送完成 -> 拉低 DE -> 清脏缓存 -> 恢复接收中断
*
* 解耦优化:
* 波特名字符串映射统一存放本文件, 提供 RS485_GetBaudStr 只读接口供 shell 调用,
* 消除 rs232_shell 内部重复定义波特表的耦合问题。
* Modbus 485 不适用环形 FIFO, 采用线性缓冲 + s_rx_index 设计。
*
* 解耦说明:
* 1. 本文件同时承载 HAL 串口回调的总分发入口 (RxCplt / Error), 按 Instance
* 区分 USART1(RS485) 与 UART4(RS232), 分别转交各自模块处理。
* 2. DE 方向引脚的换向时序完全封装在 BSP_RS485_Send 内部, 上层只管发数据。
* 3. 中断上下文只做 "存字节" 与 "打帧标记", 帧解析全部放主循环。
******************************************************************************
*/
#include "bsp_rs485.h"
#include "bsp_rs232.h"
#include "main.h"
#include <string.h>
/* -------------------------- API 快速导航 --------------------------
* void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart); HAL 接收完成总回调 (中断上下文)
* void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart); HAL 串口错误总回调 (中断上下文)
* void BSP_RS485_Init(void); 初始化, 开 IDLE + 单字节接收中断
* int8_t BSP_RS485_SetBaudrate(uint8_t baud_index); 动态切换波特率 (0 成功 / -1 非法)
* uint8_t BSP_RS485_Send(const uint8_t *data, uint16_t len); 阻塞发送一帧, 自动控制 DE
* uint8_t BSP_RS485_IsFrameReady(void); 查询是否有完整帧就绪
* void BSP_RS485_GetFrame(uint8_t *buf, uint16_t *len); 读取完整帧并清标志
* uint8_t BSP_RS485_GetBaudIndex(void); 读取当前波特率索引
* const char *RS485_GetBaudStr(uint8_t index); 索引转波特字符串 (越界返回 "?")
* void BSP_RS485_OnIdleIRQ(void); IDLE 空闲中断处理 (中断上下文)
* void BSP_RS485_OnRxByte(void); 单字节接收中断处理 (中断上下文)
* ------------------------------------------------------------------ */
/* -------------------------- 区块 1:static const 只读常量 / 映射查表 -------------------------- */
/**
* @brief 波特率索引→硬件寄存器数值映射表,底层串口初始化使用
*/
static const uint32_t s_baud_tbl[BAUD_INDEX_NUM] =
{
9600u, 19200u, 38400u, 57600u, 115200u
};
/**
* @brief 波特率索引→打印字符串映射表,仅本文件static私有
* @note 上层rs232_shell通过RS485_GetBaudStr接口读取,不直接访问此数组
*/
static const char *const g_baud_str[BAUD_INDEX_NUM] =
{
"9600", "19200", "38400", "57600", "115200"
};
/* -------------------------- 区块 2:模块内部静态全局变量 -------------------------- */
/* 接收环形缓存 */
static uint8_t s_rx_buf[RS485_RX_BUF_SIZE]; /* 线性接收缓存,存放当前正在接收的一帧原始报文 */
static uint16_t s_rx_index = 0u; /* 接收写入下标 */
static uint8_t s_rx_byte = 0u; /* 中断单字节临时缓存 */
static volatile uint8_t s_frame_ready = 0u; /* 完整帧就绪标志
* volatile:由 IDLE 中断置位、主循环查询清零,
* 禁止编译器把轮询判断优化为寄存器缓存 */
static volatile uint16_t s_frame_len = 0u; /* 完整帧字节长度
* volatile:与 s_frame_ready 同在中断中写入,
* 主循环取帧时必须读到最新值 */
/* 当前生效波特率索引 */
static uint8_t s_baud_index = BAUD_INDEX_9600;
/* 发送临时缓存 */
static uint8_t s_tx_buf[RS485_TX_BUF_SIZE];
/* -------------------------- 区块 3:HAL 库系统全局回调钩子(统一分发入口) -------------------------- */
/**
* @brief HAL库串口接收完成统一回调入口
* @param[in] huart 串口句柄指针
* @retval 无
* @note 区分USART1(RS485) / UART4(RS232)分发到各自接收处理函数
* @note 运行在中断上下文,禁止耗时操作
* 【关联指引】本钩子覆盖 HAL 弱定义,是全工程串口收字节的唯一入口,
* 分别转交 BSP_RS485_OnRxByte() 与 BSP_RS232_OnRxByte()。
*/
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if (huart->Instance == USART1)
{
BSP_RS485_OnRxByte();
}
else if (huart->Instance == UART4)
{
BSP_RS232_OnRxByte();
}
}
/**
* @brief HAL库串口错误中断统一回调入口
* @param[in] huart 串口句柄指针
* @retval 无
* @note RS485发生帧错误/溢出时清空缓存重启接收
* @note 运行在中断上下文,禁止耗时操作
*/
void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart)
{
if (huart->Instance == USART1)
{
/* 出错帧内容已不可信:直接丢弃半帧数据并重启接收 */
s_rx_index = 0u;
s_frame_ready = 0u;
HAL_UART_Receive_IT(&huart1, &s_rx_byte, 1u);
}
else if (huart->Instance == UART4)
{
BSP_RS232_OnError(huart);
}
}
/* -------------------------- 区块 4:对外公共业务 API -------------------------- */
/**
* @brief RS485模块初始化
* @retval 无
* @note 需在 MX_USART1_UART_Init 之后调用;DE 默认拉低进入接收模式
*/
void BSP_RS485_Init(void)
{
s_rx_index = 0u;
s_frame_ready = 0u;
s_frame_len = 0u;
s_baud_index = BAUD_INDEX_9600;
/* DE引脚默认低电平,接收模式 */
HAL_GPIO_WritePin(RS485_DE_GPIO_Port, RS485_DE_Pin, GPIO_PIN_RESET);
/* 使能总线空闲IDLE中断 */
__HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE);
/* 启动单字节中断接收 */
HAL_UART_Receive_IT(&huart1, &s_rx_byte, 1u);
}
/**
* @brief 动态重新配置RS485串口波特率
* @param[in] baud_index 波特率索引 BAUD_INDEX_9600~BAUD_INDEX_115200
* @retval int8_t 0:配置成功;-1:索引参数非法
* @note 会重新初始化串口,清空接收缓存,重启中断接收
* 【关联指引】索引经 s_baud_tbl 查表转换为真实波特率数值;
* 调用方通常是 flash_param 恢复配置或 rs232_shell 手动设置。
*/
int8_t BSP_RS485_SetBaudrate(uint8_t baud_index)
{
/* 入参校验:索引越界直接拒绝,避免查表越界 */
if (baud_index >= BAUD_INDEX_NUM)
{
return -1;
}
/* 注销串口重新配置波特率 */
HAL_UART_DeInit(&huart1); /* 注销当前串口外设,关闭时钟、清除寄存器状态; */
huart1.Init.BaudRate = s_baud_tbl[baud_index];
if (HAL_UART_Init(&huart1) != HAL_OK)
{
return -1;
}
s_baud_index = baud_index;
/* 重新开启IDLE总线空闲中断(Modbus帧分割依赖) */
__HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE);
HAL_GPIO_WritePin(RS485_DE_GPIO_Port, RS485_DE_Pin, GPIO_PIN_RESET);
s_rx_index = 0u;
s_frame_ready = 0u;
HAL_UART_Receive_IT(&huart1, &s_rx_byte, 1u); /* 重启单字节接收中断,恢复数据接收 */
return 0;
}
/**
* @brief RS485阻塞发送一帧数据,自动控制DE发送引脚
* @param[in] data 待发送二进制数据指针
* @param[in] len 发送字节长度
* @retval uint8_t 0:发送成功;1:空指针/长度超限/发送超时失败
* @note 阻塞式发送,禁止在中断上下文调用
* 【关联指引】发送期间会关闭接收中断并在结束后清空 s_rx_buf 脏数据,
* 避免半双工总线回环把自身报文当成接收帧。
*/
uint8_t BSP_RS485_Send(const uint8_t *data, uint16_t len)
{
/* 入参兜底:零长度、超过发送缓存容量、空指针一律判失败 */
if (len == 0u || len > RS485_TX_BUF_SIZE || data == NULL)
{
return 1u;
}
memcpy(s_tx_buf, data, len);
/*手动DE硬件专属:关闭接收中断,防止发送回环干扰接收*/
HAL_UART_AbortReceive_IT(&huart1);
/* 拉高DE引脚,485芯片切换为发送模式 */
HAL_GPIO_WritePin(RS485_DE_GPIO_Port, RS485_DE_Pin, GPIO_PIN_SET);
uint32_t timeout = (uint32_t)len * 20u + 50u; /* 计算发送超时时间:每字节20ms基础耗时 + 50ms保底超时 */
HAL_StatusTypeDef st = HAL_UART_Transmit(&huart1, s_tx_buf, len, timeout); /* 阻塞式同步发送,CPU等待硬件发送完成或超时 */
/* 轮询等待串口TC传输完成标志,确保最后一字节全部推送到总线 */
uint32_t tickstart = HAL_GetTick();
while (!__HAL_UART_GET_FLAG(&huart1, UART_FLAG_TC))
{
/* 等待10ms仍无TC标志则跳出,避免死循环 */
if ((HAL_GetTick() - tickstart) > 10u)
{
break;
}
}
/* 恢复接收模式,拉低DE,重启接收中断 */
HAL_GPIO_WritePin(RS485_DE_GPIO_Port, RS485_DE_Pin, GPIO_PIN_RESET);
/* 清空接收缓存脏数据(发送时总线回扰产生无效字节) */
s_rx_index = 0u;
s_frame_ready = 0u;
/* 重新开启单字节接收中断,恢复总线监听 */
HAL_UART_Receive_IT(&huart1, &s_rx_byte, 1u);
return (st == HAL_OK) ? 0u : 1u;
}
/**
* @brief 查询是否存在完整接收帧
* @retval uint8_t 1:存在完整帧;0:无完整接收帧
* 【关联指引】标志由 BSP_RS485_OnIdleIRQ 在 IDLE 中断中置位,
* 主循环查询到 1 后应调用 BSP_RS485_GetFrame 取走整帧。
*/
uint8_t BSP_RS485_IsFrameReady(void)
{
return s_frame_ready;
}
/**
* @brief 读取完整Modbus接收帧,读取后清空帧标志
* @param[out] buf 输出缓存,存放读取到的整帧报文
* @param[out] len 输出帧长度,记录本帧有效字节数
* @retval 无
* @note 拷贝期间关中断,避免与接收中断竞争 s_rx_buf / s_rx_index
* 【关联指引】数据由 BSP_RS485_OnRxByte 在中断中写入 s_rx_buf,
* 本函数在主循环中取走并交给 modbus_rtu 解析。
*/
void BSP_RS485_GetFrame(uint8_t *buf, uint16_t *len)
{
__disable_irq();
*len = s_frame_len;
/* 长度合法性双重校验:防止异常长度导致 memcpy 越界 */
if (s_frame_len > 0u && s_frame_len <= RS485_RX_BUF_SIZE)
{
/* 将模块私有接收缓存内完整报文拷贝到上层提供的buf */
memcpy(buf, s_rx_buf, s_frame_len);
}
/* 清除帧就绪标志、重置接收写入下标,释放缓存用于下一帧接收 */
s_frame_ready = 0u;
s_rx_index = 0u;
__enable_irq();
/* 重新开启单字节接收中断,持续监听485总线数据 */
HAL_UART_Receive_IT(&huart1, &s_rx_byte, 1u);
}
/**
* @brief 获取当前波特率索引
* @retval uint8_t 当前生效的 baud_index
*/
uint8_t BSP_RS485_GetBaudIndex(void)
{
return s_baud_index;
}
/**
* @brief 根据索引获取波特打印字符串,对外只读接口
* @param[in] index 波特率索引 0~4
* @retval const char* 波特率文本,索引越界返回 "?"
* @note 字符串表为本文件私有,上层通过本接口读取,消除跨文件重复定义
*/
const char* RS485_GetBaudStr(uint8_t index)
{
/* 越界返回哨兵字符串 "?",保证上层打印不会拿到野指针 */
if (index >= BAUD_INDEX_NUM)
{
return "?";
}
return g_baud_str[index];
}
/* -------------------------- 区块 5:内部私有底层处理函数(中断处理,放文件末尾) -------------------------- */
/**
* @brief IDLE总线空闲中断处理,标记一帧接收完成
* @retval 无
* @note 运行在中断上下文,禁止耗时操作
* @note 仅 stm32f1xx_it.c 中断服务函数内部调用,业务层禁止直接调用
* 【关联指引】置位的 s_frame_ready / s_frame_len 由主循环
* BSP_RS485_IsFrameReady / BSP_RS485_GetFrame 消费。
*/
void BSP_RS485_OnIdleIRQ(void)
{
/* 判断串口IDLE空闲标志是否置1 */
if (__HAL_UART_GET_FLAG(&huart1, UART_FLAG_IDLE))
{
/* 清除IDLE中断标志 */
__HAL_UART_CLEAR_IDLEFLAG(&huart1);
/* 至少收到1字节才算有效帧,过滤总线毛刺误触发IDLE */
if (s_rx_index > 0u)
{
s_frame_len = s_rx_index; /* 记录这一帧总长度 */
s_frame_ready = 1u; /* 标记:完整帧就绪 */
s_rx_index = 0u; /* 下标重置,准备接收下一帧 */
}
}
}
/**
* @brief 单字节接收中断处理,存入线性接收缓存(本工程未使用标准环形FIFO)
* @retval 无
* @note 运行在串口接收完成中断上下文,执行必须极简;仅存储字节、重启接收
* @note 运行在中断上下文,禁止耗时操作
* @note 跨文件被 HAL_UART_RxCpltCallback 调用,故不加 static,不对外暴露头文件声明
*/
void BSP_RS485_OnRxByte(void)
{
/* 判断当前写入下标未达到缓存最大长度,缓存未满 */
if (s_rx_index < RS485_RX_BUF_SIZE)
{
/* 将本次中断收到的字节存入缓存,下标自增指向下一空位置 */
s_rx_buf[s_rx_index++] = s_rx_byte;
}
else
{
/* 缓存溢出:新数据持续涌入,旧数据全部丢弃,从头覆盖存储 */
s_rx_index = 0u;
s_rx_buf[s_rx_index++] = s_rx_byte;
}
/* 持续开启单字节中断接收,等待下一个总线字节到来 */
HAL_UART_Receive_IT(&huart1, &s_rx_byte, 1u);
}
更多推荐
所有评论(0)