Home arrow Support arrow Forums

Luminary Micro Forums

<< Start < Prev 1 2 Next > End >>

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 startTimeUINT 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 eventIdEVENT_PARAM param); // resume at next period void resumeCyclicEvent(EVENT_ID eventIdEVENT_PARAM param);     void restartCyclicEvent(EVENT_ID eventIdEVENT_PARAM paramUINT 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 spawnEventIdEVENT_PARAM spawnParam); // return child result to root event via callback (and stop child event) void spawnEventCallback(EVENT_ID spawnEventIdEVENT_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 eventIdstruct 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_POLLINGINTERRUPT_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 interruptIdUINT usTriggerTimeFromNow); // for more flexibility // start interrupt in usTriggerTimeFromRefTime // where refTime = timeNow() at your reference point void startInterruptFromRefTime(INTERRUPT_ID interruptIdUINT usTriggerTimeFromRefTimeUINT 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 startTimeUINT currentTime);     //***************************************************************************** // The interrupt handler for timer0 interrupt. // continuous timer - never stops -  just wraps ... //***************************************************************************** void Timer0IntHandler(void) {     // ignore ...          // Clear the timer interrupt.     TimerIntClear(TIMER0_BASETIMER_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 startTimeUINT currentTime) {     UINT numClocksElapsed;     if (currentTime startTime) {         // wrap         numClocksElapsed startTime + (hwTimerWrap() - currentTime);         } else {         numClocksElapsed startTime currentTime;     }   return numClocksElapsed; } UINT clocksElapsedSince(UINT startTime) {   return clocksElapsedSinceTime(startTimetimeNow()); } //*********************************************************************** // *************************  TIMERS  *********************************** //*********************************************************************** //**************************   DATA  **************************************** #define FIRST_TIMER   1 typedef struct timerInfoType {     UINT timerNumber;     UINT startTime; }; enum eventClassType {CYCLIC_TIMERINTERRUPT}; 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 eventInfoTypeSCHEDULER_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(timersUsernewItem) != 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_BASETIMER_CFG_32_BIT_PER);        TimerLoadSet(TIMER0_BASETIMER_AHW_TIMER_WRAP);     // Setup the interrupts for the timer timeouts     IntEnable(INT_TIMER0A);     TimerIntEnable(TIMER0_BASETIMER_TIMA_TIMEOUT);     // Enable the timer     TimerEnable(TIMER0_BASETIMER_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 0sCount sPeriodsCount++) {         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 itemLIST_ITEM matchItem) {         struct eventInfoTypeevent = (struct eventInfoType*)item;     UINT *priority = (UINT*)matchItem;          return (*priority == event->priority) ? TRUE FALSE;     } boolean matchEventReady(LIST_ITEM itemLIST_ITEM matchItem) {         struct eventInfoTypeevent = (struct eventInfoType*)item;     return event->ready; } struct eventInfoType *findInterruptReady(LIST_USER_ID user) {     return (struct eventInfoType *)findFirst(user, (LIST_ITEM)NULLmatchEventReady);     } struct eventInfoType *findCyclicTimerReady(void) {     boolean ready TRUE;     return (struct eventInfoType *)findFirst(cyclicTimerUser, (LIST_ITEM)&readymatchEventReady);     } struct eventInfoType *findPriority(UINT priority) {     return (struct eventInfoType *)findFirst(cyclicTimerUser, (LIST_ITEM)&prioritymatchPriority);     } boolean highestPriorityFirst(LIST_ITEM itemALIST_ITEM itemB) {     boolean sort FALSE;          struct eventInfoTypeeventA = (struct eventInfoType*)itemA;     struct eventInfoTypeeventB = (struct eventInfoType*)itemB;     if (eventA->priority eventB->priority) {          sort TRUE;     }     return sort; } BYTE sortInterruptsByPriority() {     return sortList(interruptUserhighestPriorityFirst); } BYTE sortCyclicTimersByPriority() {     return sortList(cyclicTimerUserhighestPriorityFirst); } 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 eventInfoTypeeventUINT currentTime) {     UINT clocks clocksElapsedSinceTime(event->startTimecurrentTime);     return clocks >= event->eventClassInfo.info.cyclic.clocksTimeoutPeriod TRUE FALSE; }      void recalcCyclicTimer(struct eventInfoTypeevent) {          // 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 eventInfoTypeeventUINT currentTime) {          // need to process cyclics for timeout transitions      if (eventTimeout(eventcurrentTime)) {        // 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 (