Raw C on Teensy 4.x / WTF is CMSIS

phryk

New member
I'm currently working on a LED lighting project (4x 15W RGB+CW+WW)
and a friend recommended the Teensy 4 because it's one of the few µC
boards that actually has enough hardware PWM channels for this purpose.

Now, I already did a very similar thing, but without CW+WW about a decade
back where I coopted an Arduino Mega to work without the Arduino IDE and
language, but just used raw C with the avr-gcc/avrdude toolchain.

For this updated version, I got myself a Teensy 4.1 and am planning to go
about this the same way. Raw C, no OS, no Arduino.

I'm on FreeBSD and won't use any tool that's not FOSS.

I already figured out that to flash new firmware, I need teensy_loader_cli.
Builds fine on FreeBSD and with the bundled .hex I could also verify that
this works without problems. So far, so good.

Now I'm at the state where I would need to write and build my first "hello world"
firmware for the Teensy and I'm a bit confused. I have limited but passable C
experience, but less with embedded systems and none with ARM or Teensy.

With AVR, some toolchain package contained header files defining the supported
µCs with their registers. But for ARM cross-compilation I found no such headers
in any of the involved packages (binutils, gcc, newlib).

I'm also a bit confused that I didn't find any information about raw C on the Teensy
on this forum – maybe I just searched for the wrong terms?

Anyhow, while the "normal" ARM toolchain doesn't seem to come with µC definition
headers, I have found this thing called CMSIS. It does come with definition headers,
but the documentation of that thing is so confusing that I'm not actually sure what the
hell CMSIS *is*.

If anyone could help me clear some of this confusion and get to a state where I can
write, compile and flash the equivalent of the slow_blink firmware in raw C, I would
be much obliged.

Additionally, I would be extra grateful for help configuring UART serial-over-USB and
those weird FlexPWM things so I can actually communicate with the Teensy and have
it route 20 PWM channels to the output pins.
 
