I’m using the MiniCore library for my ATmega328PB with the internal 8 MHz and correct fuse settings. During runtime, I dynamically change the clock frequency from 8 MHz to 4 MHz using CLKPR. (Both frequencies are needed)
The issue is that after switching to 4 MHz, UART (baudrate 1200) outputs garbage data. I understand this happens because F_CPU remains at 8 MHz, but it's a compile-time constant and cannot be changed.
How can I update all dependent clocks (UART, ADC, SPI, etc.) to match the new frequency at runtime? Ideally, is there a function to handle this easily?
Try Serial.begin(2400) to mimic 1200 baud at a 2x lower clock.
For I2C you might do a similar trick with Wire.setClock(200000) but I2C should just work at half speed. SPI should also work at half speed as data is synced by the clock pulses.
Yes, I wrote 4Mhz here just to make it easier to understand. Actually I change it from 8Mhz to 250Khz to save power as it is battery operated.
But in both cases (4Mhz and 250Khz) the serial communication error and the clock mismatches are the same.
I need 8Mhz to set baudrate of another module from 115200 (its default baudrate) to 1200 and then I change atmega clock to 250Khz to work with 1200 baudrate.
That just means OP will need to use Serial.begin(38400); to get 1200 baud (the device probably doesn't even support 3600 baud since it's not a standard rate).
I decided to stick with the default 8MHz and rely heavily on sleep functions to save power.
After changing the clock, many timing functions like delay_ms(), delay_us(), millis(),... malfunctioned and it was not working as intended.
While I’m not fully familiar with compile procedure, it might be better for developers to avoid using #define for frequency if possible, and instead use a variable that can be changed during runtime. Switching between frequencies at runtime is practically too difficult unless one is chosen from the beginning.