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

Nucleo L476RG UART发送DMA填充ADC缓冲区数据全为0xFD问题

问题根因

你遇到的全0xFD问题本质是访问了非ADC缓冲区的内存区域,核心诱因有两个:

  1. 偏移和长度计算逻辑耦合度高,只要ADC_BUF_SIZE定义和预期不一致就会出现指针越界,读到RAM中默认值为0xFD的未初始化区域;
  2. 直接将volatile修饰的ADC缓冲区强转为非volatile指针传入HAL库函数,在编译器高优化等级下会出现未定义行为,导致实际读取的内存地址和预期不符。

另外你的代码还有几个隐性隐患:

  • 中断回调没有判断触发源,多ADC场景下会误触发
  • 没有校验UART发送错误,出错后会一直堵在发送状态
  • 没有校验ADC/DMA中断优先级,不符合FreeRTOS的中断优先级要求会导致随机异常
  • 没有做发送速率和ADC采样率的匹配校验,采样率过高时会出现缓冲区还未发送完成就被DMA覆盖的问题
修复方案

直接替换为以下逻辑清晰的实现,避免模糊的指针运算:

#include "ADC.hpp"
#include <string.h>
#include "FreeRTOS.h"
#include "semphr.h"
#include "task.h"
#include "main.h"
#include "utils/print.hpp"
#include "stm32l4xx_hal.h"

extern ADC_HandleTypeDef hadc3;
extern UART_HandleTypeDef huart2;
extern volatile SemaphoreHandle_t print_lock;

// 明确长度宏,避免歧义
#define ADC_TOTAL_SAMPLES  ADC_BUF_SIZE
#define ADC_HALF_SAMPLES   (ADC_TOTAL_SAMPLES / 2)
#define ADC_HALF_BYTES     (ADC_HALF_SAMPLES * sizeof(uint16_t))

namespace ADC
{
volatile SemaphoreHandle_t adc_lock;
volatile uint16_t adc_buf[ADC_TOTAL_SAMPLES];
volatile uint8_t buf[] = "abcdefg\r\n";
volatile uint8_t tx_segment = 0; // 0=前半段,1=后半段

void task(void *arg)
{
    adc_lock = xSemaphoreCreateBinary();
    HAL_ADC_Start_DMA(&hadc3, (uint32_t *)adc_buf, ADC_TOTAL_SAMPLES);
    HAL_StatusTypeDef result;
    uint8_t tx_buf[ADC_HALF_BYTES]; // 临时发送缓冲区,规避volatile和DMA并发访问问题

    while (1)
    {
        xSemaphoreTake(adc_lock, HAL_MAX_DELAY);
        uint8_t *src_addr = tx_segment ? (uint8_t*)&adc_buf[ADC_HALF_SAMPLES] : (uint8_t*)adc_buf;
        memcpy(tx_buf, src_addr, ADC_HALF_BYTES);

        xSemaphoreTake(print_lock, HAL_MAX_DELAY);
        while ((result = HAL_UART_Transmit(&huart2, tx_buf, ADC_HALF_BYTES, 100)) != HAL_OK)
        {
            if (result != HAL_BUSY) HAL_UART_Abort(&huart2); // 出错直接复位UART
        }
        xSemaphoreGive(print_lock);
    }
    vTaskDelete(nullptr);
}
} // namespace ADC

void HAL_ADC_ConvHalfCpltCallback(ADC_HandleTypeDef *hadc)
{
    if (hadc->Instance != ADC3) return;
    HAL_GPIO_WritePin(LD2_GPIO_Port, LD2_Pin, GPIO_PIN_SET);
    ADC::tx_segment = 0;
    xSemaphoreGiveFromISR(ADC::adc_lock, nullptr);
}

void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef *hadc)
{
    if (hadc->Instance != ADC3) return;
    HAL_GPIO_WritePin(LD2_GPIO_Port, LD2_Pin, GPIO_PIN_RESET);
    ADC::tx_segment = 1;
    xSemaphoreGiveFromISR(ADC::adc_lock, nullptr);
}
必做配置检查
  • 中断优先级:ADC、DMA中断的优先级数值必须大于等于configMAX_SYSCALL_INTERRUPT_PRIORITY(一般设为5及以上数值,Cortex-M内核优先级数值越小优先级越高),否则FreeRTOS API调用会出现随机异常。
  • 串口参数:确认UART初始化配置为8位数据位、无校验、1位停止位,关闭硬件流控制,和screen /dev/ttyACM0 115200的默认参数匹配。
  • 速率匹配:计算UART发送半段数据的耗时(115200波特率下每字节约87us),必须保证该耗时小于ADC填充半段缓冲区的耗时,否则会出现缓冲区未发完就被DMA覆盖的问题,速率不匹配时可以提高波特率、降低ADC采样率或者增大缓冲区。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 01:09:25