CCS C Software and Maintenance Offers
FAQFAQ   FAQForum Help   FAQOfficial CCS Support   SearchSearch  RegisterRegister 

ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

CCS does not monitor this forum on a regular basis.

Please do not post bug reports on this forum. Send them to CCS Technical Support

Basic co-operative scheduler - v: 1.0.4

 
Post new topic   Reply to topic    CCS Forum Index -> Code Library
View previous topic :: View next topic  
Author Message
PICoHolic



Joined: 04 Jan 2005
Posts: 224

View user's profile Send private message

Basic co-operative scheduler - v: 1.0.4
PostPosted: Fri Mar 22, 2019 11:01 am     Reply with quote

Hello,

This is a basic scheduler (not yet an RTOS).
Features:
- Runtime task creation
- Runtime task deletion
- Runtime task suspending/resuming
- Runtime task's execution rate change
- Task's messaging capability
- Scheduler and task statistics
- Semaphore

Cheers!

Filename: target_port.h
Code:

#ifndef _TARGET_PORT_H_
#define _TARGET_PORT_H_
///////////////////////////////////////////////////////////////////////////////
//Specify target compiler
//
///////////////////////////////////////////////////////////////////////////////
#if (defined (__PCH__) || defined (__PCD__))   //CCS C compiler?
   //types:
   typedef    int1         bool;
   typedef      unsigned int8   uint8;
   typedef      signed int8      sint8;
   typedef    unsigned int16   uint16;
   typedef    signed int16   sint16;
   typedef      unsigned int32   uint32;
   typedef      signed int32   sint32;
   //misc
   //#define   _USE_32_BIT_
   #include <stdlibm.h>
#elif defined (__NIOS2__)   //Nios2
   //types:
   #include "alt_types.h"
   typedef    alt_u8     bool;
   typedef      alt_u8     uint8;
   typedef      alt_8        sint8;
   typedef    alt_u16      uint16;
   typedef    alt_16     sint16;
   typedef      alt_u32      uint32;
   typedef      alt_32     sint32;
   //#define   rom   ?
   //misc
   #define   _USE_32_BIT_
   //#define make8(Val, B)   (uint8)...   //extract byte B from Val
   //#define make16(H, L)   (uint16)...   //combine two bytes in 1 word
   #include <string.h>
   #include <malloc.h>
   #include <sys/alt_alarm.h>
#elif defined (__ATOLLIC__)   //special ID that represents the compiler
   //types:
   typedef    ?     bool;
   typedef      ?     uint8;
   typedef      ?     sint8;
   typedef    ?   uint16;
   typedef    ?     sint16;
   typedef      ?   uint32;
   typedef      ?     sint32;
   #define   rom   ?
   //misc
   #define   _USE_32_BIT_
   #define make8(Val, B)   (uint8)...   //extract byte B from Val
   #define make16(H, L)      (uint16)...   //combine two bytes in 1 word
#endif
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
#endif


Filename: scheduler.h
Code:

#ifndef   _SCHEDULER_H_
#define   _SCHEDULER_H_
///////////////////////////////////////////////////////////////////////////////
#include "target_port.h"
///////////////////////////////////////////////////////////////////////////////
//Comment if no LED is available
//#define   _BUSY_LED_
#ifdef _BUSY_LED_
#define   BLEDON()   LED3On()
#define   BLEDOFF()   LED3Off()
#endif
///////////////////////////////////////////////////////////////////////////////
typedef struct _stask
{
   uint16 sTaskID;             //sTask ID
   uint8 Active;               //sTask status
#ifdef _USE_32_BIT_
   uint32 sTicks;              //sTask periodic ticks
   uint32 nTicks;              //sTask current tick count
   uint32 eTicks;            //Elapsed ticks
#else
   uint16 sTicks;              //sTask periodic ticks
   uint16 nTicks;              //sTask current tick count
   uint16 eTicks;            //Elapsed ticks
#endif
   uint16 MsgBytesCount;       //sTask message size in bytes
   void *MsgData;              //sTask data message
   void *sTaskPtr;             //sTask function's pointer
   struct _stask *Next;        //Pointer to next sTask
   struct _stask *Previous;    //Pointer to previous sTask
}sTask;
///////////////////////////////////////////////////////////////////////////////
typedef void (*_fptr)(sTask *);
///////////////////////////////////////////////////////////////////////////////
typedef struct _schedulerinfo
{
   sTask *sTaskHead;           //sTasks linked list's head
   sTask *sTaskTail;           //sTasks linked list's tail
   uint16 sTasksCount;       //sTasks total count
}SchedulerInfo;
///////////////////////////////////////////////////////////////////////////////
typedef struct _schedulerstats
{
   uint16 sTasksCount;         //Total number of tasks
   uint8 sTasksCPUUsage;      //CPU usage
}SchedulerStats;
///////////////////////////////////////////////////////////////////////////////
typedef struct _ssemaphore
{
   uint8 Sem;               //Semaphore variable
   uint8 sTasksCount;         //Nb of concurrent tasks using this semaphore
   //type   //TBD
}sSemaphore;
///////////////////////////////////////////////////////////////////////////////
/**
 * Function to create a scheduled task
 *
 * @author m_richa (03/21/19)
 *
 * @param Active sTask initial state
 * @param sTicks Periodic ticks
 * @param sTaskPtr Pointer to task's function
 *
 * @return 0: fail, sTaskID: success
 */
