STM32F303 DMA fails to collect data when ADC is running at maximum speed
My code works fine when STM32F303 ADC run at 8 bit, 3.6 MSPS
When I trying to run ADC at maximum speed - 8bit 7.2 MSPS, DMA fails to get data. The bit OVR in ADC1_ISR is set and conversation stopped.
If I read the ADC1_2_CDR register through the debugger - ADC make one conversation and DMA make one transaction then stopped again.
I stop the CPU and there are no interrupts.
In addition to DMA and ADC, nothing works.
Why is DMA not collecting data?
#include <stm32f3xx.h>
#define SYSTEM_CORE_CLOCK 72000000
#define DWT_IN_MICROSEC (SYSTEM_CORE_CLOCK/1000000)
void DWT_Init() {
if (!(CoreDebug->DEMCR & CoreDebug_DEMCR_TRCENA_Msk)) {
CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;
DWT->CYCCNT = 0;
DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk;
}
}
void DWT_Delay_us(uint32_t us) {
uint32_t t0 = DWT->CYCCNT;
uint32_t delta = us * DWT_IN_MICROSEC;
while ((DWT->CYCCNT - t0) < delta) {}
}
void SystemClock_Config(void) {
// External oscillator (HSE) = 24MHz
// SYS_CLK = 72MHz
// APB1 = 36MHz
RCC->CR |= RCC_CR_HSEON; // Enable HSE
while ((RCC->CR & RCC_CR_HSERDY) == 0);
RCC->CFGR |= RCC_CFGR_PLLMUL3; // PLL MUL = x3, SYS_CLK=72MHz (x9 for 8MHz)
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
}
int main(void) {
SystemClock_Config();
DWT_Init();
ADC_Init();
while (1) {
}
}
#include <stm32f3xx.h>
/**
* ADC1 channel 1 - PA0
* ADC2 channel 1 - PA4
*/
#define ADC_6BITS 0b11u
#define ADC_8BITS 0b10u
#define ADC_10BITS 0b01u
#define ADC_12BITS 0b00u
uint8_t adcResolution = ADC_8BITS;
// 0b10000: PLL clock divided by 1 // RCC_CFGR2_ADCPRE12_DIV1
// 0b10001: PLL clock divided by 2
// 0b10010: PLL clock divided by 4
// 0b10011: PLL clock divided by 6
// 0b10100: PLL clock divided by 8
// 0b10101: PLL clock divided by 10
// 0b10110: PLL clock divided by 12
// 0b10111: PLL clock divided by 16
// 0b11000: PLL clock divided by 32
// 0b11001: PLL clock divided by 64
// 0b11010: PLL clock divided by 128
// 0b11011: PLL clock divided by 256
uint8_t rccAdcDivider = 0b10001;
// Delay for interleaved mode. Set only when ADEN=0
// circle time = SAMPLE_TIME + CONV. TIME = 1.5 + 8.5 = 10 tics
// Delay = circle time /2 - SAMPLE_TIME = 10 / 2 - 1.5 = 3.5 tics
// 0b0000 - 1
// 0b0001 - 2
// 0b0010 - 3
// 0b0011 - 4 MAX: 1011 - 12
uint8_t adcDelay = 0b0010;
//000: 1.5 ADC clock cycles
//001: 2.5 ADC clock cycles
//010: 4.5 ADC clock cycles
//011: 7.5 ADC clock cycles
//100: 19.5 ADC clock cycles
//101: 61.5 ADC clock cycles
//110: 181.5 ADC clock cycles
//111: 601.5 ADC clock cycles
uint8_t sampleTime = 0b000;
#define BUF_SIZE 4096
uint8_t samplesBuffer[BUF_SIZE];
void DWT_Delay_us(uint32_t us);
void DMA_init();
void ADC_start();
void ADC_Init() {
DMA_init();
// init GPIO, VR and calibration run only once
// Enable clocks GPIOA and GPIOB
RCC->AHBENR |= RCC_AHBENR_GPIOAEN | RCC_AHBENR_GPIOBEN;
// Set mode b11 (analog input) for ADC pins
GPIOA->MODER |= (0b11u << 0u); // PA0 for ADC1 ch1
GPIOA->MODER |= (0b11u << (4 * 2)); // PA4 for ADC2 ch1
RCC->AHBENR |= RCC_AHBENR_ADC12EN; // turn on ADC12 clock
// Set Prescaler ADC for Asynchronous clock mode
MODIFY_REG(RCC->CFGR2, RCC_CFGR2_ADCPRE12_Msk, rccAdcDivider << RCC_CFGR2_ADCPRE12_Pos);
// Set ADC clock
// 00: (Asynchronous clock mode) PLL as clock source
ADC12_COMMON->CCR &= ~ADC_CCR_CKMODE_Msk;
// enable the ADC voltage regulator
ADC1->CR &= ~ADC_CR_ADVREGEN_1;
ADC2->CR &= ~ADC_CR_ADVREGEN_1;
ADC1->CR |= ADC_CR_ADVREGEN_0;
ADC2->CR |= ADC_CR_ADVREGEN_0;
DWT_Delay_us(10); // voltage regulator startup time
// start ADC calibration cycle
ADC1->CR |= ADC_CR_ADCAL;
// wait for calibration to complete
while (ADC1->CR & ADC_CR_ADCAL);
// start ADC calibration cycle
ADC2->CR |= ADC_CR_ADCAL;
// wait for calibration to complete
while (ADC2->CR & ADC_CR_ADCAL);
ADC_start();
}
void ADC_start() {
// Delay for interleaved mode. Set only when ADEN=0
ADC12_COMMON->CCR |= (adcDelay << ADC_CCR_DELAY_Pos);
// enable the ADC
ADC1->CR |= ADC_CR_ADEN;
ADC2->CR |= ADC_CR_ADEN;
// wait till ADC start
while (!(ADC1->ISR & ADC_ISR_ADRDY));
while (!(ADC2->ISR & ADC_ISR_ADRDY));
// Select ADC Channels
ADC1->SQR1 = (1u << ADC_SQR1_SQ1_Pos); // 1-st channel for ADC1
ADC2->SQR1 = (1u << ADC_SQR1_SQ1_Pos); // 1-st channel for ADC2
// Regular channel sequence length - 1
ADC1->SQR1 &= ~ADC_SQR1_L_Msk;
ADC2->SQR1 &= ~ADC_SQR1_L_Msk;
// Set Prescaler ADC for Asynchronous clock mode
MODIFY_REG(RCC->CFGR2, RCC_CFGR2_ADCPRE12_Msk, rccAdcDivider << RCC_CFGR2_ADCPRE12_Pos);
// Set sampling time for channels
MODIFY_REG(ADC1->SMPR1, ADC_SMPR1_SMP1_Msk, sampleTime << ADC_SMPR1_SMP1_Pos); // ADC1 channel 1
MODIFY_REG(ADC2->SMPR1, ADC_SMPR1_SMP1_Msk, sampleTime << ADC_SMPR1_SMP1_Pos); // ADC2 channel 1
// set data resolution
MODIFY_REG(ADC1->CFGR, ADC_CFGR_RES_Msk, adcResolution << ADC_CFGR_RES_Pos);
MODIFY_REG(ADC2->CFGR, ADC_CFGR_RES_Msk, adcResolution << ADC_CFGR_RES_Pos);
// Enable continuous conversion mode. Only on the master
ADC1->CFGR |= ADC_CFGR_CONT; // Master ADC1 + ADC2
// dual mode
// 00111: Interleaved mode only
ADC12_COMMON->CCR |= (0b00111u << ADC_CCR_DUAL_Pos);
// DMA mode. 0 -> One Shot; 1 -> Circular
// 0 - stop when DMA_CCR_TCIE
ADC12_COMMON->CCR |= ADC_CCR_DMACFG;
// COMMON DMA mode b11 - for 8-bit resolution
ADC12_COMMON->CCR |= (0b11u << ADC_CCR_MDMA_Pos);
// ADC start conversion
ADC1->CR |= ADC_CR_ADSTART;
__WFI(); // stop CPU
}
void DMA_init() {
// Enable clocks DMA1
RCC->AHBENR |= RCC_AHBENR_DMA1EN;
// interrupt disabled
// Circular mode
DMA1_Channel1->CCR |= DMA_CCR_CIRC;
// Memory increment mode
DMA1_Channel1->CCR |= DMA_CCR_MINC;
// Peripheral size
// 00: 8-bits
// 01: 16-bits
// 10: 32-bits
DMA1_Channel1->CCR |= (0b01u << DMA_CCR_PSIZE_Pos);
// Memory size
DMA1_Channel1->CCR |= (0b01u << DMA_CCR_MSIZE_Pos);
// Channel Priority level 11 - Very high
DMA1_Channel1->CCR |= DMA_CCR_PL_Msk;
// Number of data to transfer. 2 samples in 1 transfer.
DMA1_Channel1->CNDTR = BUF_SIZE / 2;
// Peripheral address register
DMA1_Channel1->CPAR = (uint32_t) &ADC12_COMMON->CDR;
// Memory address register
DMA1_Channel1->CMAR = (uint32_t) (&samplesBuffer);
// Reset interrupt flags
DMA1->IFCR |= DMA_IFCR_CGIF1 | DMA_IFCR_CTCIF1 | DMA_IFCR_CHTIF1 | DMA_IFCR_CTEIF1;
// Enable DMA
DMA1_Channel1->CCR |= DMA_CCR_EN;
}
