Author Topic: MCG_Lite Clock mode switching  (Read 2750 times)

Offline Raffaele

  • Jr. Member
  • **
  • Posts: 65
    • View Profile
MCG_Lite Clock mode switching
« on: February 12, 2021, 09:28:23 PM »
Hi,

I'm trying to switch clock mode on the KL03 from within my own task.
The "KL03 Sub-Family Reference Manual" https://www.nxp.com/docs/en/reference-manual/KL03P24M48SF0RM.pdf
on pg. 321 reports how to switch between clk modes.


I took a look at the file kinetis.c and I saw that the following instructions (not in this order) are used:

Code: [Select]
MCG_C2 |= MCG_C2_IRCS;                                               // select fast internal reference clock
MCG_C1 = MCG_C1_CLKS_HIRC;                                           // select HIRC clock source
MCG_C2 &= ~MCG_C2_IRCS;                                              // select slow internal reference clock
MCG_C2 = 1;
MCG_C2 = 0;

I have 2 questions:

1]
If I want to enter LIRC8 and move to LIRC2, passing by HIRC, I just use this set of instructions in this order in my task. Is this correct and enough?

Code: [Select]
//Enter LIRC8
MCG_C2 &= ~MCG_C2_IRCS;                                              // select slow internal reference clock
MCG_C2 = 1;

//Enter HIRC
MCG_C2 |= MCG_C2_IRCS;                                               // select fast internal reference clock
MCG_C1 = MCG_C1_CLKS_HIRC;                                           // select HIRC clock source

//Enter LIRC2
MCG_C2 &= ~MCG_C2_IRCS;                                              // select slow internal reference clock
MCG_C2 = 8;


2] What do I do with the macros in the file app_hw_kinetis.h since I would need both RUN_FROM_HIRC and RUN_FROM_LIRC? I mean, do I need to uncomment both?

Code: [Select]
#define RUN_FROM_HIRC                                                // clock from internal 48MHz RC clock
#define RUN_FROM_LIRC                                                // clock from internal 8MHz/2MHz RC clock
    #if defined RUN_FROM_HIRC
        #define SYSTEM_CLOCK_DIVIDE  2                                   // 1..16
        #define BUS_CLOCK_DIVIDE     2                                   // 1..8 (valid for bus/flash and divisor is after the system clock divider)
    #elif defined RUN_FROM_LIRC
        #define RUN_FROM_LIRC_2M                                         // select 2MHz rather than 8MHz
        #define SLOW_CLOCK_DIVIDE    2                                   // optionally divide the slow clock output (1, 2, 4, 8, 16, 32, 64 or 128)
        #define SYSTEM_CLOCK_DIVIDE  1                                   // 1..16
        #define BUS_CLOCK_DIVIDE     1                                   // 1..8 (valid for bus/flash and divisor is after the system clock divider)

Thank you
« Last Edit: February 12, 2021, 09:30:04 PM by Raffaele »

Offline mark

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 3234
    • View Profile
    • uTasker
Re: MCG_Lite Clock mode switching
« Reply #1 on: February 13, 2021, 07:55:48 PM »
Hi

See the following for a practical guide to working with the MCG: https://www.utasker.com/kinetis/MCG.html
This is not the same as the MCG_Lite in the KL03 but the basic principles still apply.

I assume that your basic mode of operation is from the IRC48M and therefore the configuration (app_hw_kinetis.c) should be left for this, which will mean that the project is build for this mode and the processor will start in this mode.

To move from this mode to IRC8M the logical steps are:
1. Select 8MHz source using IRCS and prepare the divider value to be used (FCRDIV)
2. If you plan to use MCGIRCLK for some peripherals also adjust LICR_DIV2 to suit

At this point in time the system will still be operating from IRC48M but will be pre-prepared for the following mode

3. To perform the switch over CLKS is set to select the IRC8M output instead of IRC48M output (usually one spins on the status register checking that the switch-over has completed before continuing)
Now MCGOUTCLK is equal to 8Mz rather than 48MHz and all derived clocks (core, bus/flash) will be (at lest) 6x slower than before. In some cases OUTDIV1 and OUTDIV4 will now be changed to compensate (a little).
If you no longer need IRC48M it can be disabled now.

4. IRCS can be switched between 2MHz and 8MHz at any point whereby the resulting clocks (MCGIRCLK, Core, Bus/Flash) will switch 1/4 of the 8MHz speed and again some compensation may be performed.

When compensations are performed they can be performed 'after' the swith-over when the resulting speed is reduced.
When the resulting speed is increased compensations are usually performed 'before' the switch-over to ensure that the maximum output speed limites are respected during the process.
Whenever there is a status register showing the setting of a switch it should be polled to be the new value before continuing to ensure that the switch-overs have really completed before continuing.

Once the control registers and their control flags are known in the process the code is quite simple, as you have shown in your examples.

