STM32F4 DMA不控制ADC通道

STM32F4 DMA doesn't control ADC channels

我正在尝试设置我的 STM32F407-Discovery 板以使用 DMA 控制器从 ADC1 读取多个 ADC 通道。我可以在不使用 DMA 的情况下轻松地一次读取一个模拟值,但是一旦我为 ADC 启用 DMA,ADC1->DR 始终为 0,并且 adc_vals 填充为零。此外,它挂在 while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC));.

编辑:看起来 DMA_GetCmdStatus 正在返回 DISABLED。有什么想法吗?

有没有办法启动 ADC 或我缺少的东西?

//setup adc1: in1,2,3,8,9,15
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE); //adc1 on the apb2 peripheral bus
ADC_InitTypeDef adc;
ADC_DeInit(); //set adc to default state
adc.ADC_DataAlign = ADC_DataAlign_Right;
adc.ADC_Resolution = ADC_Resolution_12b;//12 bit = 4096
adc.ADC_ContinuousConvMode = ENABLE;
adc.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T1_CC1;
adc.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None;
adc.ADC_NbrOfConversion = NUM_ADC;
adc.ADC_ScanConvMode = ENABLE;
ADC_Init(ADC1,&adc);


ADC_DMACmd(ADC1, ENABLE); //enable adc for dma.  When this line is removed, I see data on ADC1->DR


ADC_Cmd(ADC1,ENABLE);

ADC_RegularChannelConfig(ADC1,ADC_Channel_1,1,ADC_SampleTime_144Cycles);//1:1710, 0
//ADC_RegularChannelConfig(ADC1,ADC_Channel_2,2,ADC_SampleTime_144Cycles);//2:1710, 0
//ADC_RegularChannelConfig(ADC1,ADC_Channel_3,3,ADC_SampleTime_144Cycles);//3:1710, 0
ADC_RegularChannelConfig(ADC1,ADC_Channel_8,4,ADC_SampleTime_144Cycles);//8:3520
ADC_RegularChannelConfig(ADC1,ADC_Channel_9,5,ADC_SampleTime_144Cycles);//9:1000
//ADC_RegularChannelConfig(ADC1,ADC_Channel_15,6,ADC_SampleTime_144Cycles);//15:3920


//DMA for multiple adc channels:
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA1, ENABLE); //dma1 clock enable
DMA_InitTypeDef dma;

DMA_DeInit(DMA2_Stream0); //reset DMA2 stream 0 to default values
dma.DMA_Channel = DMA_Channel_0;
dma.DMA_PeripheralBaseAddr = (uint32_t)&(ADC1->DR);
dma.DMA_Memory0BaseAddr = (uint32_t)&adc_vals[0];
dma.DMA_DIR = DMA_DIR_PeripheralToMemory;
dma.DMA_BufferSize = NUM_ADC;
dma.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
dma.DMA_MemoryInc = DMA_MemoryInc_Enable;
dma.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
dma.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
dma.DMA_Mode = DMA_Mode_Circular;
dma.DMA_Priority = DMA_Priority_High;
dma.DMA_FIFOMode = DMA_FIFOMode_Disable;
dma.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull;
dma.DMA_MemoryBurst = DMA_MemoryBurst_Single;
dma.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
DMA_Init(DMA2_Stream0, &dma);

DMA_ITConfig(DMA2_Stream0, DMA_IT_TC | DMA_IT_HT, ENABLE); //Enable DMA Stream Half / Transfer Complete interrupt

DMA_Cmd(DMA2_Stream0, ENABLE); //DMA2_Stream0 enable

//dma transfer complete interrupt:
NVIC_InitTypeDef nvic;
//Enable DMA1 channel IRQ Channel
nvic.NVIC_IRQChannel = DMA2_Stream0_IRQn;
nvic.NVIC_IRQChannelPreemptionPriority = 0;
nvic.NVIC_IRQChannelSubPriority = 0;
nvic.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&nvic);

ADC_SoftwareStartConv(ADC1);//Start the adc conversion

