TNeo  v1.08
Quick guide

Table of Contents

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:

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

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.