Code sequence required or recommended

teensy3056

Active member
This is all done on a Teensy 3.6.

I replicated a program in Kinetis Peripheral Module Quick Reference, dated May, 2014, from NXP / freescale, specifically "4.1.3.1.1 Code example and explanation" on pdf. 44.

I had to change many of the constant names since they were not exactly replicated in kinetis.h. This was not difficult.

The code given belongs in setup(), which I did. I added a loop() sequence to flash the LED at about 1 Hz just to see that the code got to that point.

What struck me was that this code ran through setup() almost instantly as compared to the code I have been writing based on reading the K66 Sub-Family Reference Manual. I have run across a few places where there is advice to do this, assure it is done, before doing that.

What I have not found is a statement of general programming philosophy, especially the sequencing of statements.

Is there such a philosophy either for the Kinetis processors or for microcontrollers in general?

Thanks.
 
Do you have a link to the document in question?
And you've taken note of the forum rule?
 
The documents I referred to are at Kinetis Peripheral Module Quick Reference and K66 Sub-Family Reference Manual, respectively.

The code I copied from the first document is,

Code:
/*
    Name:       Clocks_05.ino
*/

//*****************************************************************************
#include <kinetis.h>

//*****************************************************************************
volatile bool LED_on = true;
volatile uint32_t loopCounter = 0;

