Dynamic clock change issues on T3.6

mborgerson

Well-known member
I have been working on a data logging program that operates a Teensy 3.6 at two different clock rates:

48MHz when connected to USB: The USB Serial port is used for the PC host interface and allows me to upload collected data at about 1MByte/second, which is sufficient for test files of a few MBytes. At this clock speed the logging program uses about 16mA when logging data and about 14mA during other operations.

8MHz when disconnected from USB. When USB is disconnected, the host user interface is directed to UART0, which runs at 57600 Baud. When logging 4 channels at 1.0KHz, the program draws about 5mA from the power supply.

The program is compiled for a system running at 48MHz. It can be switched by command, or by monitoring the USB 5V power (through a voltage divider) to 8MHz. The switch requires changing some system clock registers and adjusting the UART0 baud rate registers. Fortunately, SDFat 2.0b does not seem to object to the clock change---but it does take longer to write 32KB buffers to the SD card.

The major issue that crops up after the clock change is that interval timers and other timing functions no longer produce the proper delays as their code is compiled to use defined constants like F_BUS and F_CPU. As a result, an interval timer set up for 1.0KHz collection at 48MHz, will interrupt at 166.666Hz when the clock and bus run at 8MHz.

I see two possible ways to overcome this problem with intervalTimers:

1. Move the intervaltimer library code to my local libraries folder so that my modified code will be loaded instead of the code distributed with TeensyDuino. The modified code will add a member function like intervalTimer::setClock(uint32_t clockspeed). That function will set an internal clock speed variable that will be used in the calculation of the cycles variable that goes to the hardware timer. The variable will be initialized to F_Bus when the interval timer is instantiated, so no changes are required when the T3.6 stays at the speed specified by F_CPU or F_BUS.

2. Change all my calls to interval timers to calls to a shell function that adjusts the number of microseconds passed to the intervalTimer begin() function. This method means I don't have to mess with the Teensyduino-defined interval timer code. However, it also means that any library functions that depend on interval timers are going to run 6 times more slowly than desired.

The ideal solution, from the point of view of a designer of low-power loggers, would be to have the Teensy libraries become less dependent on the compile-time definitions of F_CPU and F_BUS. I realize that this would require either setClock() member functions for many objects or the rewriting of many functions to use a system variable that would be initialized to the value of F_BUS or F_CPU. Setting a new clock speed in your program without reinitializing clock speed dependent objects could lead to unexpected behavior (to say the least!). Given the possible problems and the probably low number of users really needing this change, I don't expect it to become a high-priority issue for Paul.

I will put together and post some code illustrating my solutions to the dynamic clocking issues sometime next week. I will need my oscilloscope to verify timing-related issues and I'm away from home this week.

You may ask, "Why are you so worried about a 10mA reduction in operating current?" If you are building a data logger to collect oceanographic data for one year while on a mooring in the equatorial Pacific, that reduction in current could increase your logger duration by many months, or you could reduce the number of lithium primary cells--and either reduce the pressure case volume or switch batteries to the analog supply that powers the sensors. (The cost savings resulting from using fewer batteries is not a major issue. The cruises that deploy and retrieve the loggers cost hundreds of thousands of dollars. Thankfully, those costs are shared among many projects and the cost to deploy a logger may only be a few thousand dollars.)
 
I'd do number 1, thats what I did. By coping the Interval Timer code and making a new library named IntervalTimer_LP you can make all compile time constants reflect whatever cpu speed you want to use. Since you are only going from F_CPU, F_BUS and F_MEM to a new speed (8MHz) you can replace them in the lp library and still retain the compile time optimizations that the original IntervalTimer enjoys. Also you will have override the interrupt functions for the PIT.
 
