I am currently working with a Nucleo-64 board that has the STM32F303RE chip. For programming I use the Arduino IDE with the STM32 packages. I want to avoid HAL for now because I think it is rather confusing when you have to learn the registers and the library functions at the same time.
I want to be able to sample 4 input signals parallel at 5.1 Msps (max of the F303). My plan was to keep the ADC running. Then, when I want to take the samples, I reset the DMA flags and set the counter (CNDTR-Register) to the ammount of samples I want to capture.
The following part shows my attemp to achieve this. It is basically almost working, but only a limited amount of times. How often it works seems to depend on random sleep values which I enter at certain parts in the programm. For example: If I enter a delay of 10ms after the takeSamples()-function, the programm will work for 41 cycles of the main loop - then it gets stuck.
When it gets stuck it does the following: The DMA-CNDTR register only gets reduced by one value, then it just stays there. So the programm is waiting for the register value to become zero, but this never happens. The ADC is sampling the whole time, I can read the ADC data registers just fine.
Does anyone have an idea what could be causing the DMA to stop transfering data after a certain amount of times?
Here are the relevant parts of the program:
void setup() {
Serial.begin(57600);
// Enable clocks
RCC->AHBENR |= (1 << 17); // GPIOA
RCC->AHBENR |= (1 << 18); // GPIOB
// Set ADC pins to analog input
GPIOA->MODER |= (0b11 << 0); // PA0 for ADC1
GPIOA->MODER |= (0b11 << 8); // PA4 for ADC2
GPIOB->MODER |= (0b11 << 2); // PB1 for ADC3
GPIOB->MODER |= (0b11 << 24); // PB1 for ADC4
initClock();
DMA_init();
ADC_init();
// Start conversion
ADC1->CR |= (1 << 2);
ADC3->CR |= (1 << 2);
}
void initClock()
{
FLASH->ACR |= (0b10 << 0); // add two wait states
RCC->CR |= (1 << 18); // Bypass HSE, use external clock signal from STLink instead
RCC->CR &= ~(1 << 24); // turn off PLL
delay(100);
RCC->CFGR |= (0b0000 << 4); // Do not divide system clock
RCC->CFGR |= (0b0111 << 18); // PLL multiply = 9
RCC->CFGR |= (0b10 << 15); // use HSE as PLL source
RCC->CFGR |= (1 << 10); // not divided
delay(100);
RCC->CR |= (1 << 24); // turn on PLL
delay(100);
}
void ADC_init(void) {
RCC->CFGR2 |= (0b10000 << 4); // Prescaler
RCC->CFGR2 |= (0b10000 << 9); // Prescaler
RCC->AHBENR |= (1 << 28); // turn on ADC12 clock
RCC->AHBENR |= (1 << 29); // turn on ADC34 clock
// Set ADC clock
ADC12_COMMON->CCR |= (0b01 << 16); // 0b01
ADC34_COMMON->CCR |= (0b01 << 16); // 0b01
// disable the ADC
ADC1->CR &= ~(1 << 0);
ADC2->CR &= ~(1 << 0);
ADC3->CR &= ~(1 << 0);
ADC4->CR &= ~(1 << 0);
// enable the ADC voltage regulator
ADC1->CR &= ~(1 << 29);
ADC2->CR &= ~(1 << 29);
ADC3->CR &= ~(1 << 29);
ADC4->CR &= ~(1 << 29);
ADC1->CR |= (1 << 28);
ADC2->CR |= (1 << 28);
ADC3->CR |= (1 << 28);
ADC4->CR |= (1 << 28);
// start ADC calibration cycle
ADC1->CR |= (1 << 31);
// wait for calibration to complete
while (ADC1->CR & (1 << 31));
// start ADC calibration cycle
ADC2->CR |= (1 << 31);
// wait for calibration to complete
while (ADC2->CR & (1 << 31));
// start ADC calibration cycle
ADC3->CR |= (1 << 31);
// wait for calibration to complete
while (ADC3->CR & (1 << 31));
// start ADC calibration cycle
ADC4->CR |= (1 << 31);
// wait for calibration to complete
while (ADC4->CR & (1 << 31));
// enable the ADC
ADC1->CR |= (1 << 0);
ADC2->CR |= (1 << 0);
ADC3->CR |= (1 << 0);
ADC4->CR |= (1 << 0);
while (!(ADC1->ISR & (1 << 0)));
while (!(ADC2->ISR & (1 << 0)));
while (!(ADC3->ISR & (1 << 0)));
while (!(ADC4->ISR & (1 << 0)));
// Select ADC Channels
ADC1->SQR1 = (1 << 6);
ADC2->SQR1 = (1 << 6);
ADC3->SQR1 = (1 << 6);
ADC4->SQR1 = (3 << 6);
// Set sampling time for regular group 1
ADC1->SMPR1 |= (0b000 << 3); // 0b000 -> 1.5 clock cycles, shortest available sampling time
ADC2->SMPR1 |= (0b000 << 3);
ADC3->SMPR1 |= (0b000 << 3);
ADC4->SMPR1 |= (0b000 << 3);
// Regular sequence settings
ADC1->SQR1 |= (0b0000 << 0); // One conversion in the regular sequence
ADC2->SQR1 |= (0b0000 << 0);
ADC3->SQR1 |= (0b0000 << 0);
ADC4->SQR1 |= (0b0000 << 0);
// Enable continuous conversion mode
ADC1->CFGR |= (1 << 13); // Master ADC1 + ADC2
ADC3->CFGR |= (1 << 13); // Master ADC3 + ADC4
ADC12_COMMON->CCR |= (0b00110 << 0);
ADC34_COMMON->CCR |= (0b00110 << 0);
// DMA mode
ADC12_COMMON->CCR |= (0 << 13); // 0 -> One Shot; 1 -> Circular
ADC34_COMMON->CCR |= (0 << 13);
// DMA mode for 12-bit resolution
ADC12_COMMON->CCR |= (0b10 << 14);
ADC34_COMMON->CCR |= (0b10 << 14);
}
void DMA_init(void) {
// Enable clocks
RCC->AHBENR |= (1 << 0); // DMA1
RCC->AHBENR |= (1 << 1); // DMA2
// Transfer complete interrupt enable
DMA1_Channel1->CCR |= (1 << 1);
DMA2_Channel5->CCR |= (1 << 1);
// Memory increment mode
DMA1_Channel1->CCR |= (1 << 7);
DMA2_Channel5->CCR |= (1 << 7);
// Peripheral size
DMA1_Channel1->CCR |= (0b11 << 8);
DMA2_Channel5->CCR |= (0b11 << 8);
// Memory size
DMA1_Channel1->CCR |= (0b11 << 10);
DMA2_Channel5->CCR |= (0b11 << 10);
// Number of data to transfer
DMA1_Channel1->CNDTR = uint32_t(maxSamples);
DMA2_Channel5->CNDTR = uint32_t(maxSamples);
// Peripheral address register
DMA1_Channel1->CPAR |= (uint32_t)&ADC12_COMMON->CDR;
DMA2_Channel5->CPAR |= (uint32_t)&ADC34_COMMON->CDR;
// Memory address register
DMA1_Channel1->CMAR |= uint32_t(&dataPoints1232);
DMA2_Channel5->CMAR |= uint32_t(&dataPoints3432);
// Reset flags
DMA1->IFCR |= 0xFF;
DMA2->IFCR |= 0xFF;
}
void takeSamples(void) {
// Reset flags
DMA1->IFCR |= (0b1111111111111111111111111111111 << 0);
DMA2->IFCR |= (0b1111111111111111111111111111111 << 0);
// Number of data to transfer
DMA1_Channel1->CNDTR = uint32_t(maxSamples);
DMA2_Channel5->CNDTR = uint32_t(maxSamples);
delay(10); // does not work without this random delay
elapsedTime = micros();
// Enable DMA
DMA1_Channel1->CCR |= (1 << 0);
DMA2_Channel5->CCR |= (1 << 0);
while ((DMA1_Channel1->CNDTR > 0) || (DMA2_Channel5->CNDTR > 0))
}
elapsedTime = micros() - elapsedTime;
// Reset flags
DMA1->IFCR |= (0b1111111111111111111111111111111 << 0);
DMA2->IFCR |= (0b1111111111111111111111111111111 << 0);;
DMA1_Channel1->CCR &= ~(1 << 0);
DMA2_Channel5->CCR &= ~(1 << 0);
// ADC stop conversion
ADC1->CR |= (1 << 4);
ADC3->CR |= (1 << 4);
while ((ADC1->CR & (1 << 2)) || (ADC3->CR & (1 << 2)));
ADC12_COMMON->CCR &= ~(0b10 << 14);
ADC34_COMMON->CCR &= ~(0b10 << 14);
ADC12_COMMON->CCR |= (0b10 << 14);
ADC34_COMMON->CCR |= (0b10 << 14);
// ADC start conversion
ADC1->CR |= (1 << 2);
ADC3->CR |= (1 << 2);
}
void loop() {
takeSamples();
Serial.print("Elapsed time: ");
Serial.println(elapsedTime);
}
I would be really thankfull for any tips or hints regarding this issue!
Greetings Benny
EDIT: I also had the same issue with the nucleo-64 with the STM32F401 chip. The STM32F4 Discovery on the other hand worked just fine. There was no issue like that with my F103 flightcontroller-board aswell.
setup
andloop
approach – 0___________