TNeo  v1.09
Quick guide

This page contains quick guide on system startup and important implementation details.

Using TNeo in your application

The easiest way is to download version archive from downloads page: it contains bin folder with library files for various platforms. These library files were built with default configuration file (src/tn_cfg_default.h, see the section Configuration file).

If you use MPLABX, it is probably better to add library project to your main project instead; library projects reside in the <tneo_path>/lib_project directory.

In either case, all you need is the following:

  • Add library file for appropriate platform to your project (or probably library project in case of MPLABX);
  • Add C include path: <tneo_path>/src;
  • Copy default configuration file as current configuration file: cp <tneo_path>/src/tn_cfg_default.h <tneo_path>/src/tn_cfg.h (for more information about configuration file and better ways to manage it, refer to the section Configuration file)
  • Add the file <tneo_path>/src/tn_app_check.c to your application project (see #TN_CHECK_BUILD_CFG for details on it).

That's all; you can use it in your project now. See below how to do this.

Attention
If you need to change the configuration, you can't just edit tn_cfg.h and keep using pre-built library file: you need to rebuild the library after editing tn_cfg.h. Refer to the page Building TNeo for details.

Time ticks

The kernel needs to calculate timeouts. There are two schemes available: static tick and dynamic tick. For a quick guide, it's quite enough to just read about static tick, so, for the details on it, refer to the section Static tick and then return back here.

Starting the kernel

Quick guide on startup process

  • You allocate arrays for idle task stack and interrupt stack, there is a convenience macro TN_STACK_ARR_DEF() for that. It is good idea to consult the #TN_MIN_STACK_SIZE to determine stack sizes (see example below).
  • You provide callback function like void init_task_create(void) { ... }, in which at least one (and typically just one) your own task should be created and activated. This task should perform application initialization and create all the rest of tasks. See details in TN_CBUserTaskCreate().
  • You provide idle callback function to be called periodically from idle task. It's quite fine to leave it empty.
  • In the main() you should:
    • disable system interrupts by calling tn_arch_int_dis();
    • perform some essential CPU configuration, such as oscillator settings and similar things.
    • setup system timer interrupt (from which tn_tick_int_processing() gets called)
    • call tn_sys_start() providing all necessary information: pointers to stacks, their sizes and your callback functions.
  • Kernel acts as follows:
    • performs all necessary housekeeping;
    • creates idle task;
    • calls your TN_CBUserTaskCreate() callback, in which your initial task is created with #TN_TASK_CREATE_OPT_START option;
    • performs first context switch (to your task with highest priority).
  • At this point, system operates normally: your initial task gets executed and you can call whatever system services you need. Typically, your initial task acts then as follows:
    • Perform initialization of various on-board peripherals (displays, flash memory chips, or whatever);
    • Initialize software modules used by application;
    • Create all the rest of your tasks (since everything is initialized already so that they can proceed with their job);
    • Eventually, perform its primary job (the job for which task was created at all).

Basic example for PIC32

This example project can be found in the TNeo repository, in the examples/basic/arch/pic32 directory.

Attention
Before trying to build examples, please read Building TNeo page carefully: you need to copy configuration file in the tneo directory to build it. Each example has tn_cfg_appl.h file, and you should either create a symbolic link to this file from tneo/src/tn_cfg.h or just copy this file as tneo/src/tn_cfg.h.
/**
* TNeo PIC32 basic example
*/
/*******************************************************************************
* INCLUDED FILES
******************************************************************************/
#include <xc.h>
#include <plib.h>
#include <stdint.h>
#include "tn.h"
/*******************************************************************************
* PIC32 HARDWARE CONFIGURATION
******************************************************************************/
#pragma config FNOSC = PRIPLL // Oscillator Selection
#pragma config FPLLIDIV = DIV_4 // PLL Input Divider (PIC32 Starter Kit: use divide by 2 only)
#pragma config FPLLMUL = MUL_20 // PLL Multiplier
#pragma config FPLLODIV = DIV_1 // PLL Output Divider
#pragma config FPBDIV = DIV_2 // Peripheral Clock divisor
#pragma config FWDTEN = OFF // Watchdog Timer
#pragma config WDTPS = PS1 // Watchdog Timer Postscale
#pragma config FCKSM = CSDCMD // Clock Switching & Fail Safe Clock Monitor
#pragma config OSCIOFNC = OFF // CLKO Enable
#pragma config POSCMOD = HS // Primary Oscillator
#pragma config IESO = OFF // Internal/External Switch-over
#pragma config FSOSCEN = OFF // Secondary Oscillator Enable
#pragma config CP = OFF // Code Protect
#pragma config BWP = OFF // Boot Flash Write Protect
#pragma config PWP = OFF // Program Flash Write Protect
#pragma config ICESEL = ICS_PGx2 // ICE/ICD Comm Channel Select
#pragma config DEBUG = OFF // Debugger Disabled for Starter Kit
/*******************************************************************************
* MACROS
******************************************************************************/
//-- instruction that causes debugger to halt
#define PIC32_SOFTWARE_BREAK() __asm__ volatile ("sdbbp 0")
//-- system frequency
#define SYS_FREQ 80000000UL
//-- peripheral bus frequency
#define PB_FREQ 40000000UL
//-- kernel ticks (system timer) frequency
#define SYS_TMR_FREQ 1000
//-- system timer prescaler
#define SYS_TMR_PRESCALER T5_PS_1_8
#define SYS_TMR_PRESCALER_VALUE 8
//-- system timer period (auto-calculated)
#define SYS_TMR_PERIOD \
(PB_FREQ / SYS_TMR_PRESCALER_VALUE / SYS_TMR_FREQ)
//-- idle task stack size, in words
#define IDLE_TASK_STACK_SIZE (TN_MIN_STACK_SIZE + 32)
//-- interrupt stack size, in words
#define INTERRUPT_STACK_SIZE (TN_MIN_STACK_SIZE + 64)
//-- stack sizes of user tasks
#define TASK_A_STK_SIZE (TN_MIN_STACK_SIZE + 96)
#define TASK_B_STK_SIZE (TN_MIN_STACK_SIZE + 96)
#define TASK_C_STK_SIZE (TN_MIN_STACK_SIZE + 96)
//-- user task priorities
#define TASK_A_PRIORITY 7
#define TASK_B_PRIORITY 6
#define TASK_C_PRIORITY 5
/*******************************************************************************
* DATA
******************************************************************************/
//-- Allocate arrays for stacks: stack for idle task
// and for interrupts are the requirement of the kernel;
// others are application-dependent.
//
// We use convenience macro TN_STACK_ARR_DEF() for that.
TN_STACK_ARR_DEF(idle_task_stack, IDLE_TASK_STACK_SIZE);
TN_STACK_ARR_DEF(interrupt_stack, INTERRUPT_STACK_SIZE);
TN_STACK_ARR_DEF(task_a_stack, TASK_A_STK_SIZE);
TN_STACK_ARR_DEF(task_b_stack, TASK_B_STK_SIZE);
TN_STACK_ARR_DEF(task_c_stack, TASK_C_STK_SIZE);
//-- task structures
struct TN_Task task_a = {};
struct TN_Task task_b = {};
struct TN_Task task_c = {};
/*******************************************************************************
* ISRs
******************************************************************************/
/**
* system timer ISR
*/
tn_p32_soft_isr(_TIMER_5_VECTOR)
{
INTClearFlag(INT_T5);
}
/*******************************************************************************
* FUNCTIONS
******************************************************************************/
void appl_init(void);
void task_a_body(void *par)
{
//-- this is a first created application task, so it needs to perform
// all the application initialization.
appl_init();
//-- and then, let's get to the primary job of the task
// (job for which task was created at all)
for(;;)
{
mPORTEToggleBits(BIT_0);
}
}
void task_b_body(void *par)
{
for(;;)
{
mPORTEToggleBits(BIT_1);
}
}
void task_c_body(void *par)
{
for(;;)
{
mPORTEToggleBits(BIT_2);
}
}
/**
* Hardware init: called from main() with interrupts disabled
*/
void hw_init(void)
{
SYSTEMConfig(SYS_FREQ, SYS_CFG_WAIT_STATES | SYS_CFG_PCACHE);
//turn off ADC function for all pins
AD1PCFG = 0xffffffff;
//-- enable timer5 interrupt
OpenTimer5((0
| T5_ON
| T5_IDLE_STOP
| SYS_TMR_PRESCALER
| T5_SOURCE_INT
),
(SYS_TMR_PERIOD - 1)
);
//-- set timer5 interrupt priority to 2, enable it
INTSetVectorPriority(INT_TIMER_5_VECTOR, INT_PRIORITY_LEVEL_2);
INTSetVectorSubPriority(INT_TIMER_5_VECTOR, INT_SUB_PRIORITY_LEVEL_0);
INTClearFlag(INT_T5);
INTEnable(INT_T5, INT_ENABLED);
//-- enable multi-vectored interrupt mode
INTConfigureSystem(INT_SYSTEM_CONFIG_MULT_VECTOR);
}
/**
* Application init: called from the first created application task
*/
void appl_init(void)
{
//-- configure LED port pins
mPORTESetPinsDigitalOut(BIT_0 | BIT_1 | BIT_2);
mPORTEClearBits(BIT_0 | BIT_1 | BIT_2);
//-- initialize various on-board peripherals, such as
// flash memory, displays, etc.
// (in this sample project there's nothing to init)
//-- initialize various program modules
// (in this sample project there's nothing to init)
//-- create all the rest application tasks
&task_b,
task_b_body,
TASK_B_PRIORITY,
task_b_stack,
TASK_B_STK_SIZE,
NULL,
);
&task_c,
task_c_body,
TASK_C_PRIORITY,
task_c_stack,
TASK_C_STK_SIZE,
NULL,
);
}
//-- idle callback that is called periodically from idle task
void idle_task_callback (void)
{
}
//-- create first application task(s)
void init_task_create(void)
{
//-- task A performs complete application initialization,
// it's the first created application task
&task_a, //-- task structure
task_a_body, //-- task body function
TASK_A_PRIORITY, //-- task priority
task_a_stack, //-- task stack
TASK_A_STK_SIZE, //-- task stack size (in words)
NULL, //-- task function parameter
TN_TASK_CREATE_OPT_START //-- creation option
);
}
int32_t main(void)
{
#ifndef PIC32_STARTER_KIT
/*The JTAG is on by default on POR. A PIC32 Starter Kit uses the JTAG, but
for other debug tool use, like ICD 3 and Real ICE, the JTAG should be off
to free up the JTAG I/O */
DDPCONbits.JTAGEN = 0;
#endif
//-- unconditionally disable interrupts
//-- init hardware
hw_init();
//-- call to tn_sys_start() never returns
idle_task_stack,
IDLE_TASK_STACK_SIZE,
interrupt_stack,
INTERRUPT_STACK_SIZE,
init_task_create,
idle_task_callback
);
//-- unreachable
return 1;
}
void __attribute__((naked, nomips16, noreturn)) _general_exception_handler(void)
{
PIC32_SOFTWARE_BREAK();
for (;;) ;
}