I decided to define a new object: IntervalTimer_VC, which allows the user to specify the new bus clock after changing the clock speed. I did find that I couldn't simply add a setClock() function to the existing IntervalTimer by copying the interval timer folder from the Teensy3 cores folder to my libraries folder. Apparently, the Arduino system doesn't allow you to override core functionality in the same way that you can with objects from the libraries folder. I used code from the dynamic clock switching post https://forum.pjrc.com/members/55229-rjeremy to change the clock from the 48MHz at which the code is compiled to the 8MHz for low-power operation. Here is a simple example that shows that the new interval timer and the existing delay() function work properly at the new clock speed. Switching back to 48MHz and initializing the USB Serial port is accomplished through a sofware reset. I mucked about with accomplishing that by adjusting the clock registers, but I had lots of issues getting the USB to disconnect and reconnect.

Code:
/***************************************************
   Program to test dynamic clock changes on Teensy 3.6
   Switch between initial 48MHz clock and 8Mhz using input from Serial or Serial1
   'i' and 'd' commands blink the LED at 1Hz to verify proper adjustment of
    delay() and intervaltimer setup.
*/
#include "IntervalTimer_VC.h"
const int ledpin  = 13;

#define RESTART_ADDR       0xE000ED0C
#define READ_RESTART()     (*(volatile uint32_t *)RESTART_ADDR)
#define WRITE_RESTART(val) ((*(volatile uint32_t *)RESTART_ADDR) = (val))

#define LEDON digitalWriteFast(ledpin, HIGH);
#define LEDOFF digitalWriteFast(ledpin, LOW);
uint32_t newclock = F_CPU;
uint16_t slowclockMHz = 8;
uint32_t BaudRate = 9600;
bool useWFI = false;


void setup() {
  // put your setup code here, to run once:
  // DLHost uses 57,600 baud rate
  delay(500);
  pinMode(ledpin, OUTPUT);
  Serial.print("USB Port Open in setup()\n");
  Serial.printf("\n\nStarting with F_CPU  %lu   F_BUS  %lu\n", F_CPU, F_BUS);
  Serial1.begin(BaudRate);
  Serial1.printf("\n\nStarting with F_CPU  %lu   F_BUS  %lu\n", F_CPU, F_BUS);
  ShowRegisters();
  delay(1000);
}
void loop(){
  char ch = 0;

  if (Serial1.available()) {
    ch = Serial1.read();
  }
  if (Serial.available()) {
    ch = Serial.read();
  }
  switch (ch) {
    case 'f' :
      FastClock();
      break;
    case 's' :
      SlowClock();
      break;
    case 'i' :
      iBlink();
      break;
    case 'd' :
      dBlink();
      break;
  }
  ch = 0;
  if (useWFI) asm("WFI\n");
}

// Return to 48MHz  clock.  The easiest way to do this and guarantee USB works
// is to do a sofware restart of the system
void FastClock(void) {
  if (newclock != 48000000) {
    WRITE_RESTART(0x5FA0004);
  }
}
// Set the clock to 8MHz.  Adjust Uart0 baud rate registers and
// system tick counter.  We set both system clock and bus clock to 8MHz
void SlowClock(void) {
  if(newclock == 48000000){
    Serial.printf("Changing clock to 8MHz\n");
  }
  uint32_t sclock;
  SIM_CLKDIV1 = SIM_CLKDIV1_OUTDIVS(1, 1, 1, 3);
  SIM_CLKDIV2 = 0000;
  newclock = 8000000;
  MCG_C1 = 0x00A0;
  MCG_C2 = 0x0026;
  MCG_C3 = 0x0090;
  MCG_C4 = 0x0015;
  MCG_C5 = 0x0000;
  MCG_C6 = 0x0000;
  SIM_SOPT2 = 0x0A0010C0;
  SIM_SCGC4 = 0xF0100430;

  SYST_RVR = newclock / 1000 - 1; // for system tick

  //Adjust UART0 baud rates
  sclock = (newclock * 2 + (BaudRate >> 1)) / BaudRate;
  UART0_BDH = (sclock >> 13) & 0x1F;
  UART0_BDL = (sclock >> 5) & 0xFF;
  UART0_C4 = sclock & 0x1F;

  UART0_C1 = UART_C1_ILT;
  UART0_TWFIFO = 2;
  UART0_RWFIFO = 4;
  UART0_PFIFO = UART_PFIFO_TXFE | UART_PFIFO_RXFE;
  Serial1.println("System clock changed to 8MHz");
}

