具有交错模式的STM32F3双ADC

时间:2019-01-03 14:52:22

标签: embedded stm32 sampling dma

我正在尝试达到STM32F30x ADC modes and application下“双重交错模式”部分中记录的10MSPS。

首先,我尝试使用单个DMA。我将DMA1通道1配置为从ADC1&2公共数据寄存器读取。它有效,但我只能达到8.47MSPS的采样率。超过该限制,ADC1开始溢出。 (寄存器ADC1_2-> CCR:MULT = 0x07,MDMA = 0x02,DELAY = 0x04)考虑到从ADC结束转换后DMA读取公共数据寄存器,在高采样率下,这个问题似乎是合理的。

所以我决定使用2个DMA。每个ADC一个:
 DMA1 Channel1从ADC1-> DR复制到SRAM
 DMA2 Channel1从ADC2-> DR复制到SRAM
(寄存器ADC1_2-> CCR:MULT = 0x07,MDMA = 0x00,DELAY = 0x04)

此配置也有效,但再次达到8MSPS。高于该速率,ADC2开始溢出。我不明白为什么ADC2超载。我希望此设置可以正常工作。

当我在具有DMA配置的独立模式下运行ADC1和ADC2时,一切似乎都正常运行。没有溢出,两个ADC采样均为5.1MSPS,但独立。

一个问题:当两个ADC在独立模式下运行并由相同的源(例如TIM2)触发但ADC1在时钟的上升沿触发而ADC2在时钟的下降沿触发时会发生什么?能行吗?这是我将尝试的下一个步骤。

我使用的MCU是STM32F303CB。
ADC采样时间为1.5个周期。

任何建议将不胜感激。

编辑:我提供了一个最小的示例代码,该代码在具有8 MHz晶振的STM32F3 Discovery上运行。程序直接跳转到main()

// main.c
#include "stm32f30x.h"
#define DUALDMA

void sysinit();
void clockconfig();
void delay(int d);
void timerinit();
void adcinit();
void dmainit();
void dualdmainit();

int main(){
    sysinit();
    clockconfig();
    timerinit();

    #ifdef DUALDMA
    dualdmainit();
    #else
    dmainit();
    #endif

    adcinit();

    RCC->AHBENR |= RCC_AHBENR_GPIOEEN; // GPIOE enable
    RCC->AHBENR |= RCC_AHBENR_GPIOAEN; // GPIOA enable
    GPIOE->MODER = 0x55555555; // GPIOE -> output 
    GPIOA->MODER |= 0x0000FFFF;// GPIOA -> analog

    // Reset SRAM memory area
    for(int i = 0;i<1024*4;i+=4){
        *((uint32_t*)(0x20000800+i)) = 0;
    }


    // Blink LEDs
    while(1){
        GPIOE->ODR = 0xFFFF;
        delay(1000);
        GPIOE->ODR = 0x00FF;
        delay(1000);
    }
}

void delay(int d){
    // Dummy delay
    int l = d*1000;
    for(int i = 0;i<l;i++);
}

void sysinit(){

    //STM32F303 reset state
    /* Reset the RCC clock configuration to the default reset state ------------*/
  /* Set HSION bit */
  RCC->CR |= 0x00000001U;
  /* Reset CFGR register */
  RCC->CFGR &= 0xF87FC00CU;
  /* Reset HSEON, CSSON and PLLON bits */
  RCC->CR &= 0xFEF6FFFFU;
  /* Reset HSEBYP bit */
  RCC->CR &= 0xFFFBFFFFU;
  /* Reset PLLSRC, PLLXTPRE, PLLMUL and USBPRE bits */
  RCC->CFGR &= 0xFF80FFFFU;
  /* Reset PREDIV1[3:0] bits */
  RCC->CFGR2 &= 0xFFFFFFF0U;
  /* Reset USARTSW[1:0], I2CSW and TIMs bits */
  RCC->CFGR3 &= 0xFF00FCCCU;
  /* Disable all interrupts */
  RCC->CIR = 0x00000000U;
  SCB->VTOR = 0x08000000; /* Vector Table Relocation in Internal FLASH */

}
void adcinit(){

    RCC->AHBENR |= RCC_AHBENR_ADC12EN; // Enable ADC clock
    RCC->CFGR2 |= RCC_CFGR2_ADCPRE12_4;// ADC clock prescaler = 1
    ADC1->CFGR |= ADC_CFGR_EXTEN_0; // Trigger on rising edge
    ADC1->CFGR |= ADC_CFGR_EXTSEL_3 | ADC_CFGR_EXTSEL_1; // TIM1 TRGO2

    ADC1->SQR1  |= ADC_SQR1_SQ1_0 ; // ch 1
    ADC1->CFGR |= ADC_CFGR_OVRMOD; // Stop on overrun
    ADC1->CFGR |=  ADC_CFGR_DMAEN; // DMA enable
    ADC1->CR &= ~(ADC_CR_ADVREGEN_1 | ADC_CR_ADVREGEN_0); // Enable VREG
    ADC1->CR |=  ADC_CR_ADVREGEN_0;
    ADC1->CR |= ADC_CR_ADEN;

    while( (ADC1->ISR & ADC_ISR_ADRD) == 0 );


    ADC2->SQR1  |= ADC_SQR1_SQ1_0 ; // ch 1
    ADC2->CFGR |=   ADC_CFGR_DMAEN;
    ADC2->CR &= ~(ADC_CR_ADVREGEN_1 | ADC_CR_ADVREGEN_0);
    ADC2->CR |=  ADC_CR_ADVREGEN_0;
    ADC2->CR |= ADC_CR_ADEN;
    while( (ADC1->ISR & ADC_ISR_ADRD) == 0 );

    ADC1_2->CCR |= ADC12_CCR_DELAY_2 ; // Delay = 4, 5 Cycles
    #ifndef DUALDMA
    ADC1_2->CCR |= ADC12_CCR_MDMA_1; // If single DMA is selected, configure MDMA bits for 12 bits
    #endif
    ADC1_2->CCR |= ADC12_CCR_MULTI_2 | ADC12_CCR_MULTI_1 | ADC12_CCR_MULTI_0; // Interleaved mode


}

