1
votes

I am using an stm32f4. Currently I am reading the temperature of the chip by using the ADC with DMA and TIMER tiggered. DMA is setup as a circular buffer with double buffering. The TIMER TRGO is set up as every to trigger Update Event.

It seems to be working fine but for some reason when I enable DMA within the with ADC initialization code, the ADC_SR_EOC is never set so it never executes the chunk of code within the if((ADC1->SR & ADC_SR_EOC) == ADC_SR_EOC)

Is there a way to get the ADC EOC within ADC_IRQHandler working in conjunction with the DMA configuration?

End goal is just to toggle and LED to check if I have my timings that I wanted setup for my TIMER+ADC block. (Maybe in the future i may want to add some more code in the ADC handler to execute)

void ADC_IRQHandler(void){                                                      


  if((ADC1->SR & ADC_SR_EOC) == ADC_SR_EOC){                                    

    GPIOD->ODR ^=ORANGE_LED;                                                        

    ADC1->SR &= ~(ADC_SR_EOC);                                                   

  }                                                                                                                                                                  
}   

if I move GPIOD->ODR ^=ORANGE_LED; outside of the if-statment, then it lights up the ORANGE_LED on the board

Here are all three init functions and DMA handler

void DMA2_Stream4_IRQHandler(void)                                              
{                                                                               
  /* transmission complete interrupt */                                         
  if (DMA2->HISR & DMA_HISR_TCIF4)                                              
  {                                                                             
    GPIOD->ODR ^=GREEN_LED;                                                    
    DMA2->HIFCR |= (DMA_HIFCR_CTCIF4);  // acknowledge interrupt                

    uint16_t *p;                                                                
    if ((DMA2_Stream4->CR & DMA_SxCR_CT) == 0)  // current target buffer 0 (read buffer 1)
      p = (uint16_t*) &sample_buffer0[0];                                       
    else                                        // current target buffer 1 (read buffer 0)
      p = (uint16_t*) &sample_buffer1[0];                                       


    temp_value = p[0];                                                          
    counter = 1;                                                                
  }                                                                             

  if (DMA2->HISR & DMA_HISR_TEIF4)                                              
  {                                                                             
    GPIOD->ODR ^=(RED_LED);                                                    
    DMA2->HIFCR |= (DMA_HIFCR_CTEIF4);  // acknowledge interrupt                

  }                                                                             

}    





void ADCx_Init(ADC_TypeDef * ADCx){

  // Enable ADCx
  if(ADCx == ADC1) RCC->APB2ENR |= RCC_APB2ENR_ADC1EN;
  else if(ADCx == ADC2) RCC->APB2ENR |= RCC_APB2ENR_ADC2EN;
  else RCC->APB2ENR |= RCC_APB2ENR_ADC3EN;


  /*
   * ADC Mode Selection
   *
   * Note:
   *  00000 : Independent Mode, ADC operate independently
   */
  ADC123_COMMON->CCR &= ~(ADC_CCR_MULTI);


  /*
   * Set and cleared by software to select the frequency of the clock
   *  to the ADC. The clock is common for all the ADCs.
   *
   * Note:
   *  00: PCLK2 divided by 2
   *  01: PCLK2 divided by 4
   *  10: PCLK2 divided by 6
   *  11: PCLK2 divided by 8
  */
  ADC123_COMMON->CCR &= ~(ADC_CCR_ADCPRE);  // Clear
  ADC123_COMMON->CCR |= (ADC_CCR_ADCPRE_0); // DIV2


  // Disable DMA for Dual/Triple modes
  ADC123_COMMON->CCR &= ~(ADC_CCR_DMA);

  //Configurable delay between conversions in Dual/Triple interleaved mode
  ADC123_COMMON->CCR &= ~(ADC_CCR_DELAY);

  // Resolution ot 12-bits
  ADCx->CR1 &= ~(ADC_CR1_RES);

  // Disable Scan Mode for this example
  ADCx->CR1 &= ~(ADC_CR1_SCAN);

  // Disable Continuos Mode
  ADCx->CR2 &= ~(ADC_CR2_CONT);

  // External Trigger on rising edge
  ADCx->CR2 &= ~(ADC_CR2_EXTEN);
  ADCx->CR2 |= ADC_CR2_EXTEN_0;

  // Timer 3 TRGO to drive ADC conversion
  ADCx->CR2 &= ~ADC_CR2_EXTSEL;
  ADCx->CR2 |= ADC_CR2_EXTSEL_3;

  // Data Alignment Right
  ADCx->CR2 &= ~(ADC_CR2_ALIGN);

  // Number of Conversions 
  ADCx->SQR1 &= ~(ADC_SQR1_L); // 1 conversion


  // Enable Temperature/Vref
  ADC123_COMMON->CCR |=ADC_CCR_TSVREFE;


  /* Configure Channel For Temp Sensor */
  ADCx->SQR3 &= ~(ADC_SQR3_SQ1);
  // Channel 16 for temp sensor on stm32f4 disc
  ADCx->SQR3 |= ADC_SQR3_SQ1_4;
  // Sample Time is 15 cycles (3+12)
  ADCx->SMPR1 &= ~(ADC_SMPR1_SMP16);




  // This call enables the end-of-conversion flag after each channel,
  // which triggers the end-of-conversion interrupt every time this flag is set.
  //ADCx->CR2 &= ~(ADC_CR2_EOCS);
  ADCx->CR2 |= (ADC_CR2_EOCS);

  // Enable Regular channel Interrupt
  ADCx->CR1 |= ADC_CR1_EOCIE;

  // For Double-Circular mode for DMA
  // you can continue to generate requests
  ADCx->CR2 |= ADC_CR2_DDS;

  // Enable DMA mode for ADC
  ADCx->CR2 |= ADC_CR2_DMA;


  // Set ADCx priority to 1
  NVIC_SetPriority(ADC_IRQn,1);

  // Enable ADCx interrupt
  NVIC_EnableIRQ(ADC_IRQn);


  // Turn on the ADC
  ADCx->CR2 |= ADC_CR2_ADON;

}

