Has anyone used Zephyr on the Teensy boards?

KurtE

Senior Member+
Just wondering if anyone created any good example sketches using zephyr on a Teensy 4 or 4.1

I know there have been a few threads up here where questions were asked about using SPI or I2C, but I am not
sure where these projects ended up.

More or less all of my experiences with Zephyr is trying out and extending the Arduino wrappers for Zephyr for some of
the Arduino boards, such as the GIGA, Portenta H7 and Portenta C33. More details about setting this up can be found
on the Arduino Blog:
Or the Readme file on the github project: https://github.com/arduino/ArduinoCore-zephyr

But it might be fun to try developing some programs using zephyr directly on the Teensy 4.x boards. So far, I have mainly just run a few of
their example sketches like blink on the Teensy 4 and 4.1.

While I was playing with it, I created a new branch that adds support for the Teensy Micromod as well.

But it would be more interesting to have some more real level sketches, like list the files on SD card, or display something on a screen, ...
 
I thought I would mention, that today, the Teensy Micromod PR was pulled in.

@mjs513 and I have been playing around with running Zephyr on the Teensy 4.x boards.
I find it interesting and frustrating at the same time. Both advanced and primitive...

Some background, Arduino has several boards, that currently are developed on top of MBED, another OS by ARM, and ARM has
announced an End Of Life for MBED, so Arduino is currently slowly migrating to Zephyr.