Round-robin scheduling

TNKernel has the ability to make round robin scheduling for tasks with identical priority. By default, round robin scheduling is turned off for all priorities. To enable round robin scheduling for tasks on certain priority level and to set time slices for these priority, user must call the tn_sys_tslice_set() function. The time slice value is the same for all tasks with identical priority but may be different for each priority level. If the round robin scheduling is enabled, every system time tick interrupt increments the currently running task time slice counter. When the time slice interval is completed, the task is placed at the tail of the ready to run queue of its priority level (this queue contains tasks in the RUNNABLE state) and the time slice counter is cleared. Then the task may be preempted by tasks of higher or equal priority.

In most cases, there is no reason to enable round robin scheduling. For applications running multiple copies of the same code, however, (GUI windows, etc), round robin scheduling is an acceptable solution.

Attention
Round-robin is not supported in Dynamic tick mode.
tn_task_sleep
enum TN_RCode tn_task_sleep(TN_TickCnt timeout)
Put current task to sleep for at most timeout ticks.
tn_arch_int_dis
void tn_arch_int_dis(void)
Unconditionally disable system interrupts.
tn_p32_soft_isr
#define tn_p32_soft_isr(vec)
Interrupt handler wrapper macro for software context saving.
Definition: tn_arch_pic32.h:344
tn.h
TN_Task
Task.
Definition: tn_tasks.h:330
tn_sys_start
void tn_sys_start(TN_UWord *idle_task_stack, unsigned int idle_task_stack_size, TN_UWord *int_stack, unsigned int int_stack_size, TN_CBUserTaskCreate *cb_user_task_create, TN_CBIdle *cb_idle)
Initial TNeo system start function, never returns.
TN_STACK_ARR_DEF
#define TN_STACK_ARR_DEF(name, size)
Convenience macro for the definition of stack array.
Definition: tn_sys.h:87
TN_TASK_CREATE_OPT_START
@ TN_TASK_CREATE_OPT_START
whether task should be activated right after it is created.
Definition: tn_tasks.h:226
tn_tick_int_processing
void tn_tick_int_processing(void)
Process system tick; should be called periodically, typically from some kind of timer ISR.
tn_task_create
enum TN_RCode tn_task_create(struct TN_Task *task, TN_TaskBody *task_func, int priority, TN_UWord *task_stack_low_addr, int task_stack_size, void *param, enum TN_TaskCreateOpt opts)
Construct task and probably start it (depends on options, see below).