导言
《STM32F103_LL库+寄存器学习笔记12.2 - 串口DMA高效收发实战2:进一步提高串口接收的效率》基于上一个版本,进一步提升代码的模块化水平,支持多实例化。
如上所示,收发大量的数据,没有丢包。
项目地址:
github:
gitee(国内):
- LL库: https://gitee.com/wallace89/MCU_Develop/tree/main/stm32f103_ll_library12_3_usart_multi-instantiation
一、代码
1.1、bsp_usart_drive.c
c
/**
* @file bsp_usart_drive.c
* @brief STM32F1系列 USART + DMA + RingBuffer LL底层驱动实现,支持多实例化
* @author Wallace.zhang
* @version 1.0.0
* @date 2025-01-10
*/
#include "bsp_usart_drive/bsp_usart_drive.h"
/* ============================= 私有函数声明 ============================= */
/**
* @brief 配置DMA接收通道(循环模式)
* @param usart 指向USART驱动结构体的指针
* @note 就像设置一个"自动传送带",持续将串口数据搬运到内存
* @retval 无
*/
static void USART_LL_DMA_RX_Configure(USART_LL_Driver_t *usart);
/**
* @brief 使用DMA发送字符串(非阻塞方式)
* @param usart 指向USART驱动结构体的指针
* @param data 指向发送数据的缓冲区
* @param len 发送数据长度
* @note 就像启动一个"快递员",自动把数据送到目的地
* @retval 无
*/
static void USART_LL_SendString_DMA(USART_LL_Driver_t *usart, const uint8_t *data, uint16_t len);
/**
* @brief 将DMA接收到的数据复制到RingBuffer中(支持环形)
* @param usart 指向USART驱动结构体的指针
* @note 就像将货物从"临时仓库"搬到"长期储存仓库"
* @retval 无
*/
static void USART_LL_DMA_RX_Copy(USART_LL_Driver_t *usart);
/**
* @brief 将数据写入接收RingBuffer
* @param usart 指向USART驱动结构体的指针
* @param data 指向要写入的数据缓冲区
* @param len 要写入的数据长度
* @retval 返回写入状态(同USART_LL_Put_TxData_To_Ringbuffer)
* @note 智能缓冲区管理:满了就挤掉老数据,腾出空间给新数据
*/
static uint8_t USART_LL_Put_RxData_Into_Ringbuffer(USART_LL_Driver_t *usart, const void* data, uint16_t len);
/**
* @brief 处理DMA发送通道错误
* @param usart 指向USART驱动结构体的指针
* @retval 1 检测到并处理了错误,0 无错误
* @note 就像"故障检测器",发现问题立即修复
*/
static uint8_t USART_LL_DMA_TX_Error_Handler(USART_LL_Driver_t *usart);
/**
* @brief 处理DMA接收通道错误
* @param usart 指向USART驱动结构体的指针
* @retval 1 检测到并处理了错误,0 无错误
* @note 就像"故障检测器",发现问题立即修复
*/
static uint8_t USART_LL_DMA_RX_Error_Handler(USART_LL_Driver_t *usart);
/**
* @brief 处理USART硬件错误(ORE、NE、FE、PE等)
* @param usart 指向USART驱动结构体的指针
* @retval 1 检测到并处理了错误,0 无错误
* @note 清除各种串口硬件错误标志
*/
static uint8_t USART_LL_Hardware_Error_Handler(USART_LL_Driver_t *usart);
/**
* @brief 获取TX DMA忙碌状态
* @param usart 指向USART驱动结构体的指针
* @retval 0 空闲,1 忙碌
* @note 检查"快递员"是否还在工作
*/
static uint8_t USART_LL_Get_TX_DMA_Busy(USART_LL_Driver_t *usart);
/* ============================= 公有函数实现 ============================= */
/**
* @brief 阻塞方式发送以 NUL 结尾的字符串
* @param usart 指向USART驱动结构体的指针
* @param str 指向以'\0'结尾的字符串
* @note 逐字节轮询发送,就像用"老式打字机"一个字一个字地敲
*/
void USART_LL_SendString_Blocking(USART_LL_Driver_t* usart, const char* str)
{
if (!usart || !str) return;
while (*str) {
while (!LL_USART_IsActiveFlag_TXE(usart->USARTx)); // 等待发送寄存器空
LL_USART_TransmitData8(usart->USARTx, *str++); // 发送一个字节
}
}
/**
* @brief 用户数据写入发送 RingBuffer
* @param usart 指向USART驱动结构体的指针
* @param data 指向要写入的数据缓冲区
* @param len 要写入的数据长度(字节)
* @retval 0-3 写入状态码
* @note 智能缓冲区:就像一个"智能邮箱",满了会自动清理旧邮件
*/
uint8_t USART_LL_Put_TxData_To_Ringbuffer(USART_LL_Driver_t *usart, const void* data, uint16_t len)
{
if (!usart || !data) return 3; // 输入参数无效
lwrb_t *rb = &usart->txRB;
uint16_t capacity = usart->txBufSize;
lwrb_sz_t freeSpace = lwrb_get_free(rb);
uint8_t ret = 0;
// 情况1:数据长度小于ringbuffer容量
if (len < capacity) {
if (len <= freeSpace) {
lwrb_write(rb, data, len); // 剩余空间够,直接写入
} else {
// 空间不足,挤掉一些旧数据
lwrb_sz_t used = lwrb_get_full(rb);
lwrb_sz_t skip_len = len - freeSpace;
if (skip_len > used) skip_len = used;
lwrb_skip(rb, skip_len);
lwrb_write(rb, data, len);
ret = 1;
}
} else if (len == capacity) { // 情况2:数据长度等于ringbuffer容量
if (freeSpace < capacity) { // 清空ringbuffer
lwrb_reset(rb);
ret = 1;
}
lwrb_write(rb, data, len);
} else { // 情况3:数据长度大于ringbuffer容量,截断保留 capacity 字节
const uint8_t *ptr = (const uint8_t*)data + (len - capacity);
lwrb_reset(rb);
lwrb_write(rb, ptr, capacity);
ret = 2;
}
return ret;
}
/**
* @brief USART模块主运行函数
* @param usart 指向USART驱动结构体的指针
* @note 就像一个"邮局管理员",定期检查是否有邮件要发送
*/
void USART_LL_Module_Run(USART_LL_Driver_t *usart)
{
if (!usart) return;
/* 检查发送队列,如果有数据且DMA空闲,就启动发送 */
uint16_t available = lwrb_get_full(&usart->txRB);
if (available && USART_LL_Get_TX_DMA_Busy(usart) == 0) {
uint16_t len = (available > usart->txBufSize) ? usart->txBufSize : available;
lwrb_read(&usart->txRB, usart->txDMABuffer, len); // 从RingBuffer读取到DMA缓冲区
usart->txMsgCount += len; // 统计发送数据
USART_LL_SendString_DMA(usart, usart->txDMABuffer, len); // 启动DMA发送
}
}
/**
* @brief 获取接收RingBuffer中的可读字节数
*/
uint32_t USART_LL_Get_Available_RxData_Length(USART_LL_Driver_t *usart)
{
if (!usart) return 0;
return lwrb_get_full(&usart->rxRB);
}
/**
* @brief 从接收RingBuffer中读取一个字节
*/
uint8_t USART_LL_Read_A_Byte_Data(USART_LL_Driver_t *usart, uint8_t* data)
{
if (!usart || !data) return 0;
return lwrb_read(&usart->rxRB, data, 1);
}
/* ========================= 中断处理函数 ========================= */
/**
* @brief USART发送DMA中断处理函数
* @param usart 指向USART驱动结构体的指针
* @note DMA发送完成后调用,就像"快递员完成投递后报告"
*/
void USART_LL_DMA_TX_Interrupt_Handler(USART_LL_Driver_t *usart)
{
if (!usart) return;
if (USART_LL_DMA_TX_Error_Handler(usart)) { // 处理错误
// 错误已被处理
} else if (LL_DMA_IsActiveFlag_TC(usart->DMAx, usart->dmaTxChannel)) { // 传输完成
// 清除传输完成标志
LL_DMA_ClearFlag_TC(usart->DMAx, usart->dmaTxChannel);
// 关闭DMA通道,确保下次传输前已经完全停止
LL_DMA_DisableChannel(usart->DMAx, usart->dmaTxChannel);
// 关闭USART的DMAT位(关闭DMA发送请求)
LL_USART_DisableDMAReq_TX(usart->USARTx);
// 清除DMA忙碌状态
usart->txDMABusy = 0;
}
}
/**
* @brief USART接收DMA中断处理函数
* @param usart 指向USART驱动结构体的指针
* @note 处理DMA接收的半传输和全传输中断
*/
void USART_LL_DMA_RX_Interrupt_Handler(USART_LL_Driver_t *usart)
{
if (!usart) return;
if (USART_LL_DMA_RX_Error_Handler(usart)) { // 处理错误失败
// 错误已被处理
} else if (LL_DMA_IsActiveFlag_HT(usart->DMAx, usart->dmaRxChannel)) { // 半传输中断
// 清除半传输标志
LL_DMA_ClearFlag_HT(usart->DMAx, usart->dmaRxChannel);
USART_LL_DMA_RX_Copy(usart); // 将DMA接收数据放到ringbuffer
} else if (LL_DMA_IsActiveFlag_TC(usart->DMAx, usart->dmaRxChannel)) { // 全传输中断
// 清除传输完成标志
LL_DMA_ClearFlag_TC(usart->DMAx, usart->dmaRxChannel);
USART_LL_DMA_RX_Copy(usart); // 将DMA接收数据放到ringbuffer
}
}
/**
* @brief USART全局中断处理函数
* @param usart 指向USART驱动结构体的指针
* @note 处理IDLE中断和各种错误中断
*/
void USART_LL_RX_Interrupt_Handler(USART_LL_Driver_t *usart)
{
if (!usart) return;
if (USART_LL_Hardware_Error_Handler(usart)) { // 处理硬件错误
usart->errorRX++; // 有错误,记录计数
} else if (LL_USART_IsActiveFlag_IDLE(usart->USARTx)) { // 检查 USART 是否空闲中断
/* 清除IDLE标志:必须先读SR,再读DR */
volatile uint32_t tmp = usart->USARTx->SR;
tmp = usart->USARTx->DR;
(void)tmp;
USART_LL_DMA_RX_Copy(usart); // 将接收数据放到ringbuffer
}
}
/* ========================== 初始化和配置 ========================== */
/**
* @brief 初始化USART驱动实例
* @param usart 指向USART驱动结构体的指针
* @param USARTx USART寄存器基地址
* @param DMAx DMA控制器基地址
* @param dmaTxChannel DMA发送通道
* @param dmaRxChannel DMA接收通道
* @param rxDMABuffer DMA接收缓冲区指针
* @param rxRBBuffer 接收RingBuffer缓冲区指针
* @param rxBufSize 接收缓冲区大小
* @param txDMABuffer DMA发送缓冲区指针
* @param txRBBuffer 发送RingBuffer缓冲区指针
* @param txBufSize 发送缓冲区大小
* @note 就像"搭建一个完整的邮政系统",配置所有必要组件
*/
void USART_LL_Config(USART_LL_Driver_t *usart,
USART_TypeDef *USARTx, DMA_TypeDef *DMAx,
uint32_t dmaTxChannel, uint32_t dmaRxChannel,
uint8_t *rxDMABuffer, uint8_t *rxRBBuffer, uint16_t rxBufSize,
uint8_t *txDMABuffer, uint8_t *txRBBuffer, uint16_t txBufSize)
{
if (!usart) return;
/* 硬件实例配置 */
usart->USARTx = USARTx;
usart->DMAx = DMAx;
usart->dmaTxChannel = dmaTxChannel;
usart->dmaRxChannel = dmaRxChannel;
/* RX缓冲区配置 */
usart->rxDMABuffer = rxDMABuffer;
usart->rxRBBuffer = rxRBBuffer;
usart->rxBufSize = rxBufSize;
lwrb_init(&usart->rxRB, usart->rxRBBuffer, usart->rxBufSize);
/* TX缓冲区配置 */
usart->txDMABuffer = txDMABuffer;
usart->txRBBuffer = txRBBuffer;
usart->txBufSize = txBufSize;
lwrb_init(&usart->txRB, usart->txRBBuffer, usart->txBufSize);
/* 状态初始化 */
usart->txDMABusy = 0;
usart->dmaRxLastPos = 0;
usart->rxMsgCount = 0;
usart->txMsgCount = 0;
usart->errorDMATX = 0;
usart->errorDMARX = 0;
usart->errorRX = 0;
/* 配置USART和DMA */
LL_USART_EnableDMAReq_RX(usart->USARTx); // 使能USART_RX的DMA请求
LL_USART_EnableIT_IDLE(usart->USARTx); // 开启USART空闲中断
LL_DMA_EnableIT_HT(usart->DMAx, usart->dmaRxChannel); // 使能DMA接收半传输中断
LL_DMA_EnableIT_TC(usart->DMAx, usart->dmaRxChannel); // 使能DMA接收传输完成中断
LL_DMA_EnableIT_TC(usart->DMAx, usart->dmaTxChannel); // 使能DMA发送传输完成中断
USART_LL_DMA_RX_Configure(usart); // 配置DMA接收
}
/**
* @brief DMA错误恢复处理
*/
void USART_LL_DMA_Error_Recover(USART_LL_Driver_t *usart, uint8_t dir)
{
if (!usart) return;
if (dir == 0) { // RX错误
usart->errorDMARX++; // DMA接收错误计数 */
LL_DMA_DisableChannel(usart->DMAx, usart->dmaRxChannel);
while(LL_DMA_IsEnabledChannel(usart->DMAx, usart->dmaRxChannel)); // 等待完全关闭
// 重新配置并启动DMA接收
USART_LL_DMA_RX_Configure(usart);
} else { // TX错误
usart->errorDMATX++; // DMA发送错误计数 */
LL_DMA_DisableChannel(usart->DMAx, usart->dmaTxChannel);
LL_USART_DisableDMAReq_TX(usart->USARTx);
usart->txDMABusy = 0; // 清除忙碌标志
// 一般等待主循环重新发送
}
}
/* ============================= 私有函数实现 ============================= */
/**
* @brief 配置DMA接收通道
* @param usart 指向USART驱动结构体的指针
* @note 设置DMA循环模式,从串口持续接收数据到缓冲区
*/
static void USART_LL_DMA_RX_Configure(USART_LL_Driver_t *usart)
{
if (!usart) return;
/* 配置DMA接收:从USART_DR到内存缓冲区 */
LL_DMA_SetMemoryAddress(usart->DMAx, usart->dmaRxChannel, (uint32_t)usart->rxDMABuffer);
LL_DMA_SetPeriphAddress(usart->DMAx, usart->dmaRxChannel, LL_USART_DMA_GetRegAddr(usart->USARTx));
LL_DMA_SetDataLength(usart->DMAx, usart->dmaRxChannel, usart->rxBufSize);
LL_DMA_EnableChannel(usart->DMAx, usart->dmaRxChannel);
}
/**
* @brief 使用DMA发送字符串
* @param usart 指向USART驱动结构体的指针
* @param data 指向发送数据的缓冲区
* @param len 发送数据长度
* @note 启动DMA非阻塞发送
*/
static void USART_LL_SendString_DMA(USART_LL_Driver_t *usart, const uint8_t *data, uint16_t len)
{
if (!usart || !data || len == 0 || len > usart->txBufSize) return;
// 等待上一个DMA传输完成
while(usart->txDMABusy);
usart->txDMABusy = 1; // 设置DMA正在发送
// 如果DMA通道x正在使用,先关闭以便重新配置
if (LL_DMA_IsEnabledChannel(usart->DMAx, usart->dmaTxChannel)) {
LL_DMA_DisableChannel(usart->DMAx, usart->dmaTxChannel);
while(LL_DMA_IsEnabledChannel(usart->DMAx, usart->dmaTxChannel));
}
// 配置DMA通道:从内存到USART_DR
LL_DMA_SetMemoryAddress(usart->DMAx, usart->dmaTxChannel, (uint32_t)usart->txDMABuffer);
LL_DMA_SetPeriphAddress(usart->DMAx, usart->dmaTxChannel, LL_USART_DMA_GetRegAddr(usart->USARTx));
LL_DMA_SetDataLength(usart->DMAx, usart->dmaTxChannel, len);
// 启用USART的DMA发送请求
LL_USART_EnableDMAReq_TX(usart->USARTx); // 启用USART的DMA发送请求
// 启用DMA通道,开始DMA传输
LL_DMA_EnableChannel(usart->DMAx, usart->dmaTxChannel);
}
/**
* @brief 将DMA接收位置数据复制到RingBuffer中
* @param usart 指向USART驱动结构体的指针
* @note 处理DMA环形接收缓冲区的数据搬移
*/
static void USART_LL_DMA_RX_Copy(USART_LL_Driver_t *usart)
{
if (!usart) return;
uint16_t bufsize = usart->rxBufSize;
uint16_t curr_pos = bufsize - LL_DMA_GetDataLength(usart->DMAx, usart->dmaRxChannel); // 计算当前写指针位置
uint16_t last_pos = usart->dmaRxLastPos; // 上一次读指针位置
if (curr_pos != last_pos) {
if (curr_pos > last_pos) {
// 正常情况,未环绕
USART_LL_Put_RxData_Into_Ringbuffer(usart, usart->rxDMABuffer + last_pos, curr_pos - last_pos);
usart->rxMsgCount += (curr_pos - last_pos);
} else {
// 环绕,分两段处理
USART_LL_Put_RxData_Into_Ringbuffer(usart, usart->rxDMABuffer + last_pos, bufsize - last_pos);
USART_LL_Put_RxData_Into_Ringbuffer(usart, usart->rxDMABuffer, curr_pos);
usart->rxMsgCount += (bufsize - last_pos) + curr_pos;
}
}
usart->dmaRxLastPos = curr_pos; // 更新读指针位置
}
/**
* @brief 将数据写入接收RingBuffer
* @param usart 指向USART驱动结构体的指针
* @param data 指向要写入的数据缓冲区
* @param len 要写入的数据长度
* @retval 返回写入状态
* @note 与发送RingBuffer的写入逻辑相同
*/
static uint8_t USART_LL_Put_RxData_Into_Ringbuffer(USART_LL_Driver_t *usart, const void* data, uint16_t len)
{
uint8_t ret = 0;
if (!usart || !data) return 3;
lwrb_t *rb = &usart->rxRB;
uint16_t rb_size = usart->rxBufSize;
lwrb_sz_t freeSpace = lwrb_get_free(rb); // ringbuffer剩余空间
if (len < rb_size) { // 数据长度小于ringbuffer容量
if (len <= freeSpace) { // 足够的剩余空间
lwrb_write(rb, data, len); // 将数据放入ringbuffer
} else { // 没有足够的空间,需要丢弃旧数据
lwrb_sz_t used = lwrb_get_full(rb); // 使用了多少空间
lwrb_sz_t skip_len = len - freeSpace;
if (skip_len > used) { // 跳过的数据长度不能超过已使用的长度(例如,缓存58bytes,想接收59bytes,只能丢弃58bytes)
skip_len = used;
}
lwrb_skip(rb, skip_len); // 为了接收新数据,丢弃旧数据
lwrb_write(rb, data, len); // 将数据放入ringbuffer
ret = 1;
}
} else if (len == rb_size) { // 数据长度等于ringbuffer容量
if (freeSpace < rb_size) {
lwrb_reset(rb); // 清空ringbuffer
ret = 1;
}
lwrb_write(rb, data, len); // 将数据放入ringbuffer
} else { // 数据长度大于ringbuffer容量,数据太大,仅保存最后RX_BUFFER_SIZE字节
const uint8_t* byte_ptr = (const uint8_t*)data;
data = (const void*)(byte_ptr + (len - rb_size)); // 指针偏移
lwrb_reset(rb);
lwrb_write(rb, data, rb_size);
ret = 2;
}
return ret;
}
/**
* @brief 处理DMA发送通道错误
* @param usart 指向USART驱动结构体的指针
* @retval 1 检测到并处理了错误,0 无错误
*/
static uint8_t USART_LL_DMA_TX_Error_Handler(USART_LL_Driver_t *usart)
{
if (!usart) return 0;
// 检查通道是否有传输错误(TE)
if (LL_DMA_IsActiveFlag_TE(usart->DMAx, usart->dmaTxChannel)) {
// 清除传输错误标志
LL_DMA_ClearFlag_TE(usart->DMAx, usart->dmaTxChannel);
// 关闭DMA通道,停止当前传输
LL_DMA_DisableChannel(usart->DMAx, usart->dmaTxChannel);
// 关闭USART的DMA发送请求(DMAT位)
LL_USART_DisableDMAReq_TX(usart->USARTx);
// 清除发送标志变量
usart->txDMABusy = 0;
usart->errorDMATX++;
return 1;
} else {
return 0;
}
}
/**
* @brief 处理DMA接收通道错误
* @param usart 指向USART驱动结构体的指针
* @retval 1 检测到并处理了错误,0 无错误
*/
static uint8_t USART_LL_DMA_RX_Error_Handler(USART_LL_Driver_t *usart)
{
if (!usart) return 0;
// 检查通道是否有传输错误(TE)
if (LL_DMA_IsActiveFlag_TE(usart->DMAx, usart->dmaRxChannel)) {
// 清除传输错误标志
LL_DMA_ClearFlag_TE(usart->DMAx, usart->dmaRxChannel);
// 关闭DMA通道,停止当前传输
LL_DMA_DisableChannel(usart->DMAx, usart->dmaRxChannel);
// 重新配置传输长度,恢复到初始状态
LL_DMA_SetDataLength(usart->DMAx, usart->dmaRxChannel, usart->rxBufSize);
// 重新使能DMA通道,恢复接收
LL_DMA_EnableChannel(usart->DMAx, usart->dmaRxChannel);
usart->errorDMARX++;
return 1;
} else {
return 0;
}
}
/**
* @brief 处理USART硬件错误标志
* @param usart 指向USART驱动结构体的指针
* @retval 1 检测到并处理了错误,0 无错误
*/
static uint8_t USART_LL_Hardware_Error_Handler(USART_LL_Driver_t *usart)
{
if (!usart) return 0;
// 检查是否有USART错误标志(ORE、NE、FE、PE)
if (LL_USART_IsActiveFlag_ORE(usart->USARTx) ||
LL_USART_IsActiveFlag_NE(usart->USARTx) ||
LL_USART_IsActiveFlag_FE(usart->USARTx) ||
LL_USART_IsActiveFlag_PE(usart->USARTx))
{
// 通过读SR、DR来清除错误标志
volatile uint32_t tmp = usart->USARTx->SR;
tmp = usart->USARTx->DR;
(void)tmp;
return 1;
} else {
return 0;
}
}
/**
* @brief 获取TX DMA忙碌状态
* @param usart 指向USART驱动结构体的指针
* @retval 0 空闲,1 忙碌
*/
static uint8_t USART_LL_Get_TX_DMA_Busy(USART_LL_Driver_t *usart)
{
if (!usart) return 1; // 参数无效时认为忙碌
return usart->txDMABusy;
}
1.2、bsp_usart_drive.h
c
/**
* @file bsp_usart_drive.h
* @brief STM32F1系列 USART + DMA + RingBuffer LL底层驱动接口,支持多实例化
* @author Wallace.zhang
* @version 1.0.0
* @date 2025-01-10
*
* @note 基于LL库开发,支持多个USART实例(USART1、USART2、USART3等)
* 参考bsp_usart_hal的多实例化设计,但使用LL库实现更高的性能
*/
#ifndef __BSP_USART_DRIVE_H
#define __BSP_USART_DRIVE_H
#ifdef __cplusplus
extern "C" {
#endif
#include "main.h"
#include "lwrb/lwrb.h"
/* ============================= DMA通道函数映射宏 ============================= */
/**
* @brief STM32F1 LL库DMA标志检查和清除函数映射宏
* @note STM32F1的LL库没有通用的LL_DMA_IsActiveFlag_TC()函数,
* 而是按通道编号提供具体函数,如LL_DMA_IsActiveFlag_TC1()等
*/
/* DMA传输完成标志检查宏 */
#define LL_DMA_IsActiveFlag_TC(DMAx, Channel) \
((Channel == LL_DMA_CHANNEL_1) ? LL_DMA_IsActiveFlag_TC1(DMAx) : \
(Channel == LL_DMA_CHANNEL_2) ? LL_DMA_IsActiveFlag_TC2(DMAx) : \
(Channel == LL_DMA_CHANNEL_3) ? LL_DMA_IsActiveFlag_TC3(DMAx) : \
(Channel == LL_DMA_CHANNEL_4) ? LL_DMA_IsActiveFlag_TC4(DMAx) : \
(Channel == LL_DMA_CHANNEL_5) ? LL_DMA_IsActiveFlag_TC5(DMAx) : \
(Channel == LL_DMA_CHANNEL_6) ? LL_DMA_IsActiveFlag_TC6(DMAx) : \
(Channel == LL_DMA_CHANNEL_7) ? LL_DMA_IsActiveFlag_TC7(DMAx) : 0)
/* DMA半传输标志检查宏 */
#define LL_DMA_IsActiveFlag_HT(DMAx, Channel) \
((Channel == LL_DMA_CHANNEL_1) ? LL_DMA_IsActiveFlag_HT1(DMAx) : \
(Channel == LL_DMA_CHANNEL_2) ? LL_DMA_IsActiveFlag_HT2(DMAx) : \
(Channel == LL_DMA_CHANNEL_3) ? LL_DMA_IsActiveFlag_HT3(DMAx) : \
(Channel == LL_DMA_CHANNEL_4) ? LL_DMA_IsActiveFlag_HT4(DMAx) : \
(Channel == LL_DMA_CHANNEL_5) ? LL_DMA_IsActiveFlag_HT5(DMAx) : \
(Channel == LL_DMA_CHANNEL_6) ? LL_DMA_IsActiveFlag_HT6(DMAx) : \
(Channel == LL_DMA_CHANNEL_7) ? LL_DMA_IsActiveFlag_HT7(DMAx) : 0)
/* DMA传输错误标志检查宏 */
#define LL_DMA_IsActiveFlag_TE(DMAx, Channel) \
((Channel == LL_DMA_CHANNEL_1) ? LL_DMA_IsActiveFlag_TE1(DMAx) : \
(Channel == LL_DMA_CHANNEL_2) ? LL_DMA_IsActiveFlag_TE2(DMAx) : \
(Channel == LL_DMA_CHANNEL_3) ? LL_DMA_IsActiveFlag_TE3(DMAx) : \
(Channel == LL_DMA_CHANNEL_4) ? LL_DMA_IsActiveFlag_TE4(DMAx) : \
(Channel == LL_DMA_CHANNEL_5) ? LL_DMA_IsActiveFlag_TE5(DMAx) : \
(Channel == LL_DMA_CHANNEL_6) ? LL_DMA_IsActiveFlag_TE6(DMAx) : \
(Channel == LL_DMA_CHANNEL_7) ? LL_DMA_IsActiveFlag_TE7(DMAx) : 0)
/* DMA传输完成标志清除宏 */
#define LL_DMA_ClearFlag_TC(DMAx, Channel) \
do { \
if (Channel == LL_DMA_CHANNEL_1) LL_DMA_ClearFlag_TC1(DMAx); \
else if (Channel == LL_DMA_CHANNEL_2) LL_DMA_ClearFlag_TC2(DMAx); \
else if (Channel == LL_DMA_CHANNEL_3) LL_DMA_ClearFlag_TC3(DMAx); \
else if (Channel == LL_DMA_CHANNEL_4) LL_DMA_ClearFlag_TC4(DMAx); \
else if (Channel == LL_DMA_CHANNEL_5) LL_DMA_ClearFlag_TC5(DMAx); \
else if (Channel == LL_DMA_CHANNEL_6) LL_DMA_ClearFlag_TC6(DMAx); \
else if (Channel == LL_DMA_CHANNEL_7) LL_DMA_ClearFlag_TC7(DMAx); \
} while(0)
/* DMA半传输标志清除宏 */
#define LL_DMA_ClearFlag_HT(DMAx, Channel) \
do { \
if (Channel == LL_DMA_CHANNEL_1) LL_DMA_ClearFlag_HT1(DMAx); \
else if (Channel == LL_DMA_CHANNEL_2) LL_DMA_ClearFlag_HT2(DMAx); \
else if (Channel == LL_DMA_CHANNEL_3) LL_DMA_ClearFlag_HT3(DMAx); \
else if (Channel == LL_DMA_CHANNEL_4) LL_DMA_ClearFlag_HT4(DMAx); \
else if (Channel == LL_DMA_CHANNEL_5) LL_DMA_ClearFlag_HT5(DMAx); \
else if (Channel == LL_DMA_CHANNEL_6) LL_DMA_ClearFlag_HT6(DMAx); \
else if (Channel == LL_DMA_CHANNEL_7) LL_DMA_ClearFlag_HT7(DMAx); \
} while(0)
/* DMA传输错误标志清除宏 */
#define LL_DMA_ClearFlag_TE(DMAx, Channel) \
do { \
if (Channel == LL_DMA_CHANNEL_1) LL_DMA_ClearFlag_TE1(DMAx); \
else if (Channel == LL_DMA_CHANNEL_2) LL_DMA_ClearFlag_TE2(DMAx); \
else if (Channel == LL_DMA_CHANNEL_3) LL_DMA_ClearFlag_TE3(DMAx); \
else if (Channel == LL_DMA_CHANNEL_4) LL_DMA_ClearFlag_TE4(DMAx); \
else if (Channel == LL_DMA_CHANNEL_5) LL_DMA_ClearFlag_TE5(DMAx); \
else if (Channel == LL_DMA_CHANNEL_6) LL_DMA_ClearFlag_TE6(DMAx); \
else if (Channel == LL_DMA_CHANNEL_7) LL_DMA_ClearFlag_TE7(DMAx); \
} while(0)
/**
* @brief USART驱动实例结构体(基于LL库 + DMA + RingBuffer + 统计)
*
* 这个结构体就像一个"工具箱",每个USART都有自己的一套工具:
* - 就像每个邮递员都有自己的邮包(缓冲区)
* - 自己的计数器(统计信息)
* - 自己的工作状态(忙碌标志)
*/
typedef struct
{
/* USART硬件实例 */
USART_TypeDef *USARTx; /**< USART寄存器基地址(如USART1、USART2等) */
DMA_TypeDef *DMAx; /**< DMA控制器基地址(如DMA1、DMA2等) */
uint32_t dmaTxChannel; /**< DMA发送通道(如LL_DMA_CHANNEL_4) */
uint32_t dmaRxChannel; /**< DMA接收通道(如LL_DMA_CHANNEL_5) */
/* 状态和统计信息 */
volatile uint8_t txDMABusy; /**< DMA发送忙碌标志:1=正在发送,0=空闲 */
volatile uint64_t rxMsgCount; /**< 统计接收字节总数 */
volatile uint64_t txMsgCount; /**< 统计发送字节总数 */
volatile uint16_t dmaRxLastPos; /**< DMA接收缓冲区上次读取的位置 */
/* 错误统计 */
volatile uint32_t errorDMATX; /**< DMA发送错误统计 */
volatile uint32_t errorDMARX; /**< DMA接收错误统计 */
volatile uint32_t errorRX; /**< 串口接收错误统计 */
/* RX相关缓冲区 */
uint8_t *rxDMABuffer; /**< DMA接收缓冲区数组 */
uint8_t *rxRBBuffer; /**< 接收RingBuffer缓冲区数组 */
uint16_t rxBufSize; /**< DMA缓冲区/RX Ringbuffer缓冲区大小 */
lwrb_t rxRB; /**< 接收RingBuffer句柄 */
/* TX相关缓冲区 */
uint8_t *txDMABuffer; /**< DMA发送缓冲区数组 */
uint8_t *txRBBuffer; /**< 发送RingBuffer缓冲区数组 */
uint16_t txBufSize; /**< DMA缓冲区/TX Ringbuffer缓冲区大小 */
lwrb_t txRB; /**< 发送RingBuffer句柄 */
} USART_LL_Driver_t;
/* ================================ 核心API ================================ */
/**
* @brief 阻塞方式发送以 NUL 结尾的字符串(调试用,不走DMA)
* @param usart 指向USART驱动结构体的指针
* @param str 指向以'\0'结尾的字符串
* @note 通过LL库API逐字节发送,底层轮询TXE标志位(USART_SR.TXE)。
* @retval 无
*/
void USART_LL_SendString_Blocking(USART_LL_Driver_t* usart, const char* str);
/**
* @brief 用户数据写入指定USART实例的发送 RingBuffer 中
* @param usart 指向USART驱动结构体的指针
* @param data 指向要写入的数据缓冲区
* @param len 要写入的数据长度(字节)
* @retval 0 数据成功写入,不丢数据
* @retval 1 ringbuffer 空间不足,丢弃了旧数据后成功写入
* @retval 2 数据长度超过 ringbuffer 容量,截断后保留最新数据
* @retval 3 输入参数指针为空
* @note
* - 使用 lwrb 库管理的 RingBuffer(usart->txRB)。
* - 当 len > ringbuffer 容量时自动截断,仅保存最新的数据。
* - 当空间不足,会调用 lwrb_skip() 丢弃旧数据。
*/
uint8_t USART_LL_Put_TxData_To_Ringbuffer(USART_LL_Driver_t *usart, const void* data, uint16_t len);
/**
* @brief USART模块定时运行函数(数据处理主循环,1ms内调用)
* @param usart 指向USART驱动结构体的指针
* @note 在主循环中定时调用(1ms间隔推荐)
* - 检查发送RingBuffer是否有待发送数据,通过DMA异步发送
* - 自动维护已发送数据统计
* @retval 无
*/
void USART_LL_Module_Run(USART_LL_Driver_t *usart);
/**
* @brief 获取USART接收RingBuffer中的可读字节数
* @param usart 指向USART驱动结构体的指针
* @retval uint32_t 可读取的缓冲字节数
* @note 通过主循环调用,在数据处理前先判断是否需要读取数据。
*/
uint32_t USART_LL_Get_Available_RxData_Length(USART_LL_Driver_t *usart);
/**
* @brief 从USART接收RingBuffer中读取一个字节数据
* @param usart 指向USART驱动结构体的指针
* @param data 指向存放读取数据的缓冲区指针
* @retval 1 读取成功,数据存放在 *data
* @retval 0 读取失败(无数据或data为NULL)
* @note 如果缓冲区为空,则直接返回0。
*/
uint8_t USART_LL_Read_A_Byte_Data(USART_LL_Driver_t *usart, uint8_t* data);
/* ========================= 中断处理函数 ========================= */
/**
* @brief USART发送DMA中断处理函数
* @param usart 指向USART驱动结构体的指针
* @note 在对应的DMA中断服务程序中调用
*/
void USART_LL_DMA_TX_Interrupt_Handler(USART_LL_Driver_t *usart);
/**
* @brief USART接收DMA中断处理函数
* @param usart 指向USART驱动结构体的指针
* @note 在对应的DMA中断服务程序中调用,处理HT/TC中断
*/
void USART_LL_DMA_RX_Interrupt_Handler(USART_LL_Driver_t *usart);
/**
* @brief USART全局中断处理函数(支持DMA+RingBuffer)
* @param usart 指向USART驱动结构体的指针
* @note 在USARTx_IRQHandler中调用,处理IDLE中断和错误中断
*/
void USART_LL_RX_Interrupt_Handler(USART_LL_Driver_t *usart);
/* ========================== 初始化和配置 ========================== */
/**
* @brief 初始化USART驱动实例(包括DMA、RingBuffer、中断)
* @param usart 指向USART驱动结构体的指针
* @param USARTx USART寄存器基地址(如USART1、USART2等)
* @param DMAx DMA控制器基地址(如DMA1、DMA2等)
* @param dmaTxChannel DMA发送通道(如LL_DMA_CHANNEL_4)
* @param dmaRxChannel DMA接收通道(如LL_DMA_CHANNEL_5)
* @param rxDMABuffer DMA接收缓冲区指针
* @param rxRBBuffer 接收RingBuffer缓冲区指针
* @param rxBufSize 接收缓冲区大小
* @param txDMABuffer DMA发送缓冲区指针
* @param txRBBuffer 发送RingBuffer缓冲区指针
* @param txBufSize 发送缓冲区大小
* @retval 无
* @note 必须通过CubeMX完成串口、DMA硬件配置后调用此函数
*/
void USART_LL_Config(USART_LL_Driver_t *usart,
USART_TypeDef *USARTx, DMA_TypeDef *DMAx,
uint32_t dmaTxChannel, uint32_t dmaRxChannel,
uint8_t *rxDMABuffer, uint8_t *rxRBBuffer, uint16_t rxBufSize,
uint8_t *txDMABuffer, uint8_t *txRBBuffer, uint16_t txBufSize);
/**
* @brief DMA错误恢复处理(自动重新初始化并更新统计)
* @param usart 指向USART驱动结构体
* @param dir 方向:0=RX, 1=TX
* @note 检测到DMA传输错误(TE)时调用,自动清除统计并恢复
* RX错误:自动重启DMA;TX错误:等待主循环重新发送
* @retval 无
*/
void USART_LL_DMA_Error_Recover(USART_LL_Driver_t *usart, uint8_t dir);
#ifdef __cplusplus
}
#endif
#endif /* __BSP_USART_DRIVE_H */
1.3、main.c
1.4、stm32f1xx_it.c
二、bsp_usart_deive的使用文档
要快速了解怎样使用这个模块,请认真阅读使用文档。
2.1、文档位置

2.2、使用vscode打开文档
要快速了解怎样使用这个模块,请认真阅读使用文档。
2.3、在gitee上阅读使用文档
