cancel
Showing results for 
Search instead for 
Did you mean: 

ADC init hangs after code optimization

Marc1
Associate III

Controller: STM32G431VBT6
Libraries: CMSIS
Compiler: arm-none-eabi-gcc version 13.2.1
Code Optimization: OPT = -Os

After code optimization MCU hangs, depending on code constellation. It hangs in ADC initialization.

 

... system_clock_init(); timer_init(); ADC_init(); USART_init(); ...

 

 

void clock_init(void) { // configure wait states (2.7 V .. 3.6 V @ 170 MHz) FLASH->ACR = (FLASH->ACR & ~FLASH_ACR_LATENCY) | FLASH_ACR_LATENCY_4WS; // enable and reset instruction cache FLASH->ACR |= FLASH_ACR_ICEN | FLASH_ACR_ICRST; // debug software enable FLASH->ACR |= FLASH_ACR_DBG_SWEN; // start crystal oscillator (HSE) RCC->CR |= RCC_CR_HSEON; // switch on crystal oscillator while((RCC->CR & RCC_CR_HSERDY) == 0); RCC->PLLCFGR = (8 << RCC_PLLCFGR_PLLPDIV_Pos); // P main PLLP division factor RCC->PLLCFGR |= RCC_PLLCFGR_PLLPEN; // R enable PLL P RCC->PLLCFGR |= ((2 - 2) << RCC_PLLCFGR_PLLR_Pos); // R main PLL division factor for PLL R clock (system cloc RCC->PLLCFGR |= RCC_PLLCFGR_PLLREN; // R enable PLL R RCC->PLLCFGR |= (85 << RCC_PLLCFGR_PLLN_Pos); // N main PLL multiplication factor for VCO RCC->PLLCFGR |= ((2 - 1) << RCC_PLLCFGR_PLLM_Pos); // M division factor for the main PLL input clock RCC->PLLCFGR |= (0b11 << RCC_PLLCFGR_PLLSRC_Pos); // HSE clock selected as PLL clock entry RCC->CR |= RCC_CR_PLLON; // switch on PLL while((RCC->CR & RCC_CR_PLLRDY) == 0); // select PLL as system clock RCC->CFGR = RCC_CFGR_SW_1 | RCC_CFGR_SW_0; }
void ADC_init(void) { // DMAMUX (see chapter 13 in manual) RCC->AHB1ENR |= RCC_AHB1ENR_DMAMUX1EN; // enable DMAMUX1 clock DMAMUX1_Channel4->CCR = 5; // DMA1_Channel5 - ADC1 DMAMUX1_Channel5->CCR = 36; // DMA1_Channel6 - ADC2 // DMA RCC->AHB1ENR |= RCC_AHB1ENR_DMA1EN; // enable DMA1 clock // DMA ADC1 DMA1_Channel5->CCR = (0b01 << DMA_CCR_MSIZE_Pos); // memory data size 16 bit DMA1_Channel5->CCR |= (0b01 << DMA_CCR_PSIZE_Pos); // peripheral data size 16 bit DMA1_Channel5->CCR |= DMA_CCR_MINC; // memory address pointer is incremented after each data transfer DMA1_Channel5->CCR |= DMA_CCR_CIRC; // enable circular mode DMA1_Channel5->CCR |= (0b00 << DMA_CCR_DIR_Pos); // direction is peripheral to memory DMA1_Channel5->CNDTR = ADC1_NUM; // number of data items to transfer DMA1_Channel5->CPAR = (uint32_t) &ADC1->DR; // periphal address DMA1_Channel5->CMAR = (uint32_t) DMA_ADC1_buffer; // memory address NVIC_EnableIRQ(DMA1_Channel5_IRQn); // enable interrupt DMA1_Channel5->CCR |= DMA_CCR_EN; // enable DMA1_Channel5 // DMA ADC2 DMA1_Channel6->CCR = (0b01 << DMA_CCR_MSIZE_Pos); // memory data size 16 bit DMA1_Channel6->CCR |= (0b01 << DMA_CCR_PSIZE_Pos); // peripheral data size 16 bit DMA1_Channel6->CCR |= DMA_CCR_MINC; // memory address pointer is incremented after each data transfer DMA1_Channel6->CCR |= DMA_CCR_CIRC; // enable circular mode DMA1_Channel6->CCR |= (0b00 << DMA_CCR_DIR_Pos); // direction is peripheral to memory DMA1_Channel6->CNDTR = ADC2_NUM; // number of data items to transfer DMA1_Channel6->CPAR = (uint32_t) &ADC2->DR; // periphal address DMA1_Channel6->CMAR = (uint32_t) DMA_ADC2_buffer; // memory address NVIC_EnableIRQ(DMA1_Channel6_IRQn); // enable interrupt DMA1_Channel6->CCR |= DMA_CCR_EN; // enable DMA1_Channel6 // ADC RCC->AHB2ENR |= RCC_AHB2ENR_ADC12EN; // enable ADC clock ADC12_COMMON->CCR |= (0b0000 << ADC_CCR_PRESC_Pos); // prescaler divided by 1 ADC12_COMMON->CCR |= ADC_CCR_VSENSESEL; // enable temperature sensor channel ADC12_COMMON->CCR |= (0b11 << ADC_CCR_CKMODE_Pos); // select adc_hclk / 4 // ADC1 ADC1->CR &= ~ADC_CR_ADEN; // disable ADC ADC1->CR &= ~ADC_CR_DEEPPWD; // exit Deep-power-down mode ADC1->CR |= ADC_CR_ADVREGEN; // enable ADC voltage generator // ADC voltage regulator startup time tADCVREG_STUP DELAY_US(DELAY_IDX_MAIN, 25); ADC1->CFGR |= ADC_CFGR_CONT; // continuous mode on regular channels ADC1->CFGR |= ADC_CFGR_DMACFG; // DMA circular mode ADC1->CFGR |= ADC_CFGR_DMAEN; // enable DMA ADC1->SMPR1 |= (0b100 << ADC_SMPR1_SMP1_Pos); // sampling time for ADC channel 1 = 47.5 cycles ADC1->SMPR2 |= (0b100 << ADC_SMPR2_SMP11_Pos); // sampling time for ADC channel 11 = 47.5 cycles ADC1->SMPR2 |= (0b001 << ADC_SMPR2_SMP13_Pos); // sampling time for ADC channel 13 = 6.5 cycles ADC1->SMPR2 |= (0b100 << ADC_SMPR2_SMP16_Pos); // sampling time for ADC channel 16 = 47.5 cycles ADC1->SQR1 = ((ADC1_NUM - 1) << ADC_SQR1_L_Pos); // regular channel sequence length ADC1->SQR1 |= (16 << ADC_SQR1_SQ1_Pos); // 1st conversion is ADC channel 16, ADC1_TEMP_IN ADC1->SQR1 |= (1 << ADC_SQR1_SQ2_Pos); // 2nd conversion is ADC channel 1, ADC1_TEMP_PA ADC1->SQR1 |= (11 << ADC_SQR1_SQ3_Pos); // 3rd conversion is ADC channel 11, ADC1_V_SUPPLY ADC1->SQR1 |= (13 << ADC_SQR1_SQ4_Pos); // 6th conversion is ADC channel 13, ADC1_OPAMP1_SHUNT_W ADC1->JSQR = ((1 - 1) << ADC_JSQR_JL_Pos); // injected channel sequence length // calibration for single-ended inputs mode ADC1->CR &= ~ADC_CR_ADCALDIF; // single-ended inputs mode ADC1->CR |= ADC_CR_ADCAL; // start calibration (will be reset after calibration) while (ADC1->CR & ADC_CR_ADCAL) {} // calibration for differential inputs mode ADC1->CR |= ADC_CR_ADCALDIF; // differential inputs mode ADC1->CR |= ADC_CR_ADCAL; // start calibration (will be reset after calibration) while (ADC1->CR & ADC_CR_ADCAL) {} ADC1->ISR |= ADC_ISR_ADRDY; // clear ADC_ISR_ADRDY ADC1->CR |= ADC_CR_ADEN; // enable ADC while ((ADC1->ISR & ADC_ISR_ADRDY) == 0) {} // stabilization time tSTAB DELAY_US(DELAY_IDX_MAIN, 10); for (uint8_t i = 0; i < ADC1_NUM; i++) { s_ADC1[i].sum = 0; s_ADC1[i].raw = &DMA_ADC1_buffer[i]; s_ADC1[i].offset = 0; } ADC1->IER |= ADC_IER_JEOCIE; // enable end of injected conversion interrupt // ADC2 ADC2->CR &= ~ADC_CR_ADEN; // disable ADC ADC2->CR &= ~ADC_CR_DEEPPWD; // exit Deep-power-down mode ADC2->CR |= ADC_CR_ADVREGEN; // enable ADC voltage generator // ADC voltage regulator startup time tADCVREG_STUP DELAY_US(DELAY_IDX_MAIN, 25); ADC2->CFGR |= ADC_CFGR_CONT; // continuous mode on regular channels ADC2->CFGR |= ADC_CFGR_DMACFG; // DMA circular mode ADC2->CFGR |= ADC_CFGR_DMAEN; // enable DMA ADC2->SMPR1 |= (0b100 << ADC_SMPR1_SMP8_Pos); // sampling time for ADC channel 8 = 47.5 cycles ADC2->SMPR1 |= (0b100 << ADC_SMPR1_SMP9_Pos); // sampling time for ADC channel 9 = 47.5 cycles ADC2->SMPR2 |= (0b001 << ADC_SMPR2_SMP16_Pos); // sampling time for ADC channel 16 = 6.5 cycles ADC2->SMPR2 |= (0b001 << ADC_SMPR2_SMP18_Pos); // sampling time for ADC channel 18 = 6.5 cycles ADC2->SQR1 = ((ADC2_NUM - 1) << ADC_SQR1_L_Pos); // regular channel sequence length ADC2->SQR1 |= (8 << ADC_SQR1_SQ1_Pos); // 1st conversion is ADC channel 8, ADC2_IR_FRONT ADC2->SQR1 |= (9 << ADC_SQR1_SQ2_Pos); // 2nd conversion is ADC channel 9, ADC2_IR_LEFT ADC2->SQR1 |= (16 << ADC_SQR1_SQ3_Pos); // 1st conversion is ADC channel 16, ADC2_OPAMP2_SHUNT_V ADC2->SQR1 |= (18 << ADC_SQR1_SQ4_Pos); // 2nd conversion is ADC channel 18, ADC2_OPAMP3_SHUNT_U ADC2->JSQR = ((1 - 1) << ADC_JSQR_JL_Pos); // injected channel sequence length // calibration for single-ended inputs mode ADC2->CR &= ~ADC_CR_ADCALDIF; // single-ended inputs mode ADC2->CR |= ADC_CR_ADCAL; // start calibration (will be reset after calibration) while (ADC2->CR & ADC_CR_ADCAL) {} // calibration for differential inputs mode ADC2->CR |= ADC_CR_ADCALDIF; // differential inputs mode ADC2->CR |= ADC_CR_ADCAL; // start calibration (will be reset after calibration) while (ADC2->CR & ADC_CR_ADCAL) {} ADC2->ISR |= ADC_ISR_ADRDY; // clear ADC_ISR_ADRDY ADC2->CR |= ADC_CR_ADEN; // enable ADC while ((ADC2->ISR & ADC_ISR_ADRDY) == 0) {} // stabilization time tSTAB DELAY_US(DELAY_IDX_MAIN, 10); for (uint8_t i = 0; i < ADC2_NUM; i++) { s_ADC2[i].sum = 0; s_ADC2[i].raw = &DMA_ADC2_buffer[i]; s_ADC2[i].offset = 0; } ADC2->IER |= ADC_IER_JEOCIE; // enable end of injected conversion interrupt s_ADC1[ADC1_TEMP_INT].filter = 0; s_ADC1[ADC1_V_SUPPLY].filter = 2; s_ADC1[ADC1_TEMP_PA].filter = 1; s_ADC2[ADC2_IR_FRONT].filter = 5; s_ADC2[ADC2_IR_LEFT].filter = 3; s_ADC1[ADC1_OPAMP1_SHUNT_W].filter = 2; s_ADC2[ADC2_OPAMP2_SHUNT_V].filter = 2; s_ADC2[ADC2_OPAMP3_SHUNT_U].filter = 2; NVIC_EnableIRQ(ADC1_2_IRQn); ADC1->CR |= ADC_CR_ADSTART; // start conversion ADC2->CR |= ADC_CR_ADSTART; // start conversion }
View more

 

After code optimization, depending on code constellations, the controller hangs during ADC initialization. Usually it hangs here:

 

while (ADC1->CR & ADC_CR_ADCAL) {}

 

It looks like ADC clock is missing. But in other firmware constellations ADC init works without problem.
E.g. ADC init hangs after adding a new sprintf instruction at a random position in code. Adding another instruction somewhere else in the code, the problem disappears.

Until now I haven't been able to isolate the problem. Sometimes it works and sometimes, after editing code, it hangs.

Any idea how to isolate the problem?

4 REPLIES 4
Pavel A.
Evangelist III

Usually it hangs here:

Does it indeed spin inside that source line when you break in with the debugger?

There are known issues in gcc at high levels of optimization, IIRC related to link time code generation (ltcg). If you indeed need very tight code, you may want to try Keil or IAR toolchains. Try also to selectively disable -Os on some source files or change linker optimization options.

 

> Does it indeed spin inside that source line when you break in with the debugger?

Yes it spins inside that source. If I comment out all while instructions in ADC init, the code starts, but ADC doesn't work. It looks like just ADC has no clock source.

 

> Try also to selectively disable -Os on some source files or change linker optimization options.

This could be a workaround without knowing if the problem appears again after doing further code changes.

Pavel A.
Evangelist III

IIRC there's a known issue that writing enable bit in RCC->AHB... registers may require delay of few cycles, or barriers that prevent reordering of memory accesses, which can be affected by optimization. In the awesome HAL library these delays or barriers are often missing. If you can spot such occurrence of missing barrier or delay, please post a bug report.

 

Could you tell more about that known issue and the corresponding workaround?
What kind of barriers are you talking about? Is there any documentation?