#ifdef _USE_32_BIT_
uint16 sTaskCreate(uint8 Active, uint32 sTicks, _fptr sTaskPtr);
#else
uint16 sTaskCreate(uint8 Active, uint16 sTicks, _fptr sTaskPtr);
#endif

/**
 * Function to locate and return sTask's entry pointer
 *
 * @author m_richa (03/22/19)
 *
 * @param sTaskPtr sTask's function name
 *
 * @return Pointer to sTask entry
 */
//sTask *sTaskLocate(_fptr sTaskPtr);

/**
 * Function to suspend a given sTask
 *
 * @author m_richa (03/22/19)
 *
 * @param sT sTask's entry pointer
 */
void sTaskSuspend_me(sTask *sT);

/**
 * Function to suspend a given sTask
 *
 * @author m_richa (03/22/19)
 *
 * @param sTaskPtr sTask's function name
 *
 * @return 0: fail, sTaskID: success
 */
uint8 sTaskSuspend(_fptr sTaskPtr);

/**
 * Function to resume a given sTask
 *
 * @author m_richa (03/22/19)
 *
 * @param sT sTask's entry pointer
 */
void sTaskResume_me(sTask *sT);

/**
 * Function to suspend a given sTask
 *
 * @author m_richa (03/22/19)
 *
 * @param sTaskPtr sTask's function name
 *
 * @return 0: fail, sTaskID: success
 */
uint8 sTaskResume(_fptr sTaskPtr);

/**
 * Function to get number of periodic ticks of a given sTask
 *
 * @author m_richa (03/22/19)
 *
 * @param sT sTask's entry pointer
 * 
 * @return Number of ticks 
 */
#ifdef _USE_32_BIT_
uint32 sTaskGetPeriodicTicks(sTask *sT);
#else
uint16 sTaskGetPeriodicTicks(sTask *sT);
#endif

/**
 * Function to set number of periodic ticks of a given sTask
 *
 * @author m_richa (03/22/19)
 *
 * @param sT sTask's entry pointer
 * @param sTicks Number of ticks
 * @param Run 0: Reset sTask's nTick count 1: Run sTask on next tick
 */
#ifdef _USE_32_BIT_
void sTaskSetPeriodicTicks(sTask *sT, uint32 sTicks, uint8 Run);
#else
void sTaskSetPeriodicTicks(sTask *sT, uint16 sTicks, uint8 Run);
#endif

/**
 * Function to send data bytes to a given sTask
 *
 * @author m_richa (03/22/19)
 *
 * @param sTaskPtr sTask's function name
 * @param MsgBytes Data pointer to be sent
 * @param BytesCount Data size
 *
 * @return 0: fail, sTaskID: success 
 */
uint8 sTaskWriteMsgBytes(_fptr sTaskPtr, void *MsgBytes, uint16 BytesCount);

/**
 * Function to get sTask's message size if any
 *
 * @author m_richa (03/22/19)
 *
 * @param sT sTask's entry pointer
 *
 * @return Read data size. 0: no data available
 */
uint16 sTaskGetMsgBytesCount(sTask *sT);

/**
 * Function to read data bytes of a given sTask
 *
 * @author m_richa (03/22/19)
 *
 * @param sT sTask's entry pointer
 * @param MsgBytes Data pointer to be received
 *
 * @return Read data size. 0: no data available
 */
uint16 sTaskReadMsgBytes(sTask *sT, void *MsgBytes);

/**
 * Function to delete a given sTask
 *
 * @author m_richa (03/22/19)
 *
 * @param sT sTask's entry pointer
 */
void sTaskDelete_me(sTask *sT);

/**
 * Function to delete a given sTask
 *
 * @author m_richa (03/22/19)
 *
 * @param sTaskPtr sTask's function name
 *
 * @return 0: fail, sTaskID: success
 */
uint8 sTaskDelete(_fptr sTaskPtr);

/**
 * Function to initialize scheduler and create idle sTask
 *
 * @author m_richa (04/07/19)
 *
 * @return uint16
 */
