// ==================================================================== //
// T I M _ S R C . C P P //
// -------------------------------------------------------------------- //
// Task : Subroutines to provide timers that set flags or call //
// call-back routines on expiry (i.e. on interrupts) //
// ==================================================================== //
#define _WIN32_WINNT 0x400
#include "TIM_src.h"
#include <process.h>
#include <sys/timeb.h>
#include <stdio.h>
//#define IFD1 if(dbgjpm&1) // Errors
#define IFD2 if(dbgjpm&2) // Standard
//#define IFD4 if(dbgjpm&4) // Details
#define KeyTak(y) WaitForSingleObject(y,INFINITE)
#define KeyRtn(y) SetEvent(y)
static void TIM_irpt_rtn(void*);
#define thread_wait 200 // Wait this number of mSecs for thread to start or stop
#define InitIfNot {if(!already_initialised)initialise();}
// ==================================================================== //
// Data global to the universe //
// ==================================================================== //
#pragma data_seg(".TIMshare")
long TIMvz=sizeof(TIMver)-1; // for VBusers
char TIMversion[] = TIMver;
long TIMdz=sizeof(TIMdat)-1; // for VBusers
char TIMdate[] = TIMdat;
#pragma data_seg()
extern "C" char* _stdcall TIM_getVersion(void) {return TIMversion;}
extern "C" char* _stdcall TIM_getDate (void) {return TIMdate;}
bool beginCC = true; // default to C/C++ user, it is global
static long dbgjpm; // 0none,1thread,2user,3all or 4basic
extern "C" void TIM_set_debug (long x) {dbgjpm = x;}
// ==================================================================== //
// Data global to this module //
// ==================================================================== //
static long already_initialised = false,
XitHeartBeatRqd = false;
static HANDLE ti_keyh, // Event handle for the above key
tickerh; // handle for function event on ticker thread
static tim_typ TIM[MX_NR_TIM];
static timF_typ timF[MX_NR_TIM];
static void initialise (void);
// ==================================================================== //
// T I M _ c a n _ t m r //
// ==================================================================== //
// T I M _ m o d _ t m r //
// ==================================================================== //
// T I M _ g e t _ h a n d l e //
// ==================================================================== //
// T I M _ m a k e _ t m r //
// ==================================================================== //
// T I M _ m a k e _ t m r F //
// ==================================================================== //
// T I M _ m a k e _ t m r T //
// ==================================================================== //
// static i n i t i a l i s e //
// ==================================================================== //
// T I M _ r e s e t //
// ==================================================================== //
// T I M _ s t d _ f n p //
// ==================================================================== //
// T I M _ s p i n _ t h r e a d //
// ==================================================================== //
// T I M _ k i l l _ t h r e a d //
// ==================================================================== //
// static T I M _ i r p t _ c b //
// ==================================================================== //
// static T I M _ i r p t _ r t n //
// ==================================================================== //
// T I M _ m S //
// ==================================================================== //
// T I M _ m S q c t r //
// ==================================================================== //
// T I M _ i c t r //
// ==================================================================== //
// T I M _ q c t r //
// ==================================================================== //
// T I M _ i n i t V B //
// ==================================================================== //
// T I M _ e r r t x t //
// ==================================================================== //
// ==================================================================== //
// T I M _ c a n _ t m r //
// -------------------------------------------------------------------- //
// Purpose : Cancels a timer //
// -------------------------------------------------------------------- //
// Implementation : Sets the counter for the timer to -1 to //
// indicate that the timer is not in use. //
// -------------------------------------------------------------------- //
// Input parameters : t_id - identification number for timer //
// Output parameters : None //
// Return value : _SUCCESS, _NO_KNOWN_TIMER //
// ==================================================================== //
extern "C" long _stdcall TIM_can_tmr (long t_id)
{
long timer_id = t_id - 1;
InitIfNot;
if (0 <= timer_id && timer_id < MX_NR_TIM) // timer exist?
{ // maybe
if (TIM[timer_id].count >= 0) // timer exist?
{ // yes
// =================================================== //
// Set the timer counter to -1. //
// This effectively cancels the timer. //
// =================================================== //
InterlockedExchange(&TIM[timer_id].count,-1);// declare the timer free
if (TIM[timer_id].handle != NULL)
{
CloseHandle(TIM[timer_id].handle);
TIM[timer_id].handle = NULL;
}
return TIM_SUCCESS;
}
}
return TIM_NO_KNOWN_TIMER;
}
// ==================================================================== //
// T I M _ m o d _ t m r //
// -------------------------------------------------------------------- //
// Purpose : Sets a timer about to expire or modifies it //
// -------------------------------------------------------------------- //
// Implementation : Adjusts the counter for the timer so that //
// the timer may expire at a different time. //
// -------------------------------------------------------------------- //
// Input parameters : t_id - identification number for timer //
// : time _ the new time (in mSecs) //
// Output parameters : None //
// Return value : _SUCCESS, _NO_KNOWN_TIMER //
// ==================================================================== //
extern "C" long _stdcall TIM_mod_tmr (long t_id, long time)
{
long timer_id = t_id - 1;
InitIfNot;
if (0 <= timer_id && timer_id < MX_NR_TIM) // timer exists?
{ // maybe
if (TIM[timer_id].count >= 0) // timer exists?
{ // yes
// =================================================== //
// Adjust a timer counter. //
// This may change the expiration of the timer. //
// N.B. this does not affect the repeating interval //
// =================================================== //
if (TIM[timer_id].count <= 1) return TIM_SUCCESS;
long t = time; if(t<=0) t = 1;
InterlockedExchange(&TIM[timer_id].count,t);// new time to expiration
return TIM_SUCCESS;
}
}
return TIM_NO_KNOWN_TIMER;
}
// ==================================================================== //
// T I M _ g e t _ h a n d l e //
// -------------------------------------------------------------------- //
// Purpose : return a HANDLE given a timer id //
// -------------------------------------------------------------------- //
// Input parameters : t_id - identification number for timer //
// Return value : timer HANDLE, NULL //
// ==================================================================== //
extern "C" HANDLE _stdcall TIM_get_handle (long t_id)
{
long timer_id = t_id - 1;
InitIfNot;
if (0 <= timer_id && timer_id < MX_NR_TIM) // timer exists?
{ // its possible
return TIM[timer_id].handle;
}
return NULL;
}
// ==================================================================== //
// T I M _ m a k e _ t m r //
// -------------------------------------------------------------------- //
// Purpose : Starts a timer, specifying a flag to be set //
// when the timer expires. //
// -------------------------------------------------------------------- //
// Implementation : Finds a free slot & initialises timer details.//
// Next time the interrupt occurs the timer //
// counter will automatically be decremented. //
// -------------------------------------------------------------------- //
// Input parameters : time - time in units of 1/18ths of second. //
// timer_id- ptr to identification number for //
// timer //
// flag - pointer to flag name string to //
// declare when timer expires. //
// rpt - repeat (true/false) //
// Output parameters : timer_id //
// Return value : _SUCCESS, _NO_TIMERS_LEFT //
// ==================================================================== //
extern "C" long _stdcall TIM_make_tmr (long time, long*timer_id, char*flag, bool rpt)
{
InitIfNot;
// =========================================================== //
// Finds the first free slot & sets up the time, //
// & flag in the timers slot. //
// In finding the slot try to avoid free slots with handles //
// this gives time after timer expiry to check the flag state //
// =========================================================== //
KeyTak(ti_keyh);
long j=MX_NR_TIM; for(long i=0; i<MX_NR_TIM; i++)
{
if (TIM[i].count == -1) // timer slot unused?
{ if (j == MX_NR_TIM) j = i; // yes, 1st possible slot to use
if (TIM[i].handle == NULL) break; // not last used for a flag slot
}
}
if (i == MX_NR_TIM) i = j; // entirely free slot?, no
if (i < MX_NR_TIM) // got a free slot?
{ // yes
if (TIM[i].handle != NULL && strcmp(TIM[i].flagna,flag) != 0)
{ // handle exists but flag name is different
CloseHandle(TIM[i].handle); TIM[i].handle = NULL;
}
if (TIM[i].handle == NULL)
{ // need to create handle & record flag name
if (strcmp("",flag)==0) TIM[i].handle=CreateEvent(NULL,false,false,NULL);
else TIM[i].handle=CreateEvent(NULL,false,false,flag);
if (strlen(flag)<sizeof(TIM[i].flagna)) strcpy(TIM[i].flagna,flag);
else strncpy(TIM[i].flagna,flag,sizeof(TIM[i].flagna)-1);
IFD2 printf("\nflagHandle TIM[%d].handle %d",i,TIM[i].handle);
}
TIM[i].fnp = // function pointer
TIM[i].thp = NULL; // thread pointer
long t = time; if(t<=0) t = 1;
if(rpt) TIM[i].rptcnt = t;
else TIM[i].rptcnt = -1;
InterlockedExchange(&TIM[i].count,t); // time to expiration
// ======================================================= //
// Give the caller a handle on the timer, so that he //
// can cancel it if required. //
// ======================================================= //
*timer_id = i+1;
KeyRtn(ti_keyh);
return TIM_SUCCESS;
}
KeyRtn(ti_keyh);
// =========================================================== //
// If we get to this point then there are no free slots. //
// =========================================================== //
return TIM_NO_TIMERS_LEFT;
}
// ==================================================================== //
// T I M _ m a k e _ t m r F //
// -------------------------------------------------------------------- //
// Purpose : Starts a timer, specifying a function to be //
// run when the timer expires. //
// -------------------------------------------------------------------- //
// Implementation : Finds a free slot & initialises timer details.//
// Next time the interrupt occurs the timer //
// counter will automatically be decremented. //
// -------------------------------------------------------------------- //
// Input parameters : time - time in units of 1/18ths second. //
// timer_id- ptr to identification no. for timer //
// fnp - pointer to function to be run when //
// timer expires. //
// prm - pointer to function parameters //
// rpt - repeat (true/false) //
// Output parameters : timer_id //
// Return value : _SUCCESS, _NO_TIMERS_LEFT //
// ==================================================================== //
extern "C" long _stdcall TIM_make_tmrF (long time, long*timer_id, void(*fnp)(void*), void*prm, bool rpt)
{
InitIfNot;
// =========================================================== //
// Finds the first free slot & sets up the time, //
// function & parameter address in the timers slot. //
// In finding the slot try to avoid free slots with handles //
// this gives time after timer expiry to check the flag state //
// =========================================================== //
KeyTak(ti_keyh);
long j = MX_NR_TIM;
for(long i=0; i<MX_NR_TIM; i++)
{
if (TIM[i].count == -1) // timer slot unused?
{ if (j == MX_NR_TIM) j = i; // yes, 1st possible slot to use
if (TIM[i].handle == NULL) break; // not last used for a flag slot
}
}
if (i == MX_NR_TIM) i = j; // entirely free slot?, no
if (i < MX_NR_TIM) // got a free slot?
{ // yes
if (TIM[i].handle != NULL) { CloseHandle(TIM[i].handle); TIM[i].handle= NULL; }
TIM[i].fnp = fnp; // function pointer
TIM[i].thp = NULL; // thread pointer
TIM[i].prm = prm; // parameter
long t = time; if(t<=0) t = 1;
if(rpt) TIM[i].rptcnt = t;
else TIM[i].rptcnt = -1;
InterlockedExchange(&TIM[i].count,t); // time to expiration
// ======================================================= //
// Give the caller a handle on the timer, so that he //
// can cancel it if required. //
// ======================================================= //
*timer_id = i+1;
KeyRtn(ti_keyh);
return TIM_SUCCESS;
}
KeyRtn(ti_keyh);
// =========================================================== //
// If we get to this point then there are no free slots. //
// =========================================================== //
return TIM_NO_TIMERS_LEFT;
}
// ==================================================================== //
// T I M _ m a k e _ t m r T //
// -------------------------------------------------------------------- //
// Purpose : Starts a timer, specifying a thread to be //
// spun off when the timer expires. //
// -------------------------------------------------------------------- //
// Implementation : Finds a free slot & initialises timer details.//
// Next time the interrupt occurs the timer //
// counter will automatically be decremented. //
// -------------------------------------------------------------------- //
// Input parameters : time - time in units of 1/18ths second. //
// timer_id- ptr to identification no. for timer //
// thp - pointer to thread code to be spun //
// off when timer expires. //
// prm - pointer to thread parameters //
// rpt - repeat (true/false) //
// Output parameters : timer_id //
// Return value : _SUCCESS, _NO_TIMERS_LEFT //
// ==================================================================== //
extern "C" long _stdcall TIM_make_tmrT (long time, long*timer_id, void(*thp)(void*), void*prm, bool rpt)
{
InitIfNot;
// =========================================================== //
// Finds the first free slot & sets up the time, //
// thread & parameter address in the timers slot. //
// In finding the slot try to avoid free slots with handles //
// this gives time after timer expiry to check the flag state //
// =========================================================== //
KeyTak(ti_keyh);
long j = MX_NR_TIM;
for(long i=0; i<MX_NR_TIM; i++)
{
if (TIM[i].count == -1) // timer slot unused?
{ if (j == MX_NR_TIM) j = i; // yes, 1st possible slot to use
if (TIM[i].handle == NULL) break; // not last used for a flag slot
}
}
if (i == MX_NR_TIM) i = j; // entirely free slot?, no
if (i < MX_NR_TIM) // got a free slot?
{ // yes
if (TIM[i].handle != NULL) { CloseHandle(TIM[i].handle); TIM[i].handle= NULL; }
TIM[i].fnp = NULL; // function pointer
TIM[i].thp = thp; // thread pointer
TIM[i].prm = prm; // parameter
long t = time; if(t<=0) t = 1;
if(rpt) TIM[i].rptcnt = t;
else TIM[i].rptcnt = -1;
InterlockedExchange(&TIM[i].count,t); // time to expiration
// ======================================================= //
// Give the caller a handle on the timer, so that he //
// can cancel it if required. //
// ======================================================= //
*timer_id = i+1;
KeyRtn(ti_keyh);
return TIM_SUCCESS;
}
KeyRtn(ti_keyh);
// =========================================================== //
// If we get to this point then there are no free slots. //
// =========================================================== //
return TIM_NO_TIMERS_LEFT;
}
// ==================================================================== //
// i n i t i a l i s e //
// -------------------------------------------------------------------- //
// Purpose : Performs initialisation functions //
// -------------------------------------------------------------------- //
// Implementation : Performs the following functions: //
// 1. Initialises each timer to an unused value. //
// 2. Spins off the ticker thread //
// ==================================================================== //
static void initialise (void)
{
// =========================================================== //
// Initialise the library once. //
// =========================================================== //
if (already_initialised) return;
if (dbgjpmval) dbgjpm = dbgjpmval;
already_initialised = true;
IFD2 printf("\nTIMz %d",sizeof(TIM));
// =========================================================== //
// Initialise each counter to unused(-1). //
// =========================================================== //
memset( TIM,0,sizeof( TIM));
memset(timF,0,sizeof(timF));
for(long i=0; i<MX_NR_TIM; i++) TIM[i].count = -1;
ti_keyh = CreateEvent(NULL,false,true,NULL);
// =========================================================== //
// Spin off the ticker thread //
// =========================================================== //
_beginthread(&TIM_irpt_rtn,0,0);
}
// ==================================================================== //
// T I M _ s t d _ f n p //
// -------------------------------------------------------------------- //
// Prevent the default _cdecl type call by forcing a _stdcall //
// ==================================================================== //
extern void TIM_std_fnp (void (*fnp)(void*), void *prm)
{
long (_stdcall*fn)(void*);
fn = (long(_stdcall*)(void*))fnp;
(*fn)(prm); // (*fnp)(prm);
}
// ==================================================================== //
// T I M _ r e s e t //
// -------------------------------------------------------------------- //
// Reset timers, cleanup & reset markers & flags, exit the two threads //
// ==================================================================== //
extern "C" void _stdcall TIM_reset (void)
{
long i;
// =========================================================== //
// Cancel repeaters, expire the unexpired, return handles //
// =========================================================== //
for(i=0; i<MX_NR_TIM; i++) InterlockedExchange(&TIM[i].rptcnt, -1);
for(i=0; i<MX_NR_TIM; i++) InterlockedExchange(&TIM[i].count ,TIM[i].rptcnt);
for(i=0; i<MX_NR_TIM; i++)
if (TIM[i].handle) { CloseHandle(TIM[i].handle); TIM[i].handle = NULL; }
// =========================================================== //
// Shut down the ticker threads //
// =========================================================== //
if (already_initialised)
{
XitHeartBeatRqd = true;
SetEvent(tickerh); // release the ticker thread
// ======================================================= //
// Await ticker-ticker thread exit or dt elapse //
// ======================================================= //
i = 0;
while(i < thread_wait)
{
Sleep(100); i++;
if (!XitHeartBeatRqd) break;
}
CloseHandle(ti_keyh);
XitHeartBeatRqd = already_initialised = false;
}
}
// ==================================================================== //
// T I M _ s p i n _ t h r e a d //
// -------------------------------------------------------------------- //
// Spin off a thread for a user, since BOSDLL does lots of this //
// N.B The thread entry point must be a _stdcall entry point //
// The user must Close the returned handle //
// ==================================================================== //
extern "C" uint _stdcall TIM_spin_thread (void(_stdcall*thp)(void*), void*prm)
{
uint tha;
return _beginthreadex(NULL,0,(uint(_stdcall*)(void*))(thp),prm,0,&tha);
}
// ==================================================================== //
// T I M _ k i l l _ t h r e a d //
// -------------------------------------------------------------------- //
// Kill a thread for a user, since BOSDLL does lots of this //
// ==================================================================== //
extern "C" void _stdcall TIM_kill_thread (uint han) { _endthreadex(han); }
// ==================================================================== //
// T I M _ i r p t _ c b //
// -------------------------------------------------------------------- //
// Purpose: Interrupt service routine(tickerticker thread) //
// -------------------------------------------------------------------- //
// Implementation : Scans each slot in the timer array looking //
// for a timer to decrement. //
// -------------------------------------------------------------------- //
// If timer is expiring (counter = 1) then set //
// the flag or call the user's function or //
// spin off the users's thread, as appropriate. //
// ==================================================================== //
static __int64 TmilliS = 0, Tquadctr = 0;
static long TIM_irpt_cbFIRST = true, begin_mS;
static void CALLBACK TIM_irpt_cb (UINT a, UINT b, DWORD c, DWORD d, DWORD e)
{
tim_typ *tp = TIM; // pointer is 1/3 shorter & faster
if (TIM_irpt_cbFIRST)
{ TIM_irpt_cbFIRST = false;
if (!SetThreadPriority(GetCurrentThread(),THREAD_PRIORITY_TIME_CRITICAL))
printf("\n StThPr failed %d",GetLastError());
begin_mS = true;
}
if (begin_mS)
{
_timeb stime; _ftime(&stime);
if (TmilliS !=(stime.millitm)) // mS just moved on?
{ // yes, this is most accurate
TmilliS = stime.time; // secs since 1/1/70
TmilliS *=(1000); //
TmilliS +=(stime.millitm); // msecs since 1st Jan 1970
begin_mS = false;
}
}
else TmilliS++;
Tquadctr++; // add, adc
// =========================================================== //
// Run through each slot in the timer array checking for a //
// used timer slot. If we find one then decrement the timer //
// counter. If counter is 1 then the timer is about to expire, //
// so signal the timer expired to the host application & set //
// the slot to be unused again. //
// =========================================================== //
for(long i=0; i<MX_NR_TIM; i++,tp++)
if (tp->count > 0)
{
// =================================================== //
// Trap when the signal is expiring. //
// =================================================== //
if (InterlockedDecrement(&tp->count) == 0)
{
// =============================================== //
// Signal to the user(appropriately, - depending //
// on how the timer was setup). //
// =============================================== //
if (tp->handle != NULL)
{
// =========================================== //
// Set the flag //
// =========================================== //
IFD2 printf("\nSetEvent, handle %d",tp->handle);
SetEvent(tp->handle);
}
if (tp->fnp != NULL)
{
// =========================================== //
// Call the user's function on ticker thread //
// =========================================== //
if (timF[i].fnp == NULL) // Matching slot available?
{ timF[i].fnp = tp->fnp; // yes
timF[i].prm = tp->prm;
}
else // no, use any empty slot
{ for(long j=0; j<MX_NR_TIM; j++)
if (timF[j].fnp == NULL)
{ timF[j].fnp = tp->fnp;
timF[j].prm = tp->prm;
break;
}
}
SetEvent(tickerh); // release the ticker thread
}
if (tp->thp != NULL)
{
// =========================================== //
// Spin off the user's thread. //
// =========================================== //
if (beginCC) {_beginthread( tp->thp ,0,tp->prm);}
else
{ uint th, tha;
th = _beginthreadex(NULL,0,(uint(_stdcall*)(void*))(tp->thp), tp->prm,0,&tha);
if (th > 0) CloseHandle((void*)th);
}
}
// =============================================== //
// Signal that the slot is now free or repeat //
// =============================================== //
InterlockedExchange(&tp->count,tp->rptcnt);
}
}
}
// ==================================================================== //
// T I M _ i r p t _ r t n //
// -------------------------------------------------------------------- //
// Purpose: Interrupt service routine (ticker thread). //
// start the ticker-ticker thread. //
// wait for functions to execute or exit request. //
// ==================================================================== //
static void TIM_irpt_rtn (void *p)
{
MMRESULT MMRsts;
tickerh = CreateEvent(NULL,false,false,NULL); IFD2 printf("\nflagHandle tickerh %d",tickerh);
_timeb stime; _ftime(&stime); TmilliS =(stime.millitm);
MMRsts = timeSetEvent(1,0,&TIM_irpt_cb,0,TIME_PERIODIC);
while (!XitHeartBeatRqd)
{
for(long i=0; i<MX_NR_TIM; i++)
{
if (timF[i].fnp != NULL)
{
if (beginCC) (*timF[i].fnp)(timF[i].prm);
else TIM_std_fnp(timF[i].fnp, timF[i].prm);
timF[i].fnp = NULL; // fuction called
}
}
WaitForSingleObject(tickerh,INFINITE);
}
timeKillEvent(MMRsts);
CloseHandle(tickerh);
TIM_irpt_cbFIRST = true;
XitHeartBeatRqd = false;
}
// ==================================================================== //
// T I M _ m S //
// T I M _ m S q c t r //
// T I M _ i c t r //
// T I M _ q c t r //
// -------------------------------------------------------------------- //
// return an accurate millisecond value of the current second. //
// return quad milliseconds since 1/1/70. //
// return long milliseconds since init. //
// return quad milliseconds since init. //
// ==================================================================== //
extern "C" long _stdcall TIM_mS (void){InitIfNot;return(long)(TmilliS%1000); }
extern "C" __int64 _stdcall TIM_mSqctr (void){InitIfNot;return TmilliS; }
extern "C" long _stdcall TIM_ictr (void){InitIfNot;return(long)(Tquadctr&0x7fffffff);}
extern "C" __int64 _stdcall TIM_qctr (void){InitIfNot;return Tquadctr; }
// ==================================================================== //
// T I M _ i n i t V B //
// -------------------------------------------------------------------- //
// ==================================================================== //
extern "C" void _stdcall TIM_initVB (void) { beginCC = false; }
// ==================================================================== //
// T I M _ e r r t x t //
// -------------------------------------------------------------------- //
// ==================================================================== //
extern "C" char* _stdcall TIM_errtxt (long stt)
{
#define etxxx "No such error code" // x
#define etP01 "TIM_SUCCESS" // 1
#define etM02 "TIM_FAILURE" // -2
#define etM04 "TIM_NO_KNOWN_TIMER" // -4
#define etM06 "TIM_NO_TIMERS_LEFT" // -6
#define etM08 "TIM_TIMER_EXPIRED" // -8
static err_typ et[] ={ sizeof(etxxx)-1,etxxx,
sizeof(etP01)-1,etP01,
sizeof(etM02)-1,etM02,
sizeof(etM04)-1,etM04,
sizeof(etM06)-1,etM06,
sizeof(etM08)-1,etM08 };
if (stt == 1) return et[ 1].err;
if (stt == -2) return et[ 2].err;
if (stt == -4) return et[ 3].err;
if (stt == -6) return et[ 4].err;
if (stt == -8) return et[ 5].err;
return et[0].err;
}