ADC多通道采集DMA数据BUFFER组织方式

在STM32中,使用DMA传输多个ADC通道的数据时,数据的组织方式取决于ADC的配置,特别是扫描模式和连续转换模式的设置,以及DMA的内存地址递增设置。

假设我们使用ADC的扫描模式来转换多个通道,并且使用DMA来传输数据。以下是一个典型的场景:

  1. ADC配置为扫描模式(Scan Conversion Mode)和连续转换模式(Continuous Conversion Mode)或单次转换。

  2. 在ADC的配置中,我们设置了多个通道,每个通道在序列中有自己的排名(Rank)。

  3. DMA配置为从ADC数据寄存器(如ADC_DR)传输到内存(一个数组),并且内存地址递增。

在这种情况下,ADC会按照我们设置的排名顺序转换多个通道,每转换一个通道就会产生一个DMA请求,DMA会将转换结果依次存放到内存中。

例如,我们设置了3个通道:通道0、通道1、通道2,排名分别为1、2、3。那么ADC会先转换通道0,然后通道1,然后通道2。DMA则会依次将通道0、通道1、通道2的结果存放到内存中。

因此,如果我们设置了一个数组adc_buffer,那么adc_buffer[0]对应通道0,adc_buffer[1]对应通道1,adc_buffer[2]对应通道2。

但是,如果ADC配置为连续转换模式,那么ADC会不断地重复转换序列(通道0->通道1->通道2->通道0->...),DMA也会不断地将数据存放到内存中。此时,如果我们设置了DMA的循环模式,并且内存缓冲区足够大,那么数据会按照通道0、1、2、0、1、2...的顺序存储。

另外,如果ADC的扫描模式被禁用,那么ADC只会转换第一个排名(Rank1)的通道。

下面是一个代码示例,展示如何配置ADC多通道扫描和DMA传输:

假设使用STM32F1,ADC1,通道0、1、2,使用DMA1通道1。

注意:不同的STM32系列在ADC和DMA配置上可能略有不同,但基本原理相同。

步骤:

  1. 初始化ADC句柄,设置扫描模式、连续转换模式、DMA请求等。

  2. 配置ADC通道的排名和采样时间。

  3. 初始化DMA,配置外设地址(ADC数据寄存器地址)、内存地址、数据传输方向、数据大小、循环模式等。

  4. 启动ADC的DMA传输。

DMA传输多个ADC通道的数据组织方式

当使用DMA传输多个ADC通道的数据时,数据的组织方式取决于ADC的工作模式和DMA的配置。以下是几种常见的数据组织方式:

1. 扫描模式(Scan Mode)的数据组织

基本扫描模式

c
// ADC配置为扫描多个通道
ADC_HandleTypeDef hadc;
uint16_t adc_buffer[4]; // 存储4个通道的数据

// ADC初始化配置
hadc.Init.ScanConvMode = ENABLE;           // 启用扫描模式
hadc.Init.NbrOfConversion = 4;             // 4个转换
hadc.Init.ContinuousConvMode = ENABLE;     // 连续转换
hadc.Init.DiscontinuousConvMode = DISABLE;

// 配置通道序列
ADC_ChannelConfTypeDef sConfig;
sConfig.Channel = ADC_CHANNEL_0;           // 第1个通道
sConfig.Rank = ADC_REGULAR_RANK_1;
HAL_ADC_ConfigChannel(&hadc, &sConfig);

sConfig.Channel = ADC_CHANNEL_1;           // 第2个通道  
sConfig.Rank = ADC_REGULAR_RANK_2;
HAL_ADC_ConfigChannel(&hadc, &sConfig);

sConfig.Channel = ADC_CHANNEL_2;           // 第3个通道
sConfig.Rank = ADC_REGULAR_RANK_3;
HAL_ADC_ConfigChannel(&hadc, &sConfig);

sConfig.Channel = ADC_CHANNEL_3;           // 第4个通道
sConfig.Rank = ADC_REGULAR_RANK_4;
HAL_ADC_ConfigChannel(&hadc, &sConfig);

// 启动DMA传输
HAL_ADC_Start_DMA(&hadc, (uint32_t*)adc_buffer, 4);

数据组织方式:

text
adc_buffer[0] = ADC_CHANNEL_0 的值
adc_buffer[1] = ADC_CHANNEL_1 的值  
adc_buffer[2] = ADC_CHANNEL_2 的值
adc_buffer[3] = ADC_CHANNEL_3 的值

2. 连续多次扫描的数据组织

循环缓冲区模式

c
#define NUM_CHANNELS 4
#define NUM_SAMPLES 100

uint16_t adc_buffer[NUM_CHANNELS * NUM_SAMPLES]; // 400个元素

// 启动DMA传输,采集100组数据
HAL_ADC_Start_DMA(&hadc, (uint32_t*)adc_buffer, NUM_CHANNELS * NUM_SAMPLES);

数据组织方式:

text
采样0: adc_buffer[0] = CH0, [1] = CH1, [2] = CH2, [3] = CH3
采样1: adc_buffer[4] = CH0, [5] = CH1, [6] = CH2, [7] = CH3  
采样2: adc_buffer[8] = CH0, [9] = CH1, [10] = CH2, [11] = CH3
...
采样99: adc_buffer[396] = CH0, [397] = CH1, [398] = CH2, [399] = CH3