//*****************************************************************************
void setup() {

    Serial.begin(115200);

	Serial.printf("\n1. Serial print ready.\n");
	Serial.printf("2. Serial print ready.\n");

	// LED pin
	pinMode(13, OUTPUT);

	//*********************************
	// If the internal load capacitors are being used, they should be selected
	// before enabling the oscillator. Application specific. 16 pF and 8 pF selected
	// in this example. pdf. 667
	//OSC_CR = OSC_CR_SC16P_MASK | OSC_CR_SC8P_MASK;
	OSC0_CR |= OSC_SC16P | OSC_SC8P;

	// Enabling the oscillator for 8 MHz crystal. pdf. 631 and pdf. 663 for range (16 MHz crystal)
	// RANGE=1, should be set to match the high frequency of the crystal being used
	// HGO=1, high gain is selected, provides better noise immunity but does draw
	// higher current
	// EREFS=1, enable the external oscillator
	// LP=0, low power mode not selected (not actually part of osc setup)
	// IRCS=0, slow internal ref clock selected (not actually part of osc setup)
	//MCG_C2 = MCG_C2_RANGE(1) | MCG_C2_HGO_MASK | MCG_C2_EREFS_MASK;
	MCG_C2 |= MCG_C2_RANGE0(1) | MCG_C2_HGO0 | MCG_C2_EREFS;

	// Select ext oscillator, reference divider and clear IREFS to start ext osc, pdf. 630
	// CLKS=2, select the external clock source
	// FRDIV=3, set the FLL ref divider to keep the ref clock in range
	// (even if FLL is not being used) 16 MHz / 256 = 62.5 kHz
	// IREFS=0, select the external clock
	// IRCLKEN=0, disable IRCLK (can enable it if desired)
	// IREFSTEN=0, disable IRC in stop mode (can keep it enabled in stop if desired)
	//MCG_C1 = MCG_C1_CLKS(2) | MCG_C1_FRDIV(3);
	MCG_C1 = MCG_C1_CLKS(2) | MCG_C1_FRDIV(3);

	// wait for oscillator to initialize, pdf. 637
	// while (!(MCG_S & MCG_S_OSCINIT_MASK)) {}
	while (!(MCG_S & MCG_S_OSCINIT0)) {}

	// wait for Reference clock to switch to external reference, pdf. 638
	//while (MCG_S & MCG_S_IREFST_MASK) {}
	while (MCG_S & MCG_S_IREFST) {}

	// Wait for MCGOUT to switch over to the external reference clock, pdf. 638
	//while (((MCG_S & MCG_S_CLKST_MASK) >> MCG_S_CLKST_SHIFT) != 0x2) {}
	while (((MCG_S & MCG_S_CLKST_MASK) >> 2) != 0x2) {}

	// Now configure the PLL and move to PBE mode, pdf. 634
	// set the PRDIV field to generate a 4 MHz reference clock (16 MHz / 4)
	// PRDIV=3 selects a divide by 4
	//MCG_C5 = MCG_C5_PRDIV(1);
	MCG_C5 = MCG_C5_PRDIV0(3);

	// LOLIE can be optionally set to enable the loss of lock interrupt, pdf. 635
	// the PLLS bit is set to enable the PLL
	// the clock monitor is enabled, CME=1 to cause a reset if crystal fails
	// set the VDIV field to 8, which is x24, giving 4 x 24 = 96 MHz
	//MCG_C6 = MCG_C6_CME_MASK | MCG_C6_PLLS_MASK;
	MCG_C6 = MCG_C6_PLLS | MCG_C6_CME0 | MCG_C6_VDIV0(8);

	// wait until the source of the PLLS clock has switched to the PLL, pdf. 637
	//while (!(MCG_S & MCG_S_PLLST_MASK)) {}
	while (!(MCG_S & MCG_S_PLLST)) {}

	// wait until the PLL has achieved lock, pdf. 637
	//while (!(MCG_S & MCG_S_LOCK_MASK)) {}
	while (!(MCG_S & MCG_S_LOCK0)) {}

	// set up the SIM clock dividers BEFORE switching to the PLL to ensure the
	// system clock speeds are in spec, pdf. 267
	// core = PLL (96 MHz), bus = PLL/2 (48 MHz), flexbus = PLL/2 (48 MHz), 
	//  flash = PLL/4 (24 MHz)
	//SIM_CLKDIV1 = SIM_CLKDIV1_OUTDIV1(0) | SIM_CLKDIV1_OUTDIV2(1)
	//	| SIM_CLKDIV1_OUTDIV3(1) | SIM_CLKDIV1_OUTDIV4(3);
	SIM_CLKDIV1 = SIM_CLKDIV1_OUTDIV1(0) | SIM_CLKDIV1_OUTDIV2(1)
		| SIM_CLKDIV1_OUTDIV3(15) | SIM_CLKDIV1_OUTDIV4(3);

	// Transition into PEE (PLL Engaged External, pdf. 643) by setting CLKS to 0, pdf. 630
	// previous MCG_C1 settings remain the same, just need to set CLKS to 0
	//MCG_C1 &= ~MCG_C1_CLKS_MASK;
	MCG_C1 &= ~MCG_C1_CLKS(2);

	// Wait for MCGOUT to switch over to the PLL, pdf. 637
	//while (((MCG_S & MCG_S_CLKST_MASK) >> MCG_S_CLKST_SHIFT) != 0x3) {}
	while (((MCG_S & MCG_S_CLKST_MASK) >> 2) != 0x3) {}

	// The USB clock divider in the System Clock Divider Register 2 (SIM_CLKDIV2)
	// should be configured to generate the 48 MHz USB clock before configuring
	// the USB module, pdf. 269
	SIM_CLKDIV2 |= SIM_CLKDIV2_USBDIV(1); // sets USB divider to /2 assuming reset
	// state of the SIM_CLKDIV2 register
	 
	//*********************************
	// Connect FlexBus clock to CLKOUT pin.
	// Set PTC3 to mode ALT5, pdf. 221.
	PORTC_PCR3 |= (0b101 << 8);

	// Clock gate control enabled for Port C, pdf. 261.
	//SIM_SCGC5 |= (0b1 << 11);
	SIM_SCGC5 |= SIM_SCGC5_PORTC;

	// Connects FlexBus CLKOUT to the CLKOUT pin,
	//  Teensy pin 9 when PTC3 ALT5 configuration is set, pdf. 240, 188.
	SIM_SOPT2 |= (0b000 < 5);

	//*********************************
	Serial.printf("End of setup().\n");
}

