HAL_UART_Transmit_DMA无法正常传输ADC缓冲区的问题排查与解决
问题与解决:STM32F7 ADC-DMA与USART3协同传输乱码问题
使用环境
- STM32CubeMX版本:6.10.0
- STM32CubeIDE版本:1.13.1
- 开发板:Nucleo-F767ZI
问题描述
开发DIY示波器项目时,尝试最大化STM32F7系列ADC的速度与精度。DMA流水线可在不占用CPU的情况下高效传输数据,调试确认adc_buf缓冲区已被准确填充12位ADC值,但调用HAL_UART_Transmit_DMA向板载USART3传输该缓冲区时输出乱码。
已验证USART3可正常传输字符数组,例如通过HAL_UART_Transmit(&huart3, (uint8_t*)msg, strlen(msg), 0xFFFF);发送测试消息完全正常。外设初始化顺序为MX_GPIO_Init()→MX_DMA_Init()→MX_USART3_UART_Init()→MX_ADC1_Init(),排除了初始化顺序问题。
原简化工作流程代码
/* USER CODE BEGIN PD */ #define ADC_BUF_LEN 8 /* USER CODE END PD */ /* USER CODE BEGIN PV */ uint16_t adc_buf[ADC_BUF_LEN]; /* USER CODE END PV */ int main(void) { /* USER CODE BEGIN 1 */ char msg[] = "\n\rThe cow jumped over the moon\r\n"; char buffer[50]; /* USER CODE END 1 */ //...初始化操作 /* USER CODE BEGIN 2 */ HAL_UART_Transmit(&huart3, (uint8_t*)msg, strlen(msg), 0xFFFF); // 测试消息传输正常 HAL_ADC_Start_DMA(&hadc1, (uint32_t *) &adc_buf, ADC_BUF_LEN); /* USER CODE END 2 */ /* 无限循环 */ /* USER CODE BEGIN WHILE */ while (1) { // 等待USART3传输完成 while (READ_BIT(huart3.Instance->ISR, USART_ISR_TC)==0){asm("nop");} HAL_UART_Transmit_DMA(&huart3, (uint8_t*)adc_buf, ADC_BUF_LEN * sizeof(uint8_t)); // 需重新启动ADC-DMA以持续读取数据 HAL_ADC_Start_DMA(&hadc1, (uint32_t *) &adc_buf, ADC_BUF_LEN); } /* USER CODE END WHILE */ }
预期输出为ADC数值的文本形式,但实际输出乱码。ADC的半完成和完成中断可正常触发,DMA与USART单独工作正常,但协同传输时异常。
解决方法与修改后代码
问题根源在于直接传输uint16_t类型的原始ADC数值,USART无法识别非ASCII编码的二进制数据,同时USART3的数据宽度配置不匹配。解决方式为:将ADC缓冲区的uint16_t值转换为ASCII字符串后再通过DMA传输,同时将USART3的数据宽度从半字改为字节。
修改后的主函数代码:
int main(void) { /* USER CODE BEGIN 1 */ char msg[] = "\n\rThe cow jumped\r\n"; char buffer[ADC_BUF_LEN * 6]; // 假设每个值最多占6个字符(含终止符) memset(buffer, 0, sizeof(buffer)); /* USER CODE END 1 */ /* MCU配置--------------------------------------------------------*/ /* 复位所有外设,初始化Flash接口和SysTick */ HAL_Init(); /* USER CODE BEGIN Init */ HAL_NVIC_SetPriority(USART3_IRQn, 0, 0); HAL_NVIC_EnableIRQ(USART3_IRQn); /* USER CODE END Init */ /* 配置系统时钟 */ SystemClock_Config(); /* USER CODE BEGIN SysInit */ /* USER CODE END SysInit */ /* 初始化所有配置的外设 */ MX_GPIO_Init(); MX_DMA_Init(); MX_USART3_UART_Init(); MX_ADC1_Init(); MX_ETH_Init(); MX_USB_OTG_FS_PCD_Init(); /* USER CODE BEGIN 2 */ HAL_UART_Transmit(&huart3, (uint8_t*)msg, strlen(msg), 0xFFFF); // 传输正常 HAL_ADC_Start_DMA(&hadc1, (uint32_t *) &adc_buf, ADC_BUF_LEN); //HAL_DMA_RegisterCallback(&hdma_usart3_tx, HAL_DMA_XFER_CPLT_CB_ID, &DMATransferComplete); /* USER CODE END 2 */ /* 无限循环 */ /* USER CODE BEGIN WHILE */ while (1) { // 等待USART3传输完成 while (READ_BIT(huart3.Instance->ISR, USART_ISR_TC)==0) {asm("nop");} // 等待上一次DMA传输完成 if (__HAL_DMA_GET_FLAG(&hdma_usart3_tx, DMA_FLAG_TCIF0_4) == RESET) { // 将adc_buf转换为字符串后传输 uint16_t len = 0; for (uint8_t i = 0; i < ADC_BUF_LEN; i++) { len += sprintf(buffer + len, "%d\r\n", adc_buf[i]); // 将uint16_t值转为ASCII字符串 } HAL_UART_Transmit_DMA(&huart3, (uint8_t*)buffer, len); } // 等待传输完成 if (__HAL_DMA_GET_FLAG(&hdma_usart3_tx, DMA_FLAG_TCIF0_4) == RESET) {} // 需重新启动ADC-DMA以持续读取数据 HAL_ADC_Start_DMA(&hadc1, (uint32_t *) &adc_buf, ADC_BUF_LEN); } /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ /* USER CODE END 3 */ }
正常输出示例
现在输出正常,可显示正确的ADC数值:
3839 4095 3899 3831 3845
内容的提问来源于stack exchange,提问作者Mark
相关产品推荐
相关产品推荐

