Stm32f4 adc eoc flag с dma

Я использую stm32f4. В настоящее время я считываю температуру чипа, используя АЦП с переключением DMA и TIMER. DMA настроен как кольцевой буфер с двойной буферизацией. TIMER TRGO настроен так, чтобы запускать событие обновления.

Кажется, он работает нормально, но по какой-то причине, когда я включаю DMA в коде инициализации ADC, ADC_SR_EOC никогда не устанавливается, поэтому он никогда не выполняет фрагмент кода в if((ADC1->SR & ADC_SR_EOC) == ADC_SR_EOC)

Есть ли способ заставить ADC EOC в ADC_IRQHandler работать вместе с конфигурацией DMA?

Конечная цель - просто переключить и светодиод, чтобы проверить, есть ли у меня тайминги, которые я хотел установить для своего блока TIMER + ADC. (Возможно, в будущем я захочу добавить еще код в обработчик ADC для выполнения)

void ADC_IRQHandler(void){                                                      


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

    GPIOD->ODR ^=ORANGE_LED;                                                        

    ADC1->SR &= ~(ADC_SR_EOC);                                                   

  }                                                                                                                                                                  
}   

если я перемещаю GPIOD->ODR ^=ORANGE_LED; за пределы if-statment, на плате загорается ORANGE_LED

Вот все три функции инициализации и обработчик DMA

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;



}

в 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
0
1 159
1

Ответы 1

Я сам столкнулся с подобной проблемой, используя преобразование сканирования на STM32F1, и я подозреваю, что это потому, что, согласно справочному руководству, бит EOC "является очищено программой или прочитав ADC_DR, ", который, как я считаю, DMA эффективно выполняет неявно.

В итоге я основал свое поведение на прерывании завершения передачи DMA, но похоже, что это не может быть полезной альтернативой для вашего варианта использования.

Я вижу в этом смысл. Вероятно, это безнадежное дело на данном этапе, но хорошо знать, что DMA эффективно очищает бит ADC EOC.

Moeiz Riaz 01.10.2018 05:56

Другие вопросы по теме