uint16 InitScheduler(void);

/**
 * Function to get scheduler statistics
 *
 * @author m_richa (04/10/19)
 *
 * @param SS Pointer to statistics structure
 */
void GetSchedulerStats(SchedulerStats *SS);

/**
 * Function to initialize semaphore
 *
 * @author m_richa (04/15/19)
 *
 * @param sema Pointer to semaphore structure
 * @param csTasks Number of allowed concurrent tasks
 */
void InitSemaphore(sSemaphore *sema, uint8 csTasks);

/**
 * Function to release and make semaphore available
 *
 * @author m_richa (04/15/19)
 *
 * @param sema Pointer to semaphore structure
 */
void SemaphoreRelease(sSemaphore *sema);

/**
 * Function to hold semaphore
 *
 * @author m_richa (04/15/19)
 *
 * @param sema Pointer to semaphore structure
 *
 * @return 0: fail  1: success
 */
bool SemaphoreHold(sSemaphore *sema);

/**
 * Function to check if a semaphore is available
 *
 * @author m_richa (04/15/19)
 *
 * @param sema Pointer to semaphore structure
 *
 * @return 0: not available  1: available
 */
bool SemaphoreAvailable(sSemaphore *sema);

/**
 * Function to dispatch sTasks sequentially
 *
 * @author m_richa (03/25/19)
 */
void Scheduler(void);

#endif


Filename: scheduler.c
Code:

/*********************************************************************
 * Filename:            scheduler.c
 *                                                                           
 * Description:         Contains co-operative scheduler API
 *
 * Target Processor:    PIC
 *
 * Target Compiler:     CCS PIC C Compiler, Nios2
 *
 * Compiler Revision:   5.084, 15.1
 *
 * Revision History:
 *
 * Author           Date (mm/dd/yy)         Comment
 *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 * m_richa          03/22/19                Alpha release
 *                                          (CCS 5.083)
 * m_richa          03/24/19                Beta
 *                                          sTask parameter changed as
 *                                          per jeremiah's suggestion
 *                                          (CCS 5.083)
 * m_richa          03/25/19                1.0.0
 *                                          Jerson's comment taken into
 *                                          consideration, implemented
 *                                          differently
 *                                          (CCS 5.083)
 * m_richa          03/28/19                1.0.1
 *                                          Functions modified:
 *                                          - sTaskCreate(..)
 *                                          - sTaskResume(..)
 *                                          - sTaskSetPeriodicTicks(..)
 *                                          - Scheduler()
 *                                          Optimized scheduling procedure
 *                                          Better timing management
 *                                          (CCS 5.083)
 * m_richa          04/04/19                1.0.2
 *                                          Added support to NIOS2 (15.1)
 * m_richa          04/10/19                1.0.3
 *                                          InitScheduler function added
 *                                          _sTask_idle sTask added to
 *                                          GetSchedulerStats function
 *                                          Busy LED option added
 *                                          (5.084)
 * m_richa          04/15/19                1.0.4
 *                                          eTicks added to sTask node to
 *                                          count elapsed ticks
 *                                          Semaphore handling added
 *                                          (5.084)
 ********************************************************************/
#include "scheduler.h"
///////////////////////////////////////////////////////////////////////////////
#define  TICKSPERSECOND   1000    //change accordingly
///////////////////////////////////////////////////////////////////////////////
SchedulerInfo _SI;
uint8 _cpu_usage_p = 0;
#if (defined (__PCH__) || defined (__PCD__))   //CCS C compiler?
bool _sysTick;
#ifdef _USE_32_BIT_
uint32 _sysTickCounter = 0; //tick counter
#else
uint16 _sysTickCounter = 0; //tick counter
#endif
///////////////////////////////////////////////////////////////////////////////
//Timing for _sysTick = 1ms
#define  TMR1Reload      1500      //change accordingly
///////////////////////////////////////////////////////////////////////////////
/**
 * ISR for TIMER1 in order to generate a 1ms _sysTick
 *
 * @author m_richa (03/25/19)
 */
#int_TIMER1   HIGH   //high priority interrupt
void TIMER1_isr(void)
{
    set_timer1(get_timer1() - TMR1Reload);

    _sysTickCounter++;
    _sysTick = TRUE;
}
#define GetSystemTicks()    _sysTickCounter

#elif defined (__NIOS2__)   //Nios2
#define GetSystemTicks()   alt_nticks()
#endif
///////////////////////////////////////////////////////////////////////////////
/**
 * Function to create a scheduled task
 *
 * @author m_richa (03/21/19)
 *
 * @param Active sTask initial state
 * @param sTicks Periodic ticks
 * @param sTaskPtr Pointer to task's function
 *
 * @return 0: fail, sTaskID: success
 */
