|
|
dereksoftstuff
Expert Boarder |
|
2008/11/27 06:35 |
|
Re:HowTo : Unlimited Software Timers, Events & Del
timerUtils.h & .c
built upon :-
malloc
linkLists
| Code: |
#ifndef TIMER_UTILS_H_
#define TIMER_UTILS_H_
#include <systemDefs.h>
#include <locks.h>
#include <gpio.h>
#ifdef __cplusplus
extern "C"
{
#endif
//------------------------------------------------------------------------
// General purpose timer utilities
// a) timers (uses TIMER0)
// b) delays
// c) events (cyclic and rx interrupts)
// d) tx interrupt generator (uses TIMER1)
// can select/deselect c) & d)
//------------------------------------------------------------------------
// call once only - system initialisation
boolean initTimerUtils(void);
// ********************** TIMERS *******************************
// returned from createTimer if error
#define TIMER_ID_ERROR 0
typedef void *TIMER_ID;
// *mod* was called getTimerId
// call once only for each s/w timer required
TIMER_ID createTimer(void);
void startTimer(TIMER_ID timerId);
UINT msTimeElapsed(TIMER_ID timerId);
UINT usTimeElapsed(TIMER_ID timerId);
UINT clocksElapsed(TIMER_ID timerId);
// utilities
UINT clocksElapsedSinceTime(UINT startTime, UINT currentTime);
UINT timeNow(void);
UINT hwTimerWrap(void);
TIMER_ID deleteTimer(TIMER_ID timerId);
// utility - unique number for a timer
UINT getTimerNumber(TIMER_ID timerId);
// ********************** DELAYS *******************************
void usDelay(UINT usPeriod);
void msDelay(UINT msPeriod);
void sDelay(UINT sPeriod);
// ********************** EVENTS *******************************
// **** comment/uncomment these 2 as required
#define USE_EVENTS
#define EVENTS_WITH_STATS
#if defined(EVENTS_WITH_STATS)
#if !defined(USE_EVENTS)
#error "need USE_EVENTS to use EVENTS_WITH_STATS!"
#endif // USE_EVENTS
#endif // EVENTS_WITH_STATS
#if defined(USE_EVENTS)
#define EVENT_ID_ERROR 0
typedef void *EVENT_ID;
typedef void *EVENT_PARAM;
// this is the event processor - forever loop
#define NO_EVENTS 0 // error
UINT processEvents(void);
#define NO_EVENT_PARAM 0
// *mod* was called getEventId
// call once only for each s/w event required
// returns eventId
// priority highest = 1 (do first), no duplicates allowed, can mix cyclics & interrupts
// can pass params to your handler for more flexibility (via startEvent())
// e.g. put state machine in your eventHandler
EVENT_ID createCyclicEvent(UINT msTimeoutPeriod,
UINT priority,
void(*eventHandler)(EVENT_PARAM param));
// interruptGPIOPin - only for GPIO interrupts e.g. GPIO_PIN_0 ...
#define NOT_GPIO_INTERRUPT 0
EVENT_ID createInterruptEvent(UINT hwInterruptId,
BYTE interruptGPIOPin,
UINT priority,
void(*eventHandler)(EVENT_PARAM param));
// start & stop the events (for rx interrupts - will enable / disable (not GPIO) )
// *mod* added param
void startEvent(EVENT_ID eventId, EVENT_PARAM param);
// resume at next period
void resumeCyclicEvent(EVENT_ID eventId, EVENT_PARAM param);
void restartCyclicEvent(EVENT_ID eventId, EVENT_PARAM param, UINT msTimeoutPeriod);
void stopEvent(EVENT_ID eventId);
// -----------------------------------------------------------------------------------------
// spawn uses - current event has to send a message(s) and await response(s) (without blocking),
// before continuing with processing. A spawned event can handle message reception and
// notifies original event when available via callback
// spawn - transfer processing from root event to a child event (use stopCurrentEvent() to stop
root event)
void spawnEvent(EVENT_ID spawnEventId, EVENT_PARAM spawnParam);
// return child result to root event via callback (and stop child event)
void spawnEventCallback(EVENT_ID spawnEventId, EVENT_PARAM spawnParam);
void stopCurrentEvent(void);
// -----------------------------------------------------------------------------------------
//is this the event currently being run
boolean isCurrentEvent(EVENT_ID eventId);
// utility - unique number for an event
UINT getEventNumber(EVENT_ID eventId);
//is the event in use
boolean isEventInUse(EVENT_ID eventId);
// use in ISR
void eventReady(EVENT_ID eventId);
EVENT_ID deleteEvent(EVENT_ID eventId);
#if defined(EVENTS_WITH_STATS)
// ******************************* STATS ******************************
// whats happening in the system
typedef struct eventStatsResultsType {
ULONG numEvents;
ULONG numEventsMissed;
ULONG numEventsDelayed;
ULONG numEventsLatencyLow;
ULONG numEventsLatencyHigh;
};
// *mod* ptr return
struct eventStatsResultsType *getEventStats(EVENT_ID eventId);
void resetEventStatsResults(EVENT_ID eventId);
// allows user to configure stats collected
// Event Latency - Band 1 : (0 to Low) Band 2 : (Low to High)
typedef struct eventStatsConfigType {
UINT usLatencyThresholdLow;
UINT usLatencyThresholdHigh;
};
void configureEventStats(EVENT_ID eventId, struct eventStatsConfigType config);
typedef struct cpuUsageType {
UINT eventCount;
UINT interruptEventCount;
UINT cyclicEventCount;
};
struct cpuUsageType *getCpuStats(void);
#endif // EVENTS_WITH_STATS
#endif // USE_EVENTS
// ********************** TX INTERRUPTS *******************************
// **** comment/uncomment as required
#define USE_TX_INTERRUPTS
#define TX_INTERRUPTS_WITH_STATS
#if defined(TX_INTERRUPTS_WITH_STATS)
#if !defined(USE_TX_INTERRUPTS)
#error "need USE_TX_INTERRUPTS to use TX_INTERRUPTS_WITH_STATS!"
#endif // USE_TX_INTERRUPTS
#endif // TX_INTERRUPTS_WITH_STATS
#if defined(USE_TX_INTERRUPTS)
#if !defined(USE_LOCKS)
#error "need USE_LOCKS to use tx interrupts!"
#endif // USE_LOCKS
typedef void *INTERRUPT_ID;
#define INTERRUPT_ID_ERROR 0
typedef enum interruptStatusType {INTERRUPT_CONTINUE_POLLING, INTERRUPT_STOP_POLLING};
// call once only for each interrupt required
// single shot -> usFrequency = 0
// usTolerance -> limit of lateness allowed for interrupt -> will auto stop if exceeded
INTERRUPT_ID createInterrupt(UINT usFrequency,
UINT usTolerance,
enum interruptStatusType(*interruptCallbackHandler)(void));
INTERRUPT_ID deleteInterrupt(INTERRUPT_ID interruptId);
// -----------------------------------------------------------------------
// start interrupt in usTriggerTimeFromNow or as soon as possible if your time is too soon
void startInterrupt(INTERRUPT_ID interruptId, UINT usTriggerTimeFromNow);
// for more flexibility
// start interrupt in usTriggerTimeFromRefTime
// where refTime = timeNow() at your reference point
void startInterruptFromRefTime(INTERRUPT_ID interruptId, UINT usTriggerTimeFromRefTime, UINT refTime);
void stopInterrupt(INTERRUPT_ID interruptId);
// -----------------------------------------------------------------------
// utiility - unique number for an interrupt 1 .. n
UINT getInterruptNumber(INTERRUPT_ID interruptId);
//is this the interrupt currently being run
boolean isCurrentInterrupt(INTERRUPT_ID interruptId);
//is the interrupt in use
boolean isInterruptInUse(INTERRUPT_ID interruptId);
// -----------------------------------------------------------------------
#if defined(TX_INTERRUPTS_WITH_STATS)
#define TX_INTERRUPT_STATS_MAX_NUM_STATUS 6 // max entry - used for sizing only
#define TX_INTERRUPT_STATS_ISR_CALL_MISMATCH 5
#define TX_INTERRUPT_STATS_USER_TIME_TOO_SOON 4
#define TX_INTERRUPT_STATS_LOST_SYNC 3
#define TX_INTERRUPT_STATS_TOLERANCE_EXCEEDED 2 // auto stop interrupt
#define TX_INTERRUPT_STATS_DELAYED 1
#define TX_INTERRUPT_STATS_GENERATED_SUCCESS 0
typedef struct interruptStatsResultsType {
ULONG numTxInterrupts[TX_INTERRUPT_STATS_MAX_NUM_STATUS];
};
struct interruptStatsResultsType *getInterruptStats(INTERRUPT_ID interruptId);
void resetInterruptStats(INTERRUPT_ID interruptId);
#endif // TX_INTERRUPTS_WITH_STATS
#endif // USE_TX_INTERRUPTS
#ifdef __cplusplus
}
#endif
#endif // TIMER_UTILS_H_
|
| Code: |
#include <timerUtils.h>
#include <GPIOUtils.h>
#include <systemRAM.h>
#include <linkList.h>
#include <hw_ints.h>
#include <hw_nvic.h>
#include <sysctl.h>
#include <interrupt.h>
#include <lmi_timer.h> // driverlib timer - maybe called <timer.h> for others
#include <hw_timer.h>
#include <malloc.h>
#include <string.h>
#include <stdlib.h>
#ifdef __cplusplus
extern "C"
{
#endif
#define HW_TIMER_WRAP 0xFFFFFFFF // max 32 bit
//************************** LOCAL PROTOTYPES ****************************************
void selfCalibration(void);
UINT clocksElapsedSince(UINT startTime);
void clockDelay(ULONG value);
UINT clocksRemaining(UINT startTime, UINT currentTime);
//*****************************************************************************
// The interrupt handler for timer0 interrupt.
// continuous timer - never stops - just wraps ...
//*****************************************************************************
void Timer0IntHandler(void) {
// ignore ...
// Clear the timer interrupt.
TimerIntClear(TIMER0_BASE, TIMER_TIMA_TIMEOUT);
}
// local modules
//***********************************************************************
UINT timeNow(void) {
return HWREG(TIMER0_BASE + TIMER_O_TAR);
}
UINT hwTimerWrap(void) {
return HWREG(TIMER0_BASE + TIMER_O_TAILR);
}
UINT clocksElapsedSinceTime(UINT startTime, UINT currentTime) {
UINT numClocksElapsed;
if (currentTime > startTime) {
// wrap
numClocksElapsed = startTime + (hwTimerWrap() - currentTime);
} else {
numClocksElapsed = startTime - currentTime;
}
return numClocksElapsed;
}
UINT clocksElapsedSince(UINT startTime) {
return clocksElapsedSinceTime(startTime, timeNow());
}
//***********************************************************************
// ************************* TIMERS ***********************************
//***********************************************************************
//************************** DATA ****************************************
#define FIRST_TIMER 1
typedef struct timerInfoType {
UINT timerNumber;
UINT startTime;
};
enum eventClassType {CYCLIC_TIMER, INTERRUPT};
static TIMER_ID SELF_CALIBRATION_TIMER;
static UINT clocksTimerCalibrationOffsetClocks = 0;
static UINT delayCalibrationOffsetClocks = 0;
static LIST_USER_ID timersUser;
static UINT timerCounter = FIRST_TIMER;
#if defined(USE_EVENTS)
static LIST_USER_ID cyclicTimerUser;
static LIST_USER_ID interruptUser;
static enum eventClassType currentEventClass;
static LIST_USER_ID cyclicTimerCurrentEvent;
static LIST_USER_ID interruptCurrentEvent;
static struct eventInfoType schedulerEvent;
static struct eventInfoType* SCHEDULER_EVENT = &schedulerEvent;
#endif // USE_EVENTS
#if defined(USE_LOCKS)
static struct lockType lockInfo = { NO_LOCKS };
static LOCK_ID timerUtilsLockId;
#endif // USE_LOCKS
// *************************************************************
// implementations
// *************************************************************
TIMER_ID createTimer(void) {
struct timerInfoType newTimer;
struct listItemType newItem;
newItem.size = sizeof(struct timerInfoType);
newItem.item = &newTimer;
newTimer.timerNumber = timerCounter++;
newTimer.startTime = timeNow();
if (addItemToFront(timersUser, newItem) != LIST_SUCCESS) {
return TIMER_ID_ERROR;
}
return cloneListUser(timersUser); // newly added item
}
//utility
UINT getTimerNumber(TIMER_ID timerId) {
struct timerInfoType *timer = (struct timerInfoType*)currentItem(timerId);
return (timer != NO_LIST_ITEM) ? timer->timerNumber : TIMER_ID_ERROR;
}
void startTimer(TIMER_ID timerId) {
struct timerInfoType *timer = (struct timerInfoType*)currentItem(timerId);
if (timer != NO_LIST_ITEM) {
timer->startTime = timeNow();
}
}
TIMER_ID deleteTimer(TIMER_ID timerId) {
if (removeItem(timerId) == LIST_SUCCESS) {
return deleteListUser(timerId);
}
return timerId;
}
UINT usTimeElapsed(TIMER_ID timerId) {
return clocksElapsed(timerId) / NUM_CLOCKS_IN_MICROSEC;
}
UINT msTimeElapsed(TIMER_ID timerId) {
return usTimeElapsed(timerId) / 1000;
}
UINT clocksElapsed(TIMER_ID timerId) {
struct timerInfoType *timer = (struct timerInfoType*)currentItem(timerId);
if (timer != NO_LIST_ITEM) {
UINT clocks = clocksElapsedSince(timer->startTime);
return clocks > clocksTimerCalibrationOffsetClocks
? clocks - clocksTimerCalibrationOffsetClocks : 0;
}
return 0;
}
//************************** locals ****************************************
void selfCalibration(void) {
// calibrate timer - startTimer and clocksElapsed combined
startTimer(SELF_CALIBRATION_TIMER);
clocksTimerCalibrationOffsetClocks = clocksElapsed(SELF_CALIBRATION_TIMER);
// using new calibration above - calibrate delay
startTimer(SELF_CALIBRATION_TIMER);
usDelay(0);
delayCalibrationOffsetClocks = clocksElapsed(SELF_CALIBRATION_TIMER);
startTimer(SELF_CALIBRATION_TIMER);
clockDelay(1);
delayCalibrationOffsetClocks += clocksElapsed(SELF_CALIBRATION_TIMER);
}
// timer wraps at approx 85 secs - the max timeout allowed with this config
boolean initTimers(void) {
// Enable the peripherals
SysCtlPeripheralEnable(SYSCTL_PERIPH_TIMER0);
// Configure the 32-bit periodic timer
TimerConfigure(TIMER0_BASE, TIMER_CFG_32_BIT_PER);
TimerLoadSet(TIMER0_BASE, TIMER_A, HW_TIMER_WRAP);
// Setup the interrupts for the timer timeouts
IntEnable(INT_TIMER0A);
TimerIntEnable(TIMER0_BASE, TIMER_TIMA_TIMEOUT);
// Enable the timer
TimerEnable(TIMER0_BASE, TIMER_A);
#if defined(USE_LOCKS)
timerUtilsLockId = getLockId(lockInfo);
if (timerUtilsLockId == LOCK_ID_FAILED) {
return FALSE;
}
timersUser = createListWithLock(timerUtilsLockId);
#else
timersUser = createList();
#endif // USE_LOCKS
if (timersUser == LIST_USER_ID_ERROR) {
#if defined(USE_LOCKS)
timerUtilsLockId = deleteLock(timerUtilsLockId);
#endif // USE_LOCKS
return FALSE;
}
// --------------------------------------------------
SELF_CALIBRATION_TIMER = createTimer();
if (SELF_CALIBRATION_TIMER == TIMER_ID_ERROR) {
deleteList(timersUser);
#if defined(USE_LOCKS)
timerUtilsLockId = deleteLock(timerUtilsLockId);
#endif // USE_LOCKS
return FALSE;
}
// some magic ...
selfCalibration();
return TRUE;
}
//***********************************************************************
// ************************* DELAYS ***********************************
//***********************************************************************
// for slower CPU speed (e.g. 8MHz) accuracy degrades below approx (5us)
// otherwise accuracy < 1us for smaller values, but still accurate across whole range
#define CLOCK_SCALER 5
// scaled value
void clockDelay(ULONG value) {
// assembler takes CLOCK_SCALER clocks to execute a loop iteration
__asm( " mov r3, r0 n" // load scaled value
"tloop: sub r3, #1 n" // decrement loop counter
" cmp r3, #0 n" // check if reached zero
" bne tloop "); // jump to loop label if not done
}
void usDelay(UINT usPeriod) {
ULONG periodClocks = usPeriod * NUM_CLOCKS_IN_MICROSEC;
ULONG scaledClocks;
if (periodClocks > delayCalibrationOffsetClocks) {
scaledClocks = (periodClocks - delayCalibrationOffsetClocks) / CLOCK_SCALER;
if (scaledClocks == 0) {
scaledClocks++; // run one iteration for fraction
}
clockDelay(scaledClocks); // don't pass in zero !
}
}
// (not optimised)
void msDelay(UINT msPeriod) {
usDelay(msPeriod * 1000);
}
// just for testing (not optimised)
void sDelay(UINT sPeriod) {
UINT sCount;
for (sCount = 0; sCount < sPeriod; sCount++) {
msDelay(1000);
}
}
#if defined(USE_EVENTS)
//***********************************************************************
// ************************* EVENTS ***********************************
//***********************************************************************
typedef struct eventClassInfoType {
enum eventClassType eventClass;
union {
struct {
UINT clocksTimeoutPeriod;
} cyclic;
struct {
UINT hwInterruptId;
boolean GPIO;
struct portPackedPinsType GPIOPort;
} interrupt;
} info;
};
typedef struct eventInfoType{
UINT eventNumber;
UINT startTime;
UINT priority;
boolean inUse;
boolean ready;
boolean start;
struct eventClassInfoType eventClassInfo;
void (*eventHandler)(EVENT_PARAM handlerParam);
EVENT_PARAM handlerParam;
struct eventInfoType *root; // spawn
#if defined(EVENTS_WITH_STATS)
struct eventStatsResultsType results;
struct eventStatsConfigType config;
#endif // EVENTS_WITH_STATS
};
#define FIRST_EVENT 1
#define CONFIG_OK 1
#define DEFAULT_STATS_LATENCY_LOW 10 // 10us
#define DEFAULT_STATS_LATENCY_HIGH 1000 // 1ms
//************************** DATA ****************************************
static UINT eventCounter = FIRST_EVENT;
static BYTE configStatus = CONFIG_OK; // default
static boolean recalcCyclicTimeoutsNow = FALSE;
static UINT pollTime = 0;
#if defined(EVENTS_WITH_STATS)
static struct cpuUsageType cpuStats;
#endif // EVENTS_WITH_STATS
//************************** locals ****************************************
boolean matchPriority(LIST_ITEM item, LIST_ITEM matchItem) {
struct eventInfoType* event = (struct eventInfoType*)item;
UINT *priority = (UINT*)matchItem;
return (*priority == event->priority) ? TRUE : FALSE;
}
boolean matchEventReady(LIST_ITEM item, LIST_ITEM matchItem) {
struct eventInfoType* event = (struct eventInfoType*)item;
return event->ready;
}
struct eventInfoType *findInterruptReady(LIST_USER_ID user) {
return (struct eventInfoType *)findFirst(user, (LIST_ITEM)NULL, matchEventReady);
}
struct eventInfoType *findCyclicTimerReady(void) {
boolean ready = TRUE;
return (struct eventInfoType *)findFirst(cyclicTimerUser, (LIST_ITEM)&ready, matchEventReady);
}
struct eventInfoType *findPriority(UINT priority) {
return (struct eventInfoType *)findFirst(cyclicTimerUser, (LIST_ITEM)&priority, matchPriority);
}
boolean highestPriorityFirst(LIST_ITEM itemA, LIST_ITEM itemB) {
boolean sort = FALSE;
struct eventInfoType* eventA = (struct eventInfoType*)itemA;
struct eventInfoType* eventB = (struct eventInfoType*)itemB;
if (eventA->priority > eventB->priority) {
sort = TRUE;
}
return sort;
}
BYTE sortInterruptsByPriority() {
return sortList(interruptUser, highestPriorityFirst);
}
BYTE sortCyclicTimersByPriority() {
return sortList(cyclicTimerUser, highestPriorityFirst);
}
struct eventInfoType *getCurrentEvent(void) {
struct eventInfoType *current = NO_LIST_ITEM;
switch (currentEventClass) {
case CYCLIC_TIMER :
current = (struct eventInfoType*)currentItem(cyclicTimerCurrentEvent);
break;
case INTERRUPT :
current = (struct eventInfoType*)currentItem(interruptCurrentEvent);
break;
default :
break;
} // switch
return current;
}
#if defined(EVENTS_WITH_STATS)
void collectInterruptEventStats(struct eventInfoType *event) {
event->results.numEvents++;
if (event->ready) {
// another event received before processed the last one
event->results.numEventsMissed++;
} // ready
event->startTime = timeNow(); // for latency check
}
UINT usLatency(struct eventInfoType *event) {
return clocksElapsedSince(event->startTime) / NUM_CLOCKS_IN_MICROSEC;
}
void collectLatencyEventStats(struct eventInfoType *event) {
UINT latency = usLatency(event);
if (latency <= event->config.usLatencyThresholdLow) {
event->results.numEventsLatencyLow++;
} else {
if (latency <= event->config.usLatencyThresholdHigh) {
event->results.numEventsLatencyHigh++;
}
}
}
void resetEventStatsElements(struct eventInfoType *event) {
event->results.numEvents = 0;
event->results.numEventsDelayed = 0;
event->results.numEventsMissed = 0;
event->results.numEventsLatencyLow = 0;
event->results.numEventsLatencyHigh = 0;
}
void resetEventStatsConfigElements(struct eventInfoType *event) {
event->config.usLatencyThresholdLow = DEFAULT_STATS_LATENCY_LOW;
event->config.usLatencyThresholdHigh = DEFAULT_STATS_LATENCY_HIGH;
}
#endif // EVENTS_WITH_STATS
// locals
boolean eventTimeout(struct eventInfoType* event, UINT currentTime) {
UINT clocks = clocksElapsedSinceTime(event->startTime, currentTime);
return clocks >= event->eventClassInfo.info.cyclic.clocksTimeoutPeriod ? TRUE : FALSE;
}
void recalcCyclicTimer(struct eventInfoType* event) {
// for accurate cyclicity (no drift) - calc next timeout based on last one
// not the time it was eventually processed (i.e. timeNow)
// h/w timer decrements from max to zero, then wraps
UINT nextStartTime = event->startTime - event->eventClassInfo.info.cyclic.clocksTimeoutPeriod;
if (nextStartTime > event->startTime) {
// wrap
nextStartTime = (hwTimerWrap() - event->eventClassInfo.info.cyclic.clocksTimeoutPeriod) +
event->startTime;
}
event->startTime = nextStartTime;
}
// ---------------------------------------------------------
boolean cyclicTimerTimeout(struct eventInfoType* event, UINT currentTime) {
// need to process cyclics for timeout transitions
if (eventTimeout(event, currentTime)) { // transition - timeout occurred
#if defined(EVENTS_WITH_STATS)
if (event->ready) {
// whole cycle completed without processing last timeout
event->results.numEventsMissed++; //stats
} else {
event->ready = TRUE; // go
event->results.numEvents++; //stats
}
#else
event->ready = TRUE; // go
#endif // EVENTS_WITH_STATS
event->start = FALSE; // reset
// need to update for next period
recalcCyclicTimer(event);
} else {
#if defined(EVENTS_WITH_STATS)
if (
|
|