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);
}