FreeRTOS example for tinyAVR 1-series ATtiny1616

I’m developing my first FreeRTOS project using AVR microcontroller. I have a simple PCB with ATtiny1616-MFR and two LED’s. As “it is always recommended that a new FreeRTOS project is created by starting with, and then adapting, one of the provided pre-configured demos” I found the most similar example form Microchip application note AN3007 for megaAVR 0-series. The example uses the ATmega4809 board.

I modified it to work with ATtiny1616 and created a simple “blinky”. Changed configTOTAL_HEAP_SIZE to 0x400 in FreeRTOSConfig.h and changed clock to 5Mhz. The program compiles correctly, breakpoint in port.c tick interrupt handler is hit corrctly but vTaskDelay get stucked.

I’m hoping someone can point me to an example that I can use as a reference for my application or explain to me what I’m doing wrong.

main.c

#include <avr/io.h>
#include <stdio.h>
#include “lib/lib.h”

/* Scheduler include files. */
#include “FreeRTOS.h”
#include “task.h”
#include “queue.h”
#include “stream_buffer.h”
#include “message_buffer.h”
#include “semphr.h”

/* Function Prototypes */
void tsk_blk(void *pvParams);

#define RGB_PORT PORTC
#define RGB_LED_ON OUTSET
#define RGB_LED_OFF OUTCLR
#define LED_GREEN_PIN PIN_PC0
#define LED_GREEN_PORT PORTC
#define LED_RED_PIN PIN_PC1
#define LED_RED_PORT PORTC

int main(void)
{
/* SET CLOCK SOURCE AND PRESCALER */
_PROTECTED_WRITE(CLKCTRL.MCLKCTRLA, 0);
_PROTECTED_WRITE(CLKCTRL.MCLKCTRLB, CLKCTRL_PDIV_4X_gc | CLKCTRL_PEN_bm);

/* INIT PORTS */
RGB_PORT.DIRSET = (1 << LED_GREEN_PIN) | (1 << LED_RED_PIN);
RGB_PORT.RGB_LED_OFF = (1 << LED_GREEN_PIN) | (1 << LED_RED_PIN);

/* Task Registration and creation */
xTaskCreate(tsk_blk, “blk”, configMINIMAL_STACK_SIZE, NULL, 0, NULL);

/* RUN SCHEDULER */
vTaskStartScheduler();

/* TURN ON RED IF vTaskStartScheduler ENDS */
RGB_PORT.RGB_LED_OFF = (1 << LED_GREEN_PIN);
RGB_PORT.RGB_LED_ON = (1 << LED_RED_PIN);

return 0;
}

void tsk_blk(void *pvParams)
{
while (1) {
RGB_PORT.OUTTGL = (1 << LED_GREEN_PIN);
vTaskDelay(pdMS_TO_TICKS(500));
}
}

FreeRTOSConfig.h

#ifndef FREERTOS_CONFIG_H
#define FREERTOS_CONFIG_H

/*

  • #define TCB_t to avoid conflicts between the
  • FreeRTOS task control block type (TCB_t) and the
  • AVR Timer Counter B type (TCB_t)
    */
    #define TCB_t avrTCB_t
    #include <avr/io.h>
    #undef TCB_t

#define configUSE_PREEMPTION 1
#define configCPU_CLOCK_HZ (20e6 / 4) // (20e6 / 2)
#define configTICK_RATE_HZ 1000
#define configMAX_PRIORITIES 4
#define configMINIMAL_STACK_SIZE 128 // 115
#define configMAX_TASK_NAME_LEN 8
#define configUSE_16_BIT_TICKS 1
#define configIDLE_SHOULD_YIELD 1
#define configUSE_TASK_NOTIFICATIONS 1
#define configUSE_MUTEXES 1
#define configUSE_RECURSIVE_MUTEXES 0
#define configUSE_COUNTING_SEMAPHORES 0
#define configQUEUE_REGISTRY_SIZE 0
#define configUSE_QUEUE_SETS 0
#define configUSE_TIME_SLICING 1
#define configUSE_NEWLIB_REENTRANT 0
#define configENABLE_BACKWARD_COMPATIBILITY 0
#define configNUM_THREAD_LOCAL_STORAGE_POINTERS 0

/* Memory allocation related definitions. */
#define configSUPPORT_STATIC_ALLOCATION 0
#define configSUPPORT_DYNAMIC_ALLOCATION 1
#define configTOTAL_HEAP_SIZE 0x400 //0x800
#define configAPPLICATION_ALLOCATED_HEAP 0

/* Hook function related definitions. */
#define configUSE_IDLE_HOOK 0
#define configUSE_TICK_HOOK 0
#define configCHECK_FOR_STACK_OVERFLOW 0
#define configUSE_MALLOC_FAILED_HOOK 0
#define configUSE_DAEMON_TASK_STARTUP_HOOK 0

/* Run time and task stats gathering related definitions. */
#define configGENERATE_RUN_TIME_STATS 0
#define configUSE_TRACE_FACILITY 0
#define configUSE_STATS_FORMATTING_FUNCTIONS 0