Install TeensyDuino in the IDE (paths below on Windows). This provides the gcc toolchain and all the needed header and processor startup code for void ResetHandler(void) that calls main().
See this path for IDE 1: ...\arduino-1.8.19\hardware\teensy\avr\cores\teensy4\main.cpp
Or IDE 2: ...\AppData\Local\Arduino15\packages\teensy\hardware\avr\1.58.1\cores\teensy4\main.cpp
Code:
extern "C" int main(void)
{
#ifdef USING_MAKEFILE

	[B]// To use Teensy 4.0 without Arduino, simply put your code here.[/B]
	// For example:

	pinMode(13, OUTPUT);
	while (1) {
		digitalWriteFast(13, HIGH);
		delay(500);
		digitalWriteFast(13, LOW);
		delay(500);
	}

...

This is just C code using PJRC's evolving years of life energy making T_4.1's NXP 1062 (and prior ARM's) interface to included GCC toolchain for uploadable binary files.

To see the task ahead without installing see github.com/PaulStoffregen/cores and the 1062 customizations needed on top of prior years of ARM work see github.com/PaulStoffregen/cores/tree/master/teensy4

While it does support Arduino - that is just to provide a common easy to use starting point IDE - as noted above using the makefile takes out this 'trivial' concession to "Arduino" breaking down setup() and loop() and calling yield() on each loop() iteration:
Code:
#else
	// Arduino's main() function just calls setup() and loop()....
	setup();
	while (1) {
		loop();
		yield();
	}

...
 
Raw C, no OS, no Arduino.

I know you said no Arduino, but the simplest way to get the toolchain is to install Arduino 1.8.19 (the old version) and then run the Teensyduino installer which adds all the Teensy stuff to it. If you're absolutely adamant about never running Arduino IDE, you don't need it. This install process just gets all the files onto your PC in a working setup. It could also be done with Arduino 2.1.0, but that's a lot more complex.

With Arduino 1.8.19 and Teensyduino 1.58 installed, look for the sample makefile in {Arduino}/hardware/teensy/avr/cores/teensy4/Makefile. That makefile has relative paths to run the proper toolchain. If you use Windows, the default location is C:\Program Files (x86)\Arduino. On MacOS and Linux, it's whereever you extracted the downloads. MacOS Finder shows it as a single file, but you can control-click and choose "Show Package Contents" to see all the files and folders.
 
Oh, I see now you're using FreeBSD. Only Linux, MacOS and Windows are supported. Almost nothing is published for FreeBSD. Building up a minimal system is theoretically possible, but as a practical matter requires pretty deep knowledge of how Teensy works, which you can only realistically first learn by using one of the supported systems.

But if you want to push forward with FreeBSD anyway, the first step would be the command line version of Teensy Loader. It was tested on FreeBSD a very long time ago. Who knows, maybe it could still work?

https://github.com/PaulStoffregen/teensy_loader_cli

As for the toolchain, this is the one included in Teensyduino 1.58.

https://github.com/PaulStoffregen/ARM_Toolchain_2022q3_Source/releases/tag/release

Also check out this file, which is from ARM but with more details added for the build process, as needed to make the Linux 32 bit ARM and X86 builds which ARM doesn't provide. If you're going to build the toolchain, maybe it can save you some time?

https://github.com/PaulStoffregen/ARM_Toolchain_2022q3_Source/blob/main/build_from_source.txt

The other main piece you need is the core library. This is the place on github.

https://github.com/PaulStoffregen/cores

This is the sample makefile, which you would normally find in {Arduino}/hardware/teensy/avr/cores/teensy4/Makefile after running the installer on a copy of Arduino 1.8.19.

https://github.com/PaulStoffregen/cores/blob/master/teensy4/Makefile

In particular, this part of the makefile is probably of most interest to you.

Code:
#************************************************************************
# Location of Teensyduino utilities, Toolchain, and Arduino Libraries.
# To use this makefile without Arduino, copy the resources from these
# locations and edit the pathnames.  The rest of Arduino is not needed.
#************************************************************************

# Those that specify a NO_ARDUINO environment variable will
# be able to use this Makefile with no Arduino dependency.
# Please note that if ARDUINOPATH was set, it will override
# the NO_ARDUINO behaviour.
ifndef NO_ARDUINO
# Path to your arduino installation
ARDUINOPATH ?= ../../../../..
endif

ifdef ARDUINOPATH

# path location for Teensy Loader, teensy_post_compile and teensy_reboot (on Linux)
TOOLSPATH = $(abspath $(ARDUINOPATH)/hardware/tools)

# path location for Arduino libraries (currently not used)
LIBRARYPATH = $(abspath $(ARDUINOPATH)/libraries)

# path location for the arm-none-eabi compiler
COMPILERPATH = $(abspath $(ARDUINOPATH)/hardware/tools/arm/bin)

else
# Default to the normal GNU/Linux compiler path if NO_ARDUINO
# and ARDUINOPATH was not set.
COMPILERPATH ?= /usr/bin

endif

But again, I need to emphasize only Linux, MacOS and Windows are officially supported. FreeBSD is definitely not a supported system!
 
Thank you for the kind replies.

I think we might be miscommunicating a bit, tho.

I do not plan on using the Teensy library. Rather, I want to use the Teensy like a generic
ARM development board and just use the µC as directly as possible, without extra abstractions.

I already have teensy_loader_cli built and running, so flashing shouldn't be an issue. :)

I also have these three packages installed through my package manager:
* arm-none-eabi-binutils
* arm-none-eabi-gcc
* arm-none-eabi-newlib

This should be everything that build_from_source.txt calls the "toolchain", correct?

Further, if I'm interpreting it correctly, I should be able to craft a minimal build environment
from the Makefile you linked, as well as imxrt1062_t41.ld, imxrt.h, core_cm7.h and possibly
some tangential ones such as arm_math.h – does that sound about right?

Lastly, I'm curious as to whether I also need this "teensy_post_compile" and if so which
repository I'll actually find it in.
 
...

Additionally, I would be extra grateful for help configuring UART serial-over-USB and
those weird FlexPWM things so I can actually communicate with the Teensy and have
it route 20 PWM channels to the output pins.

...
This is just C code using PJRC's evolving years of life energy making T_4.1's NXP 1062 (and prior ARM's) interface to included GCC toolchain for uploadable binary files.

To see the task ahead without installing see github.com/PaulStoffregen/cores and the 1062 customizations needed on top of prior years of ARM work see github.com/PaulStoffregen/cores/tree/master/teensy4

Thank you for the kind replies.

I think we might be miscommunicating a bit, tho.
...

Understood - p#1 suggested USB and other things working and p#2 was to suggest USB isn't trivial code as Paul has noted it was a big dev task.

Pointing out there isn't a known boilerplate solution - unless the efforts of PJRC customized for the Teensy are used. And noted with a makefile everything in the PJRC CORES is just "c" code with needed startup - no OS or other overhead except as needed to get the processor powered and ready to enter main().
 
...whether I also need this "teensy_post_compile" and if so which repository I'll actually find it in.

You don't really need teensy_post_compile. It's simply the command line utility which Arduino IDE runs to inform (GUI version) of Teensy Loader the filename and directory where it compiled your program. You'll be using the command line loader, so you won't need this.


I do not plan on using the Teensy library.

You probably do need the core library.

It has code for all the basic peripherals like FlexPWM, and also the USB stack with support for USB Serial (CDC protocol). In theory you could craft all that code from scratch. But doing so for the Teensy core library took over a year of work, starting from many years of experience with NXP's other 32 bit chips. Recreating all that driver level code, especially the USB stuff, could take a very long time and has a low chance of success if you're not already familiar with the hardware.

Starting with all the core library code and incrementally deleting parts you don't want is probably a much easier path to success than trying to start with nothing and selectively copy only the pieces you understand to be needed. Then again, you're already choosing a pretty much unsupported and difficult path, so if you want to make everything even a lot harder, you certain can. It's really up to you.
 
Okay, I finally managed to find out I was using the wrong toolchain.

The `arm-none-eabi-*` packages are not what I need and are at least missing parts of the C++ stdlib
(type_traits, specifically). `gcc-arm-embedded` is apparently the package I need.

Now `cores/teensy4` finishes compilation, but still breaks when linking because `arm_cortexM7lfsp_math`
is missing. This seems to be part of CMSIS, specifically v4 as it doesn't exist in v5 anymore.

But with the CMSIS docs being so obtuse and it not containing a Makefile or anything else I recognize as
build system recipe, I am again at a loss on how to proceed. I am also still unclear what the relation between
CMSIS and cores is. Much of the CMSIS docs I've read thus far describe patterns I've seen applied in cores,
but except for the lfsp_math thing, I'm not at all sure if there's even a dependency on it. ��

What's the process here? Do I just have to somehow build CMSIS 4 and install it to a place the compiler/linker
knows about or is some more involved fiddling required to make this work?
 
Back
Top