3. 使用不同采样率的复杂配置

交错采样模式

c
// 配置不同通道使用不同采样时间
ADC_ChannelConfTypeDef sConfig;

// 通道0 - 快速采样
sConfig.Channel = ADC_CHANNEL_0;
sConfig.Rank = ADC_REGULAR_RANK_1;
sConfig.SamplingTime = ADC_SAMPLETIME_3CYCLES;
HAL_ADC_ConfigChannel(&hadc, &sConfig);

// 通道1 - 中等采样  
sConfig.Channel = ADC_CHANNEL_1;
sConfig.Rank = ADC_REGULAR_RANK_2;
sConfig.SamplingTime = ADC_SAMPLETIME_15CYCLES;
HAL_ADC_ConfigChannel(&hadc, &sConfig);

// 通道2 - 慢速采样(高精度)
sConfig.Channel = ADC_CHANNEL_2;
sConfig.Rank = ADC_REGULAR_RANK_3;
sConfig.SamplingTime = ADC_SAMPLETIME_480CYCLES;
HAL_ADC_ConfigChannel(&hadc, &sConfig);

4. 数据处理示例

提取单个通道的所有采样

c
void ExtractChannelData(uint16_t* raw_buffer, uint16_t* channel_data, 
                       int channel_index, int num_channels, int num_samples) {
    for (int i = 0; i < num_samples; i++) {
        channel_data[i] = raw_buffer[i * num_channels + channel_index];
    }
}

// 使用示例
uint16_t channel0_data[NUM_SAMPLES];
uint16_t channel1_data[NUM_SAMPLES];
uint16_t channel2_data[NUM_SAMPLES];
uint16_t channel3_data[NUM_SAMPLES];

ExtractChannelData(adc_buffer, channel0_data, 0, NUM_CHANNELS, NUM_SAMPLES);
ExtractChannelData(adc_buffer, channel1_data, 1, NUM_CHANNELS, NUM_SAMPLES);
ExtractChannelData(adc_buffer, channel2_data, 2, NUM_CHANNELS, NUM_SAMPLES);
ExtractChannelData(adc_buffer, channel3_data, 3, NUM_CHANNELS, NUM_SAMPLES);

实时处理DMA数据(半传输和传输完成中断)

c
// 定义双缓冲区
uint16_t adc_buffer[2][NUM_CHANNELS * BUFFER_SIZE];
volatile int current_buffer = 0;

void HAL_ADC_ConvHalfCpltCallback(ADC_HandleTypeDef* hadc) {
    // 半缓冲区满 - 处理前半部分数据
    ProcessADCData(adc_buffer[current_buffer], 0, BUFFER_SIZE/2);
}

void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc) {
    // 全缓冲区满 - 处理后半部分数据
    ProcessADCData(adc_buffer[current_buffer], BUFFER_SIZE/2, BUFFER_SIZE/2);
    
    // 切换到另一个缓冲区
    current_buffer = 1 - current_buffer;
}

void ProcessADCData(uint16_t* buffer, int start, int count) {
    for (int i = start; i < start + count; i += NUM_CHANNELS) {
        float ch0_voltage = (buffer[i + 0] * 3.3f) / 4095.0f;
        float ch1_voltage = (buffer[i + 1] * 3.3f) / 4095.0f;
        float ch2_voltage = (buffer[i + 2] * 3.3f) / 4095.0f;
        float ch3_voltage = (buffer[i + 3] * 3.3f) / 4095.0f;
        
        // 处理电压数据...
    }
}

5. 不同ADC分辨率的数据组织

12位分辨率(默认)

c
uint16_t adc_buffer[NUM_CHANNELS * NUM_SAMPLES]; // 每个数据占16位

其他分辨率配置

c
// 在ADC初始化中设置分辨率
hadc.Init.Resolution = ADC_RESOLUTION_12B;  // 12位
// hadc.Init.Resolution = ADC_RESOLUTION_10B; // 10位
// hadc.Init.Resolution = ADC_RESOLUTION_8B;  // 8位

// 数据仍然存储在16位数组中,但实际有效位不同

6. 多ADC同步采样的数据组织(高级应用)

c
// 使用多个ADC同步采样
uint32_t adc_triple_buffer[3 * NUM_CHANNELS * NUM_SAMPLES];

// 数据组织更加复杂,通常是:
// [ADC1_CH0, ADC2_CH0, ADC3_CH0, ADC1_CH1, ADC2_CH1, ADC3_CH1, ...]

关键配置要点总结

  1. 扫描模式:必须启用才能转换多个通道

  2. 通道排名:决定转换顺序,对应数据在缓冲区中的位置

  3. DMA缓冲区大小:要足够容纳所有通道×采样次数的数据

  4. 数据对齐:通常使用右对齐,数据在低有效位

  5. 循环模式:适合连续数据采集应用

数据组织的基本原则是:按照ADC通道的转换顺序(Rank顺序)依次排列在DMA缓冲区中。

 
 
 
posted @ 2025-10-28 10:00  多多和羊羊  阅读(29)  评论(0)    收藏  举报