As I mentioned in the first post, They have had a few releases of the Beta (more like an Alpha) builds for several different boards:
(Arduino GIGA, Portenta H7, Portenta C33, Nano 33 BLE and others. None of these boards are complete yet, and there are
still a lot of things that is unclear to me how Arduino is going to implement. So as I said Interesting and frustrating at the same time

For example, one of the main concepts within zephyr is the idea of device trees and config files. Where when you are building for
a board lets say that uses an IMXRT1062 type processor, there are config files for this processor, that may contain things like
what pins can do what, what hardware devices there are possible, ... Then the board can add on to this, like the board has
N LPUARTS on it and the pins used for it are... And then you may have a Shield that board connects with, which contributes
to this, and then your sketch or program further defines this. So your app controls what systems are brought in.

Zephyr has a lot of different Subsystems, and devices and Shields defined, including things like Display drivers, where I think
most of them revolve around LVGL, plus Cameras, ...

Currently the ArduinoCore-zephyr stuff - is a layer above, zephyr, where each release there is Board Variant or maybe variants defined which
contains of the pinouts, devices, and the like in a Arduino like way, as well as what things are exported from this base and there are scripts/programs run that build this into what currently is called a bootloader, which you program onto the board using the Burn bootloader command, And then you compile your sketch and then then you can load this sketch using the Zephyr conectp of LLEXT(Linked Loader Extensions).
To me, this feels a lot like the MicroPython/CircuitPython
Current Experiments:
As mentioned above, we have been trying to learn more about the underpinnings and how things work. For example, can I implement
my own ILI9341 driver and not use the Adafruit Shield for it. We now have a version of it running, that does most of the graphics test.
I have not fully implemented the whole library...

Today I have been playing with trying to get the XPT2046 controller to work. There is a driver built in to zephyr:
So in my apps config file I needed to tell it to use the Input system by adding a line:
Code:
CONFIG_INPUT=y

In the apps overlay I added stuff like:
Code:
/ {
    chosen {
        zephyr,touch = &xpt2046;
    };
};

...

&lpspi4 {
    transfer-delay = <50>;
    status = "okay";   
    pinctrl-0 = <&pinmux_lpspi4>;
    pcs-sck-delay= <25>;
    sck-pcs-delay= <25>;
    pinctrl-names = "default";

    cs-gpios = <&gpio2 12 GPIO_ACTIVE_LOW>,  <&gpio4 8 GPIO_ACTIVE_LOW>,  <&gpio2 17 GPIO_ACTIVE_LOW>, <&gpio1 30 GPIO_ACTIVE_LOW>;
 
    ili9341_spi_dev: ili9341_spi_dev@0 {
        compatible = "vnd,spi-device";
        reg = <0>;
        spi-max-frequency = <30000000>;
    };

    ili9341atp_spi_dev: ili9341atp_spi_dev@1 {
        compatible = "vnd,spi-device";
        reg = <1>;
        spi-max-frequency = <30000000>;
    };

    ili9341prjc_spi_dev: ili9341pjrc_spi_dev@2 {
        compatible = "vnd,spi-device";
        reg = <2>;
        spi-max-frequency = <30000000>;
    };

    /* 26 is CS 27 is Interrupt */
    xpt2046: xpt2046@2 {
        compatible = "xptek,xpt2046";
        reg = <3>;
        int-gpios = <&gpio1 31 GPIO_ACTIVE_HIGH>;
        touchscreen-size-x = <1000>;
        touchscreen-size-y = <1000>;
        min-x = <0>;
        min-y = <0>;
        max-x = <4000>;
        max-y = <4000>;
        spi-max-frequency = <2000000>;
        status = "okay";
    };
};
Note: on the SPI object not all of this is for touch. Some of this is the define for the display and we have defined two or three of them
where in our sketch we define if we are using our ATP pins or My MMOD board, or Paul's MMOD board

Also I have no idea yet the setting that hopefully will work properly for the screen size and min/max... And timings and the like...

Then in the sketch you can define the usage of the object, like:
Code:
// Lets define our touch event.
static const struct device *const touch_dev = DEVICE_DT_GET(DT_CHOSEN(zephyr_touch));


static struct k_sem sync;


static struct {
  size_t x;
  size_t y;
  bool pressed;
} touch_point;




static void touch_event_callback(struct input_event *evt, void *user_data)
{
  printk("TECB: %u %u %u\n", evt->code, evt->value, evt->sync);
  if (evt->code == INPUT_ABS_X) {
    touch_point.x = evt->value;
  }
  if (evt->code == INPUT_ABS_Y) {
    touch_point.y = evt->value;
  }
  if (evt->code == INPUT_BTN_TOUCH) {
    touch_point.pressed = evt->value;
  }
  if (evt->sync) {
    k_sem_give(&sync);
  }
}
INPUT_CALLBACK_DEFINE(touch_dev, touch_event_callback, NULL);

This will define the the touch controller from the device tree, looking at the chosen field, data structure to hold data
from the callbacks, and the the code to be called when events happen where the INPUT_CALLBACK_DEFINE will put
the link in to this at build time.

The main loop in the code, just simply waits for events and prints them out to the USB Serial port...

Code:
for (;;) {
      k_sem_take(&sync, K_FOREVER);
      USBSerial.printf("TOUCH %s X, Y: (%d, %d)\n", touch_point.pressed ? "PRESS" : "RELEASE",
        touch_point.x, touch_point.y);     
      }
So far I am getting some of the callbacks and the like, but X, Ys are not tracking yet... Right now I have bogus
settings, just to see what it will do..

Not sure if anyone else is interested or not. But we do have some of our experiments up on github...

Right now they are really messy as started with one thing, got it sort of working
Created next sketch for something else and copy all of the stuff...
May clean it up if we go much farther.

Now back to playing
 
@KurtE - interesting notes - MBed going away and Zephyr may be the next Arduino pathway.

Does, the variants work you did for the SDRAM/TMM have any value in abstracting the CORES to the Zephyr scheme? Or is that already part of the 1062 layer porting work done in Zephyr core?
 
So what is the actual benefit of running Zypher on a single core MCU? Task management?
 
As I mentioned in the first post, They have had a few releases of the Beta (more like an Alpha) builds for several different boards:
(Arduino GIGA, Portenta H7, Portenta C33, Nano 33 BLE and others. None of these boards are complete yet, and there are
still a lot of things that is unclear to me how Arduino is going to implement. So as I said Interesting and frustrating at the same time
All I can say is that is an understatement. Just from my point of few moving from mbed to zephyr is going to take quite a while. Especially with making sure all the libraries work as before. This is especially true if you use any mbed specific functions in the libraries like USBHost.

For example, one of the main concepts within zephyr is the idea of device trees and config files. Where when you are building for
a board lets say that uses an IMXRT1062 type processor, there are config files for this processor, that may contain things like
what pins can do what, what hardware devices there are possible, ... Then the board can add on to this, like the board has
N LPUARTS on it and the pins used for it are... And then you may have a Shield that board connects with, which contributes
to this, and then your sketch or program further defines this. So your app controls what systems are brought in.
There are definitely pros and cons to this. An example using Arduino is that the Portenta H7 has three shields that bring out additional pins from the HD connectors (slightly different for each board) but currently on the edge pins are defined in the overlay for the H7. The additional pins have to be add somehow for each breakout board.

While @KurtE was testing I was trying using sensors such as the BMM150 and BMI270 that are built in to zephyr. To be honest more a pain to use that with current libraries just using Wire. If brought into Arduino-zephyr project would be alot easier to convert. But not sure how much further to take it.

One thing that is nice is the threading.
 
Note: There is a webpage that shows some of the information about the Teensy boards on zephyr:
@KurtE - interesting notes - MBed going away and Zephyr may be the next Arduino pathway.

Does, the variants work you did for the SDRAM/TMM have any value in abstracting the CORES to the Zephyr scheme? Or is that already part of the 1062 layer porting work done in Zephyr core?
Yes and No... That is for example with the MicroMod Teensy... I had to add in some of the micromod specific things like the dts file has:
Code:
    / {
    ...
    micromod_header: connector {
            compatible = "sparkfun,micromod-gpio";
            #gpio-cells = <2>;
            gpio-map-mask = <0xffffffff 0xffffffc0>;
            gpio-map-pass-thru = <0 0x3f>;
            gpio-map = <0 0 &gpio1 18 0>,    /* A0         MMOD 34 */
                  <1 0 &gpio1 19 0>,        /* A1         MMOD 38 */
                  <2 0 &gpio4 6 0>,            /* D0         MMOD 10 */
                  <3 0 &gpio4 8 0>,            /* D1/CAM_TRIG MMOD 18 */
                  <4 0 &gpio4 31 0>,        /* I2C_INT#   MMOD 16 */
                  <5 0 &gpio2 4 0>,            /* G0/BUS0    MMOD 40 */
                  <6 0 &gpio2 5 0>,            /* G1/BUS1    MMOD 42 */
                  <7 0 &gpio2 6 0>,            /* G2/BUS2    MMOD 44 */
                  <8 0 &gpio2 7 0>,            /* G3/BUS3    MMOD 46 */
                  <9 0 &gpio2 8 0>,            /* G4/BUS4    MMOD 48 */
                  <10 0 &gpio2 9 0>,        /* G5/BUS5    MMOD 73 */
                  <11 0 &gpio2 10 0>,        /* G6/BUS6    MMOD 71 */
                  <12 0 &gpio2 11 0>,        /* G7/BUS7    MMOD 69 */
                  <13 0 &gpio1 30 0>,        /* G8         MMOD 67 */
                  <14 0 &gpio2 12 0>,        /* G9/ADC_D-/ MMOD 65 */
                  <15 0 &gpio4 7 0>,        /* G10/ADC_D+ MMOD 63 */
                  <16 0 &gpio1 31 0>,        /* G11/SWO    MMOD  8 */
                  <17 0 &gpio2 0 0>;        /* SPI_CS     MMOD 55 */
    };
...
};
...
micromod_1_uart: &lpuart6 {};
micromod_2_uart: &lpuart3 {};
micromod_0_i2c: &lpi2c1 {};
micromod_1_i2c: &lpi2c4 {};
micromod_0_spi: &lpspi4 {};

Which is a very incomplete connector definition. In that it is specific to only one of the Sparkfun carriers: where they defined
a "Shield" sparkfun_carrier_asset_tracker
whose .overlay file has things in it like:

Code:
&micromod_1_uart {
    current-speed = <115200>;
    hw-flow-control;
    status = "okay";


    modem: sara_r5 {
        compatible = "u-blox,sara-r5";
        mdm-power-gpios = <&micromod_header 7 0>; /* G2 */
        mdm-reset-gpios = <&micromod_header 12 0>; /* D6 */
        status = "okay";
    };
};
Which for example if we built it with the Teensy Micromod. the: micromod_1_uart -> lpuart6
and the modem would use logical pins (7 and 12) on that Logical connector GPIO2 pin 6, GPIO pin 11...

One could extend this, and define a Teensy4 logical connector and define similar for each of the variants.

So what is the actual benefit of running Zypher on a single core MCU? Task management?
TBD! - So far I am just trying to understand it.

So far I don't think it is completely ready for prime time.

Device Tree - I am trying to keep an open mind. At one point I had a Beagle Bone Black (when it first came out). Was fun at first, but when they introduced the device tree stuff, I found it so frustrating, I think I tossed the board :oops:
But I am trying to keep an open mind. It does allow you to setup things such that all of the initialization of different components are
done at build/loader time. And with things like their logical connectors and the like, it does make it maybe easier in some cases to
share code... Although as @mjs513 mentioned, that is plus or minus.

Task management - As Mike mentioned... I have mentioned before there are often times I wished that there was some accepted form
of threading on the Teensy boards. There are several larger components in the Teensy world that might be a lot
more advanced, cleaner and easier to maintain if we had something like this, the two that come to mind are:

MTP - Have a simple task that waits until an MTP message arrives, processes it and goes back to sleep. Things like receiving or sending
files should be simple, like: read buffer... write buffer, each of these simply wait until they are done... Depending on your sketch and your
needs, you can setup what priorities things should run at. For example I may want my task to give high priority to MTP to quickly upload
or download files, where others may want to give it a lower priority as maybe they use Audio...

USBHost - A lot of the current code is semi state table driven. Which for a small number of states is fine, but it can get really convoluted when for example there are 20 different devices that all may have different requirements for what message should be sent next when message Y arrives and in many cases the same message might be used several different ways... A lot easier for cases where I am starting up device Y, and it needs to do some 10 step process... For example, I believe the MSC code sort of tries to circumvent some of this...

Trade off is, a lot of our current code is not thread safe. We probably should doing stuff to fix this anyway as many of us roll or own ways
of doing tasking, like using intervalTimers and the like. Also this could introduce overhead.

But your milage may vary ;)
 
Man it has been a long time since I worked with a DTS file. Back when I was playing with a Panda board and Wand board setting up SPI usage :)Got it working but never took it any further. At that time I was using Linux OS's setup by Robert E. Nelson. It would be fun to try it out on the Devboard 5. Thinking that it would be close to the same as the MicroMod version maybe. Where to start?
 
