Arduino CPU Speed change

I need to reduce the CPU speed of an Arduino Due.
I have examined using the F_CPU value in the boards.txt file, but haven't yet had the nerve to try it.

So, to help me decide:

  1. Does that option (boards.txt , F_CPU value, say changed to 42000000 to halve the CPU Speed) work?;
  2. Does anything else have to be done?
  3. Is it reversible by simply changing the value back?
  4. Am I correct in thinking that the options have to be divisible by 7?
  5. Is there a lower limit?

Thanks in advance.

KAS

Why do you want to reduce the clock speed? Tell us what the real situation is and someone may have a better solution.

I don't know, but you might have to modify the bootloader (if you are using it) or else the serial timing will be off and it won't communicate.

I think F-CPU tells the compiler what the CPU clock is, so that the serial baud rate and other timing is correct (in the machine/binary code). I don't think it actually changes the physical clock speed.

1 Like

Maybe it's an XY-problem?

@DVDDoug Thanks Doug.
I have tried changing it to 42000000L and it certainly continues to communicate without difficulty (MIDI over USB goes into the target application, stuff also comes in via the pins). However, my code runs as usual but there's no evidence of a slower CPU.

I have also seen reference to the fact that F_CPU doesn't actually change the CPU frequency.

However, I'm not sure that the CPU was any slower. In any case it didn't solve my problem: see my response to Faraday for a bit of background.

@MicroBahner and @sonofcy
The issue is related EMI and Crosstalk across several ribbon cables, half of which are inputs to Due pins, and the other half come out of the target application as MIDI over USB to a decoder. When the Decoder is connected and alive, the Due (whose only possible contact with the decoder is electromagnetic.

When I had an identical issue within a previous project, that problem was cured instantly and painlessy by slowing down the CPU. However, that was with a Teensy 4.2, and if you know anything at all about the Teensy you'll know that in the Teensy Boards/Hardware menus in the Arduino IDE it is a doddle to change CPU frequency because the valid options are there, in the Boards sub-menu to select. If only I could do this project on a Teensy, but I cannot, because the Due has those wonderful 71 pins!!! No need for shift registers!

Thanks chaps (I assume you are chaps) - any new ideas will always be given due attention and appreciation.

KAS

Hi @kaspencer

@ard_newbie has previously described how to change the main clock frequency here:

In order to change the Due's main clock from 84MHz to say 42MHz, it's necessary to modifiy Phase Locked Loop A's multipler value. This is achieved by setting the MULA bitfield in the PMC->CGKR_PLLAR register with the multiplier set to a value of MULA + 1. (See Power Management Controller (PMC) section 28 of the SAM3X/SAM3A datasheet).

By default the board's 12MHz external crystal is divided by 2 then multiplied up by 14 to achieve 84MHz. In this case the MULA bitfield is set to 13, (since the muliplier value is MULA + 1). This occurs during start-up with the board's "variant.cpp" file calling the SystemInit() function. The SystemInit() function is located in the "system_sam3x.c" file, located (on my machine at least) in the following directory:

C:\Users\Computer\AppData\Local\Arduino15\packages\arduino\hardware\sam\1.6.12\system\CMSIS\Device\ATMEL\sam3xa\source

Therefore to generate a 42MHz main clock the MULA bifield needs to be be set to 6, since 12MHz/2 * (6 + 1) = 42MHz.

Here's some code that demonstrates setting the clock to 42MHz together with blink and analogWrite() test code to ensure that the microcontroller is working at half speed:

// Change Arduino Due's main clock from 84MHz to 42MHz
void setup() 
{
  //SerialUSB.begin(115200);                            // Initialise the native SerialUSB port
  //while (!SerialUSB);                                 // Wait for the console to open
  
  PMC->CKGR_PLLAR = CKGR_PLLAR_ONE |                  // Set CKGR_PLLAR write bit
				    CKGR_PLLAR_MULA(0x6UL) |          // Set MULA to multiply 12MHz/2 = 6MHz clock by 7 = 42MHz 
			        CKGR_PLLAR_PLLACOUNT(0x3fUL) |    // Set the PLLA time to LOCK to maximum
	   		        CKGR_PLLAR_DIVA(0x1UL);           // Bypass the divider
  while (!(PMC->PMC_SR & PMC_SR_LOCKA));              // Wait for PLLA to lock
  SystemCoreClockUpdate();                            // Update the system core clocks   
  
  pinMode(LED_BUILTIN, OUTPUT);                       // Set the built-in LED pin to an OUTPUT
  analogWrite(7, 128);                                // Output analog write - should output PWM at 500Hz rather than the default 1kHz
  //SerialUSB.println(F("Serial port active"));         // Output text to the console
}

void loop() 
{
  digitalWrite(LED_BUILTIN, HIGH);                    // Set the built-in LED output to HIGH
  delay(1000);                                        // Wait for 2 seconds (1000ms * 2)
  digitalWrite(LED_BUILTIN, LOW);                     // Clear the built-in LED output to LOW
  delay(1000);                                        // Wait for 2 seconds (1000ms * 2)
}

@MartinL Thanks indeed for those ideas and for the suggestion.
I shall see whether I can reduce the CPU speed using those ideas - ideally to 21MHz or below.

I have tried the library DueOverclock. It is on Git Hub and is by AlexandreRouma, and is stated to handle underclocking too).

