Using MPLAB® Harmony v2 Dynamic Drivers to Control Timers

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:

  1. Using MPLAB® Harmony Configurator (MHC) to create the project structure with a dynamic driver for Timer1.
  2. Verifying the function DRV_TMR0_Initialize() establishes Timer1's operating parameters.
  3. Implementing the Interrupt Service Routine (ISR) in the file system_interrupt.c.
  4. Modifying the function APP_Init() to enable the MCU to accept Timer1 interrupts.
  5. 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.

mhc.png

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);

}

 Learn More

 
Harmony Timer System Services Tutorial
Learn more >
 
Entire Timer System Services Interface
Learn more >
 
Timer Hardware Description
Learn more >
© 2024 Microchip Technology, Inc.
Notice: ARM and Cortex are the registered trademarks of ARM Limited in the EU and other countries.
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.