//*****************************************************************************
void loop() {

	if (++loopCounter > 1'000'000) {

		loopCounter = 0;
		digitalWrite(13, LED_on);

		LED_on = !LED_on;
	}

}

I don't have a specific example of my code where the setup() ran slowly since I have picked away at those instances by using print statements to see how far code had gotten and then trying something different. Frequently it would be the case where setup() was not not going to complete because my code was waiting for a confirming while loop which was never going to exit.
 
Code:
  //MCG_C2 = MCG_C2_RANGE(1) | MCG_C2_HGO_MASK | MCG_C2_EREFS_MASK;
  MCG_C2 |= MCG_C2_RANGE0(1) | MCG_C2_HGO0 | MCG_C2_EREFS;

One minor thing I notice is for MCG_C2 you use "|=" where the original code uses "=". MCG_C2 is not 0 on reset, so the difference might matter.
 
You know the Teensy runtime already sets up the clocking before setup() is called? You may be misconfiguring things
if using a code example that's designed to run from reset state?
 
You know the Teensy runtime already sets up the clocking before setup() is called? You may be misconfiguring things
if using a code example that's designed to run from reset state?

That, and after calling pinMode(13, OUTPUT); only a single changed bit write is needed to go HIGH or LOW on that pin.
 
Look at mk20dx128.c in the core library.

It does have comments which explain the startup sequence. Not sure if that could be considered a statement of philosophy?

This code runs before setup(), so normally you would not do similar initialization in setup(). For some of this, duplicating it in setup() is merely redundant. But some other part, like configuring clocks and PLL probably should not be attempted after the mk20dx128.c code has already initialized the hardware.
 
Last edited:
Paul,

I did not know that the code runs before setup(). Is that common for microcontrollers or just the way the Teensy 3.6 system was designed by you and your colleagues?

Where would I find the information on this? I had assumed that that was my job and the reason for the K66 Sub-Family Reference Manual being linked on the Teensy 3.6 page.

It was not my intention to reinvent the wheel. I just thought that it was necessary to tell the Kinetis exactly what I wanted it to do and that all its capability were just waiting to be called upon.
 
Perhaps 'Arduino' support defines the 'philosophy' at hand? See :: arduino.cc/reference

User code enters setup() with 'device' 'online' and ready to provide and respond to Arduino documented functionality.

User code in setup does one time init setup as needed for the task at hand and exits

Arduino philosophy then mandates calling loop() function every time it exits. On exit of loop(), before being called again, a call is made to yield() that can service certain 'background' tasks.

Looking at Arduino.cc and their reference materials online will help perhaps showing the other design details supporting that
 
Paul,

I did not know that the code runs before setup(). Is that common for microcontrollers or just the way the Teensy 3.6 system was designed by you and your colleagues?

Where would I find the information on this? I had assumed that that was my job and the reason for the K66 Sub-Family Reference Manual being linked on the Teensy 3.6 page.

It was not my intention to reinvent the wheel. I just thought that it was necessary to tell the Kinetis exactly what I wanted it to do and that all its capability were just waiting to be called upon.

Any program that runs on any computing platform, whether it be Windows or Linux or Teensy, goes through some initialization code before it executes the code written by the "user". In C and C++, user code begins executing in a function called main(). Prior to executing main(), the computing platform has to do basic things like initialize variables, set up heap and stack, etc. In a microcontroller platform like Teensy, or Arduino generally, where there is only one program (the firmware), there are more and lower-level things that have to be done before the user code is run, such as configuring clocks, setting up interrupt vectors, etc. Arduino does actually have a function called main(), but you never see it. It looks something like the code below, so when setup() executes, all of the basic configuration is complete, and all you have to do is take care of what you want your own program to do. As you can see, setup() is executed once, and loop() is executed over and over for the life of the program.

Code:
main() {
  setup();
  while (1)
    loop();
}
 
joepasquariello,

Thanks for that explanation.