However, while it seems as though it should be very easy to use, (but primarily intended for over- rather than under- clocking), I could not quite see my way to understanding whether what I had intended to happen (reduce CPU speed to 41MHz or even lower, at 21MHz) was really happening. I suspected that other clocks (serial etc) where not functioning properly. Also I simply could not be sure that my adjustments to F_CPU in boards.txt were correct, or even recognised.

(You may wish to know the objective: I am trying to reduce crosstalk and EMI in some 120 MIDI control signal wires (60 are input, 60 are output). I have previously resolved this issue in a Teensy 4.1, but that had CPU clock control on it's IDE Baord Menu! I have an alternative strategy to try if every attempt to do it by cutting the clock speed.)

Thanks again: should I get anywhere, I''l let you know!

Best wishes

Kenneth Spencer

Did you consider the problem may be caused by not having a ground wire between each signal wire in the ribbon cable?

@Paul_KD7HB Thank you Paul ...
... indeed I am quite sure that it is caused by EMI/crosstalk, although I'm not quite certain as to where it is arising.
Previously I was able to persuade the microcontroller (a diiferent one, for a different purpose) to behave, in the face of a tendancy to crosstalk simply by cutting the CPU speed, as high CPU speed is not required in that, nor this, application.
If I have to, I will resort to a re-design of the wiring, but I'd prefer to go for a rapid and easily implemented solution.
Thanks, however for the message - I get it!

kaspencer

@MartinL One little additional comment/question to add to my reply:

I note the expected reduction in the clock speed in the delay timer in your demo code.
If I change the F_CPU value in boards.txt to match the factor of the decreased CPU clock speed, am I right in supposing that will correct all the clock based functions in the board?

In hope!

Kenneth Spencer

@MartinL

Am I right in supposing that your solution is not permanent and that the Due will return to full speed if code is loaded and booted but with your core clock update code?
Or, do I need to create some code without the clock by 2 and run that so as to return the clock to 84MHz if necessary?

Thanks for your help: I have been programming the Due since about 2017, but never had to do this kind of thiong with it! (The only time I did it was with the Teensy and that has CPU Clock control built into its Board Options drop down list!).

Thaks again, in anticipation!

Kenneth Spencer

Hi @kaspencer

I think I've found the solution. It looks like the missing piece of the jig-saw was the configuration of the Systick timer used to generate millis() and micros() timing.

In order to run the Due at 42MHz, as you previously mentioned, requires the F_CPU value in the board's "boards.txt" file to be set to 42000000. However, for the F_CPU value to take effect, I found that it was necessary to first close and restart the Arduino IDE:

arduino_due_x.build.f_cpu=42000000L

Additionally, code is the same as above but with the inclusion of the Systick configuration function, taken from the Due's "variant.cpp" file:

SysTick_Config(SystemCoreClock / 1000);             // Configure the SysTick timer

My test code (using the board's native USB) is as follows:

// Change Arduino Due's main clock from 84MHz to 42MHz
void setup() 
{
  SerialUSB.begin(115200);                            // Initialise the native SerialUSB port
  while (!SerialUSB);                                 // Wait for the console to open
  
  PMC->CKGR_PLLAR = CKGR_PLLAR_ONE |                  // Set CKGR_PLLAR write bit
							      CKGR_PLLAR_MULA(0x6UL) |          // Set MULA to multiply 12MHz/2 = 6MHz clock by 7 = 42MHz 
							      CKGR_PLLAR_PLLACOUNT(0x3fUL) |    // Set the PLLA time to LOCK to maximum
							      CKGR_PLLAR_DIVA(0x1UL);           // Bypass the divider
  while (!(PMC->PMC_SR & PMC_SR_LOCKA));              // Wait for PLLA to lock
  SystemCoreClockUpdate();                            // Update the system core clocks 
  SysTick_Config(SystemCoreClock / 1000);             // Configure the SysTick timer
  
  pinMode(LED_BUILTIN, OUTPUT);                       // Set the built-in LED pin to an OUTPUT
  analogWrite(7, 128);                                // Output analog write - should output at PWM 500Hz rather than 1kHz
  SerialUSB.println(F("Serial port active"));         // Output text to the console
  SerialUSB.println(clockCyclesPerMicrosecond());     // Output the number of clock cycles per microsecond (42)
  SerialUSB.println(SystemCoreClock);                 // Output the system core clock speed (42000000)
}

void loop() 
{
  digitalWrite(LED_BUILTIN, HIGH);                    // Set the built-in LED output to HIGH
  delay(1000);                                        // Wait for 1 second (1000ms)
  SerialUSB.println(millis());                        // Display the current millis value
  digitalWrite(LED_BUILTIN, LOW);                     // Clear the built-in LED output to LOW
  delay(1000);                                        // Wait for 1 second (1000ms)
  SerialUSB.println(millis());                        // Display the current millis value
}

Furthermore, I can confirm that it's possible to revert back to normal speed simply by reverting the "boards.txt" file back to its original state with F_CPU set to 84000000, (in addition to removing the PLLA code from the sketch).

@kaspencer Just thought I'd mention that it's also possible to check on the F_CPU value with:

SerialUSB.println(F_CPU);     // Output the value of F_CPU

@MartinL Thanks for that - could be very useful!
By the time you will have read this, I will have posted a question about using links to build.txt files - maybe you tried that....