You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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();
  }
}

问题根源分析

  1. 数据格式不匹配:UART传输二进制数据,Putty默认按ASCII编码解析。直接发送uint16_t数值(如4096对应0x1000)会被拆分为两个不可见的ASCII控制字符,导致乱码。
  2. ADC数据未进入发送链路:ADC_DMA将数据存入adcdata[1],但无逻辑将其写入ADC_buffer,UART发送的是未初始化的垃圾数据。
  3. 乒乓缓冲区逻辑错位:用UART TX回调触发数据处理,实际应在ADC采集完成后,将数据转成字符串再启动UART DMA传输。
  4. 类型参数错误: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)
    {
        // 主循环无需额外操作,由回调处理数据传输
    }
}

额外配置说明

  1. 确保ADC配置为TIM2触发(保持原有配置)或连续转换模式
  2. UART配置保持默认(波特率、8位数据位、无校验、1停止位),Putty端匹配相同参数
  3. 开启ADC和UART的DMA中断,确保回调函数正常触发

内容的提问来源于stack exchange,提问作者Arthur

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.27 04:15:04