Programming the Teensy 4.0 with GCC. Examples?

dettus

Member
Hey guys! So... i got this wonderful Teensy 4.0. It is my first endeavour into the world of Arduino and i would like to apologize for asking stupid questions.

Anyways... since i am an experienced C programmer, i would like to be able to program it with gcc.
So far i was able find the makefile which is compiling the blinking LED example. Which is a good start!
Now i want more. For one thing, i would like to use the USB port as a serial output interface. How would I be able to do that?

Whenever I google, i seem to stumble upon example .ino files. I am not sure if those are helpful... are they?
 
INO files are C/cpp files that the Arduino IDE uses as source to use the gcc compiler to generate the executable loaded onto the Teensy.

Same tools if you get the makefile working or use the IDE : it uses the gcc tools prepared, installed, and tested for creating valid Teensy ARM binaries.

Using Arduino IDE along with TeensyDuino Installer just gives access to the years of coding that has gone into USB for Serial output or input in various forms as well as tested libraries for SPI, i2c, Serial and other libraries for onboard hardware.

The IDE's INO file is by dafault CPP and gets some preprocessing to allow function use without prototypes and other hand holding. But it only needs to supply setup() and loop() that can call to .c or CPP files of your choice to have the installed gcc toolchain build them as well.
 
Nice!
Okay, that helped. Thank you.


Since I am not a big fan of IDE, all I would have to do is to figure out would be what kind of "pre-processing" is being done.
 
Pre-Processing is trivial help as noted. It scans for funcs and makes forward declaration prototypes as needed so users don't have to - just pre-processing manipulations that have no effect on the resulting code otherwise. Variables and everything else follow C/Cpp rules to build.

If you are a Windows user ? With a normal IDE + TeensyDuino install and minor path edits to github.com/Defragster/Tset you can run to trigger a build from the command line - perhaps from within your editor of choice.
 
Nice!
Okay, that helped. Thank you.


Since I am not a big fan of IDE, all I would have to do is to figure out would be what kind of "pre-processing" is being done.

You can easily avoid Arduino pre-processing but continue to use Arduino IDE
Put all your code into a local .cpp file (main.cpp is fine, but can be anything.cpp) and let the sketch.ino file empty.
Arduino Pre-processor will manipulate empty ino file, but not touch the .cpp files.
This allows you to develop code according to c/c++ rules with or without Arduino IDE, which needs a sketch.ino file
That is my normal way of doing it.
I typically program with VSCode + makefile, but share only after tested from Arduino IDE, so I need the ketch.ino file.
 
Hi Dettus,
As defragster said, an .ino file is a .cpp file.
If you prefer to work with .cpp files (as I do), then all you need do is add #include <Arduino.h> to your list of header files and then your .cpp file will behave exactly like a .ino file. You then need to provide setup() and loop() functions instead of the standard main() function.

Instead of using a make file I suggest you call arduino-builder instead. arduino-builder generates the necessary compile and link instructions for GCC. This greatly simplifies things because arduino-builder knows where your libraries are and can resolve them.

If you run the Arduino IDE and do a compile you will see the first two lines in the messages box call arduino-builder. By inspecting these lines you can work out what it does.

I have written a bash script to mimic its operation and call this from my favourite IDE(Geany) so that I do not have to use the terribly limited Arduino IDE.

To help you I am including below my bash script that I regularly use. It should give you a good start. The advantages of doing it this way is that you always remain compatible with the Arduino IDE which you should regard as the reference standard.
Code:
#!/bin/bash
#teensy builder

BASE_PATH=$1
TARGET_PATH=$2
TARGET_NAME=$3
VERBOSE=$4

p0="-dump-prefs"
p1="-compile"
p2="-logger=human"
p3="-hardware $BASE_PATH/../installation/hardware"
p4="-hardware $HOME/.arduino15/packages"
p5="-tools $BASE_PATH/../installation/tools-builder"
p6="-tools $BASE_PATH/../installation/hardware/tools/avr"
p7="-tools $HOME/.arduino15/packages"
p8="-built-in-libraries $BASE_PATH/../installation/libraries"
p9="-libraries $BASE_PATH/../libraries"
p10="-libraries $BASE_PATH/libraries"
p11="-libraries $TARGET_PATH/libraries"
p110="-libraries $BASE_PATH/../installation/hardware/teensy/avr/cores/teensy4"
p12="-fqbn=teensy:avr:teensy40:usb=serial,speed=600,opt=o2std,keys=en-us"
p13="-ide-version=10812"
p14="-build-path $TARGET_PATH/build"
p15="-warnings=default"
p16="-build-cache $TARGET_PATH/cache"
p17=""
p18="$TARGET_PATH/$TARGET_NAME" 

