Using the Harmony System Service to Control Timers

This page shows how to setup the Harmony Timer System Service to trigger two interrupts, each operating at different intervals, from one timer. Both of the ISRs are programmed for the simplistic task of toggling an I/O pin.

Steps demonstrated on this page include:

  1. Using MPLAB® Harmony Configurator (MHC) to enable the Timer System Service
  2. Verifying Timer and System Service initialization parameters
  3. Writing the two Interrupt Service Routines (ISRs)
  4. "Registering" the ISRs with the Timer System Service
  5. Initializing application code which starts the timer

Using MHC to Enable the Timer System Service

  • The MPLAB® Harmony Configurator (MHC) is used to establish the System Clock.
  • To configure a system timer with MHC, check the Use Timer System Service box and the Interrupt Notification Enabled box.
  • You will be asked to select the Timer Driver instance to use as the system clock, as well as the desired frequency of the system clock.
    • MHC will create a Timer Driver instance for you using default values. It's likely you will need to modify these Timer Driver selections.
  • The field labeled "Maximum Timer Clients" determines the number of separate ISRs that can be triggered by the System Clock.
  • You access the Timer System Service through the APIs provided in sys-tmr.h.
  • Enabling the Timer System Service creates the data structure sSysTmrObject to hold the data for the System Timer.
  • When created, the content of sSysTmrObject is left blank. The status field sSysTmrObject.status is set to SYS_STATUS_UNINITIALIZED ( 0x0000).
mhc-tmr-system-services.png
Click image to enlarge.
"click" to enlarge

Placing sSysTmrObject.status (or the entire structure) into the debugger Watch Window can be very useful in tracking down problems with initializing the System Timer.

watch-window.png

Initializing Timer System Service

  • MHC places calls to the functions DRV_TMR_Initialize and SYS_TMR_Initialize into the system initialization function SYS_Initialize.
  • After both the Timer and the System Service initialization functions successfully execute, sSysTmrObject will contain the data for the System Timer with sSysTmrObject.status set to SYS_STATUS_BUSY ( 0x0001).

system-init.c

void SYS_Initialize (void* data)
{



/* Initialize Timer Driver */
sysObj.drvTmr0 = DRV_TMR_Initialize(DRV_TMR_INDEX_0, (SYS_MODULE_INIT *)&drvTmr0InitData);

/* Initialize System Services */
SYS_INT_Initialize();

/* TMR Service Initialization Code */
sysObj.sysTmr = SYS_TMR_Initialize(SYS_TMR_INDEX_0, (const SYS_MODULE_INIT * const)&sysTmrInitData);



}

  • MHC inserts calls to DRV_TMR_Tasks(SysObj.drvTMR0) and SYS_TMR_Tasks(SysObj.sysTmr) into the function SYS_Tasks(). These functions will maintain the state machines for the hardware timer and for the Timer System Service.

system-init.c

void SYS_Tasks (void)
{
/* Maintain the state machines of all library modules executing polled in
the system. */


/* Maintain system services */
SYS_DEVCON_Tasks(sysObj.sysDevcon);
SYS_TMR_Tasks(sysObj.sysTmr);

/* Maintain Device Drivers */
DRV_TMR_Tasks(sysObj.drvTmr0);

/* Maintain the application's state machine. */
APP_Tasks();

}

  • The structures drvTmr0InitData and sysTmrInitData contain the parameters needed to configure the Timer Driver and the Timer System Service. These structures are created by MHC and placed in the file system-init.c.

system-init.c

void SYS_Tasks (void)
{
/* Maintain the state machines of all library modules executing polled in
the system. */


/* Maintain system services */
SYS_DEVCON_Tasks(sysObj.sysDevcon);
SYS_TMR_Tasks(sysObj.sysTmr);

/* Maintain Device Drivers */
DRV_TMR_Tasks(sysObj.drvTmr0);

/* Maintain the application's state machine. */
APP_Tasks();

}

Writing the Interrupt Service Routines

  • You will write an interrupt service routine for each task sharing the System Timer. In this example we have two ISRs ISR_Callback1 and ISR_CallBack2. These are very simple routines which simply toggle an I/O pin when they are executed.