I am used to writing main() myself on a Windows platform with Visual Studio.

On the Teensy, since the initiating code is not obvious, can I find a listing of it or a documentation of the outcome or configuration of the Teensy when the firmware is done running? Does Teensy have a main() in firmware or called upon the exit from firmware?

I would like to know the state of the Teensy before I write anything in setup() or loop().

Clearly I did not understand how the microcontroller system works. My experience with the Arduino was that the built in functions which, e.g., generated a PWM output, were of too low a frequency to be useful for audio work. I then wrote my own PWM routines using the ATmega programming guide to generate acceptable audio output.

Basically, where do I find the documentation for the Teensy's "as booted", so to speak, starting point?

Thanks.
 
Suggest to inspect main.cpp in both
....\hardware\teensy\avr\cores\teensy(3/4)
which does what joepasquariello described
 
Suggest to inspect main.cpp in both
....\hardware\teensy\avr\cores\teensy(3/4)
which does what joepasquariello described

WMXZ,

Yes, I see in main.cpp what joepasquariello pointed out.

I still have the question, viz., what is the state of the Teensy 3.6, or, for that matter, any Teensy development board, as a result of the firmware run having been completed.

I would think that it is necessary to know the starting configuration of the development board before implementing one's code.
 
I still have the question, viz., what is the state of the Teensy 3.6, or, for that matter, any Teensy development board, as a result of the firmware run having been completed.

I would think that it is necessary to know the starting configuration of the development board before implementing one's code.

All of the code executed before main() is in the Teensy core. File mk20dx128.c has function ResetHandler(). Also look at array _VectorsFlash[]. You'll notice that the second entry is ResetHandler, i.e. the address of the function. The first entry is the initial stack pointer. When the processor comes out of the reset state, the first thing it does is set the stack pointer to the value at the beginning of that array, and then jump to execute the code at the address in the 2nd entry. After that, you can just follow all of the code that gets executed, culminating in a call to main(), which is where user code begins. It is definitely not necessary to understand all of that to write useful programs for Teensy, but if you want to know, it's all there.
 
All of the code executed before main() is in the Teensy core. File mk20dx128.c has function ResetHandler(). Also look at array _VectorsFlash[]. You'll notice that the second entry is ResetHandler, i.e. the address of the function. The first entry is the initial stack pointer. When the processor comes out of the reset state, the first thing it does is set the stack pointer to the value at the beginning of that array, and then jump to execute the code at the address in the 2nd entry. After that, you can just follow all of the code that gets executed, culminating in a call to main(), which is where user code begins. It is definitely not necessary to understand all of that to write useful programs for Teensy, but if you want to know, it's all there.

joepasquariello,

That is helpful, thanks, and I will examine it further.

I still have the question of what is the startup configuration of the microcontroller as setup() is called. The Teensy 3.6 MK66FX1M0 Manual shows the states of all the registers at reset.

May I assume that that is the state of the Teensy 3.6 development board when setup() is started?
 
Paul beat me to it, but I was about to say....

No, you may not. The code in file mk20dx128.c in folder cores\Teensy3 is what modifies the registers from their default (reset) configuration. Study function ResetHandler() and follow it through to the call to main(). Grasshopper, when you understand the reset state of the processor and every line of code between ResetHandler() and main(), then you will know everything about the state of the processor on entry to setup().
 
FWIW, writing a web page to document the 3 startup hooks and some general concepts about the startup process is on my long to-do list. But it won’t be hardware focused like NXP’s document. How to use the 3 startup hooks is will be the main focus.

