How can I use RTOS in Teensy 4.0?

Would be interesting to know it is there as an option.

So far TeensyThreads has gotten some attention. It would be cool to see how much size difference there is for similar samples code ... what is working now and as redone with RTOS just to know how much it brings in.

Here is the basic example. This one shown for freeRTOS, the one for TeensyThreads is basically the same.

Code:
#include <Arduino.h>

#include "arduino_freertos.h"
#include "avr/pgmspace.h"


volatile uint16_t a,b,c;


void task1(void*) 
{
    a=0;

    while (true) 
    {
        a++;
        vTaskDelay(pdMS_TO_TICKS(1));
    }
}

void task2(void*) 
{
    b=0;

    while (true) 
    {
        b++;

        //digitalWriteFast(arduino::LED_BUILTIN, arduino::HIGH);
        vTaskDelay(pdMS_TO_TICKS(25));
        
        //digitalWriteFast(arduino::LED_BUILTIN, arduino::LOW);
        vTaskDelay(pdMS_TO_TICKS(50));

    }
}


void task3(void*) 
{
    c=0;

    while (true) 
    {   
        c++;
        Serial.printf("a[%06u] : b[%06u] : c[%06u]\r\n",a,b,c);
        vTaskDelay(pdMS_TO_TICKS(10));
    }
}


void setup()
{
    Serial.begin(115200);


    while (!Serial); // wait for Arduino Serial Monitor

    xTaskCreate(task1, "task1", 2048, nullptr, 3, nullptr);
    xTaskCreate(task2, "task2", 2048, nullptr, 2, nullptr);
    xTaskCreate(task3, "task3", 2048, nullptr, 1, nullptr);

    Serial.println("setup(): starting scheduler...");
    Serial.flush();

    vTaskStartScheduler();

    
}


void loop()
{

}


TeensyThreads compile output:
teensy_size: Memory Usage on Teensy 4.1:
teensy_size: FLASH: code:28056, data:5016, headers:8908 free for files:8084484
teensy_size: RAM1: variables:5760, code:25384, padding:7384 free for local variables:485760
teensy_size: RAM2: variables:12384 free for malloc/new:511904


freeRTOS compile output:
RAM 1: [= ] 8.7% (used 45600 bytes from 524288 bytes)
RAM 2: [ ] 2.5% (used 13164 bytes from 524288 bytes)
Flash: [ ] 0.8% (used 61440 bytes from 8126464 bytes)

Not sure why the two outputs look different.
 
Here is the basic example. This one shown for freeRTOS, the one for TeensyThreads is basically the same.

...

Good info - thanks for sharing. That isn't too big a hit for the added functionality and overhead it introduces.

The SIZE output difference is odd - almost familiar - but not sure why the output and order is so wrong.

If anything else shows up in use it would be good to share if others look.
 
Good info - thanks for sharing. That isn't too big a hit for the added functionality and overhead it introduces.

The SIZE output difference is odd - almost familiar - but not sure why the output and order is so wrong.

If anything else shows up in use it would be good to share if others look.

The output difference, is probably because tsandmann/freertos-teensy GitHub states this.

Furthermore it uses a more recent compiler toolchain (e.g. to support C++20) and a slightly modified Arduino core library. The latter is needed because FreeRTOS needs some interrupt vectors that are also used by the Arduino core (SysTick, SVT, PendSV, EventResponder, Audio) to be set up for the RTOS. The modified core library supports these services by running them ontop of the RTOS.
 
So now that I have tested freertos-teensy out enough to be comfortable with it. I started to add the things I really needed to do which is connect a USB keyboard to it.
Turns out, that when using USBHost_t36 crashes the RTOS.

So let me ask you guys something that is really blowing my mind with not having a RTOS on this super fact processor. With 600MHZ, how do you guys deal with not being able to do multiple things like you can with a RTOS?
Seems like your hands are tied behind your backs doing everything in void loop().
 
So now that I have tested freertos-teensy out enough to be comfortable with it. I started to add the things I really needed to do which is connect a USB keyboard to it.
Turns out, that when using USBHost_t36 crashes the RTOS.

