This page illustrates the steps needed to configure a timer and associated interrupt vector using a dynamic driver in Harmony. The code example used on this page manipulates one digital I/O pin on each Timer1 interrupt.
Steps demonstrated on this page include:
- Using MPLAB® Harmony Configurator (MHC) to create the project structure with a dynamic driver for Timer1.
- Verifying the function DRV_TMR0_Initialize() establishes Timer1's operating parameters.
- Implementing the Interrupt Service Routine (ISR) in the file system_interrupt.c.
- Modifying the function APP_Init() to enable the MCU to accept Timer1 interrupts.
- Modifying the main application program, APP_Tasks() to start Timer1.
Project Configuration
Under the Driver section check the Use Timer Driver? box. Ensure the dynamic Timer driver is used. After selecting the driver type you will have the option of selecting the number of timers to be used as well as the setting for each of the timers.
Timer Initialization
- Harmony inserts into the function, System_Initialize(), the initialization code for each timer defined with a dynamic driver.
- The parameters passed to the initialization function include the number of the timer to be initialized and a pointer to a flash-based block of data containing the parameters to be loaded into the timer special function registers.
- The code below shows the initialization code for Timer1 DRV_TMR_INDEX_0 into the first system object (sysObj.drvTmr0).
- The definitions for the timer index numbers are given in timer.h.
system-int.c
void SYS_Initialize (void* data)
{
/* Core Processor Initialization */
SYS_CLK_Initialize( NULL );
sysObj.sysDevcon = SYS_DEVCON_Initialize(SYS_DEVCON_INDEX_0,
(SYS_MODULE_INIT*)&sysDevconInit);
SYS_DEVCON_PerformanceConfig(SYS_CLK_SystemFrequencyGet());
SYS_DEVCON_JTAGDisable();
SYS_PORTS_Initialize();
/* Initialize Drivers */
sysObj.drvTmr0 = DRV_TMR_Initialize(DRV_TMR_INDEX_0,
(SYS_MODULE_INIT *)&drvTmr0InitData);
SYS_INT_VectorPrioritySet##(INT_VECTOR_T1, INT_PRIORITY_LEVEL4);
SYS_INT_VectorSubprioritySe##t(INT_VECTOR_T1, INT_SUBPRIORITY_LEVEL0);
/* Initialize and enable interrupts*/
SYS_INT_Initialize();
SYS_INT_Enable();
/* Initialize application*/
APP_Initialize();
}
Opening the Timer
- The example below creates a global variable, MyHandle, to contain the handle for the timer.
- The data type of MyHandle, SYS_MODULE_OBJ, is defined as a pointer to a data structure containing the status of a timer.
- DRV_TMR_Open checks the status of the timer and if available for the intended use, will return a valid handle.
app.c
SYS_MODULE_OBJ MyHandle ;
void APP_Initialize (void)
{
/* Place the App state machine in its initial state. */
appData.state = APP_STATE_INIT;
// set the direction of the pins and Open the Timer
PLIB_PORTS_DirectionOutputSet(PORTS_ID_0, PORT_CHANNEL_A, 0x03) ;
MyHandle = DRV_TMR_Open( DRV_TMR_INDEX_0,
DRV_IO_INTENT_EXCLUSIVE );
// calculate the divider value and register the ISR
uint32_t desiredFrequency = 10 ; // 10 hertz
uint32_t actualFrequency = DRV_TMR_CounterFrequencyGet(MyHandle) ;
uint32_t divider = actualFrequency/desiredFrequency; // cacluate divider value
DRV_TMR_AlarmRegister(MyHandle, divider,** true, 0 , MyISR);
// Starting the Timer
DRV_TMR_Start(MyHandle);
}
Creating the ISR
- This example generates a simple function, MyISR, which toggles an I/O pin.
- MyISR will become the ISR when it is 'registered'.
app.c
void MyISR(uintptr_t context, uint32_t **alarmCount)
{
PLIB_PORTS_PinToggle(PORTS_ID_0, PORT_CHANNEL_A, PORTS_BIT_POS_0);
}
Registering the ISR
- Using DRV_TMR_AlarmRegister, the function MyISR is set as the ISR for Timer1.
- The Interrupt Request Flag is cleared by the shared interrupt vector and does not need to be cleared by the user-written ISR.
- Users typically generate the value for the divider as a calculation based upon the desired interrupt frequency and the returned value of DRV_TMR_CounterFrequencyGet.
app.c
SYS_MODULE_OBJ MyHandle ;
void APP_Initialize (void)
{
/* Place the App state machine in its initial state. */
appData.state = APP_STATE_INIT;
// set the direction of the pins and Open the Timer
PLIB_PORTS_DirectionOutputSet(PORTS_ID_0, PORT_CHANNEL_A, 0x03) ;
MyHandle = DRV_TMR_Open( DRV_TMR_INDEX_0,
DRV_IO_INTENT_EXCLUSIVE );
// calculate the divider value and register the ISR
uint32_t desiredFrequency = 10 ; // 10 hertz
uint32_t actualFrequency = DRV_TMR_CounterFrequencyGet(MyHandle) ;
uint32_t divider = actualFrequency/desiredFrequency; // cacluate divider value
DRV_TMR_AlarmRegister(MyHandle, divider,** true, 0 , MyISR);
// Starting the Timer
DRV_TMR_Start(MyHandle);
}
Starting the Timer
app.c
SYS_MODULE_OBJ MyHandle ;
void APP_Initialize (void)
{
/* Place the App state machine in its initial state. */
appData.state = APP_STATE_INIT;
// set the direction of the pins and Open the Timer
PLIB_PORTS_DirectionOutputSet(PORTS_ID_0, PORT_CHANNEL_A, 0x03) ;
MyHandle = DRV_TMR_Open( DRV_TMR_INDEX_0,
DRV_IO_INTENT_EXCLUSIVE );
// calculate the divider value and register the ISR
uint32_t desiredFrequency = 10 ; // 10 hertz
uint32_t actualFrequency = DRV_TMR_CounterFrequencyGet(MyHandle) ;
uint32_t divider = actualFrequency/desiredFrequency; // cacluate divider value
DRV_TMR_AlarmRegister(MyHandle, divider,** true, 0 , MyISR);
// Starting the Timer
DRV_TMR_Start(MyHandle);
}