|
time.c |
|
|---|---|
|
|
/*
* time.c
*
* ------------------------------------------------------------
* A Kla2 Module
* Copyright (c) 2003, David Clifton
* All Rights Reserved
* http://www.codelode.com
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice, website reference, this permission
* notice, and the disclaimer of warranty below shall be included
* in all copies, derivatives, or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
* -------------------------------------------------------------
*
* Provides routines for measuring time intervals
* and triggering prioritized functions on expiration
* of time intervals, as well as periodically,
* are located in this object. One-shot, and
* N-shot triggered pfcns are supported, but they will
* introduce greater interrupt latency than
* periodic triggers.
*
*/
#include "types.h"
#include "sizes.h"
#include "err.h"
#include "pfcn.h"
#include "intrpt.h"
#include "time.h"
/* -------------------------------------------------
* local typedefs
* ------------------------------------------------- */
struct tpfcn {
s16 pfcnToPost; // index of pfcn to post
s16 repeatCount; // 0 for indefinite repetition
s32 totalTicks; // ticks before/between invocations
s32 ticksToGo; // ticks before next invocation
struct tpfcn *nextTpfcn; // next entry in priority chain
};
typedef struct tpfcn TPFCN;
/* -------------------------------------------------
* data elements for time object
* ------------------------------------------------- */
/*
* tick counter
*/
u32 Time_ticks=0;
/*
* Timer interrupt overrun flag
*/
bool tickInProgress=FALSE;
/*
* head of timed pfcn priority chain
*/
TPFCN *firstTpfcn=NULL; // highest priority entry
/*
* timed pfcn structure array
*/
TPFCN timedPfcns[SYSMAX_TIMED_PFCNS];
/*
* private method declarations
*/
void postTpfcns(void);
void insertTpfcn(TPFCN *);
TPFCN *firstFreeTpfcn(void);
TPFCN *removeTpfcn(TPFCN *);
/* --------------------------------------------------
* Public methods
* -------------------------------------------------- */
/*
* Time_setup
*
* Perform whatever actions are necessary to set up
* the system clock and periodic timer interrupts,
* and have them call the periodic Time function Time_ticks().
* Don't start the clock or enable the interrupts yet.
*/
void Time_setup(void)
{
/*
* processor specific calls to set up processor clock
* and timer interrupt period
*/
}
/*
* Time_init
*
* Initialize time interval processing and time
* triggering of pfcns (prioritized functions)
*
* WARNING: do not call this method until after
* Pfcn_init() has been called.
*/
void Time_init(void)
{
s16 j;
Time_ticks=0; // clock starts at zero
firstTpfcn=NULL; // empty priority chain
for(j=0;j<SYSMAX_TIMED_PFCNS;j++) {
timedPfcns[j].pfcnToPost=NIL;
timedPfcns[j].repeatCount=NIL;
timedPfcns[j].totalTicks=0;
timedPfcns[j].ticksToGo=0;
timedPfcns[j].nextTpfcn=NULL;
}
tickInProgress=FALSE;
/*
* processor specific call to start timer
*/
;
}
/*
* Time_get
* Return the value of Time_ticks
*/
u32 Time_get(void)
{
return Time_ticks;
}
/*
* Time_since
* Return the number of ticks that have occurred
* since the time provided in the single argument
*/
u32 Time_since(u32 prev)
{
bool intsav;
u32 diff;
intsav=Intrpt_disable();
diff=Time_get()-prev;
Intrpt_restore(intsav);
return diff;
}
/*
* Time_tick
* Routine called by the periodic timer interrupt
* or signal routine (if rtos embedded in unix).
*
* This routine must be called with interrupts
* disabled.
*/
void Time_tick(void)
{
Intrpt_markDisabled();
if(tickInProgress) {
Err_shutdown(TIMER_INTERRUPT_OVERRUN);
} else {
tickInProgress=TRUE;
Time_ticks++;
Intrpt_enable();
postTpfcns();
tickInProgress=FALSE;
}
}
/*
* Time_registerPfcn
* Register a prioritized function (pfcn), identified by
* the first argument, to be posted after the number of ticks
* indicated in the second argument(#ticks>=1), This will be
* repeated the number of times indicated in the third argument.
* If the third argument is zero, it will be reposted indefinitely,
* each time after waiting the number of ticks specified in
* the second argument.
*
* EXAMPLES:
* 1) To register a one-shot timer, which posts a
* prioritized method on expiration:
* Time_registerPfcn(pfcndex,ticks,1)
*
* 2) To register an N-shot timer, which posts a
* prioritized method on each of N expirations:
* Time_registerPfcn(pfcndex,ticks,N)
*
* 3) To register a timer which periodically posts
* a prioritized method:
* Time_registerPfcn(pfcndex,ticks,0)
*
*/
void Time_registerPfcn(s16 pfcndex,s32 ticks,s16 repeat)
{
s16 pri;
bool intsav;
TPFCN *tpptr;
/*
* timed pfcns will be chained in priority order
* that is the order in which they will be executed
* when more than one is to be executed in a particular
* periodic interrupt
*/
pri=Pfcn_priority(pfcndex);
if(pri==NIL) { // No active pfcn
Err_shutdown(NO_SUCH_PFCN); // at the supplied index
} else {
intsav=Intrpt_disable();
tpptr=firstFreeTpfcn();
if(tpptr==NULL) { // No more slots available
Err_shutdown(OUT_OF_TIMED_PFCNS); // for timed pfcns
} else {
tpptr->pfcnToPost=pfcndex; // fill in the
tpptr->repeatCount=repeat; // with the data
if(ticks<=1) ticks=1; // don't allow a bad value
tpptr->totalTicks=ticks; // supplied
tpptr->ticksToGo=ticks;
tpptr->nextTpfcn=NULL;
insertTpfcn(tpptr); // insert entry into list
Intrpt_restore(intsav);
}
}
}
/* --------------------------------------------------
* Private methods
* -------------------------------------------------- */
/*
* postTpfcns
*
* This routine scans the priority list for active
* TPFCN's. For each one it finds, it checks to see
* if ticksToGo is zero. If so, it posts the associated
* pfcn, and performs logic to decide whether to re-start
* the timer for another interval. If ticksToGo is not
* zero, it just decrements it.
*
* NOTE: If you don't want to pay the interrupt latency
* penalty associated with inserting and removing one-shot
* and N-shot timers, just don't use them. Insertion of
* periodic timers will not pose as much of a latency problem
* if you register them during powerup, before latency is
* an issue.
*
* NOTE: this routine may be called with interrupts
* enabled. The only place in the system where timed
* pfcns are removed from the tpfcn list is here, when
* a one-shot or N-repeater timer is finished. Therefore
* all the links followed in the priority chain will be
* to valid entries, even if a new entry is missed for
* one tick period.
*/
void postTpfcns(void)
{
s16 pfcnToPost;
bool intsav;
TPFCN *next;
next=firstTpfcn;
while(next!=NULL) {
pfcnToPost=next->pfcnToPost;
if(next->ticksToGo==0) { // time to post
Pfcn_post(pfcnToPost); // post it
if(next->repeatCount==0) { // reset periodic poster
next->ticksToGo=(next->totalTicks - 1);
next=next->nextTpfcn;
} else { // N repeater... see if last shot
next->repeatCount--;
if(intsav=next->repeatCount==0) { // when done, remove it and
intsav=Intrpt_disable();
next=removeTpfcn(next); // point next to next one in list
Intrpt_restore(intsav);
} else {
next->ticksToGo=(next->totalTicks - 1);
next=next->nextTpfcn;
}
}
} else {
next->ticksToGo--;
next=next->nextTpfcn;
}
}
}
/*
* insertTpfcn
*
* Insert the timed pfcn pointed to by the
* single argument into the priority chain of
* registered timed pfcns.
*
* Make sure this routine is called with
* interrupts disabled.
*/
void insertTpfcn(TPFCN *tpptr)
{
TPFCN *next,*prev;
s16 thispri,nextpri;
if(firstTpfcn==NULL) { // if list is empty
firstTpfcn=tpptr; // just add the
tpptr->nextTpfcn=NULL; // entry supplied
} else { // otherwise insert by priority
prev=NULL;
thispri=Pfcn_priority(tpptr->pfcnToPost);
next=firstTpfcn;
nextpri=Pfcn_priority (next->pfcnToPost);
while(thispri<=nextpri) { // list starts with highest priority
prev=next; // ends with lowest
next=next->nextTpfcn;
if(next==NULL) {
break;
} else {
nextpri=Pfcn_priority(next->pfcnToPost);
}
}
if(prev==NULL) { // if new one is highest priority
tpptr->nextTpfcn=firstTpfcn; // put it first
firstTpfcn=tpptr;
} else { // otherwise just link it in
tpptr->nextTpfcn=prev->nextTpfcn; // where
prev->nextTpfcn=tpptr; // it belongs
}
}
}
/*
* removeTpfcn
*
* Remove from the timed pfcn priority list, the
* entry pointed to by the argument supplied. Return
* NULL if it was the last one of the list. Otherwise
* return a pointer to the one that followed it
* in the list.
*
* Make sure this routine is called with
* interrupts disabled.
*/
TPFCN *removeTpfcn(TPFCN *tpptr)
{
TPFCN *next,*prev;
/*
* check pointer argument
*/
if(tpptr==NULL) {
Err_shutdown(REMOVED_NONEXISTENT_TIMER);
}
/*
* take care of list first
*/
prev=NULL;
next=firstTpfcn;
while(next!=tpptr) {
if(next==NULL) {
Err_shutdown(REMOVED_NONEXISTENT_TIMER);
}
prev=next;
next=next->nextTpfcn;
}
next=tpptr->nextTpfcn;
if(prev==NULL) {
firstTpfcn=next;
} else {
prev->nextTpfcn=next;
}
/*
* next clear the entry removed
*/
tpptr->pfcnToPost=NIL;
tpptr->repeatCount=NIL;
tpptr->totalTicks=0;
tpptr->ticksToGo=0;
tpptr->nextTpfcn=NULL;
return next;
}
/*
* firstFreeTpfcn
*
* This routine returns a pointer to the first free
* TPFCN in the array timedPfcns[]. If there is no
* free TPFCN, this routine returns NULL.
*/
TPFCN *firstFreeTpfcn(void)
{
s16 j;
for(j=0;j<SYSMAX_TIMED_PFCNS;j++) {
if(timedPfcns[j].pfcnToPost==NIL) {
return &timedPfcns[j];
}
}
return NULL;
}
|