使用DMA将ADC值传输至UART时出现乱码问题求助
问题描述
个人项目中需掌握基于DMA的ADC使用基础,并通过UART将采集值传输至电脑。使用STM32CubeIDE配置如下:
- A0引脚接电位器,ADC输出0-4096的16位模拟值
- 采用环形缓冲区+乒乓配置,配合UART回调函数,用Putty验证接收数据
- 当前问题:Putty收到的是特殊字符(乱码),无法用于后续温度算法;通过TIM2可获取真实ADC数值,但无法通过UART DMA流式传输至PC
当前代码片段
#include "main.h" /* Private includes ----------------------------------------------------------*/ /* USER CODE BEGIN Includes */ #include <stdio.h> #include "uart.h" /* USER CODE END Includes */ /* Private define ------------------------------------------------------------*/ /* USER CODE BEGIN PD */ #define BUFFER_SIZE 128 int i; // AO! char msgbuf[BUFFER_SIZE]; // AO! /* USER CODE END PD */ /* Private variables ---------------------------------------------------------*/ ADC_HandleTypeDef hadc; DMA_HandleTypeDef hdma_adc; TIM_HandleTypeDef htim2; UART_HandleTypeDef huart2; DMA_HandleTypeDef hdma_usart2_tx; DMA_HandleTypeDef hdma_usart2_rx; /* USER CODE BEGIN PV */ uint16_t ADC_buffer[BUFFER_SIZE]; uint16_t DAC_buffer[BUFFER_SIZE]; static volatile uint16_t *inBufPtr = &ADC_buffer[0]; static volatile uint16_t *outBufPtr = &DAC_buffer[0]; uint8_t dataReadyFlag; // flag in order to process the data uint16_t adcdata[1]; /* USER CODE END PV */ /* Private function prototypes -----------------------------------------------*/ void SystemClock_Config(void); static void MX_GPIO_Init(void); static void MX_DMA_Init(void); static void MX_USART2_UART_Init(void); static void MX_ADC_Init(void); static void MX_USB_PCD_Init(void); static void MX_TIM2_Init(void); /* USER CODE BEGIN PFP */ void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef *hadc); void HAL_ADC_ConvHalfCpltCallback(ADC_HandleTypeDef *hadc); /* USER CODE END PFP */ HAL_DMA /* Private user code ---------------------------------------------------------*/ /* USER CODE BEGIN 0 */ void HAL_UART_TxHalfCpltCallback(UART_HandleTypeDef *huart2) { inBufPtr = &ADC_buffer[0]; outBufPtr = &DAC_buffer[0]; HAL_GPIO_WritePin(LD2_GPIO_Port, LD2_Pin, GPIO_PIN_SET); dataReadyFlag = 1; } void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart2) { inBufPtr = &ADC_buffer[BUFFER_SIZE / 2]; outBufPtr = &DAC_buffer[BUFFER_SIZE / 2]; HAL_GPIO_WritePin(LD2_GPIO_Port, LD2_Pin, GPIO_PIN_RESET); dataReadyFlag = 1; } void processData() { static uint16_t leftIn, leftOut; static uint16_t rightIn, rightOut; for (uint8_t n = 0; n < (BUFFER_SIZE / 2) - 1; n += 2) { /* * Left Channel */ /* Get ADC input and convert to float */ leftIn = (uint16_t)inBufPtr[n]; if (leftIn > 1) leftIn -= 2; /*Compute new output sample */ leftOut = leftIn; /* Convert back to signed int and set DAC output*/ outBufPtr[n] = (uint16_t)leftOut; /* * Right Channel */ /* Get ADC input and convert to float */ rightIn = (uint16_t)inBufPtr[n + 1]; if (rightIn > 1) rightIn -= 2; /* Compute new output sample */ rightOut = rightIn; /* Convert back to signed int and set DAC output */ outBufPtr[n + 1] = (uint16_t)rightOut; } dataReadyFlag = 0; } /* USER CODE END 0 */ /** * @brief The application entry point. * @retval int */ int main(void) { /* MCU Configuration--------------------------------------------------------*/ /* Reset of all peripherals, Initializes the Flash interface and the Systick. */ HAL_Init(); /* Configure the system clock */ SystemClock_Config(); /* Initialize all configured peripherals */ MX_GPIO_Init(); MX_DMA_Init(); MX_USART2_UART_Init(); MX_ADC_Init(); MX_USB_PCD_Init(); MX_TIM2_Init(); /* USER CODE BEGIN 2 */ HAL_UART_Receive_DMA(&huart2, (uint16_t *)DAC_buffer, BUFFER_SIZE); HAL_UART_Transmit_DMA(&huart2, (uint16_t *)ADC_buffer, BUFFER_SIZE); HAL_ADC_Start_DMA(&hadc, (uint32_t *)adcdata, 1); HAL_TIM_Base_Start(&htim2); // HAL_UART_Transmit_DMA(&huart2, (uint8_t *)ADC_buffer, BUFFER_SIZE); // uart_Init(&huart2); /* USER CODE END 2 */ /* Infinite loop */ while (1) { if (dataReadyFlag) processData(); } }
问题根源分析
- 数据格式不匹配:UART传输二进制数据,Putty默认按ASCII编码解析。直接发送uint16_t数值(如4096对应0x1000)会被拆分为两个不可见的ASCII控制字符,导致乱码。
- ADC数据未进入发送链路:ADC_DMA将数据存入
adcdata[1],但无逻辑将其写入ADC_buffer,UART发送的是未初始化的垃圾数据。 - 乒乓缓冲区逻辑错位:用UART TX回调触发数据处理,实际应在ADC采集完成后,将数据转成字符串再启动UART DMA传输。
- 类型参数错误:
HAL_UART_Transmit_DMA要求参数为uint8_t*(字节流传输),代码传入uint16_t*会导致DMA按半字传输,与UART字节传输机制冲突。
修正方案与代码示例
核心修改点
- 将ADC数值转为ASCII字符串(如"3276\r\n"),确保Putty正确显示整数
- 调整ADC回调逻辑,采集完成后立即处理数据并启动UART DMA发送
- 修正UART DMA参数类型,使用字符串缓冲区存储待发送数据
- 简化乒乓缓冲区逻辑,适配单通道ADC流式传输需求
修改后的关键代码
/* USER CODE BEGIN PD */ #define BUFFER_SIZE 32 // 每个ADC值转字符串约占6字节,32字节足够存储多组数据 /* USER CODE END PD */ /* USER CODE BEGIN PV */ uint16_t adc_data; char uart_tx_buf[2][BUFFER_SIZE]; // 乒乓字符串缓冲区 volatile uint8_t tx_buf_idx = 0; // 当前使用的发送缓冲区索引 volatile uint8_t tx_ready = 1; // 缓冲区可写入标志 /* USER CODE END PV */ /* USER CODE BEGIN 0 */ // ADC完成采集回调 void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef *hadc) { if(tx_ready) { tx_ready = 0; // 将ADC数值转换为带换行的字符串 uint8_t len = snprintf(uart_tx_buf[tx_buf_idx], BUFFER_SIZE, "%d\r\n", adc_data); // 启动UART DMA发送 HAL_UART_Transmit_DMA(&huart2, (uint8_t*)uart_tx_buf[tx_buf_idx], len); // 切换乒乓缓冲区索引 tx_buf_idx = 1 - tx_buf_idx; } // 重启ADC DMA采集 HAL_ADC_Start_DMA(&hadc, (uint32_t*)&adc_data, 1); } // UART发送完成回调 void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart) { tx_ready = 1; // 标记缓冲区可再次写入 } /* USER CODE END 0 */ int main(void) { // ... 省略Cube生成的初始化代码 ... /* USER CODE BEGIN 2 */ // 启动ADC DMA采集 HAL_ADC_Start_DMA(&hadc, (uint32_t*)&adc_data, 1); HAL_TIM_Base_Start(&htim2); // 保持TIM2触发ADC的配置(如果使用TIM触发) /* USER CODE END 2 */ while (1) { // 主循环无需额外操作,由回调处理数据传输 } }
额外配置说明
- 确保ADC配置为TIM2触发(保持原有配置)或连续转换模式
- UART配置保持默认(波特率、8位数据位、无校验、1停止位),Putty端匹配相同参数
- 开启ADC和UART的DMA中断,确保回调函数正常触发
内容的提问来源于stack exchange,提问作者Arthur
相关产品推荐
相关产品推荐

