Code examples

Previous Page

Example 1

The first example is a simple initialization of 2+1(idle) processes. No special tricks or functionality is involved.

#include <avr/io.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <avr/pgmspace.h>
#include <avr/interrupt.h>
#include <util/atomic.h>
#include "kernel_memory.h"
#include "kernel_process.h"
#include "kernel_mutex.h"

k_pid proc1 = K_NO_PID; //idle process PID
k_pid proc2 = K_NO_PID; //first process PID
k_pid proc3 = K_NO_PID; //second process PID

struct kproc_mutex * mux0 = NULL; //mutex 0

void idle(void * arg)
{
    /*
     * The firstly added process will always be considered as IDLE.
     * The IDLE process is called when rest processes are sleeping or waiting or whatever, in
     *  other words - processes do not consume CPU.
     * The programmer can use this unit at its discretion i.e call AVR POWER MANAGMENT to set
     *  MCU to sleep until next tick occurs, but make sure that the MCU sleep mode should be
     *  set not to block the Timer 0 ISR.
     */
    while (1)
    {
        for (uint8_t i=0; i < 100; i++)
        {
            asm volatile ("nop");
        }
    }
}

/*
 * Process 1
 * User code placed there in the infinite loop.
 */
void process1(void * arg)
{
    //at this place, there can be initialized/configured required hardware
    DDRG |= (1 << DDG0);

    //entering loop
    while (1)
    {
        for (uint32_t i=0; i < 0x50000; i++)
        {
            asm volatile ("nop");
        }
        PORTG |= (1 << DDG0);
        for (uint32_t i=0; i < 0x50000; i++)
        {
            asm volatile ("nop");
        }

            PORTG &= ~(1 << DDG0);

        kernel_process_usleep(10000, 0);
    }
    return;
}

/*
 * Process 2
 * User code placed there in the infinite loop.
 */
void process2(void * arg)
{
    DDRG |= (1 << DDG2);
    while (1)
    {
        for (uint32_t i=0; i < 0x50000; i++)
        {
            asm volatile ("nop");
        }
        PORTG |= (1 << PG2);
        for (uint32_t i=0; i < 0x50000; i++)
        {
            asm volatile ("nop");
        }
        PORTG &= ~(1 << PG2);

        kernel_process_usleep(10000000);

    }
    return;
}

/*
 * The defined (on compile time) __heap_start and __heap_end.
 * Usually __heap_end is equeal to there, so it is recomended to set this value manually.
 * Also, it is recomended not to ignore the __heap_start, and if it nessesary, make offset
 *  from that point.
 */
extern uint8_t __heap_start;
extern uint8_t __heap_end;

/*
 * Entry point.
 */
int main(void)
{   
    //Memory initialization must be performed before the processes initialization
    kernel_memory_init((uint8_t *)&__heap_start, (uint8_t *)((uint16_t)&__heap_start+(uint16_t)5600));
    //PID = 0 running only when the rest are sleeping, waiting or stoped
    //%PID                          %TITLE      %RESERVED         %STACK_SIZE %PROCESS %ARGUMENT
    proc1 = kernel_create_process(PSTR("idle"), KPROC_TYPE_SYSTEM, 60,          idle,    NULL);         
    proc2 = kernel_create_process(PSTR("task1"), KPROC_TYPE_SYSTEM, 300, process1, NULL);
    proc3 = kernel_create_process(PSTR("task2"), KPROC_TYPE_SYSTEM, 300, process2, NULL);

    //ren the processes
    kernel_processes_start();

    while (1)
    {
    }
}

Example 2

Involving the mutex to interlock simultaneous access to the specific hardware. In our case, this is going to be a PORT G PIN 1.
When processes starts, the process1 will be executed earlier than process2. Process1 will lock mux0 and set the PORTG to HIGH. When the timer tick occurs, the execution will be switched to the process2. The process2 will try to lock mutex, but the mutex mux0 has been already locked, so the kernel routine kernel_mutex_lock will set the 'mutex lock order' number to process, increment 'waiting for mutex counter' and set the process to the 'wait state'. The control will be immidiatly released. From this point the process2 is no longer receiving an execution control from sheduler and only process1 is running until process1 will not release the mutex. As long as it happens, the sheduler checks if current process is waiting for the mutex and if so, sheduler checks if the 'waiting order' match with mutex counter. If it matches, the sheduler grants the execution control to the selected process (in our case process2) which is still remains in the kernel call and locks the mutex for process2...

#include <avr/io.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <avr/pgmspace.h>
#include <avr/interrupt.h>
#include <util/atomic.h>
#include "kernel_memory.h"
#include "kernel_process.h"
#include "kernel_mutex.h"

k_pid proc1 = K_NO_PID; //idle process PID
k_pid proc2 = K_NO_PID; //first process PID
k_pid proc3 = K_NO_PID; //second process PID
struct kproc_mutex * mux0; //mutex 0

void idle(void * arg)
{
    /*
     * The firstly added process will always be considered as IDLE.
     * The IDLE process is called when rest processes are sleeping or waiting or whatever, in
     *  other words - processes do not consume CPU.
     * The programmer can use this unit at its discretion i.e call AVR POWER MANAGMENT to set
     *  MCU to sleep until next tick occurs, but make sure that the MCU sleep mode should be
     *  set not to block the Timer 0 ISR.
     */
    while (1)
    {
        for (uint8_t i=0; i < 100; i++)
        {
            asm volatile ("nop");
        }
    }
}