#ifdef _USE_32_BIT_
uint16 sTaskCreate(uint8 Active, uint32 sTicks, _fptr sTaskPtr)
#else
uint16 sTaskCreate(uint8 Active, uint16 sTicks, _fptr sTaskPtr)
#endif
{
    static uint16 sTaskID = 0;    //task ID
    sTask *New_sTask = malloc(sizeof(sTask));

    if (New_sTask != NULL)
    {
        //Assign values:
        New_sTask->sTaskID = ++sTaskID;
        New_sTask->Active = Active;
        New_sTask->sTicks = sTicks;
        New_sTask->nTicks = GetSystemTicks();
        New_sTask->MsgBytesCount = 0;
        New_sTask->MsgData = NULL;
        New_sTask->sTaskPtr = sTaskPtr;
        New_sTask->Next = NULL;
        New_sTask->Previous = NULL;

        _SI.sTasksCount++;  //Increment count

        if (_SI.sTaskHead != NULL)   //nth sTask?
        {
            New_sTask->Previous = _SI.sTaskTail;
            _SI.sTaskTail->Next = New_sTask;
            _SI.sTaskTail = New_sTask;  //Point tail
        } else    //1st sTask?
        {
            _SI.sTaskHead = New_sTask;  //Point head
            _SI.sTaskTail = New_sTask;  //Point tail
        }

        return sTaskID;   //Success
    }

    return 0;   //Fail, no available RAM
}
///////////////////////////////////////////////////////////////////////////////
/**
 * Function to locate and return sTask's entry pointer
 *
 * @author m_richa (03/22/19)
 *
 * @param sTaskPtr sTask's function name
 *
 * @return Pointer to sTask entry
 */
sTask* sTaskLocate(_fptr sTaskPtr)
{
    sTask *p = _SI.sTaskHead;

    if (_SI.sTasksCount == 0) return NULL;

    while (p != NULL)
    {
        if (p->sTaskPtr == sTaskPtr) return p;
        else p = p->Next;
    }

    return NULL;
}
///////////////////////////////////////////////////////////////////////////////
/**
 * Function to suspend a given sTask
 *
 * @author m_richa (03/22/19)
 *
 * @param sT sTask's entry pointer
 */
void sTaskSuspend_me(sTask *sT)
{
    sT->Active = 0; //disable
}
///////////////////////////////////////////////////////////////////////////////
/**
 * Function to suspend a given sTask
 *
 * @author m_richa (03/22/19)
 *
 * @param sTaskPtr sTask's function name
 *
 * @return 0: fail, sTaskID: success
 */
uint8 sTaskSuspend(_fptr sTaskPtr)
{
    sTask *p = sTaskLocate(sTaskPtr);

    if (p != NULL)
    {
        sTaskSuspend_me(p);    //suspend sTask
        return 1;   //success
    }

    return 0;   //Fail
}
///////////////////////////////////////////////////////////////////////////////
/**
 * Function to resume a given sTask
 *
 * @author m_richa (03/22/19)
 *
 * @param sT sTask's entry pointer
 */
void sTaskResume_me(sTask *sT)
{
    sT->Active = 1; //enable
    sT->nTicks = GetSystemTicks() - sT->sTicks;  //load ticks
}
///////////////////////////////////////////////////////////////////////////////
/**
 * Function to suspend a given sTask
 *
 * @author m_richa (03/22/19)
 *
 * @param sTaskPtr sTask's function name
 *
 * @return 0: fail, sTaskID: success
 */
uint8 sTaskResume(_fptr sTaskPtr)
{
    sTask *p = sTaskLocate(sTaskPtr);

    if (p != NULL)
    {
        sTaskResume_me(p); //resume sTask
        return 1;   //success
    }

    return 0;   //Fail
}
///////////////////////////////////////////////////////////////////////////////
/**
 * Function to get number of periodic ticks of a given sTask
 *
 * @author m_richa (03/22/19)
 *
 * @param sT sTask's entry pointer
 * 
 * @return Number of ticks 
 */
#ifdef _USE_32_BIT_
uint32 sTaskGetPeriodicTicks(sTask *sT)
#else
uint16 sTaskGetPeriodicTicks(sTask *sT)
#endif
{
    return sT->sTicks;
}
///////////////////////////////////////////////////////////////////////////////
/**
 * Function to set number of periodic ticks of a given sTask
 *
 * @author m_richa (03/22/19)
 *
 * @param sT sTask's entry pointer
 * @param sTicks Number of ticks
 * @param Run 0: Reset sTask's nTick count 1: Run sTask on next tick
 */