echo "$VERBOSE Compile started, watch this space..."
echo "$HOME/Arduino/installation/arduino-builder $p1 $p2 $p3 $p4 $p5 $p6 $p7 $p8 $p9 $p10 $p11 $p110 $p12 $p13 $p14 $p15 $p16 $p17 $p18"

if [ "$VERBOSE" == "-verbose" ];
then
  p17="-verbose"
  $HOME/Arduino/installation/arduino-builder $p0 $p2 $p3 $p4 $p5 $p6 $p7 $p8 $p9 $p10 $p11 $p12 $p13 $p14 $p15 $p16 $p17 $p18
fi

$HOME/Arduino/installation/arduino-builder $p1 $p2 $p3 $p4 $p5 $p6 $p7 $p8 $p9 $p10 $p11 $p12 $p13 $p14 $p15 $p16 $p17 $p18
RETURN=$?
echo "Compile completed"
echo "Project: $1"
echo "Folder: $2"
echo "Target: $3"

exit $RETURN
 
There remains the problem of how to load the code into the teensy
This is how I do it
Code:
#!/bin/bash
# upload Teensy

BASE_PATH=$1
TARGET_PATH=$2
TARGET_NAME=$3

echo "Teensy upload"
echo $BASE_PATH
echo $TARGET_PATH
echo $TARGET_NAME

suffix="/manufacturer"
string=`ack -s -l "Teensyduino" "/sys/devices/pci0000:00"`
TARGET_PORT=${string%"$suffix"}
echo $TARGET_PORT


p1="-file=$TARGET_NAME"
p2="-path=$TARGET_PATH/build"
p3="-tools=$BASE_PATH/../installation/hardware/tools"
p4="-board=TEENSY40"
p5="-reboot"
p6="-port=$TARGET_PORT"
p7="-portlabel=/dev/bus/usb/001/054 Bootloader"
p8="-portprotocol=Teensy"

$HOME/Arduino/installation/hardware/tools/teensy_post_compile $p1 $p2 $p3 $p4 $p5 $p6 $p7 $p8
RETURN=$?
echo "Upload completed"
sleep 3  # don't start putty too quickly
nohup /usr/bin/putty -load arduino &
#nohup /usr/bin/moserial &
#nohup /usr/bin/cutecom -s Arduino &
exit $RETURN
 
@PDOS - that is similar to what Frank B pulled out for Windows Batch file with hardcoded board setting - as noted above that was extended with with another Batch file [github.com/Defragster/Tset] that prompts for "Teensy Model, Speed, Optimization, USB type" which then assembles a Compile.bat into the sketch directory.

And secondly - with no error in build - it then handles the upload as it goes using TeensyLoader - or if installed and integrated TyCommander that does both upload and better SerMon - especially when using multiple Teensy connected at once.
 
Defragster,
Yes, you and Frank B did a more complete job than I did. For example, if I change my board, etc then I have to once more inspect the command line in the Arduino IDE and correct my bash script accordingly, whereas Frank B makes that process easier.

I did not use yours and FrankB's excellent contribution because I needed a Linux solution.
 
@PDOS - As noted Frank did the initial version like yours - my addition was the tedious part that makes it easier to automate any Teensy build setting. I'm now repeating that for another processor. noted in that thread it should work similarly to automate on linux but yours was the first I've seen. If you can find that thread 'SubLimeText' (or linked from github) it could be discussed more there. But with 5 prompts - including the ending acceptance {Y - N restart- debug- or subfolder options} at the end - it results in a duplicate set of settings to feed the IDE builder and Upload in one.
 
Arduino's preprocessing does more than just adding forward function declarations and the Arduino.h include (which is done to a single .cpp file made by concatenating all your .ino files). The other main feature is scanning all the #include lines to figure out which libraries your program uses. It builds a list of -I{dir} inputs to give to the compiler, and builds a complete linker command with all the libraries.

In the early versions of Arduino, this was done in a very simple & limited way using regex patterns on every line in all your .ino files. If you included SD.h, you also had to include SPI.h even if your program never calls any SPI library functions directly.

Modern Arduino is so much better. I and many others had a hand in that development years ago. It's much more complicated than it looks, because libraries like SD may include SPI.h and SPI might include even more on some platforms. SD might also have #ifdef conditional compilation where it includes different things depending on C preprocessor defines from the command line or from other headers it included. To allow for all that, today Arduino runs "gcc -E" many times, where the -E flag tells gcc to only run the preprocessor and not actually compile anything.