/*
 * Process 1
 * User code placed there in the infinite loop.
 */
void process1(void * arg)
{
    //at this place, there can be initialized/configured required hardware

    //entering loop
    while (1)
    {
        //lock mutex
        kernel_mutex_lock(mux0);

        //setting DDRG 1 to high
        PORTG = (1 << PG1);

        for (uint32_t i=0; i < 0x50000; i++)
        {
            asm volatile ("nop");
        }
        PORTG |= (1 << DDG0);
        for (uint32_t i=0; i < 0x50000; i++)
        {
            asm volatile ("nop");
        }

            PORTG &= ~(1 << DDG0);

        kernel_mutex_unlock(mux0);
    }
    return;
}

/*
 * Process 2
 * User code placed there in the infinite loop.
 */
void process2(void * arg)
{

    while (1)
    {
        //lock mutex
        kernel_mutex_lock(mux0);

        //setting DDRG 1 to low
        PORTG &= ~(1 << PG1);

        for (uint32_t i=0; i < 0x50000; i++)
        {
            asm volatile ("nop");
        }
        PORTG |= (1 << PG2);
        for (uint32_t i=0; i < 0x50000; i++)
        {
            asm volatile ("nop");
        }
        PORTG &= ~(1 << PG2);

        kernel_mutex_unlock(mux0);

    }

    return;
}

/*
 * The defined (on compile time) __heap_start and __heap_end.
 * Usually __heap_end is equeal to there, so it is recomended to set this value manually.
 * Also, it is recomended not to ignore the __heap_start, and if it nessesary, make offset
 *  from that point.
 */
extern uint8_t __heap_start;
extern uint8_t __heap_end;

/*
 * Entry point.
 */
int main(void)
{   
    //Memory initialization must be performed before the processes initialization
    kernel_memory_init((uint8_t *)&__heap_start, (uint8_t *)((uint16_t)&__heap_start+(uint16_t)5600));
    //PID = 0 running only when the rest are sleeping, waiting or stoped
    //%PID                          %TITLE      %RESERVED         %STACK_SIZE %PROCESS %ARGUMENT
    proc1 = kernel_create_process(PSTR("idle"), KPROC_TYPE_SYSTEM, 60,          idle,    NULL);         
    proc2 = kernel_create_process(PSTR("task1"), KPROC_TYPE_SYSTEM, 300, process1, NULL);
    proc3 = kernel_create_process(PSTR("task2"), KPROC_TYPE_SYSTEM, 300, process2, NULL);

    //initializing mutex
    mux0 = kernel_mutex_init();
    if (mux0 == NULL)
    {
        //for some reason mutex was not initialized (possible reasons: out of free heap space, internal error)
        exit(1);
    }

    //hardware init
    DDRG |= (1 << DDG1);
    PORTG &= ~(1 << PG1);

    //ren the processes
    kernel_processes_start();

    while (1)
    {
    }
}

Example 3

In this example, the code in main section is declaring 2 ISRs (INT0, INT1) external interrupts and set the first as immidiate and second as delayed processed. The delayed processed ISR handler is running in the process and its stack space. The process called [ISR_ITERATOR] which is running only when you utilising delayed processing interrupts. The normal interrupts are executed immidiatly and using common stack space. The normal interrupts does not support nesting, because the common stack space is not protected from overwriting.

...
void int0_isr();
void int0_isr()
{
    _delay_ms(4);
}

void int1_isr();
void int1_isr()
{
    _delay_ms(15);
}

int main(void)
{   
    //Preparing INT0 and INT1 ports
    DDRD &= ~(1 << PD0) & ~(1 << PD1);
    PORTD |= (1 << PD0) | (1 << PD1);

    MCU_Init();
    kernel_memory_init((uint8_t *)&__heap_start, (uint8_t *)((uint16_t)&__heap_start+(uint16_t)HEAP_ENDS_AT));
    proc1 = kernel_create_process(PSTR("idle"), KPROC_TYPE_SYSTEM, 60, idle, NULL);         //PID = 0 running only when the rest are sleeping, waiting or stoped
    proc2 = kernel_create_process(PSTR("task1"), KPROC_TYPE_SYSTEM, 300, process1, NULL);
    proc3 = kernel_create_process(PSTR("task2"), KPROC_TYPE_SYSTEM, 300, process2, NULL);
    proc4 = kernel_create_process(PSTR("top"), KPROC_TYPE_SYSTEM, 200, process_top, NULL);
    proc5 = kernel_create_process(PSTR("task4"), KPROC_TYPE_SYSTEM, 160, process5, NULL);
    mux0 = kernel_mutex_init();
    sem0 = kernel_sem_init(2);
    //grabbing ISR with vector number INT0_vect_num which corresponds to INT0 and set handler to user defined function int0_isr()
    kernel_isr_grab(INT0_vect_num, int0_isr, ISR_NORMAL);
    //allow interrput
    EICRA = (1 << ISC01);
    EIMSK = (1<<INT0);

    //initialising delayed processing ISR which will run process [ISR_ITERATOR]
    kernel_isr_init();
    //set INT1 to int1_isr
    kernel_isr_grab(INT1_vect_num, int1_isr, ISR_DELAYED);
    EICRA |= (1 << ISC11);
    EIMSK |= (1<<INT1);
    //run
    kernel_processes_start();

    while (1)
    {
    }
}