I am trying to scan multiple ADC channels on my STM32H753ZI Nucleo board. I have a TIM2 running at a super slow frequency (2Hz) which triggers the ADC conversions. I have it very slow so that I can see the data printing in real time. I also have a few interrupts running that check for errors in the DMA transmission and an overrun in the ADC conversion. I typically get a "Transfer Error" flag when I first run it. None after that. I also would usually get ADC overrun errors until I set the AUTDLY bit in the CFGR register.
At the moment, I just have two potentiometers connected to ADC3_CH2,CH3 (PF9, PF7). When I print the data being sent over DMA, it is all zeroes. I have looked at this post and took a lot of info from it. Unfortunately, it still does not work. One question among many is why you have to enable the SRAM clock?
My code is posted below. Any help is greatly appreciated.
--EDIT--
The above issue was resolved by moving the location that the value is stored to the SRAM.
However, the data that I am receiving is not totally accurate. Only one of the channels is correct, while the other 2 give junk data. Below is the edited code to reflect changes I have made since the original post.
I think it might be some sort of timing issue or something with the clocks, but I really don't know.
main.c
#include "stm32h7xx.h"
#include "printUSART.h"
#include "timer.h"
#include "adc.h"
#include "dma.h"
int tim_flag = 0;
int dma_flag = 0;
int adc_flag = 0;
int main(void){
USART3_Init();
ADC3_CH32_Init();
ADC3_CH32_Scan();
DMA_Init();
TIM2_CH1_Init();
TIM2_CH1_Freq_DC(2, 0.5);
printf("Start of Conversions:\n");
while(1){
if(tim_flag == 1){
printf("\nData: %f, %f, %f\n", (val[0] / 65535.0 * 3.3), (val[1] / 65535.0 * 3.3), (val[2] / 65535.0 * 3.3)); // print value in data register
tim_flag = 0;
}
if(dma_flag == 1){
printf("Transfer Error\n");
dma_flag = 0;
}
else if(dma_flag == 2){
// printf("Transfer Complete\n");
dma_flag = 0;
}
if(adc_flag == 1){
printf("Overrun\n");
adc_flag = 0;
}
}
}
void TIM2_IRQHandler(void){
if(TIM2->SR & TIM_SR_UIF){
TIM2->SR &= ~TIM_SR_UIF;
tim_flag = 1;
}
}
void ADC3_IRQHandler(void){
if(ADC3->ISR & ADC_ISR_OVR){
ADC3->ISR |= ADC_ISR_OVR;
adc_flag = 1;
}
}
void DMA1_Stream0_IRQHandler(void){
if(DMA1->LISR & DMA_LISR_TEIF0){
DMA1->LIFCR |= DMA_LIFCR_CTEIF0;
dma_flag = 1;
}
if(DMA1->LISR & DMA_LISR_TCIF0){
DMA1->LIFCR |= DMA_LIFCR_CTCIF0;
dma_flag = 2;
}
}
adc.c
/*
* adc.c
*
* Created on: Jul 1, 2021
* Author: jeremywolfe
*
* ADC3_INP3 on PF7
* ADC3_INP2 on PF9
* ADC3_INP4 on PF5
*/
#include "adc.h"
void ADC3_CH32_Init(void){
ADC3->CR &= ~ADC_CR_ADEN; // ensure that the ADC is off
RCC->AHB4ENR |= RCC_AHB4ENR_GPIOFEN; // enable clock for port F
RCC->AHB4ENR |= RCC_AHB4ENR_ADC3EN; // enable clock for ADC3
ADC3_COMMON->CCR |= ADC_CCR_CKMODE_0 | ADC_CCR_CKMODE_1; // (25.4.3) choosing the clock source for ADC
GPIOF->MODER = GPIO_MODER_MODE9_0 | GPIO_MODER_MODE9_1; // setting PF5, 7, 9 as alternate function
GPIOF->MODER = GPIO_MODER_MODE7_0 | GPIO_MODER_MODE7_1;
GPIOF->MODER = GPIO_MODER_MODE5_0 | GPIO_MODER_MODE5_1;
ADC3->CR &= ~ADC_CR_DEEPPWD; // (25.4.6) takes ADC out of deep power down mode
ADC3->CR |= ADC_CR_ADVREGEN; // (25.4.6) enable the voltage regulator
while(!(ADC3->ISR & ADC_ISR_LDORDY)){} // (25.4.6) waits for reg to startup
/* Turning on the ADC (25.4.9) */
ADC3->ISR |= ADC_ISR_ADRDY; // clear the ADRDY bit
ADC3->CR |= ADC_CR_ADEN; // enable the ADC
while(!(ADC3->ISR & ADC_ISR_ADRDY)){} // wait for ADC to be ready
ADC3->ISR |= ADC_ISR_ADRDY;
ADC3->CR &= ~ADC_CR_ADSTART;
}
void ADC3_CH32_Scan(void){
ADC3->IER |= ADC_IER_OVRIE; // enable overrun interupts
NVIC_EnableIRQ(ADC3_IRQn); // enable on the NVIC
ADC3->PCSEL |= ADC_PCSEL_PCSEL_2; // preselect channel
ADC3->PCSEL |= ADC_PCSEL_PCSEL_3; // preselect channel
ADC3->PCSEL |= ADC_PCSEL_PCSEL_4; // preselect channel
ADC3->SQR1 |= ADC_SQR1_L_1; // sequence of 3 channels
ADC3->SQR1 |= ADC_SQR1_SQ1_1; // channel 2 first
ADC3->SQR1 |= ADC_SQR1_SQ2_1 | ADC_SQR1_SQ1_0; // channel 3 second
ADC3->SQR1 |= ADC_SQR1_SQ3_2; // channel 4 third
ADC3->SMPR1 |= ADC_SMPR1_SMP2_0| ADC_SMPR1_SMP2_1 | ADC_SMPR1_SMP2_2; // max clock cycles
ADC3->SMPR1 |= ADC_SMPR1_SMP3_0| ADC_SMPR1_SMP3_1 | ADC_SMPR1_SMP3_2; // max clock cycles
ADC3->SMPR1 |= ADC_SMPR1_SMP4_0| ADC_SMPR1_SMP4_1 | ADC_SMPR1_SMP4_2; // max clock cycles
ADC3->CFGR |= ADC_CFGR_AUTDLY; // wait for conversion to complete
ADC3->CFGR &= ~ADC_CFGR_CONT; // disable continuous mode
ADC3->CFGR |= ADC_CFGR_EXTEN_0; // trigger on rising edge
ADC3->CFGR |= ADC_CFGR_EXTSEL_0 | ADC_CFGR_EXTSEL_1 | ADC_CFGR_EXTSEL_3;// trigger on event 9, tim1_trgo
ADC3->CFGR |= ADC_CFGR_DMNGT_0 | ADC_CFGR_DMNGT_1; // set DMA circular mode as data management
ADC3->CR |= ADC_CR_ADSTART; // start conversions
}
int ADC3_Calibration(void){
/* Calibrating the ADC */
ADC3->CR &= ~ADC_CR_ADEN; // ensure that the ADC is off
ADC3->CR |= ADC_CR_ADCAL; // start calibration
while(ADC3->CR & ADC_CR_ADCAL){} // wait until ADCAL is 0 and cal is complete
printf("\nCalibration is complete");
return (int)ADC3->CALFACT;
}
dma.c
/*
* dma.c
*
* Created on: Aug 30, 2021
* Author: jeremywolfe
*/
#include "dma.h"
uint16_t *val = (uint16_t*)0x30000000;
void DMA_Init(void){
DMA1_Stream0->CR &= ~DMA_SxCR_EN; // turn off DMA controller
while(DMA1_Stream0->CR & DMA_SxCR_EN){} // wait until off
RCC->AHB2ENR |= RCC_AHB2ENR_D2SRAM1EN; // SRAM clock???
RCC->AHB1ENR |= RCC_AHB1ENR_DMA1EN; // DMA clock
DMA1_Stream0->CR |= DMA_SxCR_TEIE | DMA_SxCR_TCIE; // interrupts for transfer error and transfer complete
NVIC_EnableIRQ(DMA1_Stream0_IRQn); // enable on the NVIC
DMA1_Stream0->PAR = (uint32_t)(&(ADC3->DR)); // setting the ADC data register as the peripheral address
DMA1_Stream0->M0AR = (uint32_t)(val); // setting the "val" array as the memory location
DMA1_Stream0->NDTR = 3; // 3 datas items
DMAMUX1_Channel0->CCR = 115; // DMAMUX 115 (adc3_dma)
DMA1_Stream0->CR |= DMA_SxCR_PL_0 | DMA_SxCR_PL_1; // set highest priority
// set up DMA settings
// circular mode, peripheral, memory size = 2 bytes, memory increments by 2 bytes every time
DMA1_Stream0->CR |= DMA_SxCR_CIRC | DMA_SxCR_PSIZE_0 | DMA_SxCR_MINC | DMA_SxCR_MSIZE_0;
DMA1_Stream0->CR |= DMA_SxCR_EN; // enable the DMA
}
```
val
array is located in the DTCM memory block which only the core and MDMA modules can access, and not the 'normal' DMA - so it can't write there. #2 is that reads by the code from yourval
array are being cached and you're only seeing the 'stale' values from the cache. \$\endgroup\$printf
the values. Depending on your compiler this could take one of many forms:DSB()
,__DSB
,asm("DSB")
,__asm("DSB")
or something similar to that. Note though that this is a bit of a sledgehammer approach because it invalidates the entire data cache and not just the tiny bit forval
. \$\endgroup\$val
array and check if its address is in the 0x20000000-0x2001ffff DTCM range. If it is then you'll need to move it into a different memory area to be able to use the DMA to write into it. \$\endgroup\$