void timer_init(void){

  // Enable Timer 3 clock
  RCC->APB1ENR |= RCC_APB1ENR_TIM3EN;

  // Disable Timer 3
  TIM3->CR1 &= ~TIM_CR1_CEN;

  // Counting Direction: 0 = up-counting, 1 = down-counting
  TIM3->CR1 &=~(TIM_CR1_DIR);

  // Clock Division - same as input clock
  TIM3->CR1 &=~(TIM_CR1_CKD);

  //Clock Prescaler

  TIM3->PSC =420; //Timer clock should be 42MHz/420 = 2 kHz

  // Auto Reload: up-counting (0-> ARR), down-counting (ARR -> 0)

  TIM3->ARR = 49;

  // Master Mode Selection
  // Use Update Event as the trigger output (TRGO)
  TIM3->CR2 &= ~(TIM_CR2_MMS);
  TIM3->CR2 |= (TIM_CR2_MMS_1);

  // Enable Timer 3 after all of the initialization
  TIM3->CR1 |= TIM_CR1_CEN;

}

void DMAx_Init(DMA_Stream_TypeDef * DMAx, ADC_TypeDef * ADCx){

  // Enable DMA Clock
  RCC->AHB1ENR |= RCC_AHB1ENR_DMA2EN;

  // Disable DMA2 so that we can configure it
  DMAx->CR &= ~(DMA_SxCR_EN);

  // Initialize the Channel Member
  // ADC on stream 4 channel 0 of DMA2
  DMAx->CR &= ~(DMA_SxCR_CHSEL);

  // Initialize number of transactions to perform,
  // transaction can be thought of number of sources you need to transfer
  // data from. this is decremented after each transfer.
  DMAx->NDTR &= ~(DMA_SxNDT);
  DMAx->NDTR |= (DMA_SxNDT_0); //3
  //DMAx->NDTR |= (DMA_SxNDT_0|DMA_SxNDT_1); //3

  // Direction, Periphery to Memory
  DMAx->CR &= ~(DMA_SxCR_DIR);

  // No Fifo mode. Direct mode
  DMAx->FCR &= ~(DMA_SxFCR_DMDIS);

  // Fifo Threshold, since using direct mode, just set this to default value
  // Not used in Direct mode
  DMAx->FCR &= ~(DMA_SxFCR_FTH);
  DMAx->FCR |= ~(DMA_SxFCR_FTH_0);

  // Memory Burst Mode
  // In direct mode, these bits are forced to 0x0
  // by hardware as soon as bit EN= '1'.
  DMAx->CR &= ~(DMA_SxCR_MBURST);

  // Periphery Burst Mode
  // In direct mode, these bits are forced to 0x0
  // by hardware as soon as bit EN= '1'.
  DMAx->CR &= ~(DMA_SxCR_PBURST);

  // Circular Buffer
  DMAx->CR |= (DMA_SxCR_CIRC);

  // Use Double buffering
  DMAx->CR |= (DMA_SxCR_DBM);

  // Set the Priority
  DMAx->CR |= (DMA_SxCR_PL); // Highest

  /* Periphery Source configuration */
  DMAx->PAR = (uint32_t)&ADCx->DR; // Source of the Data to grab
  DMAx->CR &= ~(DMA_SxCR_PSIZE);
  DMAx->CR |= (DMA_SxCR_PSIZE_0);
  // Keep the pointer incremenent constant
  DMAx->CR &= ~(DMA_SxCR_PINC);

  /* Memory Destination Configuration */
  DMAx->M0AR =(uint32_t) &sample_buffer0;
  DMAx->M1AR =(uint32_t) &sample_buffer1;
  // In direct mode, MSIZE is forced by hardware to the
  // same value as PSIZE as soon as bit EN= '1'.
  DMAx->CR &= ~(DMA_SxCR_MSIZE);
  DMAx->CR |= (DMA_SxCR_MSIZE_0);
  // Increment the pointer
  DMAx->CR |= (DMA_SxCR_MINC);

  // Set the DMA as the flow controller
  DMAx->CR &= ~(DMA_SxCR_PFCTRL);

  // Enable the DMA transfer complete interrupt
  DMAx->CR |= DMA_SxCR_TCIE;
  DMAx->CR |= DMA_SxCR_TEIE;

  // Set DMAx priority to 1
  NVIC_SetPriority(DMA2_Stream4_IRQn,1);

  // Enable DMAx interrupt
  NVIC_EnableIRQ(DMA2_Stream4_IRQn);

  // Enable the DMA
  DMAx->CR  |= DMA_SxCR_EN;



}

in main() :

  while(1){                                                                     

    while(counter == 0); // Wait till conversion is done, 
                         // DMA_Handler will set counter = 1 after transfer.                        
    counter = 0;                                                                


    LCD_print(&rgb_lcd, "TEMP: %4d", temp_value);                               

    LCD_home(&rgb_lcd);                                                         


  }   
1

1 Answers

1
votes

I just ran into a similar issue myself using scan conversion on an STM32F1, and I suspect it's because, according to the reference manual, the EOC bit "is cleared by software or by reading the ADC_DR," which I believe the DMA is effectively doing implicitly.

I ended up basing my behavior on the DMA transfer complete interrupt instead, but it sounds like that might not be a helpful alternative for your use case.