So let me ask you guys something that is really blowing my mind with not having a RTOS on this super fact processor. With 600MHZ, how do you guys deal with not being able to do multiple things like you can with a RTOS?
Seems like your hands are tied behind your backs doing everything in void loop().

Can you tell us about your application, how you have assigned various parts to FreeRTOS tasks, and how you are managing the interactions between tasks?
 
perhaps a demo example of usbhost on t3.6 with rtos and how it crashes, it could just be a code issue, maybe UB or sepamore issue. RTOS is very finicky sometimes, I have an ESP32 using quad ble connections and CAN and it was quite an experience getting those crash resets ressolved, mostly because the BLE tasks conflicted with the loop or other race conditions, that and the issue with them running on different cores. Although it is all ressolved, I still prefer bare metal :)
 
Here is the sample code.

Code:
#include <Arduino.h>
#include "USBHost.h"
#include "arduino_freertos.h"


// You can have this one only output to the USB type Keyboard and
// not show the keyboard data on Serial...
#define SHOW_KEYBOARD_DATA


USBHost myusb;

KeyboardController keyboard1(myusb);

uint8_t keyboard_last_leds = 0;
uint8_t keyboard_modifiers = 0;  // try to keep a reasonable value

void ShowUpdatedDeviceListInfo();
void OnRawPress(uint8_t keycode);


void task1(void*) 
{


    while (true) 
    {
        myusb.Task();
        ShowUpdatedDeviceListInfo();        

        vTaskDelay(pdMS_TO_TICKS(10));
    }
}
void task2(void*) 
{
    uint16_t a=0;

    while (true) 
    {
        a++;
        vTaskDelay(pdMS_TO_TICKS(10));
    }
}

void setup()
{
    Serial.begin(115200);
    Serial1.begin(115200);

    myusb.begin();

    while (!Serial); // wait for Arduino Serial Monitor

    Serial.println("\n\nUSB Host Testing");
    Serial.println(sizeof(USBHub), arduino::DEC);

    keyboard1.attachRawPress(OnRawPress);

    xTaskCreate(task1, "task1", 2048, nullptr, 5, nullptr);
    xTaskCreate(task2, "task2", 8192, nullptr, 4, nullptr);

    Serial.println("setup(): starting scheduler...");
    Serial.flush();

    vTaskStartScheduler();    
}

void loop()
{

}

void OnRawPress(uint8_t keycode)
{

    Serial.print("OnRawPress keycode: ");
    Serial.print(keycode, arduino::HEX);
    Serial.print(" Modifiers: ");
    Serial.println(keyboard_modifiers, arduino::HEX);
}

//=============================================================
// Device and Keyboard Output To Serial objects...
//=============================================================

USBDriver* drivers[] = { &keyboard1 };
#define CNT_DEVICES (sizeof(drivers)/sizeof(drivers[0]))
const char* driver_names[CNT_DEVICES] = { "KB" };
bool driver_active[CNT_DEVICES] = { false };


void ShowUpdatedDeviceListInfo()
{

    for (uint8_t i = 0; i < CNT_DEVICES; i++)
    {
        if (*drivers[i] != driver_active[i])
        {
            if (driver_active[i])
            {
                Serial.printf("*** Device %s - disconnected ***\n", driver_names[i]);
                driver_active[i] = false;
            }
            else
            {
                Serial.printf("*** Device %s %x:%x - connected ***\n", driver_names[i], drivers[i]->idVendor(), drivers[i]->idProduct());
                driver_active[i] = true;

                const uint8_t* psz = drivers[i]->manufacturer();

                if (psz && *psz) Serial.printf("  manufacturer: %s\n", psz);
                psz = drivers[i]->product();

                if (psz && *psz) Serial.printf("  product: %s\n", psz);
                psz = drivers[i]->serialNumber();

                if (psz && *psz) Serial.printf("  Serial: %s\n", psz);

                // Note: with some keyboards there is an issue that they don't output in boot protocol mode
                // and may not work.  The above code can try to force the keyboard into boot mode, but there
                // are issues with doing this blindly with combo devices like wireless keyboard/mouse, which
                // may cause the mouse to not work.  Note: the above id is in the builtin list of
                // vendor IDs that are already forced
                if (drivers[i] == &keyboard1)
                {
                    if (keyboard1.idVendor() == 0x04D9)
                    {
                        Serial.println("Gigabyte vendor: force boot protocol");
                        // Gigabyte keyboard
                        keyboard1.forceBootProtocol();
                    }
                }
            }
        }
    }
}


