Skip to main content
Visitor II
March 13, 2024
Question

STM32F407 Continuous Conversion Issue Using ADC and DMA

  • March 13, 2024
  • 4 replies
  • 1117 views

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?

 
    This topic has been closed for replies.

    4 replies

    Super User
    March 13, 2024

    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.
    Visitor II
    March 14, 2024

    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.

     

    Super User
    March 14, 2024

    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.

    Super User
    March 15, 2024

    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