This page shows how to set up the MPLAB® Harmony Timer System Service to trigger two interrupts, each operating at different intervals, from one timer. Both of the Interrupt Service Routines (ISRs) are programmed for the simplistic task of toggling an I/O pin.
Steps demonstrated on this page include:
- Using MPLAB Harmony Configurator (MHC) to enable the Timer System Service.
- Verifying timer and system service initialization parameters.
- Writing the two ISRs.
- Registering the ISRs with the Timer System Service.
- Initializing application code which starts the timer.
Using the MHC to enable the Timer System Service
- The MHC is used to establish the system clock.
- To configure a system timer with the 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.
- The 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 Clientsndetermines the number of separate ISRs that can be triggered by the system clock.
- You can access the Timer System Service through the Application Programming Interfaces (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).
Initializing Timer System Service
- The 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);
…
}
- The MHC inserts calls 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 the 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 (ISRs)
- You will write an ISR 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;
}
}
}