|
|
View previous topic :: View next topic |
Author |
Message |
PICoHolic
Joined: 04 Jan 2005 Posts: 224
|
Basic co-operative scheduler - v: 1.0.4 |
Posted: Fri Mar 22, 2019 11:01 am |
|
|
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: 1353
|
|
Posted: Sat Mar 23, 2019 6:34 am |
|
|
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
|
|
Posted: Sat Mar 23, 2019 3:27 pm |
|
|
Well, surprisingly, the compiler did not allow me to use sTask *. So I had to put void * instead and then cast it |
|
|
jeremiah
Joined: 20 Jul 2010 Posts: 1353
|
|
Posted: Sun Mar 24, 2019 11:02 am |
|
|
PICoHolic wrote: | Well, surprisingly, the compiler did not allow me to use sTask *. So I had to put void * instead and then cast it |
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
|
|
Posted: Sun Mar 24, 2019 1:23 pm |
|
|
You have a point, will have to try it |
|
|
Jerson
Joined: 31 Jul 2009 Posts: 125 Location: Bombay, India
|
|
Posted: Sun Mar 24, 2019 10:21 pm |
|
|
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
|
|
Posted: Mon Mar 25, 2019 1:13 am |
|
|
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
|
|
Posted: Thu Mar 28, 2019 1:55 pm |
|
|
Updated to v: 1.0.1 |
|
|
Jerson
Joined: 31 Jul 2009 Posts: 125 Location: Bombay, India
|
|
Posted: Thu Mar 28, 2019 10:19 pm |
|
|
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
|
|
Posted: Wed Apr 17, 2019 12:37 am |
|
|
Updated to v: 1.0.4
Note: not all features have been thoroughly tested. |
|
|
|
|
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
|