volatile uint16_t iblinkcount = 0;
void iBlinkChore(void) {
  if (iblinkcount & 1) LEDON else LEDOFF
  iblinkcount++;
}

// blink at 5 times  1Hz using an interval timer
void iBlink(void) {
  Serial1.println("Blink using IntervalTimer_LC");
  IntervalTimer_VC itimer;
  itimer.setClock(newclock);
  iblinkcount = 0;
  itimer.begin(iBlinkChore, 500000);
  if (useWFI) asm("WFI\n");
  while (iblinkcount < 11);
  itimer.end();  // release the PIT
}


// Blink LED using delay() to make sure it has adjusted to new clock rate
void dBlink(void) {
    Serial1.println("Blink using delay()");
    uint16_t i;
    for (i = 0; i < 5; i++) {
      LEDON
      delay(500);
      LEDOFF
      delay(500);
    }
  }

  void ShowRegisters(void) {

    Serial1.printf("\n\nnew F_CPU  %lu \n", newclock);
    Serial1.printf("SIM_CLKDIV1 = %04X   SIM_CLKDIV2 = %04X \n", SIM_CLKDIV1, SIM_CLKDIV2);
    Serial1.printf("SMC_PMCTRL = %04X\n", SMC_PMCTRL);
    Serial1.printf("MCG_C1 = %04X\n",  MCG_C1);
    Serial1.printf("MCG_C2 = %04X\n",  MCG_C2);
    Serial1.printf("MCG_C3 = %04X\n",  MCG_C3);
    Serial1.printf("MCG_C4 = %04X\n",  MCG_C4);
    Serial1.printf("MCG_C5 = %04X\n",  MCG_C5);
    Serial1.printf("MCG_C6 = %04X\n",  MCG_C6);
    Serial1.printf("SIM_SOPT2 = %04X\n", SIM_SOPT2);
    Serial1.printf("SIM_SCGC4 = %04X\n", SIM_SCGC4);
    Serial1.printf("SYST_RVR = %lu\n", SYST_RVR);
  }


Here is the code for the new IntervalTimer_VC:

