Nucleo L476RG UART发送DMA填充ADC缓冲区数据全为0xFD问题
问题根因
你遇到的全0xFD问题本质是访问了非ADC缓冲区的内存区域,核心诱因有两个:
- 偏移和长度计算逻辑耦合度高,只要ADC_BUF_SIZE定义和预期不一致就会出现指针越界,读到RAM中默认值为0xFD的未初始化区域;
- 直接将
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
相关产品推荐
相关产品推荐

