cancel
Showing results for 
Search instead for 
Did you mean: 

STM32F407 Continuous Conversion Issue Using ADC and DMA

mikey880870
Associate

I am currently using ADC for continuous conversion. Below is my program.

#include "adc.h"
 
void adc_init(ADC_TypeDef* adc){
// ADC3 PC0 PC1 (IN10 IN11)
RCC->AHB1ENR |= RCC_AHB1ENR_GPIOCEN;
RCC->APB2ENR |= RCC_APB2ENR_ADC3EN;
// analog mode
GPIOC->MODER |= (3 << GPIO_MODER_MODE0_Pos);
GPIOC->MODER |= (3 << GPIO_MODER_MODE1_Pos);
// enable scan mode
adc->CR1 = (0x1 << ADC_CR1_SCAN_Pos);
// 12bit resolution
adc->CR1 |= (0x0 << ADC_CR1_RES_Pos);
//Continuous Conversion
adc->CR2 = (0x1 << ADC_CR2_CONT_Pos);
//EOC after each conversion
adc->CR2 |= (0x1 << ADC_CR2_EOCS_Pos);
// Data Alignment RIGHT
adc->CR2 &= ~(0x1 << ADC_CR2_ALIGN_Pos);
// Enable DMA for ADC
adc->CR2 |= (0x1 << ADC_CR2_DMA_Pos);
// Enable Continuous Request
adc->CR2 |= (0x1 << ADC_CR2_DDS_Pos);
// sampling time
adc->SMPR1 |= ((0x3 << ADC_SMPR1_SMP10_Pos) | (0x3 << ADC_SMPR1_SMP11_Pos));
adc->SMPR1 |= (0x3 << ADC_SMPR1_SMP10_Pos);
//Set the Regular channel sequence length in ADC_SQR1
adc->SQR1 |= (0x0 << ADC_SQR1_L_Pos);
// Channel Sequence
adc->SQR3 |= (10 << ADC_SQR3_SQ1_Pos);
adc->SQR3 |= (11 << ADC_SQR3_SQ2_Pos);
// enable adc
adc->CR2 |= (1 << ADC_CR2_ADON_Pos);
delay_ms(2000);
}
void adc_psc(ADC_Common_TypeDef* adc){
// ADC prescaler divide two
adc->CCR |= (0x0 << ADC_CCR_ADCPRE_Pos);
}
 
void start_adc(ADC_TypeDef* adc){
adc->SR = 0;
adc->CR2 |= (1 << ADC_CR2_SWSTART_Pos);
}
#include "dma.h"
 
void init_dma(DMA_TypeDef* dma){
//enable dma2
RCC->AHB1ENR |= RCC_AHB1ENR_DMA2EN;
// ADC3 -> DMA2 stream 0 channel 2
// Data direction
DMA2_Stream0->CR &= ~(3 << DMA_SxCR_DIR_Pos);
// Select Circular mode
DMA2_Stream0->CR |= (1 << DMA_SxCR_CIRC_Pos);
// Enable Memory Address Increment
DMA2_Stream0->CR |= (1 << DMA_SxCR_MINC_Pos);
// Set the size for data 16 bit 
DMA2_Stream0->CR |= ((1 << DMA_SxCR_MSIZE_Pos) | (1 << DMA_SxCR_PSIZE_Pos));
// channel 2
DMA2_Stream0->CR |= (0x2 << DMA_SxCR_CHSEL_Pos);
}
 
void start_dma(uint32_t scrAddress,uint32_t destAddress){
//  Set the size of the transfer
DMA2_Stream0->NDTR = 0x2;
// Source address is peripheral address
DMA2_Stream0->PAR = scrAddress;
// memory address
DMA2_Stream0->M0AR = destAddress;
//start
DMA2_Stream0->CR |= (1 << DMA_SxCR_EN);
}
#include "adc.h"
#include "uart.h"
#include "delay.h"
#include "dma.h"
#include <stdio.h>
#define pc_mcu_uart USART2
 
uint16_t Rxdata[2] = {0};
 
int fputc(int ch, FILE* stream){
USART_sendByte(USART2,ch);
return ch;
}
 
int main(void){
init_dma(adc_dma);
adc_psc(ADC123_COMMON);
adc_init(hw504_adc);
init_uart(pc_mcu_uart);
start_adc(hw504_adc);
start_dma((uint32_t)&ADC3->DR,(uint32_t)Rxdata);
delay_ms(100);
while(1){
printf("%d",ADC3->DR);
printf("x = %d\n",Rxdata[0]);
printf("y = %d\n",Rxdata[1]);
delay_ms(200);
}
return 0;
}
My ADC conversion is only successful after the first reset, and the Rxdata values are all zero.
mikey880870_0-1710329065054.png

Does any expert know where the problem is in my program?

 
4 REPLIES 4
TDK
Guru

Rxdata should be defined as volatile.

> //Set the Regular channel sequence length in ADC_SQR1
> adc->SQR1 |= (0x0 << ADC_SQR1_L_Pos);
With 2 channels, shouldn't this be 1?
 
Might want to use CubeMX to code this and compare your register values to what it produces. Can also debug the program and examine registers to determine what is happening.
If you feel a post has answered your question, please click "Accept as Solution".
mikey880870
Associate

Yes, you're right, here it should be changed to 0X1, but the result remains the same as before.
I've tried comparing it with mx, but still don't know where the problem is.

 

TDK
Guru

Rxdata should be defined as volatile.

> I've tried comparing it with mx

Keep trying. Show a screenshot with your values and the values from CubeMX. If one works and the other doesn't, there's a difference somewhere.

If you feel a post has answered your question, please click "Accept as Solution".

Always start debugging peripherals by reading out and checking content of relevant registers - here, ADC and DMA.

If you'd do that, you would soon discover, that the DMA channel's CR does not have the enable bit set.

> //start
> DMA2_Stream0->CR |= (1 << DMA_SxCR_EN);

is incorrect, should be

//start
DMA2_Stream0->CR |= (1 << DMA_SxCR_EN_Pos);

As conversions were ongoing but DMA was not there to pick the data, ADC overrun and stopped (btw. for that reason you want to enable DMA before starting ADC).

JW