Man it has been a long time since I worked with a DTS file. Back when I was playing with a Panda board and Wand board setting up SPI usage :)Got it working but never took it any further. At that time I was using Linux OS's setup by Robert E. Nelson. It would be fun to try it out on the Devboard 5. Thinking that it would be close to the same as the MicroMod version maybe. Where to start?
Install Zephyr:

I have both a Windows setup as well as a Ubuntu setup... When you install and do the west update and the like:
The sources specific to teensy are up at:
On my machine that is at: d:\zephyrproject\zephyr\boards\pjrc\teensy4

The Teensy4 board files were the base ones. The T4.1 and mmod are derived from T4...

Once installed and, your at the root directory like: d:\zephyrproject\zephyr you can build:
Code:
west build -b teensymm samples/basic/blinky

Which blinks. To program the teensy: you need a copy of teensy_loader_cli somewhere on your path
and then do: west flash
you have to first hit program button and I often have to do flash twice.

Wish we had a full blown official maintained command line, that supports things like protected, but...
 
Thanks - I just now got step 7 of the install working. Was having the same problem with <venv> and pip that we saw on the GIGA. Ran "pip install -r zephyr/scripts/requirements.txt" then "west update" again. Then ran "west packages pip --install" again and this time it found conflicts with poetry and seemed to correct it. After that I was able to run "west sdk install" successfully. Are the "pjrc/Teensy4" files from the Zephyr install the same as the ones in your repo? Anyway, it built blinky successfully:
Code:
-- Configuring done
-- Generating done
-- Build files have been written to: /home/wwatson/zephyrproject/zephyr/build
-- west build: building application
[1/138] Preparing syscall dependency handling