#ifdef _USE_32_BIT_
void sTaskSetPeriodicTicks(sTask *sT, uint32 sTicks, uint8 Run)
#else
void sTaskSetPeriodicTicks(sTask *sT, uint16 sTicks, uint8 Run)
#endif
{
    sT->sTicks = sTicks;

    if (Run)    //should run on next tick?
        sT->nTicks = GetSystemTicks() - sTicks;  //load ticks
    else sT->nTicks = GetSystemTicks();   //load ticks
}
///////////////////////////////////////////////////////////////////////////////
/**
 * Function to send data bytes to a given sTask
 *
 * @author m_richa (03/22/19)
 *
 * @param sTaskPtr sTask's function name
 * @param MsgBytes Data pointer to be sent
 * @param BytesCount Data size
 *
 * @return 0: fail, sTaskID: success 
 */
uint8 sTaskWriteMsgBytes(_fptr sTaskPtr, void *MsgBytes, uint16 BytesCount)
{
    sTask *p = sTaskLocate(sTaskPtr);

    if (p != NULL)
    {
        if (p->MsgBytesCount == 0)  //no pending msgs?
        {
            p->MsgBytesCount = BytesCount;
            p->MsgData = malloc(BytesCount);
            if (p->MsgData != NULL)
            {
                memcpy(p->MsgData, MsgBytes, BytesCount);
                return 1;   //success
            }
            return 0;   //Fail
        }
        return 0;   //Fail
    }
    return 0;   //Fail
}
///////////////////////////////////////////////////////////////////////////////
/**
 * Function to get sTask's message size if any
 *
 * @author m_richa (03/22/19)
 *
 * @param sT sTask's entry pointer
 *
 * @return Read data size. 0: no data available
 */
uint16 sTaskGetMsgBytesCount(sTask *sT)
{
    return sT->MsgBytesCount;
}
///////////////////////////////////////////////////////////////////////////////
/**
 * Function to read data bytes of a given sTask
 *
 * @author m_richa (03/22/19)
 *
 * @param sT sTask's entry pointer
 * @param MsgBytes Data pointer to be received
 *
 * @return Read data size. 0: no data available
 */
uint16 sTaskReadMsgBytes(sTask *sT, void *MsgBytes)
{
    uint16 BytesCount = sT->MsgBytesCount;

    if (BytesCount > 0)  //data available?
    {
        //Copy and free:
        memcpy(MsgBytes, sT->MsgData, sT->MsgBytesCount);
        free(sT->MsgData);
        sT->MsgData = NULL;
        sT->MsgBytesCount = 0;  //reset
    }

    return BytesCount;
}
///////////////////////////////////////////////////////////////////////////////
/**
 * Function to delete a given sTask
 *
 * @author m_richa (03/22/19)
 *
 * @param sT sTask's entry pointer
 */
void sTaskDelete_me(sTask *sT)
{
    if (sT == _SI.sTaskHead) //on head?
    {
        if (_SI.sTasksCount == 1)   //Head = Tail?
        {
            //Reset all:
            _SI.sTaskHead = NULL;
            _SI.sTaskTail = NULL;
        } else
        {
            _SI.sTaskHead = _SI.sTaskHead->Next;
            _SI.sTaskHead->Previous = NULL;
        }
    } else if (sT == _SI.sTaskTail)    //on tail?
    {
        _SI.sTaskTail = _SI.sTaskTail->Previous;
        _SI.sTaskTail->Next = NULL;
    } else    //somewhere in the middle
    {
        sT->Previous->Next = sT->Next;
        sT->Next->Previous = sT->Previous;
    }

    if (sT->MsgBytesCount)
        free(sT->MsgData);  //Free msg data if any
    free(sT);           //Free sTask
    _SI.sTasksCount--;  //decrement sTasks count
}
///////////////////////////////////////////////////////////////////////////////
/**
 * Function to delete a given sTask
 *
 * @author m_richa (03/22/19)
 *
 * @param sTaskPtr sTask's function name
 *
 * @return 0: fail, sTaskID: success
 */
uint8 sTaskDelete(_fptr sTaskPtr)
{
    sTask *p = sTaskLocate(sTaskPtr);

    if (p != NULL)
    {
        sTaskDelete_me(p); //delete sTask entry
        return 1;   //Success
    }

    return 0;   //Fail
}
///////////////////////////////////////////////////////////////////////////////
/**
 * The idle sTask, used to calculate CPU usage
 *
 * @author m_richa (04/07/19)
 *
 * @param me
 */
