lxl's blog - DMA ADC
type: channel
link: https://blog.lxyz.ink/tag/DMA-ADC/
items: TI单片机FIFO MODE的记录

TI单片机FIFO MODE的记录
type: item
date: 2025-07-16T11:26:00+08:00
description: 尝试在MSPM0G3507实现6路ADC DMA采样。TI单片机的ADC DMA与FIFO高度绑定，一开始不了解FIFO，以为只是单纯的先进先出队列，后在使用时发现数据错乱的问题。翻阅数据手册发现ADC 的 FIFO 总是将两个样本打包成一个 32 位的 Word 数据。当你读取 FIFODAT 寄存器（无论是 CPU 还是 DMA），你总是会得到一个 32 位的 Word也就是说DMA读取的FIFO结果不能直接使用，需要处理,分离高16位与低16位。分离示例void extract_samples(void) { for (int i = 0; i < ADC_FIFO_WORD_COUNT; ++i) { uint32_t word = gADCWordBuffer[i]; gADCSamples[i * 2] = word & 0xFFFF; if ((i * 2 + 1) < ADC_CHANNEL_COUNT) { gADCSamples[i * 2 + 1] = (word >> 16) & 0xFFFF; } } }另外sysconfig工具中DMA Samples Count比较有迷惑性。官方文档中对 DMA Samples Count 的定义：Set number of ADC results to be transferred on a DMA trigger.它不是指“DMA 一次搬运多少数据”，而是：ADC FIFO 每累计 N 个样本，就触发一次 DMA 请求，而不是采完一个完整序列才触发。实际应用中与adc采样通道数相同即可（其实只要有值就可以）Transfer Size项才是真真有用的，因为FIFO将两个样本打包成一个 32 位的 Word 数据，所以Transfer Size应该为样本数的一半。一般建议在主程序调用DL_DMA_setTransferSize实现全功能示例代码#include "ti_msp_dl_config.h" #define ADC_CHANNEL_COUNT 6 #define ADC_FIFO_WORD_COUNT ((ADC_CHANNEL_COUNT + 1) / 2) // 每个Word打包2个样本 volatile uint32_t gADCWordBuffer[ADC_FIFO_WORD_COUNT]; // 每个word包含两个样本 volatile uint16_t gADCSamples[ADC_CHANNEL_COUNT]; // 提取后的样本数组 void extract_samples(void) { for (int i = 0; i < ADC_FIFO_WORD_COUNT; ++i) { uint32_t word = gADCWordBuffer[i]; gADCSamples[i * 2] = word & 0xFFFF; if ((i * 2 + 1) < ADC_CHANNEL_COUNT) { gADCSamples[i * 2 + 1] = (word >> 16) & 0xFFFF; } } } int main(void) { SYSCFG_DL_init(); // 设置 DMA DL_DMA_setSrcAddr(DMA, DMA_CH0_CHAN_ID, (uint32_t) DL_ADC12_getFIFOAddress(ADC12_0_INST)); DL_DMA_setDestAddr(DMA, DMA_CH0_CHAN_ID, (uint32_t) gADCWordBuffer); DL_DMA_setTransferSize(DMA, DMA_CH0_CHAN_ID, ADC_FIFO_WORD_COUNT); // 启用 DMA DL_DMA_enableChannel(DMA, DMA_CH0_CHAN_ID); // 启动 ADC 转换 DL_ADC12_startConversion(ADC12_0_INST); while (1) { // 每次 DMA 更新完 gADCWordBuffer，手动提取 16位样本 extract_samples(); // 你可以在这里使用 gADCSamples[0..2] 的数据 __NOP(); // 仅用于调试打断点 } }