app.c



/* ISR to Toggle PIN RF0 when called */
void ISR_Callback_1 ( uintptr_t context, uint32_t currTick )
{
PLIB_PORTS_PinToggle(PORTS_ID_0, PORT_CHANNEL_F, PORTS_BIT_POS_0);
}

/* ISR to Toggle pin RA0 when called*/
void ISR_Callback_2 ( uintptr_t context, uint32_t currTick )
{
PLIB_PORTS_PinToggle(PORTS_ID_0, PORT_CHANNEL_A, PORTS_BIT_POS_0);
}

Registering the ISR with the Timer System Service

  • Your ISRs are registered as System Service Callbacks by the function SYS_TMR_CallbackPeriodic. In this example the ISRs are registered when your application is first executed. The code below registers ISR_Callback1 to run every 400 milliseconds, and ISR_Callback2 to run every 200 milliseconds.

app.c

void APP_Tasks ( void )
{
/* Check the application's current state. */
switch ( appData.state )
{
/* Application's initial state. */
case APP_STATE_INIT:
{
PLIB_PORTS_PinDirectionOutputSet(PORTS_ID_0, PORT_CHANNEL_F, PORTS_BIT_POS_0);
PLIB_PORTS_PinDirectionOutputSet(PORTS_ID_0, PORT_CHANNEL_A, PORTS_BIT_POS_0);

SYS_TMR_CallbackPeriodic ( 400, 0, ISR_Callback_1 );
SYS_TMR_CallbackPeriodic ( 200, 0, ISR_Callback_2 );

appData.state=APP_STATE_RUN ;
break;
}

case APP_STATE_RUN:
{
break;
}

}
}

Initializing the Application

  • SYS_TMR_CallbackPeriodic must be called after the Timer System Service is initialized. This only needs to be called once. Repeated calls to SYS_TMR_CallbackPeriodic can disrupt the interrupt process. To avoid this function from being called multiple times, the code below inserts the state APP-STATE-RUN.
  • The code also shows how to set the desired output pins as output.

app.c

void APP_Tasks ( void )
{
/* Check the application's current state. */
switch ( appData.state )
{
/* Application's initial state. */
case APP_STATE_INIT:
{
PLIB_PORTS_PinDirectionOutputSet(PORTS_ID_0, PORT_CHANNEL_F, PORTS_BIT_POS_0);
PLIB_PORTS_PinDirectionOutputSet(PORTS_ID_0, PORT_CHANNEL_A, PORTS_BIT_POS_0);

SYS_TMR_CallbackPeriodic ( 400, 0, ISR_Callback_1 );
SYS_TMR_CallbackPeriodic ( 200, 0, ISR_Callback_2 );

appData.state=APP_STATE_RUN ;
break;
}

case APP_STATE_RUN:
{
break;
}

}
}

 Learn More

 
Harmony Timer Tutorial
Learn more >
 
Example Code and Projects
Learn more >
 
Entire Timer System Service Interface
Learn more >
 
PIC32 Timer Hardware Description
Learn more >

20th Annual
Microchip MASTERs Conference 2016
Register now - Deadline: July 29

JW Marriott Desert Ridge Resort-Phoenix, AZ

© 2016 Microchip Technology, Inc.
Information contained on this site regarding device applications and the like is provided only for your convenience and may be superseded by updates. It is your responsibility to ensure that your application meets with your specifications. MICROCHIP MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY KIND WHETHER EXPRESS OR IMPLIED, WRITTEN OR ORAL, STATUTORY OR OTHERWISE, RELATED TO THE INFORMATION, INCLUDING BUT NOT LIMITED TO ITS CONDITION, QUALITY, PERFORMANCE, MERCHANTABILITY OR FITNESS FOR PURPOSE. Microchip disclaims all liability arising from this information and its use. Use of Microchip devices in life support and/or safety applications is entirely at the buyer's risk, and the buyer agrees to defend, indemnify and hold harmless Microchip from any and all damages, claims, suits, or expenses resulting from such use. No licenses are conveyed, implicitly or otherwise, under any Microchip intellectual property rights.