void _sTask_idle(sTask *me)
{
#ifdef _USE_32_BIT_
    static uint32 initTicks = 0, idleTicks = 0;
#else
    static uint16 initTicks = 0, idleTicks = 0;
#endif

    if ((GetSystemTicks() - initTicks) >= TICKSPERSECOND)   //1 s elapsed?
    {
        //Calculate CPU usage:
        _cpu_usage_p = (uint8)(100 - (((float)idleTicks / (float)TICKSPERSECOND) * 100.0));
        // Other alternative: Add eTicks of all sTaks and divide by TICKSPERSECOND

        initTicks = GetSystemTicks();   //reload
        idleTicks = 0;  //reset idle ticks
    } else idleTicks++;
}
///////////////////////////////////////////////////////////////////////////////
/**
 * Function to initialize scheduler and create idle sTask
 *
 * @author m_richa (04/07/19)
 *
 * @return uint16
 */
uint16 InitScheduler(void)
{
    _SI.sTaskHead = NULL;
    _SI.sTaskTail = NULL;
    _SI.sTasksCount = 0;

    return sTaskCreate(1, 1, _sTask_idle); //create idle sTask
}
///////////////////////////////////////////////////////////////////////////////
/**
 * Function to get scheduler statistics
 *
 * @author m_richa (04/10/19)
 *
 * @param SS Pointer to statistics structure
 */
void GetSchedulerStats(SchedulerStats *SS)
{
    SS->sTasksCount = _SI.sTasksCount;
    SS->sTasksCPUUsage =  _cpu_usage_p;
}
///////////////////////////////////////////////////////////////////////////////
#if 0
/**
 * Function to dispatch sTasks sequentially
 *
 * @author m_richa (03/21/19)
 */
void Scheduler(void)
{
    sTask *CsTask = _SI.sTaskHead;
    _fptr f;

    while (CsTask)
    {
        if (CsTask->Active)  //is it active?
        {
            if (CsTask->sTaskPtr != NULL)    //task's function exists?
            {
                if (++(CsTask->nTicks) == CsTask->sTicks)    //should run?
                {
                    CsTask->nTicks = 0;     //reset ticks
                    f = CsTask->sTaskPtr;   //cast it to _fptr
                    f(CsTask);              //run task
                }
            }
        }
        CsTask = CsTask->Next;
    }
}
#endif
///////////////////////////////////////////////////////////////////////////////
/**
 * Function to initialize semaphore
 *
 * @author m_richa (04/15/19)
 *
 * @param sema Pointer to semaphore structure
 * @param csTasks Number of allowed concurrent tasks
 */
void InitSemaphore(sSemaphore *sema, uint8 csTasks)
{
    sema->Sem = csTasks;
    sema->sTasksCount = csTasks;
}
///////////////////////////////////////////////////////////////////////////////
/**
 * Function to release and make semaphore available
 *
 * @author m_richa (04/15/19)
 *
 * @param sema Pointer to semaphore structure
 */
void SemaphoreRelease(sSemaphore *sema)
{
    if (sema->Sem < sema->sTasksCount)
    {
        sema->Sem++; //increment
    }
}
///////////////////////////////////////////////////////////////////////////////
/**
 * Function to hold semaphore
 *
 * @author m_richa (04/15/19)
 *
 * @param sema Pointer to semaphore structure
 *
 * @return 0: fail  1: success
 */
bool SemaphoreHold(sSemaphore *sema)
{
    if (sema->Sem)
    {
        sema->Sem--; //decrement
        return 1;
    }
    return 0;   //fail
}
///////////////////////////////////////////////////////////////////////////////
/**
 * Function to check if a semaphore is available
 *
 * @author m_richa (04/15/19)
 *
 * @param sema Pointer to semaphore structure
 *
 * @return 0: not available  1: available
 */
bool SemaphoreAvailable(sSemaphore *sema)
{
    return (sema->Sem)?1:0;
}
///////////////////////////////////////////////////////////////////////////////
/**
 * Function to dispatch sTasks sequentially
 *
 * @author m_richa (03/25/19)
 */
void Scheduler(void)
{
    sTask *CsTask = _SI.sTaskHead;
    _fptr f;
#ifdef _USE_32_BIT_
    uint32 CsysTickCounter;
    uint32 TicksDiff;
#else
    uint16 CsysTickCounter;
    uint16 TicksDiff;
#endif

    while (CsTask)
    {
        if (CsTask->Active)  //is it active?
        {
            if (CsTask->sTaskPtr != NULL)    //task's function exists?
            {
                CsysTickCounter = GetSystemTicks();  //ticksounter snapshot just to
                                                     //make sure value does not change
                                                     //during this process

                TicksDiff = CsysTickCounter - CsTask->nTicks;   //get diff

                if (TicksDiff >= CsTask->sTicks)    //should run?
                {
                    CsTask->nTicks = CsysTickCounter;   //reload
#ifdef _BUSY_LED_
                    BLEDON();
#endif
                    //prepare call:
                    CsTask->eTicks = CsysTickCounter;   //Save snapshot
                    f = CsTask->sTaskPtr;   //cast it to _fptr
                    f(CsTask);              //run task
                    CsTask->eTicks = GetSystemTicks() - CsTask->eTicks; //calculate elapsed ticks
#ifdef _BUSY_LED_
                    BLEDOFF();
#endif
                }
            }
        }
        CsTask = CsTask->Next;
    }
}
///////////////////////////////////////////////////////////////////////////////