After starting this is the terminal output

Code:
> Executing task: C:\Users\Jim\.platformio\penv\Scripts\platformio.exe device monitor <

--- Available filters and text transformations: colorize, debug, default, direct, hexlify, log2file, nocontrol, printable, send_on_enter, time
--- More details at https://bit.ly/pio-monitor-filters
--- Miniterm on COM6  9600,8,N,1 ---
--- Quit: Ctrl+C | Menu: Ctrl+T | Help: Ctrl+T followed by Ctrl+H ---


USB Host Testing
960
setup(): starting scheduler...

ASSERT in [.pio\libdeps\teensy41\freertos-teensy\src\portable\port.c:437]       vPortEnterCritical(): ( ( (*(volatile uint32_t *)0xE000ED04) ) & ( 0xFFUL ) ) == 0

Stack trace:
 
Here is the sample code.

I don't know anything about USBHost, but it looks like you are calling Serial.print/println from different contexts. Much of the cores and libraries are not re-entrant, so if you're going to call those from more than one task/context, you should surround those blocks of code with OS lock/unlock. Similar problems have been solved this way.
 
that's an assert error from free-rtos end on the memory map itself, that could be a problem with the freertos port

have you tried fresh rtos with blinking light?
and
have you tried all your code just using the loop in freertos?
 
Last edited:
that's an assert error from free-rtos end on the memory map itself, that could be a problem with the freertos port

have you tried fresh rtos with blinking light?
and
have you tried all your code just using the loop in freertos?

Yes, I made another test application with 5 threads doing differnt things without the USB and it works just fine.
When I add the USB is when it get goofed.

This is why It puzzles me why there is no de-facto RTOS for teensy. If you need to keep polling myusb.Task(); as often as possible, doing other things in loop() like talking to SPI & I2C peripherals along with updating the LCD and Ethernet is going to be challenging to do in the loop() function without an RTOS.
 
According to the author of the port:

Introduction