/* Co-routine related definitions. */
#define configUSE_CO_ROUTINES 0
#define configMAX_CO_ROUTINE_PRIORITIES 1

/* Software timer related definitions. */
#define configUSE_TIMERS 0
#define configTIMER_TASK_PRIORITY 3
#define configTIMER_QUEUE_LENGTH 10
#define configTIMER_TASK_STACK_DEPTH configMINIMAL_STACK_SIZE

/* Define to trap errors during development. */
#define configASSERT( x ) if( ( x ) == 0 ) { asm volatile (“cli”); while(1){ asm volatile (“BREAK”); } }

/* Optional functions - most linkers will remove unused functions anyway. */
#define INCLUDE_vTaskPrioritySet 0
#define INCLUDE_uxTaskPriorityGet 0
#define INCLUDE_vTaskDelete 0
#define INCLUDE_vTaskSuspend 1
#define INCLUDE_xResumeFromISR 0
#define INCLUDE_vTaskDelayUntil 1
#define INCLUDE_vTaskDelay 1
#define INCLUDE_xTaskGetSchedulerState 0
#define INCLUDE_xTaskGetCurrentTaskHandle 0
#define INCLUDE_uxTaskGetStackHighWaterMark 0
#define INCLUDE_xTaskGetIdleTaskHandle 0
#define INCLUDE_eTaskGetState 0
#define INCLUDE_xEventGroupSetBitFromISR 0
#define INCLUDE_xTimerPendFunctionCall 0
#define INCLUDE_xTaskAbortDelay 0
#define INCLUDE_xTaskGetHandle 0
#define INCLUDE_xTaskResumeFromISR 0

#define pdMS_TO_TICKS(xTimeInMs) ((TickType_t)(((uint32_t)(xTimeInMs) * (uint32_t)configTICK_RATE_HZ) / (uint32_t)1000))

#endif /* FREERTOS_CONFIG_H */

There is an old demo for an ATMega323/ATMega32, which like the ATtiiny1616, also has 2K of RAM here: FreeRTOS/FreeRTOS/Demo/AVR_ATMega323_WinAVR at main · FreeRTOS/FreeRTOS · GitHub

Can you elaborate on vTaskDelay() getting stuck? It looks like you only have one task running, although the kernel will also create the Idle task, so one of those two tasks should be running. What is the processor running if you pause the debugger after vTaskDelay() is called? Also, does xTickCount continue to count up? That is, do you keep entering the tick interrupt at the correct frequency, or does it only run once?

I added hooks for idle and tick to monitor program behaviour and xTickCount value. I also placed breakpoint in the tick Interrupt Service Routine. Breakpoints are place as shown below.

port.c

ISR(TCB0_INT_vect, ISR_NAKED)
{
/* Clear RTC interrupt flags */
asm volatile(“push r16”);
asm volatile(“ldi r16, 0x1”);
asm volatile(“sts 0x0A86, r16”);
asm volatile(“pop r16”);

vPortYieldFromTick(); // BREAKPOINT

asm volatile(“reti”);
}

main.c

void tsk_blk(void *pvParams)
{
while (1) {
RGB_PORT.OUTTGL = (1 << LED_GREEN_PIN); // BREAKPOINT
vTaskDelay(pdMS_TO_TICKS(20));
}
}

void vApplicationIdleHook( void )
{
asm(“nop”); // BREAKPOINT
//vCoRoutineSchedule();
}

void vApplicationTickHook( void )
{
asm(“nop”); // BREAKPOINT
}

When all breakpoints are active the “blik task” breakpoint is hit only once, and then the idle task is running forever without increasing TickCount. I logged messages on breakpoints:

Blink task, xTickCoun=0
Idle hook, xTickCoun=0
Idle hook, xTickCoun=0
Idle hook, xTickCoun=0
Idle hook, xTickCoun=0
Idle hook, xTickCoun=0
Idle hook, xTickCoun=0
Idle hook, xTickCoun=0
Idle hook, xTickCoun=0 …

However when I disabled the idle task breakpoint the ISR and tick hook breakpoints works, and the xTickCount continue to count up, but the “blink task” is still stuck forever:

Blink task, xTickCoun=0
TCB0 ISR, xTickCount=0
Tick Hook, xTickCoun=1
TCB0 ISR, xTickCount=1
Tick Hook, xTickCoun=2
TCB0 ISR, xTickCount=2
Tick Hook, xTickCoun=3
TCB0 ISR, xTickCount=3
Tick Hook, xTickCoun=4
TCB0 ISR, xTickCount=4
Tick Hook, xTickCoun=5
TCB0 ISR, xTickCount=5 …

Are there any architectural differences between the ATMega and ATTiny?

To my knowledge the only differences between ATMega4808 and ATtiny1616 are memory size, clock sources nad some of peripherals. Both of the microcontrollers have TCB0 timer which is used for tick. Both are listed as “relevant devices” for AN3007 Microchip application note.
ATMega323 is quite old and there may be more differences between ATtiny1616 and ATMega323.

Hello @Master,
Are you logging these messages manually? Because if you print them in ISR, it won’t be pretty and will have some side effects.

Can you see what happens in the scheduler code after the tick interrupt happens by stepping through the code?

-Aniruddha