Usage example:
Code:

///////////////////////////////////////////////////////////////////////////////
int1 _sysTick;
///////////////////////////////////////////////////////////////////////////////
void sTask1(sTask *);
void sTask2(sTask *);
void sTask3(sTask *);
///////////////////////////////////////////////////////////////////////////////
void sTask1(sTask *me)
{
    unsigned int16 MsgSize;
    unsigned int8 *MyMsg;

    /////////////////////////////////
    //TODO: user code
    //sTask body
    /////////////////////////////////

    //Check for pending message:
    MsgSize = sTaskGetMsgBytesCount(me); //Msg size
    if (MsgSize)    //Msg pending?
    {
        MyMsg = malloc(MsgSize);        //allocate RAM
        sTaskReadMsgBytes(me, MyMsg);   //Read msg
        /////////////////////////////////
        //TODO with incoming msg:
       
        /////////////////////////////////
        free(MyMsg);    //deallocate RAM
    }
}
///////////////////////////////////////////////////////////////////////////////
void sTask2(sTask *me)
{
    unsigned int8 sTaskMsg[4] = {1, 2, 3, 4};   //some data to send


    /////////////////////////////////
    //TODO: user code
    //sTask body
    /////////////////////////////////

    //Send msg ex:
    sTaskWriteMsgBytes(sTask1, sTaskMsg, sizeof(sTaskMsg));    //send msg to sTask1

    //ex to delete myself
    sTaskDelete_me(me);    //this will remove the current sTask
}
///////////////////////////////////////////////////////////////////////////////
void sTask3(sTask *me)
{
    SchedulerStats Stats;

    /////////////////////////////////
    //TODO: user code
    //sTask body
    /////////////////////////////////

    //ex to suspend sTask2:
    sTaskSuspend(sTask2);

    //ex to suspend myself:
    sTaskSuspend_me(me);

    //ex to change my execution rate and execute on next sysTick:
    sTaskSetPeriodicTicks(me, 500, 1);

    //ex to resume myself:
    sTaskResume_me(me);

    //Get statistics
    GetSchedulerStats(&Stats);
}
///////////////////////////////////////////////////////////////////////////////
void _main(void)
{
    //TODO: some user code goes here

    InitScheduler();    //Initialize scheduler

    sTaskCreate(1, 10, sTask1);      //Fastest sTask, every 10 sysTicks
    sTaskCreate(1, 100, sTask2);    //sTask2 runs every 100 sysTicks
    sTaskCreate(1, 1000, sTask3);   //Slowest sTask3, runs every 1000 sysTicks
    //Add more tasks....

    while (TRUE)
    {
        if (_sysTick)       //sysTick is a periodic flag (could be generated by a timer)
                            //a typical value of a periodic tick is 1ms
                            //Note: one should make sure periodic sysTick is slow enough
                            //      to handle the worst case delay of the fastest sTask
                            //      especially when the fastest sTask is set to 1 sysTick
        {
            _sysTick = 0;   //reset flag
            Scheduler();    //call scheduler
        }
    }
}


Last edited by PICoHolic on Tue Jul 16, 2019 8:02 am; edited 14 times in total
jeremiah



Joined: 20 Jul 2010
Posts: 1351

View user's profile Send private message

PostPosted: Sat Mar 23, 2019 6:34 am     Reply with quote

This looks pretty neat. I'll have to try it out sometime!. Curiousity: Why the indirection of the _fptr's function parameter to void *? It seems like all your uses of it cast it to sTask * anyways.
PICoHolic



Joined: 04 Jan 2005
Posts: 224

View user's profile Send private message

PostPosted: Sat Mar 23, 2019 3:27 pm     Reply with quote

Well, surprisingly, the compiler did not allow me to use sTask *. So I had to put void * instead and then cast it Confused
jeremiah



Joined: 20 Jul 2010
Posts: 1351

View user's profile Send private message

PostPosted: Sun Mar 24, 2019 11:02 am     Reply with quote

PICoHolic wrote:
Well, surprisingly, the compiler did not allow me to use sTask *. So I had to put void * instead and then cast it Confused