Code:
// IntervalTimer_VC.h
/* Teensyduino Core Library
 * http://www.pjrc.com/teensy/
 * Copyright (c) 2017 PJRC.COM, LLC.
 *
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to
 * the following conditions:
 *
 * 1. The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * 2. If the Software is incorporated into a build system that allows
 * selection among a list of target devices, then similar target
 * devices manufactured by PJRC.COM must be included in the list of
 * target devices and selectable in the same manner.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

#ifndef __IntervalTimer_VC_VC_H__
#define __IntervalTimer_VC_VC_H__

#include "kinetis.h"

#ifdef __cplusplus
extern "C" {
#endif

class IntervalTimer_VC {
private:
	uint32_t f_bus = F_BUS;
	uint32_t max_period = UINT32_MAX / (F_BUS / 1000000.0);
public:
	IntervalTimer_VC() {
		channel = NULL;
		nvic_priority = 128;
	}
	~IntervalTimer_VC() {
		end();
	}
	bool begin(void (*funct)(), unsigned int microseconds) {
		if (microseconds == 0 || microseconds > max_period) return false;
		uint32_t cycles = (f_bus / 1000000) * microseconds - 1;
		if (cycles < 36) return false;
		return beginCycles(funct, cycles);
	}
	bool begin(void (*funct)(), int microseconds) {
		if (microseconds < 0) return false;
		return begin(funct, (unsigned int)microseconds);
	}
	bool begin(void (*funct)(), unsigned long microseconds) {
		return begin(funct, (unsigned int)microseconds);
	}
	bool begin(void (*funct)(), long microseconds) {
		return begin(funct, (int)microseconds);
	}
	bool begin(void (*funct)(), float microseconds) {
		if (microseconds <= 0 || microseconds > max_period) return false;
		uint32_t cycles = (float)(f_bus / 1000000) * microseconds - 0.5;
		if (cycles < 36) return false;
		return beginCycles(funct, cycles);
	}
	bool begin(void (*funct)(), double microseconds) {
		return begin(funct, (float)microseconds);
	}
	void update(unsigned int microseconds) {
		if (microseconds == 0 || microseconds > max_period) return;
		uint32_t cycles = (f_bus / 1000000) * microseconds - 1;
		if (cycles < 36) return;
		if (channel) channel->LDVAL = cycles;
	}
	void update(int microseconds) {
		if (microseconds < 0) return;
		return update((unsigned int)microseconds);
	}
	void update(unsigned long microseconds) {
		return update((unsigned int)microseconds);
	}
	void update(long microseconds) {
		return update((int)microseconds);
	}
	void update(float microseconds) {
		if (microseconds <= 0 || microseconds > max_period) return;
		uint32_t cycles = (float)(f_bus / 1000000) * microseconds - 0.5;
		if (cycles < 36) return;
		if (channel) channel->LDVAL = cycles;
	}
	void update(double microseconds) {
		return update((float)microseconds);
	}
	void end();
	void priority(uint8_t n) {
		nvic_priority = n;
		#if defined(KINETISK)
		if (channel) {
			int index = channel - KINETISK_PIT_CHANNELS;
			NVIC_SET_PRIORITY(IRQ_PIT_CH0 + index, nvic_priority);
		}
		#elif defined(KINETISL)
		if (channel) {
			int index = channel - KINETISK_PIT_CHANNELS;
			nvic_priorites[index] = nvic_priority;
			if (nvic_priorites[0] <= nvic_priorites[1]) {
				NVIC_SET_PRIORITY(IRQ_PIT, nvic_priorites[0]);
			} else {
				NVIC_SET_PRIORITY(IRQ_PIT, nvic_priorites[1]);
			}
		}
		#endif
	}
	operator IRQ_NUMBER_t() {
		if (channel) {
			#if defined(KINETISK)
			int index = channel - KINETISK_PIT_CHANNELS;
			return (IRQ_NUMBER_t)(IRQ_PIT_CH0 + index);
			#elif defined(KINETISL)
			return IRQ_PIT;
			#endif
		}
		return (IRQ_NUMBER_t)NVIC_NUM_INTERRUPTS;
	}
	void setClock(uint32_t newclock){
		f_bus = newclock;
		max_period = UINT32_MAX / (f_bus / 1000000.0); 
	}
private:
	KINETISK_PIT_CHANNEL_t *channel;
	uint8_t nvic_priority;
	#if defined(KINETISL)
	static uint8_t nvic_priorites[2];
	#endif
	bool beginCycles(void (*funct)(), uint32_t cycles);

};


#ifdef __cplusplus
}
#endif

#endif



// IntervalTimer_VC.cpp
/* Teensyduino Core Library
 * http://www.pjrc.com/teensy/
 * Copyright (c) 2017 PJRC.COM, LLC.
 *
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to
 * the following conditions:
 *
 * 1. The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * 2. If the Software is incorporated into a build system that allows
 * selection among a list of target devices, then similar target
 * devices manufactured by PJRC.COM must be included in the list of
 * target devices and selectable in the same manner.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */
 
 /****************************************************************************
 *  This version of the IntervalTimer has a new member function, setClock().
 *  with this function, the timer is no longer dependent on the F_BUS
 *  compile-time constant--although it is initialized with that value.
 *  You must call setClock() after you change the bus clock as part of a dynamic
 *  clock speed change.
 *****************************************************************************/