The most complicated thing is that when a new clock speed affects operating peripherals (such as UART) these will need to be set up with a new Baud rate value according to the new clock rate. This can lead to loss of data when switches are performed during operation and so such effects need to be carefully considered in the overall design. Specifially to the MCG_Lite that leaves IRC48M operating as clock source for peripherals in all modes, such peripheral speed are not effected by switch-overs to slower core, bus, flash speeds, which can simplify overall system design.

Regards

Mark

Offline Raffaele

  • Jr. Member
  • **
  • Posts: 65
    • View Profile
Re: MCG_Lite Clock mode switching
« Reply #2 on: February 15, 2021, 08:46:37 PM »
Hi Mark,


yes my basic mode of operation is from the IRC48M. However, the only reason I start from IRC48M is because, as it appears from the MCG_Lite mode state diagram (see attached illustration) I can't go directly from LIRC8M to LIRC2M and viceversa, but I need to go to IRC48M first.
But from your answer 4 it looks like I'm wrong and I could go directly from  LIRC8M to LIRC2M (?)

1. If I understood correctly, the FCRDIV is set with the SLOW_CLOCK_DIVIDE as I can also see from this snippet of kinetis.h

Code: [Select]
      #elif defined RUN_FROM_LIRC
            #if defined RUN_FROM_LIRC_2M
                #define IRC_CLOCK      2000000
            #else
                #define IRC_CLOCK      8000000
            #endif
            #if defined SLOW_CLOCK_DIVIDE
                #if (SLOW_CLOCK_DIVIDE == 1)
                    #define SLOW_CLOCK_DIVIDE_VALUE  (MCG_SC_FCRDIV_1)
                #elif SLOW_CLOCK_DIVIDE == 2
                    #define SLOW_CLOCK_DIVIDE_VALUE  (MCG_SC_FCRDIV_2)
                #elif SLOW_CLOCK_DIVIDE == 4
                    #define SLOW_CLOCK_DIVIDE_VALUE  (MCG_SC_FCRDIV_4)
                    ...
                    ...


 2. No, I don't plan to use peripherals, like UART, so I don't need LIRC_DIV2. But I'm using SPI, so I need BUS_CLOCK_DIVIDE.

So here are a couple of questions on the logic of OUTDIV1, OUTDIV4 and BUS_CLOCK_DIVIDE.
a) Do I just need to set a value for the main clock and BUS_CLOCK_DIVIDE and uTasker will automatically define the values for OUTDIV1 and OPUTDIV4?
b) If I switch from LIRC8M to LIRC2M, can I  (and if so, how?), change the values for  SLOW_CLOCK_DIVIDE, or BUS_CLOCK_DIVIDE? Since the clock is slower now, I might need to update them.

3. How can I disable IRC48M when I don't need it any more?


Yes I remember the issue with UART with slower clocks and you already helped me fix it  8)

Thanks,
Raff

Offline mark

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 3234
    • View Profile
    • uTasker
Re: MCG_Lite Clock mode switching
« Reply #3 on: February 16, 2021, 10:21:41 PM »
Raffael

I just checked and see that in fact moving between 2MHz and 8MHz LIRC is not allowed according to the user's manual. It says that there are shared clock generators involved and so passing through HIRC mode looks to be needed.
Just below the figure that you have shown is a list of registers to be written to do this.

To control the core and bus/flash clock speeds the register SIM_CLKDIV1 is written:
SIM_CLKDIV1 = (((SYSTEM_CLOCK_DIVIDE - 1) << 28) | ((BUS_CLOCK_DIVIDE - 1) << 16)); // prepare system and bus/flash clock divides
Ensure that when switching to a faster clock source that this is set for the new value before switching to the new, faster source to that the clocks are never too high (out of specification). You need to control this yourself since the default values are valid only for the main clock configuration -
you can define SYSTEM_CLOCK_DIVIDE_2 and BUS_CLOCK_DIVIDE_2 to be used in the alternative mode and write
SIM_CLKDIV1 = (((SYSTEM_CLOCK_DIVIDE_2 - 1) << 28) | ((BUS_CLOCK_DIVIDE_2 - 1) << 16));

The HIRC can be enabled in MCG_MC

MCG_MC |= MCG_MC_HIRCEN;        // this is optional and would allow the HIRC to run even when the processor is not working in HIRC mode

however it is enabled automatically when the HIRC is selected as clock source. I believe that it is disabled automatically too when a different clock source if selected when the enable bit is not specifically set, meaning that there is nothing to do if you want HIRC to be disabled when in the other mode.

Regards

Mark


Offline Raffaele

  • Jr. Member
  • **
  • Posts: 65
    • View Profile
Re: MCG_Lite Clock mode switching
« Reply #4 on: February 18, 2021, 04:33:14 AM »
Great, it all makes sense. It's working

Thank you