news 2026/5/1 7:28:53

stm32f103 dma+uart通信小记

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
stm32f103 dma+uart通信小记

使用stm32f103vct6,串口通信时遇到一个小问题 记录一下:

设置dma传输的长度是 maxLen,在接收时判断了剩余dma缓存长度,当接收的长度等于maxlen时就出现了问题,返回的是maxLen长度.当接收长度小于maxLen时 返回的长度是剩余长度.

/** * @brief Returns the number of remaining data units in the current * DMAy Channelx transfer. * @param DMAy_Channelx: where y can be 1 or 2 to select the DMA and * x can be 1 to 7 for DMA1 and 1 to 5 for DMA2 to select the DMA Channel. * @retval The number of remaining data units in the current DMAy Channelx * transfer. */ uint16_t DMA_GetCurrDataCounter(DMA_Channel_TypeDef* DMAy_Channelx) { /* Check the parameters */ assert_param(IS_DMA_ALL_PERIPH(DMAy_Channelx)); /* Return the number of remaining data units for DMAy Channelx */ return ((uint16_t)(DMAy_Channelx->CNDTR)); }

这段描述也是返回剩余字节数.实际仿真maxLen等于接收长度时会出现 剩余长度等于maxLen的现象.使用中增大 dma缓存 大于接收的字节数返回都是正常的.

代码例程如下:

/* 初始化部分 */ void USART2_Init(uint32_t baudrate) { GPIO_InitTypeDef ioCfg; USART_InitTypeDef uartCfg; DMA_InitTypeDef dmaCfg; NVIC_InitTypeDef nvicCfg; RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO | RCC_APB2Periph_GPIOD, ENABLE); RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE); RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); GPIO_PinRemapConfig(GPIO_Remap_USART2, ENABLE); ioCfg.GPIO_Pin = GPIO_Pin_5; // TX ioCfg.GPIO_Mode = GPIO_Mode_AF_PP; ioCfg.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOD, &ioCfg); ioCfg.GPIO_Pin = GPIO_Pin_6; // RX ioCfg.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_Init(GPIOD, &ioCfg); // 1. 开启时钟 uartCfg.USART_BaudRate = baudrate; uartCfg.USART_WordLength = USART_WordLength_8b; uartCfg.USART_StopBits = USART_StopBits_1; uartCfg.USART_Parity = USART_Parity_No; uartCfg.USART_HardwareFlowControl = USART_HardwareFlowControl_None; uartCfg.USART_Mode = USART_Mode_Tx | USART_Mode_Rx; USART_Init(USART2, &uartCfg); // DMA接收配置(循环模式) DMA_DeInit(DMA1_Channel6); dmaCfg.DMA_PeripheralBaseAddr = (uint32_t)&USART2->DR; dmaCfg.DMA_MemoryBaseAddr = (uint32_t)dmaU2RxBuffer[currentU2RxBuf]; dmaCfg.DMA_DIR = DMA_DIR_PeripheralSRC; dmaCfg.DMA_BufferSize = UART2_RX_BUFFER_SIZE; dmaCfg.DMA_PeripheralInc = DMA_PeripheralInc_Disable; dmaCfg.DMA_MemoryInc = DMA_MemoryInc_Enable; dmaCfg.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; dmaCfg.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; dmaCfg.DMA_Mode = DMA_Mode_Circular; dmaCfg.DMA_Priority = DMA_Priority_High; dmaCfg.DMA_M2M = DMA_M2M_Disable; DMA_Init(DMA1_Channel6, &dmaCfg); // DMA发送配置 DMA_DeInit(DMA1_Channel7); dmaCfg.DMA_PeripheralBaseAddr = (uint32_t)&USART2->DR; dmaCfg.DMA_MemoryBaseAddr = (uint32_t)dmaU2TxBuffer; // 需要添加dmaU2TxBuffer定义 dmaCfg.DMA_DIR = DMA_DIR_PeripheralDST; dmaCfg.DMA_BufferSize = UART2_TX_BUFFER_SIZE; dmaCfg.DMA_PeripheralInc = DMA_PeripheralInc_Disable; dmaCfg.DMA_MemoryInc = DMA_MemoryInc_Enable; dmaCfg.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; dmaCfg.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; dmaCfg.DMA_Mode = DMA_Mode_Normal; dmaCfg.DMA_Priority = DMA_Priority_Medium; dmaCfg.DMA_M2M = DMA_M2M_Disable; DMA_Init(DMA1_Channel7, &dmaCfg); nvicCfg.NVIC_IRQChannel = DMA1_Channel7_IRQn; nvicCfg.NVIC_IRQChannelPreemptionPriority = 5; nvicCfg.NVIC_IRQChannelSubPriority = 1; nvicCfg.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&nvicCfg); DMA_ITConfig(DMA1_Channel7, DMA_IT_TC, ENABLE); // 使能DMA和空闲中断 USART_DMACmd(USART2, USART_DMAReq_Rx, ENABLE); // NVIC配置 nvicCfg.NVIC_IRQChannel = USART2_IRQn; nvicCfg.NVIC_IRQChannelPreemptionPriority = 5; nvicCfg.NVIC_IRQChannelSubPriority = 0; nvicCfg.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&nvicCfg); USART_ITConfig(USART2, USART_IT_IDLE, ENABLE); DMA_Cmd(DMA1_Channel6, ENABLE); USART_Cmd(USART2, ENABLE); }