#include "IntervalTimer_VC.h"

static void dummy_funct(void);

#if defined(KINETISK)
#define NUM_CHANNELS 4
static void (*funct_table[4])(void) = {dummy_funct, dummy_funct, dummy_funct, dummy_funct};

#elif defined(KINETISL)
#define NUM_CHANNELS 2
static void (*funct_table[2])(void) = {dummy_funct, dummy_funct};
uint8_t IntervalTimer_VC::nvic_priorites[2] = {255, 255};
#endif


bool IntervalTimer_VC::beginCycles(void (*funct)(), uint32_t cycles)
{
	if (channel) {
		channel->TCTRL = 0;
		channel->TFLG = 1;
	} else {
		SIM_SCGC6 |= SIM_SCGC6_PIT;
		__asm__ volatile("nop"); // solves timing problem on Teensy 3.5
		PIT_MCR = 1;
		channel = KINETISK_PIT_CHANNELS;
		while (1) {
			if (channel->TCTRL == 0) break;
			if (++channel >= KINETISK_PIT_CHANNELS + NUM_CHANNELS) {
				channel = NULL;
				return false;
			}
		}
	}
	int index = channel - KINETISK_PIT_CHANNELS;
	funct_table[index] = funct;
	channel->LDVAL = cycles;
	channel->TCTRL = 3;
#if defined(KINETISK)
	NVIC_SET_PRIORITY(IRQ_PIT_CH0 + index, nvic_priority);
	NVIC_ENABLE_IRQ(IRQ_PIT_CH0 + index);
#elif defined(KINETISL)
	nvic_priorites[index] = nvic_priority;
	if (nvic_priorites[0] <= nvic_priorites[1]) {
		NVIC_SET_PRIORITY(IRQ_PIT, nvic_priorites[0]);
	} else {
		NVIC_SET_PRIORITY(IRQ_PIT, nvic_priorites[1]);
	}
	NVIC_ENABLE_IRQ(IRQ_PIT);
#endif
	return true;
}


void IntervalTimer_VC::end() {
	if (channel) {
		int index = channel - KINETISK_PIT_CHANNELS;
#if defined(KINETISK)
		NVIC_DISABLE_IRQ(IRQ_PIT_CH0 + index);
#elif defined(KINETISL)
		// TODO: disable IRQ_PIT, but only if both instances ended
#endif
		funct_table[index] = dummy_funct;
		channel->TCTRL = 0;
#if defined(KINETISL)
		nvic_priorites[index] = 255;
		if (nvic_priorites[0] <= nvic_priorites[1]) {
			NVIC_SET_PRIORITY(IRQ_PIT, nvic_priorites[0]);
		} else {
			NVIC_SET_PRIORITY(IRQ_PIT, nvic_priorites[1]);
		}
#endif
		channel = 0;
	}
}


#if defined(KINETISK)
void pit0_isr()
{
	PIT_TFLG0 = 1;
	funct_table[0]();
}

void pit1_isr() {
	PIT_TFLG1 = 1;
	funct_table[1]();
}

void pit2_isr() {
	PIT_TFLG2 = 1;
	funct_table[2]();
}

void pit3_isr() {
	PIT_TFLG3 = 1;
	funct_table[3]();
}

#elif defined(KINETISL)
void pit_isr() {
	if (PIT_TFLG0) {
		PIT_TFLG0 = 1;
		funct_table[0]();
	}
	if (PIT_TFLG1) {
		PIT_TFLG1 = 1;
		funct_table[1]();
	}
}
#endif

static void dummy_funct(void)
{
}


For testing, I used a Sparkfun USB-to-Serial adapter to connect to UART0 (Serial1). I used homegrown host programs, but I think any terminal program should work. My setup ended up with USB Serial on COM6 and the Serial1 I/O on COM7. Your ports may be different.
 