As each new header include is found, Arduino implements a rather complex set of rules for where it will look to find the library which supplies than header. If it's found in your program's folder, than it's not a library at all. The traditional library search order is anything you put in {Documents}/Arduino/libraries overrides all other locations. Then a set of "platform" libraries for whatever board / architecture you're using is supposed to come next, and finally the set of libraries that come with Arduino are used. Recent versions of Arduino also take some of the libraries.properties metadata, particularly whether the library is meant for a specific architecture or all architectures, in the decision about which library locations have precedence over others. They may have added yet another location into the process as well, maybe a hardware folder inside {Documents}/Arduino? Honestly, I haven't followed it so closely since they switch from Java to Google Go language.

How libraries are compiled also isn't simple. If a "src" folder is present, all the source code is assumed to be in that folder. I believe Arduino finds source files in all subfolders of "src" and compile them all. But if "src" isn't present, then all the .cpp, .c, and .s files in the main folder are compiled. An optional "utility" folder is also scanned and all source files compiled, but no recursive scanning of other subfolders, as it does with "src". The library.properties metadata also can instruct Arduino's build system to put all of the .o files into an .a archive and then use that with the linker.

Much of this is the nitty gritty of how Arduino's fairly complex build system works. What you consider "preprocessing" may vary. If "preprocessing" means giving the compiler different source than you actually wrote, then indeed the addition of Arduino.h and function declarations is pretty much it. Though even that involves hidden complexity, adding #line directives and using preprocessor tricks so error messages are in terms of the original .ino files.

But if you consider preprocessing to mean reading all the source files and using the text of the source code to configure how it will run the actual compiler commands, Arduino definitely does that!

Maybe the decisions about how to compile & link based on detecting which folders and reading metadata isn't really preprocessing?
 
So this is not criticising anyone, just an attempt at finding the bases, and starting at the OP invitation to discussion. I have myself transitioned between developed environments and paradigms so I know this is a bit painful.

.. since i am an experienced C programmer, i would like to be able to program it with gcc

The Arduino framework for Teensy uses gcc, so any little *.ino sketch you write will be compiled by gcc and deemed worthy or not from gcc rules. The programming language is C/C++ and compiler is gcc.

The next element is the build system, in old fashioned, UNIX based, setups this is cantered around Makefiles meticulously crafted by the programmer as long as we progress beyound Hello World where no Makefile is needed. In Arduino this is handled by the IDE, there are makefiles but the user doesnt have to write them, it is done by the framework, in much the same that any modern 'professional' framework hides such low level nitty gritty from the user, and to a large extent abstracts away the processor and system dependent details from the user. The Arduino IDE and framework does this quite successfully. Of course there are situations where problems appear and a deep dive into internals is needed, this is the same for all vendor supplied 'proffesional' environments.

So we come to the IDE and its quality or lack thereof, well it is perfectly usable, perhaps not so much for larger projects factored into sub modules. For beginners there is only a *.ino sketch, then libraries are added, just like stdlib and stdio must be added for any useful oldfashioned command line C programming ( been there , done it ). Its also easy and efficient to add files to the sketch directory to factor code into smaller chunks, those can later be turned into libraries. At the moment no intrusive typing completion ..., is it great no perhaps not, useable for sure.

Then we come to debugging, in my old days trying to run gdb in text mode that was painful, current IDE has no debug support but the coming IDE 2.0 has it, but still feels buggy. Good enterprise environments have superb debugging but there is a price, and they are limited to that specific platfrom.

