I've recently started a project with the Arduino MKR1310 and I'm struggling with the RTOS implementation for the SAMD21 processors.
According to the Arduino home page (freertos_samd21) this board should be compatible with the implementation from BriscoeTech.
I've tested the basic examples with MKR1000 and it works fine. However, with this model, the firmware is not loading. The serial port simply goes away and everything seems to be frozen. I already attempted to put some led blinking during the setup() but even that doesn't work, which from my point of view could indicate that this is something related to the bootloader. Maybe the freertos implementation is writing to some register that it shouldn't? How can I debug this issue? I really want to have freertos running in order to schedule the publishing via LoRa and others without stopping the main tasks.
FreeRTOS isn't connected in anyway to the bootloader, therefore it's most likely that your program has simply crashed, preventing the native USB and any further uploads from working.
If you're currently unable to upload code, just double tap the reset button two times in quick succession. This will put your board into bootloader mode. Select the new COM port in the Arduino IDE menu (Tools->Port->COMx), then attempt to upload as normal. You should be able to always recover the board this way.
Hi @MartinL,
Thank you for the reply. I know how to get the device again ready to be programmed using the method mentioned. But that’s not the issue. The issue is that every time I upload the demo sketch it will break.
//**************************************************************************
// FreeRtos on Samd21
// By Scott Briscoe
//
// Project is a simple example of how to get FreeRtos running on a SamD21 processor
// Project can be used as a template to build your projects off of as well
//
//**************************************************************************
#include <FreeRTOS_SAMD21.h>
//**************************************************************************
// Type Defines and Constants
//**************************************************************************
#define ERROR_LED_PIN 13 //Led Pin: Typical Arduino Board
//#define ERROR_LED_PIN 2 //Led Pin: samd21 xplained board
#define ERROR_LED_LIGHTUP_STATE HIGH // the state that makes the led light up on your board, either low or high
// Select the serial port the project should use and communicate over
// Some boards use SerialUSB, some use Serial
#define SERIAL SerialUSB //Sparkfun Samd21 Boards
//#define SERIAL Serial //Adafruit, other Samd21 Boards
//**************************************************************************
// global variables
//**************************************************************************
TaskHandle_t Handle_aTask;
TaskHandle_t Handle_bTask;
TaskHandle_t Handle_monitorTask;
//**************************************************************************
// Can use these function for RTOS delays
// Takes into account processor speed
// Use these instead of delay(...) in rtos tasks
//**************************************************************************
void myDelayUs(int us)
{
vTaskDelay( us / portTICK_PERIOD_US );
}
void myDelayMs(int ms)
{
vTaskDelay( (ms * 1000) / portTICK_PERIOD_US );
}
void myDelayMsUntil(TickType_t *previousWakeTime, int ms)
{
vTaskDelayUntil( previousWakeTime, (ms * 1000) / portTICK_PERIOD_US );
}
//*****************************************************************
// Create a thread that prints out A to the screen every two seconds
// this task will delete its self after printing out afew messages
//*****************************************************************
static void threadA( void *pvParameters )
{
SERIAL.println("Thread A: Started");
for(int x=0; x<100; ++x)
{
SERIAL.print("A");
SERIAL.flush();
myDelayMs(500);
}
// delete ourselves.
// Have to call this or the system crashes when you reach the end bracket and then get scheduled.
SERIAL.println("Thread A: Deleting");
vTaskDelete( NULL );
}
//*****************************************************************
// Create a thread that prints out B to the screen every second
// this task will run forever
//*****************************************************************
static void threadB( void *pvParameters )
{
SERIAL.println("Thread B: Started");
while(1)
{
SERIAL.println("B");
SERIAL.flush();
myDelayMs(2000);
}
}
//*****************************************************************
// Task will periodically print out useful information about the tasks running
// Is a useful tool to help figure out stack sizes being used
// Run time stats are generated from all task timing collected since startup
// No easy way yet to clear the run time stats yet
//*****************************************************************
static char ptrTaskList[400]; //temporary string buffer for task stats
void taskMonitor(void *pvParameters)
{
int x;
int measurement;
SERIAL.println("Task Monitor: Started");
// run this task afew times before exiting forever
while(1)
{
myDelayMs(10000); // print every 10 seconds
SERIAL.flush();
SERIAL.println("");
SERIAL.println("****************************************************");
SERIAL.print("Free Heap: ");
SERIAL.print(xPortGetFreeHeapSize());
SERIAL.println(" bytes");
SERIAL.print("Min Heap: ");
SERIAL.print(xPortGetMinimumEverFreeHeapSize());
SERIAL.println(" bytes");
SERIAL.flush();
SERIAL.println("****************************************************");
SERIAL.println("Task ABS %Util");
SERIAL.println("****************************************************");
vTaskGetRunTimeStats(ptrTaskList); //save stats to char array
SERIAL.println(ptrTaskList); //prints out already formatted stats
SERIAL.flush();
SERIAL.println("****************************************************");
SERIAL.println("Task State Prio Stack Num Core" );
SERIAL.println("****************************************************");
vTaskList(ptrTaskList); //save stats to char array
SERIAL.println(ptrTaskList); //prints out already formatted stats
SERIAL.flush();
SERIAL.println("****************************************************");
SERIAL.println("[Stacks Free Bytes Remaining] ");
measurement = uxTaskGetStackHighWaterMark( Handle_aTask );
SERIAL.print("Thread A: ");
SERIAL.println(measurement);
measurement = uxTaskGetStackHighWaterMark( Handle_bTask );
SERIAL.print("Thread B: ");
SERIAL.println(measurement);
measurement = uxTaskGetStackHighWaterMark( Handle_monitorTask );
SERIAL.print("Monitor Stack: ");
SERIAL.println(measurement);
SERIAL.println("****************************************************");
SERIAL.flush();
}
// delete ourselves.
// Have to call this or the system crashes when you reach the end bracket and then get scheduled.
SERIAL.println("Task Monitor: Deleting");
vTaskDelete( NULL );
}
//*****************************************************************
void setup()
{
SERIAL.begin(115200);
delay(1000); // prevents usb driver crash on startup, do not omit this
while (!SERIAL) ; // Wait for serial terminal to open port before starting program
SERIAL.println("");
SERIAL.println("******************************");
SERIAL.println(" Program start ");
SERIAL.println("******************************");
SERIAL.flush();
// Set the led the rtos will blink when we have a fatal rtos error
// RTOS also Needs to know if high/low is the state that turns on the led.
// Error Blink Codes:
// 3 blinks - Fatal Rtos Error, something bad happened. Think really hard about what you just changed.
// 2 blinks - Malloc Failed, Happens when you couldn't create a rtos object.
// Probably ran out of heap.
// 1 blink - Stack overflow, Task needs more bytes defined for its stack!
// Use the taskMonitor thread to help gauge how much more you need
vSetErrorLed(ERROR_LED_PIN, ERROR_LED_LIGHTUP_STATE);
// sets the serial port to print errors to when the rtos crashes
// if this is not set, serial information is not printed by default
vSetErrorSerial(&SERIAL);
// Create the threads that will be managed by the rtos
// Sets the stack size and priority of each task
// Also initializes a handler pointer to each task, which are important to communicate with and retrieve info from tasks
xTaskCreate(threadA, "Task A", 256, NULL, tskIDLE_PRIORITY + 3, &Handle_aTask);
xTaskCreate(threadB, "Task B", 256, NULL, tskIDLE_PRIORITY + 2, &Handle_bTask);
xTaskCreate(taskMonitor, "Task Monitor", 256, NULL, tskIDLE_PRIORITY + 1, &Handle_monitorTask);
// Start the RTOS, this function will never return and will schedule the tasks.
vTaskStartScheduler();
// error scheduler failed to start
// should never get here
while(1)
{
SERIAL.println("Scheduler Failed! \n");
SERIAL.flush();
delay(1000);
}
}
//*****************************************************************
// This is now the rtos idle loop
// No rtos blocking functions allowed!
//*****************************************************************
void loop()
{
// Optional commands, can comment/uncomment below
SERIAL.print("."); //print out dots in terminal, we only do this when the RTOS is in the idle state
SERIAL.flush();
delay(100); //delay is interrupt friendly, unlike vNopDelayMS
}
//*****************************************************************
I really don't know anything about this version of freeRTOS but I am familiar with freeRTOS on the ESP32.
I noticed that the tasks only have a stack size of 256 bytes of ram. Is that enough? On a ESP32 I start with 10000 bytes and then use the stack size water mark to get the task size ram use as I develop the task. I then set the stack size to 2000 more bytes then the minimum reported stack size.
Perhaps it is not the case in this version of freeRTOS but on ESP32 putting code in loop() prevents the freeRTOS housekeeping chores from being ran. Also, on a ESP32, loop() is not guarenteed to run, depending upon task load.
I noticed the use of delay(x). On a ESP32 when delay is used it is translated into vTaskDelay(x) which in a task stops the task but does not stop the CPU. Not sure if that is the case with this version of freeRTOS.
It's not supported by my interpretation of 'main.cpp'. The app_main() function spools up the 'loopTask' task. That task calls setup() and then enters an infinite 'for loop' calling loop() and (conditionally) esp_task_wdt_reset(), and serialEventRun().
So, whether or not there's any code in the user's loop() function, the 'loopTask' task still runs. And whether or not there's any code in the user's loop() function, I don't see anything in there to prevent "housekeeping chores" from running.