Last edited:
After some more testing, I added code to disable the USB port when the Teensy switches to 8MHz. Without this code, plugging in a USB cable while at the slow speed with the USB pullup at the previous setting would mess up the PC because the Teeny would not enumerate. With the new code, when you switch to 8MHz, the pull-ups are turned off and the USB driver is disabled. The PC thinks you have pulled the cable and makes the disconnect sound. If you plug in again at 8MHz, the PC doesn't know the cable is connected. When you switch back to 48MHz, the usb driver restarts and the PC notices the connection.

I did notice that the Teensy must initialize the USB at startup if the clock is fast enough. That happens well before you do a Serial.begin(). I suppose that is necessary because there might be other USB connections---particularly that to the Teensyduino loader.

Here is the code I use to shut down the USB port. This is done before switching to 8MHz.

USB0_CTL &= 0xFE; // clear the enable bit to disable usb
// enable DM and DP pulldowns and disable DP pullup
// to indicate USB disconnect;
USB0_OTGCTL = 0x34; // turns off DP pullup and turns on pulldowns.



Here is the updated code for the test application
Code:
***************************************************
   Program to test dynamic clock changes on Teensy 3.6
   switch between 48MHz and 8 MHz
   2/28/20  Added code to shut down USB on transition to 8MHz
*/
#include "IntervalTimer_VC.h"
const int ledpin  = 13;

#define RESTART_ADDR       0xE000ED0C
#define READ_RESTART()     (*(volatile uint32_t *)RESTART_ADDR)
#define WRITE_RESTART(val) ((*(volatile uint32_t *)RESTART_ADDR) = (val))

#define LEDON digitalWriteFast(ledpin, HIGH);
#define LEDOFF digitalWriteFast(ledpin, LOW);
uint32_t newclock = F_CPU;
uint16_t slowclockMHz = 8;
uint32_t BaudRate = 57600;
bool useWFI = false;


void setup() {
  // put your setup code here, to run once:
  // DLHost uses 57,600 baud rate
  delay(1500);
  pinMode(ledpin, OUTPUT);
  Serial.begin(57600);
  delay(100);
  Serial.println("\n*****************************************************");
  Serial.print("USB Port Open in setup()\n");
  Serial.printf("\nStarting with F_CPU  %lu   F_BUS  %lu\n", F_CPU, F_BUS);
  Serial1.begin(BaudRate);
  
  Serial1.printf("\nStarting with F_CPU  %lu   F_BUS  %lu\n", F_CPU, F_BUS);
  Serial1.printf("USB0_OTGCTL = %02X\n", USB0_OTGCTL);
  Serial1.printf("USB0_CTL = %02X\n", USB0_CTL);
  Serial1.printf("USB0_OTGCTL = %02X\n", USB0_OTGCTL);

  ShowRegisters();
  delay(1000);
}
void loop(){
  char ch = 0;
  uint8_t usbctl;
  if (Serial1.available()) {
    ch = Serial1.read();
  }
  if (Serial.available()) {
    ch = Serial.read();
  }
  switch (ch) {
    case 'f' :
      FastClock();  // forces a restart
      break;
    case 's' :
      Serial1.println("changing to 8MHz clock");
     // Serial.end();
  
      USB0_CTL  &= 0xFE;   // clear the enable bit
      // enable pull DM and DP pulldowns and disable DP pullup
      // to indicate USB disconnect;        
      USB0_OTGCTL = 0x34; 

      delay(100);
      Serial1.printf("USB0_CTL = %02X\n", USB0_CTL);
      Serial1.printf("USB0_OTGCTL = %02X\n", USB0_OTGCTL);
      delay(100);
      SlowClock();     
      break;
    case 'i' :
      iBlink();
      break;
    case 'd' :
      dBlink();
      break;
  }
  ch = 0;
  if (useWFI) asm("WFI\n");
}