Just to explain a bit more about the startup code, many of the details are from practical experience learned (sometimes the hard way) as so many people use Teensy for a wide range of applications, often using code or libraries developed & tested on other boards. For example, you can see a 300ms delay near the end of the startup process. Many years ago we regularly had people report that Teensy wasn’t retaining their program in Flash memory. Of course the flash is very reliable, but they experienced a problem which manifested in a manner which led them to incorrectly believe the flash was defective. They would upload a program which responds to motion sensors. It would always work after uploading. But then it would do nothing after power cycling. They had used a known-good library for the sensor and nothing complicated in their program. Eventually, we realized the real problem was those sensors take a brief time to internally boot up. All of Arduino’s boards spend time a startup running their bootloader, so none of the libraries had ever been tested with a board that boots up faster then the motion sensor. These sorts of software development choices don’t come from any single well documented location. They are the result years of refinements driven by the practical problems which arise over time as huge numbers of people use Teensy together with countless other hardware and many software libraries, many of which were developed and test on non-Teensy boards.
 
FWIW, writing a web page to document the 3 startup hooks and some general concepts about the startup process is on my long to-do list. But it won’t be hardware focused like NXP’s document. How to use the 3 startup hooks is will be the main focus.

Just to explain a bit more about the startup code, many of the details are from practical experience learned (sometimes the hard way) as so many people use Teensy for a wide range of applications, often using code or libraries developed & tested on other boards. For example, you can see a 300ms delay near the end of the startup process. Many years ago we regularly had people report that Teensy wasn’t retaining their program in Flash memory. Of course the flash is very reliable, but they experienced a problem which manifested in a manner which led them to incorrectly believe the flash was defective. They would upload a program which responds to motion sensors. It would always work after uploading. But then it would do nothing after power cycling. They had used a known-good library for the sensor and nothing complicated in their program. Eventually, we realized the real problem was those sensors take a brief time to internally boot up. All of Arduino’s boards spend time a startup running their bootloader, so none of the libraries had ever been tested with a board that boots up faster then the motion sensor. These sorts of software development choices don’t come from any single well documented location. They are the result years of refinements driven by the practical problems which arise over time as huge numbers of people use Teensy together with countless other hardware and many software libraries, many of which were developed and test on non-Teensy boards.

Paul,

I am sure I will find such a web page useful. Thanks for spending the time to prepare it.
 
I have gone through ResetHandler() in mk20dx128.c. I found it to be a good exercise to partly explain the starting configuration of the Teensy 3.6 as it enters setup().

I decided to focus on the ADC system. In the MK66FX1M0 Manual, pdf. 962, ADC0_CFG1 and ADC1_CFG1 both have reset values of 0x0. However, when I run this code,

Code:
/*
    Name:       Clocks_08.ino
*/

//*****************************************************************************
//#include <kinetis.h>

//*****************************************************************************
volatile bool LED_on = true;
volatile uint32_t loopCounter1 = 0, loopCounter2 = 0;
uint16_t seconds = 0, minutes = 0;
volatile uint32_t countDown;
uint32_t milCounter = 0;

//*****************************************************************************
void setup() {

    Serial.begin(115200);

	Serial.printf("\n1. Serial print ready.\n");
	Serial.printf("2. Serial print ready.\n");

	// LED pin
	pinMode(13, OUTPUT);

	Serial.printf("ADC0_CFG1 = 0x%x\n", ADC0_CFG1);
	Serial.printf("ADC1_CFG1 = 0x%x\n", ADC1_CFG1);
	//Serial.printf("SYST_CVR = %d\n", SYST_CVR);

	//*********************************

	//*********************************
	Serial.printf("End of setup().\n");

	 
}

//*****************************************************************************
void loop() {

	if (++loopCounter1 > 2'000'000) {

		loopCounter1 = 0;
		digitalWrite(13, LED_on);

		LED_on = !LED_on;
	}

}

the values for both are 0x39.

Neither ADC0_CFG1 nor ADC1_CFG1 are changed in either mk20dx128.c or kinetis.h.

In particular, I would like to know where they are changed from their reset values of 0x0.

In general, I would like to know where I will find a list of the code that runs before setup(). That is, I did not know that mk20dx128.c is run before setup(). Is there someplace in the Teensyduino installed files which specify this calling sequence? Is the calling sequence in the Teensy 3.6 installed firmware?
 