I thought there was a way to forward declare, but it might be ansi C only. I'd have to mess with it to be sure. If I can make a suggestion then:

Assuming the idea is for the user to supply the function, you might consider changing the sTask structure to internally use void * for sTaskPtr:

Code:

typedef struct _stask
{   
   unsigned int16 sTaskID;         //sTask ID
   unsigned int8 Active;           //sTask status
   unsigned int16 sTicks;          //sTask periodic ticks
   unsigned int16 nTicks;          //sTask current tick count
   unsigned int16 MsgBytesCount;   //sTask message size in bytes
   void *MsgData;                  //sTask data message
   void *sTaskPtr;                 //sTask function's pointer
   struct _stask *Next;            //Pointer to next sTask
   struct _stask *Previous;        //Pointer to previous sTask
}sTask;


Then you can move the typedef for _fptr below the struct and add
the task pointer:

Code:

typedef void (*_fptr)(sTask *);


After that, you should only have to change one part of the Scheduler() function:

Code:

///////////////////////////////////////////////////////////////////////////////
/**
 * Function to dispatch sTasks sequentially
 *
 * @author m_richa (03/21/19)
 */
void Scheduler(void)
{
    sTask *CsTask = _SI.sTaskHead;
    _fptr f;

    while (CsTask)
    {
        if (CsTask->Active)  //is it active?
        {
            if (CsTask->sTaskPtr != NULL)    //task's function exists?
            {
                if (++(CsTask->nTicks) == CsTask->sTicks)    //should run?
                {
                    CsTask->nTicks = 0;     //reset ticks
                    f = CsTask->sTaskPtr;
                    f(CsTask); //run task
                }
            }
        }
        CsTask = CsTask->Next;
    }
}

or you can play with just casting it to _fptr when you call it.

The reason I suggest this is that it (in the general case) takes out the need for the user to handle casting to the right type or the me variable in your task examples. You'd want to test it out of course.
PICoHolic



Joined: 04 Jan 2005
Posts: 224

View user's profile Send private message

PostPosted: Sun Mar 24, 2019 1:23 pm     Reply with quote

You have a point, will have to try it
Jerson



Joined: 31 Jul 2009
Posts: 125
Location: Bombay, India

View user's profile Send private message Visit poster's website

PostPosted: Sun Mar 24, 2019 10:21 pm     Reply with quote

Please permit me to make a critical observation on your code.

The round-robin co-operative scheduler that you've implemented makes an assumption that the tasks will always run to completion faster than the SysTick. If this is not the case, the tasks will run behind time and be grossly inaccurate

I would re-write this part
Code:
 if (++(CsTask->nTicks) == CsTask->sTicks)    //should run?
                {
                    CsTask->nTicks = 0;     //reset ticks

like this
Code:
 if (gloSysTick - CsTask->nTicks >= TaskPeriod )    //should run?
                {
                    CsTask->nTicks = gloSysTick+TaskPeriod;     //reset ticks to new match value

where gloSysTick is a global variable that just increments every mS in the background and TaskPeriod is the rate at which you want the task to run.
_________________
Regards
Jerson Fernandes
PICoHolic



Joined: 04 Jan 2005
Posts: 224

View user's profile Send private message

PostPosted: Mon Mar 25, 2019 1:13 am     Reply with quote

Quote:

The round-robin co-operative scheduler that you've implemented makes an assumption that the tasks will always run to completion faster than the SysTick


Thank you.

Yes I know, actually I have added a comment about that.
It's just the initial release, I was planning to adjust it sometime later on.
PICoHolic



Joined: 04 Jan 2005
Posts: 224

View user's profile Send private message

PostPosted: Thu Mar 28, 2019 1:55 pm     Reply with quote

Updated to v: 1.0.1
Jerson



Joined: 31 Jul 2009
Posts: 125
Location: Bombay, India

View user's profile Send private message Visit poster's website

PostPosted: Thu Mar 28, 2019 10:19 pm     Reply with quote

Now that you've got the scheduler working off the systick counter, the _sysTick variable seems redundant. You could let the main run scheduler at natural speed dependent on the task times like this

Code:

    while (TRUE)
    {
            Scheduler();    //call scheduler
    }


The individual tasks will continue to run at the specified interval.

One caveat that should be mentioned is that all tasks should run-to-completion. Also, no delay_ms type blocking functions should be used within a task to maintain co-operative task sanity.
_________________
Regards
Jerson Fernandes
PICoHolic



Joined: 04 Jan 2005
Posts: 224

View user's profile Send private message

PostPosted: Wed Apr 17, 2019 12:37 am     Reply with quote

Updated to v: 1.0.4

Note: not all features have been thoroughly tested.
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> Code Library All times are GMT - 6 Hours
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2005 phpBB Group