// Return to 48MHz  clock.  The easiest way to do this and guarantee USB works
// is to do a sofware restart of the system
void FastClock(void) {
  if (newclock != 48000000) {
    WRITE_RESTART(0x5FA0004);
  }
}

// Set the clock to 8MHz.  Adjust Uart0 baud rate registers and
// system tick counter.  We set both system clock and bus clock to 8MHz
//#define F_CPU 8000000
void SlowClock(void) {
  if(newclock == 48000000){
    Serial.printf("Changing clock to 8MHz\n");
    delay(100);
  }
  uint32_t sclock;
  SIM_CLKDIV1 = SIM_CLKDIV1_OUTDIVS(1, 1, 1, 3);
  SIM_CLKDIV2 = 0000;
  newclock = 8000000;
  MCG_C1 = 0x00A0;
  MCG_C2 = 0x0026;
  MCG_C3 = 0x0090;
  MCG_C4 = 0x0015;
  MCG_C5 = 0x0000;
  MCG_C6 = 0x0000;
  SIM_SOPT2 = 0x0A0010C0;
  SIM_SCGC4 = 0xF0100430;

  SYST_RVR = newclock / 1000 - 1; // for system tick

  //Adjust UART0 baud rates
  sclock = (newclock * 2 + (BaudRate >> 1)) / BaudRate;
  UART0_BDH = (sclock >> 13) & 0x1F;
  UART0_BDL = (sclock >> 5) & 0xFF;
  UART0_C4 = sclock & 0x1F;

  UART0_C1 = UART_C1_ILT;
  UART0_TWFIFO = 2;
  UART0_RWFIFO = 4;
  UART0_PFIFO = UART_PFIFO_TXFE | UART_PFIFO_RXFE;
  Serial1.println("System clock changed to 8MHz");
}


volatile uint16_t iblinkcount = 0;
void iBlinkChore(void) {
  if (iblinkcount & 1) LEDON else LEDOFF
  iblinkcount++;
}

// blink at 5 times  1Hz using an interval timer
void iBlink(void) {
  Serial1.println("Blink using IntervalTimer_VC");
  IntervalTimer_VC itimer;
  itimer.setClock(newclock);
  iblinkcount = 0;
  itimer.begin(iBlinkChore, 500000);
  if (useWFI) asm("WFI\n");
  while (iblinkcount < 11);
  itimer.end();  // release the PIT
}


// Blink LED using delay() to make sure it has adjusted to new clock rate
void dBlink(void) {
    Serial1.println("Blink using delay()");
    uint16_t i;
    for (i = 0; i < 5; i++) {
      LEDON
      delay(500);
      LEDOFF
      delay(500);
    }
  }

  void ShowRegisters(void) {

    Serial1.printf("\n\nnew F_CPU  %lu \n", newclock);
    Serial1.printf("SIM_CLKDIV1 = %04X   SIM_CLKDIV2 = %04X \n", 
                SIM_CLKDIV1, SIM_CLKDIV2);
    Serial1.printf("SMC_PMCTRL = %04X\n", SMC_PMCTRL);
    Serial1.printf("MCG_C1 = %04X\n",  MCG_C1);
    Serial1.printf("MCG_C2 = %04X\n",  MCG_C2);
    Serial1.printf("MCG_C3 = %04X\n",  MCG_C3);
    Serial1.printf("MCG_C4 = %04X\n",  MCG_C4);
    Serial1.printf("MCG_C5 = %04X\n",  MCG_C5);
    Serial1.printf("MCG_C6 = %04X\n",  MCG_C6);
    Serial1.printf("SIM_SOPT2 = %04X\n", SIM_SOPT2);
    Serial1.printf("SIM_SCGC4 = %04X\n", SIM_SCGC4);
    Serial1.printf("SYST_RVR = %lu\n", SYST_RVR);
  }
 
Back
Top