To make FreeRTOS work on the teensy boards I had to adjust some minor parts of the Teensy Arduino core library. Therefore a custom platform setting is used (platform = https://github.com/tsandmann/platform-teensy). Furthermore it uses a more recent compiler toolchain (e.g. to support C++20) and a slightly modified Arduino core library. The latter is needed because FreeRTOS needs some interrupt vectors that are also used by the Arduino core (SysTick, SVT, PendSV, EventResponder, Audio) to be set up for the RTOS. The modified core library supports these services by running them ontop of the RTOS.
Notice

Consider this as experimental code and work in progress. If it breaks, you get to keep both pieces.

I am wondering if the modified core as well as updated toolchain have a side affect to this...

By the way USBHost.h was not found, which library you using so I can download it?

EDIT: nevermind, using Teensyduino's version #include "USBHost_t36.h", seems to compile
EDIT2: uploaded to a Teensy 4.0, no crash: (nothing connected to usb host)
Code:
USB Host Testing
960
setup(): starting scheduler...
I added:
Code:
void task2(void*)
{
  uint16_t a = 0;

  while (true)
  {
    static uint32_t t = millis();
    if ( millis() - t > 1000 ) {
      a++;
      Serial.println(a);
      t = millis();
    }

    vTaskDelay(pdMS_TO_TICKS(10));
  }
}


Seems to be printing fine...
Code:
USB Host Testing
960
setup(): starting scheduler...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
...


EDIT3: although i dont have a USB header i also checked if the other task crashed or not but its still running:
Code:
#include <Arduino.h>
#include "USBHost_t36.h"
#include "arduino_freertos.h"


// You can have this one only output to the USB type Keyboard and
// not show the keyboard data on Serial...
#define SHOW_KEYBOARD_DATA


USBHost myusb;

KeyboardController keyboard1(myusb);

uint8_t keyboard_last_leds = 0;
uint8_t keyboard_modifiers = 0;  // try to keep a reasonable value

void ShowUpdatedDeviceListInfo();
void OnRawPress(uint8_t keycode);


void task1(void*)
{


  while (true)
  {
    myusb.Task();
    ShowUpdatedDeviceListInfo();
    static uint32_t t = millis();
    if ( millis() - t > 1000 ) {
      Serial.println("MyUSB Task");
      t = millis();
    }

    vTaskDelay(pdMS_TO_TICKS(10));
  }
}
void task2(void*)
{
  uint16_t a = 0;

  while (true)
  {
    static uint32_t t = millis();
    if ( millis() - t > 1000 ) {
      a++;
      Serial.println(a);
      t = millis();
    }

    vTaskDelay(pdMS_TO_TICKS(10));
  }
}

void setup()
{
  Serial.begin(115200);
  Serial1.begin(115200);

  myusb.begin();

  while (!Serial); // wait for Arduino Serial Monitor

  Serial.println("\n\nUSB Host Testing");
  Serial.println(sizeof(USBHub), arduino::DEC);

  keyboard1.attachRawPress(OnRawPress);

  xTaskCreate(task1, "task1", 2048, nullptr, 5, nullptr);
  xTaskCreate(task2, "task2", 8192, nullptr, 4, nullptr);

  Serial.println("setup(): starting scheduler...");
  Serial.flush();

  vTaskStartScheduler();
}

void loop()
{

}

void OnRawPress(uint8_t keycode)
{

  Serial.print("OnRawPress keycode: ");
  Serial.print(keycode, arduino::HEX);
  Serial.print(" Modifiers: ");
  Serial.println(keyboard_modifiers, arduino::HEX);
}

//=============================================================
// Device and Keyboard Output To Serial objects...
//=============================================================

USBDriver* drivers[] = { &keyboard1 };
#define CNT_DEVICES (sizeof(drivers)/sizeof(drivers[0]))
const char* driver_names[CNT_DEVICES] = { "KB" };
bool driver_active[CNT_DEVICES] = { false };


void ShowUpdatedDeviceListInfo()
{

  for (uint8_t i = 0; i < CNT_DEVICES; i++)
  {
    if (*drivers[i] != driver_active[i])
    {
      if (driver_active[i])
      {
        Serial.printf("*** Device %s - disconnected ***\n", driver_names[i]);
        driver_active[i] = false;
      }
      else
      {
        Serial.printf("*** Device %s %x:%x - connected ***\n", driver_names[i], drivers[i]->idVendor(), drivers[i]->idProduct());
        driver_active[i] = true;

        const uint8_t* psz = drivers[i]->manufacturer();

        if (psz && *psz) Serial.printf("  manufacturer: %s\n", psz);
        psz = drivers[i]->product();

        if (psz && *psz) Serial.printf("  product: %s\n", psz);
        psz = drivers[i]->serialNumber();

        if (psz && *psz) Serial.printf("  Serial: %s\n", psz);

        // Note: with some keyboards there is an issue that they don't output in boot protocol mode
        // and may not work.  The above code can try to force the keyboard into boot mode, but there
        // are issues with doing this blindly with combo devices like wireless keyboard/mouse, which
        // may cause the mouse to not work.  Note: the above id is in the builtin list of
        // vendor IDs that are already forced
        if (drivers[i] == &keyboard1)
        {
          if (keyboard1.idVendor() == 0x04D9)
          {
            Serial.println("Gigabyte vendor: force boot protocol");
            // Gigabyte keyboard
            keyboard1.forceBootProtocol();
          }
        }
      }
    }
  }
}

Code:
USB Host Testing
960
setup(): starting scheduler...
MyUSB Task
1
MyUSB Task
2
MyUSB Task
3
MyUSB Task
4
MyUSB Task
5
MyUSB Task
6
MyUSB Task
7
MyUSB Task
8
MyUSB Task
9
MyUSB Task
10
MyUSB Task
11
MyUSB Task
12
MyUSB Task
13
MyUSB Task
 
Last edited:
I’m a hug Teensy fan, I’ve used the 3.x and 4.x boards in loads of my hobby projects. But last month I got an ESP32 based board (Lilygo Display T3) and have totally fallen in love with FreeRTOS… hopefully we’ll see FreeRTOS fully supported on our favourite Teensy soon :)
 