至少你可以试试:

  • 更改DMA_PeripheralInc以启用
  • 组转换后使用 ADC_DMARequestAfterLastTransferCmd(ADC, Enable); 进行 dma 请求。
  • 初始化ADC_CommonInitTypeDef
  • 初始化 GPIO,如果没有

而且在所有配置之前启用ADC很奇怪。

在我看来,最好的方法是从标准外围设备库中复制粘贴 "ADC_DualModeRegulSimu"示例。它包含完全符合您的目的的漂亮代码。

有一些错误。这是我的工作代码,供遇到同样问题的其他人使用:

    //DMA for multiple adc channels:
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE); //ADC1 on APB2 peripheral bus

DMA_InitTypeDef dma; //dma2/stream0/channel0
dma.DMA_Channel = DMA_Channel_0; 
dma.DMA_Memory0BaseAddr = (uint32_t)&adcVal;
dma.DMA_PeripheralBaseAddr = (uint32_t)&(ADC1->DR);
dma.DMA_DIR = DMA_DIR_PeripheralToMemory;
dma.DMA_BufferSize = 6;
dma.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
dma.DMA_MemoryInc = DMA_MemoryInc_Enable;
dma.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
dma.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
dma.DMA_Mode = DMA_Mode_Circular;
dma.DMA_Priority = DMA_Priority_High;
dma.DMA_FIFOMode = DMA_FIFOMode_Enable;              
dma.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull;
dma.DMA_MemoryBurst = DMA_MemoryBurst_Single;
dma.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
DMA_Init(DMA2_Stream0, &dma);
DMA_Cmd(DMA2_Stream0, ENABLE); //dMA2_Stream0 enable

//analog inputs: PA1,2,3, PC5, PB0,1
gpio.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3;
gpio.GPIO_Mode = GPIO_Mode_AN;
gpio.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(GPIOA, &gpio);
gpio.GPIO_Pin = GPIO_Pin_5;
GPIO_Init(GPIOC, &gpio);
gpio.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1;
GPIO_Init(GPIOB, &gpio);

//ADC common init
ADC_CommonInitTypeDef adcCommon;
adcCommon.ADC_Mode = ADC_Mode_Independent;
adcCommon.ADC_Prescaler = ADC_Prescaler_Div2;
adcCommon.ADC_DMAAccessMode = ADC_DMAAccessMode_Disabled;
adcCommon.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_5Cycles;
ADC_CommonInit(&adcCommon);

//setup adc1: in1,2,3,8,9,15
ADC_InitTypeDef adc;
ADC_DeInit(); //set adc to default state
adc.ADC_DataAlign = ADC_DataAlign_Right; //mask 0b 00001111 11111111
adc.ADC_Resolution = ADC_Resolution_12b;//12 bit = 4096
adc.ADC_ContinuousConvMode = ENABLE; //continuous: constantly converting data - can always read register
adc.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T1_CC1;//external trigger conversion (?)
adc.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None;
adc.ADC_NbrOfConversion = 6;
adc.ADC_ScanConvMode = ENABLE;//single/multichannel
ADC_Init(ADC1,&adc);

ADC_RegularChannelConfig(ADC1,ADC_Channel_1,1,ADC_SampleTime_56Cycles); ADC_RegularChannelConfig(ADC1,ADC_Channel_2,2,ADC_SampleTime_56Cycles);
ADC_RegularChannelConfig(ADC1,ADC_Channel_3,3,ADC_SampleTime_56Cycles);
ADC_RegularChannelConfig(ADC1,ADC_Channel_8,4,ADC_SampleTime_56Cycles);
ADC_RegularChannelConfig(ADC1,ADC_Channel_9,5,ADC_SampleTime_56Cycles);
ADC_RegularChannelConfig(ADC1,ADC_Channel_15,6,ADC_SampleTime_56Cycles);


ADC_DMARequestAfterLastTransferCmd(ADC1, ENABLE); //single adc repeated

ADC_DMACmd(ADC1, ENABLE);
ADC_Cmd(ADC1,ENABLE);

感谢 ethan 的代码,使 minor mod 适合我的情况并作为通用函数工作。

