当前位置: 首页 > news >正文

STM32F103_LL库+寄存器学习笔记12.3 - 串口DMA高效收发实战3:支持多实例化的版本

导言


《STM32F103_LL库+寄存器学习笔记12.2 - 串口DMA高效收发实战2:进一步提高串口接收的效率》基于上一个版本,进一步提升代码的模块化水平,支持多实例化。
在这里插入图片描述如上所示,收发大量的数据,没有丢包。

项目地址:
github:

  • LL库: https://github.com/q164129345/MCU_Develop/tree/main/stm32f103_ll_library12_3_usart_multi-instantiation

gitee(国内):

  • LL库: https://gitee.com/wallace89/MCU_Develop/tree/main/stm32f103_ll_library12_3_usart_multi-instantiation

一、代码


1.1、bsp_usart_drive.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) { // 清空ringbufferlwrb_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_DRLL_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); // 将数据放入ringbufferret = 1;}} else if (len == rb_size) { // 数据长度等于ringbuffer容量if (freeSpace < rb_size) {lwrb_reset(rb); // 清空ringbufferret = 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

/*** @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上阅读使用文档

在这里插入图片描述

相关文章:

  • 如何在MacOS系统和Windows系统安装节点小宝远程工具
  • Java-52 深入浅出 Tomcat SSL工作原理 性能优化 参数配置 JVM优化
  • 爬虫获取数据:selenium的应用
  • Docker简单介绍与使用以及下载对应镜像(项目前置)
  • CVE-2024-6387漏洞、CVE-2025-26465漏洞、CVE-2025-26466漏洞 一口气全解决
  • 【JS-4.3-鼠标常用事件】深入理解DOM鼠标事件:全面指南与最佳实践
  • FPGA四十年创新:因仿真加速而生,AI加速而盛!
  • 股票账户的管理和交易
  • 车载电子电器架构 --- 法律和标准对电子电气架构的影响
  • Mac电脑-Markdown编辑器-Typora
  • 数据库part3---表关联、索引、视图
  • 深入浅出:Go语言中的Cookie、Session和Token认证机制
  • Vibe Coding - 进阶 Cursor Rules
  • gRPC 框架面试题精选及参考答案
  • C++模板基础
  • Python实现MySQL建表语句转换成Clickhouse SQL
  • OpenAI与微软的未来合作之路:充满挑战的AI竞赛与共赢
  • [Github]GitHub 2FA快速安全配置全攻略
  • 前端登录不掉线!Vue + Node.js 双 Token 无感刷新方案
  • RJ45 网口实现千兆传输速率(1Gbps)的原理,涉及物理层传输技术、线缆标准、信号调制及网络协议等多方面的协同设计。以下从技术维度展开详细解析:
  • 深圳附近做个商城网站找哪家公司好/百度小说风云榜排行榜官网
  • 无锡建设网站的公司哪家好/国际新闻最新消息今天军事新闻
  • 淘宝客如何做网站/中国万网域名注册服务内容
  • 东莞营销型网站建设流程/自动点击关键词软件
  • 浙江网站建设推广公司找哪家/2024年重大新闻摘抄
  • 3有免费建网站/河南关键词优化搜索