中断部分:

/* dma+串口中断 */ void USART2_IRQHandler(void) { BaseType_t xHigherPriorityTaskWoken = pdFALSE; fData_t mData; if (USART_GetITStatus(USART2, USART_IT_IDLE) != RESET) { uint16_t recvLen; DMA_Cmd(DMA1_Channel6, DISABLE); USART_ClearITPendingBit(USART2, USART_IT_IDLE); USART_ReceiveData(USART2); // 清除空闲中断标志 recvLen = UART2_RX_BUFFER_SIZE - DMA_GetCurrDataCounter(DMA1_Channel6); // 如果接收到了数据,就发送到队列处理 if (recvLen == rfFrameMaxLen) { // 发送到FreeRTOS队列 memcpy(mData.fData, dmaU2RxBuffer[currentU2RxBuf], recvLen); memset(dmaU2RxBuffer[currentU2RxBuf],0, recvLen); mData.fLen = recvLen; xQueueSendFromISR(uart2_rx_queue, &mData, &xHigherPriorityTaskWoken); // 切换缓冲区 currentU2RxBuf ^= 1; DMA1_Channel6->CMAR = (uint32_t)dmaU2RxBuffer[currentU2RxBuf]; // 检查是否需要立即调度更高优先级的任务 if (xHigherPriorityTaskWoken != pdFALSE) { portYIELD_FROM_ISR(xHigherPriorityTaskWoken); } } DMA_Cmd(DMA1_Channel6, ENABLE); } } void DMA1_Channel7_IRQHandler(void) { if (DMA_GetITStatus(DMA1_IT_TC7)) { DMA_ClearITPendingBit(DMA1_IT_TC7); USART_DMACmd(USART2, USART_DMAReq_Tx, DISABLE); } }

/* 任务部分 */

/* 队列初始化 */ static void initQueueAndTimer(void) { // 创建发送队列 uart2_tx_queue = xQueueCreate(U2_QUEUE_LENGTH, sizeof(fData_t)); if (uart2_tx_queue == NULL) { // dPrintf("Transmit queue creation failed\n"); return; } // 创建接收队列 uart2_rx_queue = xQueueCreate(U2_QUEUE_LENGTH, sizeof(fData_t)); if (uart2_rx_queue == NULL) { // dPrintf("Receive queue creation failed\n"); return; } } /* 串口实际初始化调用位置 */ void uart2Init(void) { USART2_Init(9600); initQueueAndTimer(); } // 2. 修改接收任务函数 void uart2_rx_task(void *pvParameters) { fData_t mData; #ifdef debug volatile UBaseType_t uxHighWaterMark; #endif while (1) { #ifdef debug uxHighWaterMark = uxTaskGetStackHighWaterMark(NULL); #endif if (xQueueReceive(uart2_rx_queue, &mData, portMAX_DELAY) == pdTRUE) { // 处理接收到的数据 if (parse_packet(mData.fData, mData.fLen) != 0) { // 错误处理 } } vTaskDelay(2); } } // 优化发送任务处理函数 void uart2_tx_task(void *pvParameters) { fData_t mData; #ifdef debug volatile UBaseType_t uxHighWaterMark; #endif while (1) { #ifdef debug uxHighWaterMark = uxTaskGetStackHighWaterMark(NULL); #endif if (xQueueReceive(uart2_tx_queue, &mData, portMAX_DELAY) == pdTRUE) { // 使用优化的发送函数 TickType_t xStartTime = xTaskGetTickCount(); while (DMA_GetCurrDataCounter(DMA1_Channel7) != 0) { if ((xTaskGetTickCount() - xStartTime) > pdMS_TO_TICKS(100)) { break; // 超时100ms后强制退出 } vTaskDelay(1); } // 配置DMA发送 DMA_Cmd(DMA1_Channel7, DISABLE); // 复制数据到DMA发送缓冲区 memcpy(dmaU2TxBuffer, mData.fData, mData.fLen); // 配置DMA发送缓冲区和长度 DMA1_Channel7->CMAR = (uint32_t)dmaU2TxBuffer; DMA1_Channel7->CNDTR = mData.fLen; DMA_Cmd(DMA1_Channel7, ENABLE); USART_DMACmd(USART2, USART_DMAReq_Tx, ENABLE); } vTaskDelay(5); } } void sendData2U2(u8 *data, u16 sLen) { fData_t txData = {0}; do { /* */ if (data == NULL || sLen == 0 || sLen > UART2_TX_BUFFER_SIZE) { break; } /* 将数据发送到串口队列 */ txData.fLen = sLen; memcpy(txData.fData, data, sLen); if (xQueueSend(uart2_tx_queue, &txData, 0) != pdPASS) { } } while (0); }

解析部分就不贴出了;

贴一下帧头部分:

// 定义缓冲区大小 QueueHandle_t uart2_rx_queue; // 接收队列句柄 QueueHandle_t uart2_tx_queue; // 发送队列句柄 #define U2_QUEUE_LENGTH 5 // 接收队列长度 #define U2_RX_ITEM_SIZE sizeof(fData_t) // 接收队列项大小 #define rfFrameMaxLen 7 uint8_t dmaU2RxBuffer[2][UART2_RX_BUFFER_SIZE] = {0}; uint8_t dmaU2TxBuffer[UART2_TX_BUFFER_SIZE] = {0}; volatile uint8_t currentU2RxBuf = 0;
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/22 11:07:31

华为ensp:端口安全的配置

端口安全是用来限制交换机端口接入设备的一种技术,核心目的是防止未经授权的设备随意接入局域网、占用网络资源,甚至窃取数据。这里要先明确一个概念:我们说的 “端口” 是交换机的物理端口(比如网线插的那个接口)&…

作者头像 李华
网站建设 2026/4/30 14:36:33

布局AI,就是布局未来:创客匠人智能体开启企业增长新引擎

每一次技术浪潮来临,最先抓住红利的,永远不是“最懂的人”,而是“最先行动的人”。 从网站、移动互联网到短视频,历史一次次证明:行动比看懂更重要,布局比犹豫更值钱。 如今,AI已不再是“锦上添…

作者头像 李华
网站建设 2026/4/26 14:57:46

全球化测试最佳实践:构建高效多语言软件质量保障体系

全球化测试(Globalization Testing)是确保软件产品适应全球市场,包括语言、文化、区域设置和技术兼容性的关键环节。随着软件行业的国际化加速,测试从业者需掌握系统性方法以应对多维度挑战。本文基于行业经验,结合实践…

作者头像 李华
网站建设 2026/4/26 12:42:33

rabbitmq-深入理解exchange/queue/routing-key等概念

​ 日拱一卒,功不唐捐。大家好,最近有项目要用到消息队列,所以考虑到了使用rabbitmq。使用rabbitmq之前需要对其相关理论概念有些了解,大家一块熟悉一下。 ​ RabbitMQ是一个开源的消息代理和队列服务器,用来通过…

作者头像 李华
网站建设 2026/4/19 3:07:37

探索安川七伺服电机方案:从原理到代码实现

安川七伺服电机方案,含原理图,源 代码,解析文档。最近一直在研究安川七伺服电机方案,今天就来和大家分享一下这个有趣且实用的项目,包含原理图、源代码以及详细的解析文档,希望能给对这方面感兴趣的小伙伴一…

作者头像 李华
网站建设 2026/4/20 21:49:04

软件安全之CRC检测

CRC介绍 在玩某些游戏,例如fps类游戏时,你想要修改某些特定的数值实现一些功能,这时你很有可能会被查封账号甚至禁封机器码。因为你更改了游戏中的数据,从而导致接收方收到”错误的数据“。为尽量提高接收方收到数据的正确率&…

作者头像 李华