[ Not trying to trample any toes but this is about my feeling, I mostly use Arduino and avoid supplier IDE's ]
 
I use CMake for Teensy 3.x, 4.x, and LC:
https://github.com/bolderflight/core/

Has 'hex' and 'upload' targets and uses the command line uploader for uploading to the Teensy. It's modified from the standard Teensy core, but you can get a sense of how to implement a CMake build system.

Vagrant is used to automate setting up the build environment. The uploader integration only works under Linux or the Windows Subsystem for Linux, but the bootstrap.sh script in Vagrant shows how I setup my system to build with GCC and CMake (there's some protobuf stuff in there, since I'm using that for serialization):
https://github.com/bolderflight/build-tools

Libraries are then easy to write and link, as an example, this is my BME280 library using the CMake approach:
https://github.com/bolderflight/bme280
 
Arduino's preprocessing does more than just adding forward function declarations and the Arduino.h include ...

Maybe the decisions about how to compile & link based on detecting which folders and reading metadata isn't really preprocessing?

It is awesome to have all the needed files gathered and assembled into the needed executable - without committing to a makefile - preprocessing makes the 'link' work.

Was just saying - with regard to OP - it doesn't change that gcc is used and the sources provided are used as provided and detailed ... they are not transmogrified with 'changes to the sources' - or built with anything other than the gcc toolchain after the extended preprocessing makes finding the needed sources work.
 
As long as we're on this subject, may I ask what the yield() function in main is supposed to be doing? I looked at the code and it seems to provide servicing to USB / COMs. I have run my code with both returning from loop() and not returning from loop() and it seems to make no difference. (I do use USB com for debug printf.)

Code:
	// Arduino's main() function just calls setup() and loop()....

	setup();

	while (1) {
		loop();
		yield();
	}

Like the OP, I usually do not use an environment like the Arduino simply because it seems it's just a restrictive wrapper around GCC. Although, it's excellent for diving in with a new PCB. Some projects I leave in the Arduino IDE because they're too simple or non-mission-critical to worry about. But I think many professional programmers feel the Arduino IDE is either novice-level (and beneath them), or like me, fear there are unwanted things linked-in, or features of the IC being grabbed without a heads-up, or other code running that isn't needed.

Some Arduino-compatible PCB makers bring a bunch of baggage with their boards. I am new to Teensy, but have used Adafruit, Arduino, and others for years. Frankly, I think the Teensy is the most professional of all of them and I look forward to using these boards from now on.
 
Sorry, at times I don't understand statements like, it is a restrictive wrapper on GCC...

It is GCC. Every .c file or .cpp file is compiled with the GCC tools.

As for yield - it is setup to do some other tasks for you, which you may or may not use.
That is if your code uses any of the things like: serialEvent or serialEvent1... then this is the code that gets called that looks to see if there are any data in the appropriate SerialRX queue and calls that method. There are some hacks in this code that tries to remove these calls if your code doe snot actually provide your own version of that method...

The yield code also handles some of the event responder code. if again you use that code, which will call any triggered events that were setup to be called by yield. Again there is code to try to reduce the overhead in yield if you actually don't use these features.

Yes you can bypass some of this by not returning from setup or loop... However yield does get called by other places like: delay()...

You will also notice that the current yield code is defined with an attribute weak:
void yield(void) __attribute__ ((weak));

So you can bypass that code as well, but again may break future functionality.

As for the IDE... Again there are several alternatives, which many of us use. I do most of my stuff building and editing within sublimeText (currently using beta of Version 4), Others use Platform IO, Eclipse, ...

As for Build systems, every system has issues, especially for people like me who is retired and doing it for the fun of it. I used one form or another of Makefiles for decades.

Like Makefiles can be a pain in the ... Dito for CMake! Dito for tools like VisualStudio projects... But they all have their pluses as well.

Projects like ROS/ROS2, have gone through at least three different build systems.

...

As for Teensy and Arduino - You are pretty much in control of almost exactly what gets into your sketch or not. As you have the sources to almost everything...

The main place that you may not have all of the sources is for whatever libraries/code GCC brings in.
 
Hello guys.
Thank you for your answers.

I am glad this forum exists.
After 3 months of hiatus, I got myself a Teensy 4.1 board. And I am tinkering around with that one now.
 
As for Teensy and Arduino - You are pretty much in control of almost exactly what gets into your sketch or not. As you have the sources to almost everything...

The main place that you may not have all of the sources is for whatever libraries/code GCC brings in.
I would like to add, that you can bypass Arduino preprocessing by putting all your code into .c/.cpp code and let .ino be completely empty.
 
Can you use just teensydino install from command line with Makefile?

I would like to add, that you can bypass Arduino preprocessing by putting all your code into .c/.cpp code and let .ino be completely empty.

One of the items listed on the pjrc project page under development environments is that you can use teensyduno and a Makefile to build c/c++ code for Teensy; at least that is the way I understand what it is saying? So if I just want to use Make to build from the command line on Windows can I just install teensyduno, and run Make from the command line in the "Blinky" directory? This would be very equivalent to WinAVR basically if you can do this? I guess ultimately what I'm asking is WinAVR is 11 years old and is not longer supported and does not work correctly on Windows 10 anyway (at least I installed it and it would not run Make correctly). So if I have a Teensy 2.0 that I still want to use what is the best way to build my .c files? Can I use teensyduno and a command prompt into my teensy project directory with a proper makefile that use to build with WinAVR just fine, and just run Make?

If I upgrade to Teensy 3.2 or greater can I just have a simple Makefile and run Make at a windows command prompt with just teensyduno - that is what I would like to do just have a Makefile and run make to build my teensy 2.0 project and someday upgrade to a more current Teensy? Then in the future I'll work on using the complete Arduino IDE enviroment to edit, build, debug, repeat...
 
Back
Top