I've got a Teensy 4.1 and the example code posted by MikeMoy on 3/29/2022 seems to work. But when I uncomment the digitalIO lines to blink the LED, The LED does not blink.

If I run the standard "Blink" example using the Arduino library (no FreeRTOS) then the LED blinks.

If I try to merge the two and use the Arduino code in the FreeRTOS example, The system crashes with the following crashdump on the serial port ...

Code:
Fault IRQ:    0x00000004
 sp:          0x20207210
 lr:          0xFFFFFFFD
 stacked_r0:  0x600018E1
 stacked_r1:  0x00000001
 stacked_r2:  0x25D03B28
 stacked_r3:  0x60001811
 stacked_r12: 0xA5A5A5A5
 stacked_lr:  0x00000119
 stacked_pc:  0x0000321E
 stacked_psr: 0x61000200
 _CFSR:       0x00000082
  (DACCVIOL)    Data Access Violation
  (MMARVALID)   MemMange Fault Address Valid
 _HFSR:       0x00000000
 _DFSR:       0x00000000
 _AFSR:       0x00000000
 _BFAR:       0x25D03B2C
 _MMAR:       0x25D03B2C

Active task (TCB): task2
Active task (stack): task2

Stack trace:

Here is the offending code ...

Code:
#include <Arduino.h>

#include "arduino_freertos.h"
#include "avr/pgmspace.h"

volatile uint16_t a,b,c;

void task1(void*) 
{
    a=0;
    while (true) 
    {
        a++;
        vTaskDelay(pdMS_TO_TICKS(100));
    }
}

void task2(void*) 
{
    b=0;
    int led = 13; 
    pinMode(led, arduino::OUTPUT);
    
    while (true) 
    {
        b++;
        digitalWrite(led, arduino::HIGH);
        //digitalWriteFast(led, arduino::HIGH);
        vTaskDelay(pdMS_TO_TICKS(250));
        
        digitalWrite(led, arduino::LOW);
        //digitalWriteFast(led, arduino::LOW);
        vTaskDelay(pdMS_TO_TICKS(500));

    }
}


void task3(void*) 
{
    c=0;
    Serial.begin(115200);
    while (!Serial){vTaskDelay(pdMS_TO_TICKS(10));}; // wait for Arduino Serial Monitor

    while (true) 
    {   
        c++;
        Serial.printf("a[%06u] : b[%06u] : c[%06u]\r\n",a,b,c);
        vTaskDelay(pdMS_TO_TICKS(250));
    }
}


void setup()
{
    
    xTaskCreate(task1, "task1", 2048, nullptr, 3, nullptr);
    xTaskCreate(task2, "task2", 2048, nullptr, 2, nullptr);
    xTaskCreate(task3, "task3", 2048, nullptr, 1, nullptr);

    vTaskStartScheduler();
}


void loop()
{

}
 
Is the Teensy Arduino library threadsafe? If not, you would need to keep all your Arduino code in a single thread and use other threads for processing, using proper synchronisation primitives.
 
I've got a Teensy 4.1 and the example code posted by MikeMoy on 3/29/2022 seems to work. But when I uncomment the digitalIO lines to blink the LED, The LED does not blink.

If I run the standard "Blink" example using the Arduino library (no FreeRTOS) then the LED blinks.

If I try to merge the two and use the Arduino code in the FreeRTOS example, The system crashes with the following crashdump on the serial port ...

Your code works for me as expected on a Teensy 4.1.
Are you using Teensyduino or platformio?
 
Back
Top