[3/138] Generating include/generated/zephyr/version.h
-- Zephyr version: 4.1.99 (/home/wwatson/zephyrproject/zephyr), build: v4.1.0-3631-g77bb19c2a94a
[138/138] Linking C executable zephyr/zephyr.elf
Memory region         Used Size  Region Size  %age Used
           FLASH:       30594 B        16 MB      0.18%
             RAM:        4288 B       256 KB      1.64%
            ITCM:          0 GB       128 KB      0.00%
            DTCM:          0 GB       128 KB      0.00%
           OCRAM:          0 GB       256 KB      0.00%
          OCRAM2:          0 GB       512 KB      0.00%
        IDT_LIST:          0 GB        32 KB      0.00%
Generating files from /home/wwatson/zephyrproject/zephyr/build/zephyr/zephyr.elf for board: teensymm

Thanks again @KurtE for the assist:D
EDIT: Uploads and works as well...
 
Last edited:
From the looks of that is it splitting FlexRAM into 3 different regions, ITCM/DTCM/OCRAM? I tried to get that working with teensyduino once but it caused issues with things that used hardcoded address like the crash reporter.
 
Installed Zephyr dev on a second computer with Ubuntu 24.04 LTS without issue. The first install on the first computer was Ubuntu 22.04.5 LTS. I may have messed up the first install or it may have been a problem with Ubuntu 22.04.5 LTS. There was no conflict with "Poetry" on Ubuntu 24.04 LTS this time:unsure:
 
Back
Top