...
In general, I would like to know where I will find a list of the code that runs before setup(). That is, I did not know that mk20dx128.c is run before setup(). Is there someplace in the Teensyduino installed files which specify this calling sequence? Is the calling sequence in the Teensy 3.6 installed firmware?

When the T_3.6 {defined(__MK66FX1M0__)} powers on it enters the indicated ResetHandler() stored in Flash as uploaded from building the code at hand. That is the 'firmware' that runs on powerup.

All the code executed from "power up" to setup() begins with the call to ResetHandler() and nothing else from that power on state of the processor.

This file sets that up hardware\teensy\avr\cores\teensy3\mk20dx128.c:
Code:
void (* const [B]_VectorsFlash[/B][NVIC_NUM_INTERRUPTS+16])(void) =
{
	(void (*)(void))((unsigned long)&_estack),	//  0 ARM: Initial Stack Pointer
	[B]ResetHandler[/B],					//  1 ARM: Initial Program Counter
	nmi_isr,					//  2 ARM: Non-maskable Interrupt (NMI)
	hard_fault_isr,					//  3 ARM: Hard Fault

Given this linker script for the T_3.6 hardware\teensy\avr\cores\teensy3\mk66fx1m0.ld:
Code:
ENTRY([B]_VectorsFlash[/B])

See hardware\teensy\avr\cores\teensy3\analog.c :: void analog_init(void)

Called by void _init_Teensyduino_internal_(void)

from:
Code:
...\hardware\teensy\avr\cores\teensy3\mk20dx128.c:
   68  extern int main (void);
   69  void ResetHandler(void);
   70: void _init_Teensyduino_internal_(void) __attribute__((noinline));
   71  void __libc_init_array(void);
   72  
   ..
  691: void ResetHandler(void)
  692  {
  693  	uint32_t *src = &_etext;
   ..
 1122  	__enable_irq();
 1123  
 1124: 	[B]_init_Teensyduino_internal_();[/B]
 
In general, I would like to know where I will find a list of the code that runs before setup(). That is, I did not know that mk20dx128.c is run before setup(). Is there someplace in the Teensyduino installed files which specify this calling sequence? Is the calling sequence in the Teensy 3.6 installed firmware?

Just to add to @defragster's answer, there is no "list" of code that runs before reaching your setup() function. If you want to review every line of code that runs before setup(), you can, but it's not as simple as searching one file (mk20dx128.c). Code execution begins in that file at ResetHandler(), but that functions calls other functions, and those functions call functions, etc., so you may have to follow the code execution through many layers to review every line of code. For each function called, you must search the core for that function, open that file, and walk through the function, when that function calls another function, you must follow it, etc. It is not easy to do. There is no document that details everything done before reaching setup(), for Teensy or any other microcontroller.
 
Using SublimeText as source editor here with folder source search and looked for the indicated: ADC0_CFG1

That led to the notes in p#22.

Finding it used in that analog_init - then seeing where that was called ... it led back to ResetHandler() as indicated.

NotePad++ and other editors offer similar 'folder' grep/search.

The code is the documentation at this LOW LEVEL. It works as required to get to setup() to support the Arduino environment. Full sources are installed and ready for use or review.

The code is subject to change when any improvements/changes are needed and maintaining external parallel lists or documentation would add work or quickly become out of date.

The code PJRC maintains for the Teensy Family starts with the MCU manuals and results in best possible Arduino functionality.

The only thing unique about the Teensy that isn't shared is the bootloader code that makes USB interface for code upload. But once that code is in FLASH - the bootloader chip/code plays no part.
 
Having gone through ResetHandler(), I have a good sense that doing much more tracing would be very time consuming and not necessarily useful.

From what has been said by defragster and joepasquariello, perhaps a reasonable alternative would be to query registers germane to my programming goal, determine their contents at setup() enter, and then alter those contents as necessary.

Is there perhaps a better way to access the the power of the Teensy 3.6?

Thanks for your engagement.
 
Back
Top