Important changes to forums and questions
All forums and questions are now archived. To start a new conversation or read the latest updates go to forums.mbed.com.
7 years, 6 months ago.
Why does attaching an ADC interrupt clobber the mbed builtin pc.printf?
Greetings. After getting the ADC on a Nucleo F302R8 properly configured, I found that enabling the ADC interrupt makes printf() statements stop working (line 46). If I comment out that line, of course the ADC interrupt handler is no longer executed, but the printf() works again. Why might this be?
Thank you :)
calling NVIC_EnableIRQ(ADC1_IRQn); makes serial not work
void config(){ /* Enable ADC, GPIOA, B, C, and DMA clocks */ RCC->AHBENR |= RCC_AHBENR_ADC1EN; RCC->AHBENR |= RCC_AHBENR_GPIOAEN; RCC->AHBENR |= RCC_AHBENR_GPIOBEN; RCC->AHBENR |= RCC_AHBENR_GPIOCEN; RCC->AHBENR |= RCC_AHBENR_DMA1EN; RCC->APB1ENR |= RCC_APB1ENR_DAC1EN; DAC->CR |= DAC_CR_EN1; //Enable the DAC RCC->CFGR2 |= RCC_CFGR2_ADC1PRES_DIV2; //Divide some clock by 2 /* Configure PA_0, PC_1, PC_0, and PB_1 GPIOs as analog inputs*/ GPIOA->MODER |= (GPIO_MODER_MODER0); GPIOC->MODER |= (GPIO_MODER_MODER0 << GPIO_MODER_MODER1_Pos); GPIOC->MODER |= (GPIO_MODER_MODER0); GPIOB->MODER |= (GPIO_MODER_MODER0 << GPIO_MODER_MODER1_Pos); GPIOA->MODER |= GPIO_MODER_MODER8_0; //PA_8 configured as digital out ADC1->CFGR |= ADC_CFGR_OVRMOD; // The data register is overwritten with the last conversion result and the previous unread data is lost ADC1->SQR1 |= 3U; //number of channels -1 to convert. The "Length" field is 4 bits long and starts at the zeroth bit of the SQR "Regular Sequence" register /* Populate the sequence of ADC inputs to be read */ ADC1->SQR1 |= ADC_SQR1_SQ1_0; //ADC1_IN1 ADC1->SQR1 |= 7U << ADC_SQR1_SQ2_Pos; //ADC1_IN7 (bits (0,1,2) = 7) ADC1->SQR1 |= 6U << ADC_SQR1_SQ3_Pos; //ADC1_IN6 ADC1->SQR1 |= 12U << ADC_SQR1_SQ4_Pos; //ADC1_IN12 // Calibration procedure ADC1->CR &= ~ADC_CR_ADVREGEN; ADC1->CR |= ADC_CR_ADVREGEN_0; // 01: ADC Voltage regulator enabled ADC1->CR &= ~ADC_CR_ADCALDIF; // calibration in Single-ended inputs Mode. ADC1->CR |= ADC_CR_ADCAL; // Start ADC calibration // Read at 1 means that a calibration in progress. while (ADC1->CR & ADC_CR_ADCAL); // wait until calibration done calibration_value = ADC1->CALFACT; // perhaps this will be useful later... ADC1->CFGR &= ~ADC_CFGR_RES; // 12-bit data resolution ADC1->CFGR &= ~ADC_CFGR_ALIGN; // Right data alignment ADC1->CR |= ADC_CR_ADEN; //ADEN=1 enables the ADC. The flag ADRDY will be set once the ADC is ready for operation. ADC1->IER |= ADC_IER_EOCIE; //enable end of regular conversion interrupt. There are multiple CONVERSIONS per SEQUENCE ADC1->CFGR |= ADC_CFGR_CONT; //enable continuous conversion NVIC_SetPriority(ADC1_IRQn, 3U); // set priority level NVIC_EnableIRQ(ADC1_IRQn); //Attach the ADC interrupt ADC1->CR |= ADC_CR_ADEN; // Enable ADC1 while(!ADC1->ISR & ADC_ISR_ADRD); // wait for ADRDY ADC1->CR |= ADC_CR_ADSTART; // Start ADC1 Software Conversion DAC->CR |= DAC_CR_EN1; //Enable the DAC }
1 Answer
7 years, 6 months ago.
So the interrupts of the ADC are blocking your system. Where is your interrupt handler defined? I see you are not using NVIC_SetIRQ, so I assume you use the standard naming. Which in general I would not advice you to do (except on NRF51822, since they are only mbed target I am aware of which does not support interrupt remapping). And what does your interrupt handler do?
Option 1: It does not actually clear the interrupt, so it keeps being called.
Option 2: It takes too long, so it is continiously handling interrupts
Option 3: It is invalid and the system ends with a hardfault :).
Erik, thanks for your response. I've been coming to the mbed forums for a couple of years now, and I appreciate your dedication and willingness to approach even the most peculiarly worded problems.
I ended up sidestepping this by using DMA to pipe the continuous ADC conversions into memory.
DMA configuration
void dma_config(){ DMA1_Channel1->CCR = 0U; //disable the DMA DMA1_Channel1->CCR |= DMA_CCR_CIRC; DMA1_Channel1->CCR &= ~DMA_CCR_DIR; // sets DMA direction to:(peripheral --> memory). techinically this is unnecessary because the CCR is initialized to 0. DMA1_Channel1->CCR |= DMA_CCR_MSIZE_1; // the memory size is a 32 bit integer DMA1_Channel1->CCR |= DMA_CCR_PSIZE_0; // the peripheral size is a 16 bit integer DMA1_Channel1->CCR |= DMA_CCR_MINC; // Memory Increment mode enabled DMA1_Channel1->CCR &= ~DMA_CCR_PINC; // Peripheral Increment mode disabled: the ADC1->DR is always in the same place DMA1_Channel1->CPAR = (uint32_t) &ADC1->DR; //point the DMA at the address of the ADC1->DR //The ADC1->DR is a 32 bit register, but only the least significant 16 bits are used. The rest are 'reserved' DMA1_Channel1->CMAR = (uint32_t) a_in; // point the DMA at the zeroth address of the array of int DMA1_Channel1->CNDTR = NUM_ANALOG_CHANNELS; // tell the DMA how many data to transfer DMA1_Channel1->CCR |= DMA_CCR_EN; //Enable the DMA }
main loop
int main(){ clock_config(); adc_config(); dac_config(); dma_config(); adc_start(); while(1){ DAC->DHR12R1 = a_in[3]; } }
Does printf stop functioning, or does everything stop functioning? If you flash an LED, does that still flash?
posted by Erik - 06 May 2017Hi Erik, I am toggling an IO pin in the main loop in addition to serial printing. The main loop does not seem to be executing when the ADC interrupt is enabled. Could the interrupt be getting called so fast that nothing else is ever called? Here are the NVIC registers as shown by Keil's debugger when ADC_IRQ1 is enabled:
ADC_IRQ1 enabled
With ADC1_IRQ un-enabled, the main loop executes (toggling the IO pin and serial printing). The NVIC registers look like this:
ADC_IRQ1 disabled
`stm32f302x8.h` defines: ADC1_IRQn = 18
posted by N K 06 May 2017