ADC init hangs after code optimization
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
}
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?
