2024-03-12 06:26 AM
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?
2024-03-12 07:28 AM
> 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.
2024-03-12 07:55 AM
> 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.
2024-03-12 09:18 AM - edited 2024-03-12 09:18 AM
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.
2024-03-12 10:14 AM
Could you tell more about that known issue and the corresponding workaround?
What kind of barriers are you talking about? Is there any documentation?