void dmainit(){
    // DMA config for Single DMA, 32 bits
    RCC->AHBENR |= RCC_AHBENR_DMA1EN;

    DMA1_Channel1->CPAR = (uint32_t)&ADC1_2->CDR;
    DMA1_Channel1->CMAR = 0x20000800;
    DMA1_Channel1->CNDTR = 1024;
    DMA1_Channel1->CCR = DMA_CCR_EN | DMA_CCR_MINC | DMA_CCR_MSIZE_1 | DMA_CCR_PSIZE_1;
    //DMA1_Channel1->CCR = DMA_CCR_EN | DMA_CCR_MINC ;

}

void dualdmainit(){
    // DMA config for DUAL DMA, 16bits
    RCC->AHBENR |= RCC_AHBENR_DMA1EN; // DMA1 Enable
    RCC->AHBENR |= RCC_AHBENR_DMA2EN; // DMA2 Enable

    DMA1_Channel1->CPAR = (uint32_t)&ADC1->DR;
    DMA1_Channel1->CMAR = 0x20000800;
    DMA1_Channel1->CNDTR = 1024;
    DMA1_Channel1->CCR = DMA_CCR_EN | DMA_CCR_MINC | DMA_CCR_MSIZE_0 | DMA_CCR_PSIZE_0;

    DMA2_Channel1->CPAR = (uint32_t)&ADC2->DR;
    DMA2_Channel1->CMAR = 0x20000800+1024*2;
    DMA2_Channel1->CNDTR = 1024;
    DMA2_Channel1->CCR = DMA_CCR_EN | DMA_CCR_MINC | DMA_CCR_MSIZE_0 | DMA_CCR_PSIZE_0;

}

void timerinit(){
    RCC->APB2ENR |= RCC_APB2ENR_TIM1EN; // Enable TIM1
    TIM1->CR2 |= TIM_CR2_MMS2_1; // Update event selected as TRGO2
    TIM1->PSC = 0;
    TIM1->ARR = 0x0d; // 5 MHz (72 MHz / 14 )
    TIM1->CR1 |= TIM_CR1_CEN;
}
void clockconfig(){
    // External oscillator (HSE): 8MHz 
    RCC->CR |= RCC_CR_HSEON; // Enable HSE
    while( (RCC->CR & RCC_CR_HSERDY) == 0 );

    RCC->CFGR |= RCC_CFGR_PLLMULL9; // PLL MUL = x9
    RCC->CFGR |= RCC_CFGR_PPRE1_DIV2; // APB1 Prescaler = 2
    RCC->CFGR |= RCC_CFGR_PLLSRC; // PLL source = HSE

    FLASH->ACR |= FLASH_ACR_LATENCY_1; // Two wait states

    RCC->CR |= RCC_CR_PLLON;  // Enable and wait PLL
    while( (RCC->CR & RCC_CR_PLLRDY) == 0 );

    RCC->CFGR |= RCC_CFGR_SW_PLL; // Select PLL as system clock

}

分散文件:


LR_IROM1 0x08000000 0x00020000  {    ; load region size_region
  ER_IROM1 0x08000000 0x00020000  {  ; load address = execution address
   *.o (RESET, +First)
   *(InRoot$$Sections)
   .ANY (+RO)
  }
  RW_IRAM2 0x10000000 0x00000200  {  ; RW data
   .ANY (+RW +ZI)
  }
}

1 个答案:

答案 0 :(得分:2)

您不能以这种方式这样做。您只需要使用一个DMA通道,并且两个样本都通过一个32位DMA事务传输。

在6位模式下,我已存档超过18MSPS

enter image description here

我不知道如何使用HAL对其进行编程,因为我个人仅使用裸寄存器方法

也存在硬件问题(请阅读勘误),有时在> 8位模式下,传输无法正常工作。

对于双DMA,您需要: 通过将堆栈和变量(ADC缓冲区除外)放置在CCM RAM中,或通过进入睡眠模式来暂停任何内核活动,可以防止内核访问SRAM存储器。