STM32F746-Disco, Win 10, Cube 1.8.0; CMSIS-only (no HAL). C.
I've been messing around with STM32F746 and learning to do various stuff directly via registers (interrupts, dma no problem). I left configuring clock for the better days and was using default 16MHz HSI until today.
Better days have come. I've decided to go full 216MHz from 25MHz crystal with PLL. As usual, covered myself head to toe with documentation. Here is my code:
main.c:
#include "main.h"
int main(void)
{
rcc_setup();
gpio_setup();
while(1){
blink_ld1();
}
}
part of rcc.c:
void rcc_init(void) {
//enable HSE
RCC->CR |= RCC_CR_HSEON;
while (!(RCC->CR & RCC_CR_HSERDY)); //wait while hardware signals HSE is stable
//configure AHB/APB clocks
RCC->CFGR &= ~RCC_CFGR_HPRE_DIV1; //AHB Prescaler 1
RCC->CFGR |= RCC_CFGR_PPRE1_DIV4; //APB1 Prescaler 4
RCC->CFGR |= RCC_CFGR_PPRE2_DIV2; //APB2 Prescaler 2
//configure PLL
RCC->CR |= RCC_CR_PLLON; //Enable PLL
while (!(RCC->CR & RCC_CR_PLLRDY)); //wait while hardware signals PLL is OK
RCC->PLLCFGR |= RCC_PLLCFGR_PLLSRC_HSE; //PLL/PLL2S source = HSE
RCC->PLLCFGR |= 25U << RCC_PLLCFGR_PLLM_Pos; //PLLM 25
RCC->PLLCFGR |= 432U << RCC_PLLCFGR_PLLN_Pos; //PLLN 432
RCC->PLLCFGR &= ~RCC_PLLCFGR_PLLP; //PLLP 2
//Set System Clock to PLL
RCC->CFGR |= RCC_CFGR_SW_PLL; // System Clock Mux: Select PLL as system clock
while ((RCC->CFGR & RCC_CFGR_SWS) != (RCC_CFGR_SWS_PLL)); //wait until PLL is established as system clock
}
rcc_init()
is literally the first thing rcc_setup()
does.
It crashes with "WWDG interrupt infinite loop" if and only if I execute RCC->CFGR |= RCC_CFGR_SW_PLL;
. Otherwise, runs correctly.
As I was debugging, I decided to put a breakpoint exactly where my code breaks to look at the PLL registers and make sure the values I set are OK. And they're clearly not.
RCC_PLLCFGR
is all wrong. I commented out all the code that modifies it. No change to values. So the register is at reset value. And I don't understand why.
Also, I tried commenting out APB/AHB prescaler modifications. No change (and it still works with new prescalers as long as system clock is from HSI).
I checked all AHB1ENR and APB's in case I forgot to activate the clock to something. Doesn't seem to be the case.
What did I miss?
EDIT1:
I swapped PLL parameter initialization (25 and 432) and PLL activation. Now the data in the register is changed from reset value. PLLM is correct. PLLN is NOT correct - not 432. Tinkering on. Mini-Edit. Wrote HEX value of 432. Still same fault.
EDIT2:
I went full pen and paper and manually calculated every single bit of the register, taking into account its reset value and bits that aren't supposed to be changed.
RCC_PLLCFGR reset value is 0x2400 3010
I did RCC->PLLCFGR = 0x29406C19;
This puts correct values into the correct places, triple checked with debugger and on paper. Same issue.
HAL library enables power for PWR peripheral. I read its part in the reference manual and found no connection to clock speeds. Can it be related or has nothing to do with it?
Also, I don't know why it writes that it fires window watchdog interrupt. There has never been a single program that used it on the disco board, absolutely certain. I think it jumps there due to clock error.
EDIT3: I examined how auto-generated code uses HAL and initializes 216MHz. All PLL values are the same, but it also enables PWR peripheral and sets Overdrive and Overdrive switching. So I did functionally exactly the same as HAL, and I see no changes.
In the rcc_init function now after HSE enable I have the following:
//enable PWR peripheral
volatile uint32_t temp;
RCC->APB1ENR |= RCC_APB1ENR_PWREN;
temp = RCC->APB1ENR & RCC_APB1ENR_PWREN;
(void) temp; //waste cycles until activated
PWR->CR1 |= PWR_CR1_VOS; //explicit default value for internal voltage regulator
(void) temp; //waste cycles until activated
PWR->CR1 |= PWR_CR1_ODEN;
while (!(PWR->CSR1 & PWR_CSR1_ODRDY)); //wait while overdrive gets ready
PWR->CR1 |= PWR_CR1_ODSWEN; //enable overdrive switching
while (!(PWR->CSR1 & PWR_CSR1_ODSWRDY)); //wait while overdrive gets ready
Double checked register values, they're all set correctcly for overdrive. I even went so far as to debugging the CubeMX generated code and literally screenshotting RCC and PWR registers.
RCC:
CR - identical to the bit
PLLCFGR - identical to the bit
CFGR - identical to the bit
PWR:
CR1 - identical to the bit
CSR1 - identical to the bit
CR2 - identical to the bit
CSR2 - identical to the bit