volatile uint16_t adcVal[6];

void Setup_DMA_ADC( void )
{
    GPIO_InitTypeDef gpio;
    //DMA for multiple adc channels:
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE); //ADC1 on APB2 peripheral bus
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE);

    DMA_InitTypeDef dma; //dma2/stream0/channel0
    dma.DMA_Channel = DMA_Channel_0;
    dma.DMA_Memory0BaseAddr = (uint32_t) &adcVal[0];
    dma.DMA_PeripheralBaseAddr = (uint32_t)&(ADC1->DR);
    dma.DMA_DIR = DMA_DIR_PeripheralToMemory;
    dma.DMA_BufferSize = 6;
    dma.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
    dma.DMA_MemoryInc = DMA_MemoryInc_Enable;
    dma.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
    dma.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
    dma.DMA_Mode = DMA_Mode_Circular;
    dma.DMA_Priority = DMA_Priority_High;
    dma.DMA_FIFOMode = DMA_FIFOMode_Enable;
    dma.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull;
    dma.DMA_MemoryBurst = DMA_MemoryBurst_Single;
    dma.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
    DMA_Init(DMA2_Stream0, &dma);
    DMA_Cmd(DMA2_Stream0, ENABLE); //dMA2_Stream0 enable

    //analog inputs: PA1,2,3, PC5, PB0,1
    GPIO_StructInit(&gpio);
    gpio.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3;
    gpio.GPIO_Mode = GPIO_Mode_AN;
    gpio.GPIO_PuPd = GPIO_PuPd_NOPULL;
    GPIO_Init(GPIOA, &gpio);
    gpio.GPIO_Pin = GPIO_Pin_5;
    GPIO_Init(GPIOC, &gpio);
    gpio.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1;
    GPIO_Init(GPIOB, &gpio);

    //ADC common init
    ADC_CommonInitTypeDef adcCommon;
    adcCommon.ADC_Mode = ADC_Mode_Independent;
    adcCommon.ADC_Prescaler = ADC_Prescaler_Div2;
    adcCommon.ADC_DMAAccessMode = ADC_DMAAccessMode_Disabled;
    adcCommon.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_5Cycles;
    ADC_CommonInit(&adcCommon);

    //setup adc1: in1,2,3,8,9,15
    ADC_InitTypeDef adc;
    ADC_DeInit(); //set adc to default state
    adc.ADC_DataAlign = ADC_DataAlign_Right; //mask 0b 00001111 11111111
    adc.ADC_Resolution = ADC_Resolution_12b;//12 bit = 4096
    adc.ADC_ContinuousConvMode = ENABLE; //continuous: constantly converting data - can always read register
    adc.ADC_ExternalTrigConv = DISABLE;//ADC_ExternalTrigConv_T1_CC1;//external trigger conversion (?)
    adc.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None;
    adc.ADC_NbrOfConversion = 6;
    adc.ADC_ScanConvMode = ENABLE;//single/multichannel
    ADC_Init(ADC1,&adc);

    ADC_RegularChannelConfig(ADC1,ADC_Channel_1,1,ADC_SampleTime_56Cycles);
    ADC_RegularChannelConfig(ADC1,ADC_Channel_2,2,ADC_SampleTime_56Cycles);
    ADC_RegularChannelConfig(ADC1,ADC_Channel_3,3,ADC_SampleTime_56Cycles);
    ADC_RegularChannelConfig(ADC1,ADC_Channel_8,4,ADC_SampleTime_56Cycles);
    ADC_RegularChannelConfig(ADC1,ADC_Channel_9,5,ADC_SampleTime_56Cycles);
    ADC_RegularChannelConfig(ADC1,ADC_Channel_15,6,ADC_SampleTime_56Cycles);


    ADC_DMARequestAfterLastTransferCmd(ADC1, ENABLE); //single adc repeated

    ADC_DMACmd(ADC1, ENABLE);
    ADC_Cmd(ADC1,ENABLE);
    // Start ADC conversion
    ADC_SoftwareStartConv(ADC1);
    return;
}