8-Bit Timer1 Interrupt Using Function Pointers

When developing code for a project, using a function pointer can be a useful feature. Within the MPLAB® Code Configurator (MCC) interrupt drivers are set up to use function pointers so the software developer only needs to modify the main.c file and not the interrupt driver code. This allows for easier to follow code development. This project shows a simple interrupt example of how to use a function pointer for a Timer1 peripheral interrupt.

proj1.png

Step by Step Instructions

Using MCC, this project will set up the peripherals and generate the code for the PIC16F18875 on the Curiosity HPC board. The last step will program the board and make the project run.

proj6bd.jpg

The project uses:

  • PIC16F18875
  • HPC Curiosity Board
  • MPLAB X IDE
  • MPLAB Code Configurator (MCC) plug-in
  • MPLAB XC8 Compiler

To follow along with these steps, MPLAB X should be open and the HPC Curiosity Board connected to the computer through a USB cable.

1

Create a new standalone project in MPLAB X for a PIC16F18875.

If this is your first time creating an MPLAB X project, please visit the "Create a Standalone Project" page to follow a step-by-step instruction on how to do this.

2

Open MCC under the Tools>Embedded menu of MPLAB X IDE.

mcclaunch.png

3

Select the peripherals for your project.
In this project you need to select these peripherals:

  • System Module
  • Interrupt Module
  • Pin Module
  • TMR1

The System Module, Interrupt Module, and the Pin Module will all be automatically included when you launch MCC. The TMR1 module needs to be added by double-clicking on it under Timer in the Device Resources section of MCC. The result should look like the picture below:

proj10per2.png

4

The System Module needs to be setup next. Click on the System Module name under Project Resources.

project7menusystem.png

In this section, the oscillator settings and the configuration settings are selected.

Oscillator

  1. Select the HFINTOSC from the Oscillator Select drop-down menu.
  2. Select the 4_MHz from the HF Internal Clock drop-down menu.
  3. Select Clock Divider value of '4'.

This will enable the internal 1 MHz internal oscillator as the system clock.

project7osc.png

Also, make sure the Low-voltage Programming Enable mode is selected at the bottom of the System Module screen.

lvp.png

5

TMR1 needs to be setup. Click on the TMR1 label in the Project Resources menu to make the TMR1 setup screen appear.

tmr1.png

The steps below will setup TMR1:

  1. Check the Enable Timer box
  2. Check the Enable Synchronization box
  3. Set the Clock Source to FOSC/4
  4. Set the Prescaler to 1:2
  5. Set the Timer Period to 500.00 ms output
  6. Check the Enable Timer Interrupt box

The window should look like the one below after the setup:

tmr1setup.png

6

Open the Pin Manager and then click on the PORTA bit 4 (RA4) Output blue lock symbol. It should turn green. This adds the LED2 connection to the project. It should look like the picture here when completed:

project10pin.png

7

Close the Pin Manager and then click on the Pin Module selection in the Project Resources section.

pin2.png

The center screen should show RA4 listed on the I/O chart. Click on the output box to make the pin an output (if not checked) and make sure Analog and WPU are not checked (click on them to uncheck them).
Change the name to D2_LED. This makes it easier to understand the pin's function in the code.

project10pinmodule.png

8

Click on the Interrupt Module to view the settings for the interrupt structure.

interrupt.png

You will see that the Timer1 interrupt is enabled.

intenable.png

9

Click on the Generate button to have MCC create the software libraries for this project.

generate.png

10

The project will now have both generated Header Files and Source Files. It should also have a generated main.c file.

Note: MCC may offer to generate a main.c file. Click YES to allow it to complete that task.

proj10files2.png

Double click on the main.c file to open it up in the editor window.

mainfile.png

11

The main.c file needs to be modified to handle the Timer1 interrupt and there are several additions to the file that are associated with the tmr1.c file that is generated by MCC.

main.c

The main.c file requires a few lines to be uncommented for the interrupt to work properly. To enable the interrupt to work, Global Interrupts and Peripheral Interrupts need to be enabled.

    // Enable the Global Interrupts
    INTERRUPT_GlobalInterruptEnable();

    // Enable the Peripheral Interrupts
    INTERRUPT_PeripheralInterruptEnable();

The main loop doesn't require any additional code beyond the default while(1) loop. The Interrupt Service Routine will handle the changing of the I/O pin.

while (1)
    {
        // Add your application code

    }

The function pointer allows the main.c file to define and contain the Interrupt Service Routine (ISR). To establish this, a function prototype declaration must be placed near the top of the main.c. In this example, the function prototype is called TimerISR. Then the actual ISR function is added after the main while() loop. This is where the actions of the ISR are located. In this example a macro from the pin_manager.h file, IO_RA2_Toggle() is used to toggle the state of the LED on the RA2 pin at every Timer1 interrupt.

void myTimerISR(void);

void myTimerISR(void){
    IO_RA2_Toggle();
}

tmr1.c

The MCC generated tmr1.c file contains the function pointer to allow this option of controlling the ISR within the main.c file. The TMR1_ISR function handles all the flag clearing and timer reload after an interrupt. Then it calls the TMR1_InterruptHandler(). This TMR1_InterruptHandler() is defined by the function TMR1_SetInterruptHandler(void* InterruptHandler). The function pointer is indicated by the star (*) preceding the InterruptHandler. These described sections of the tmr1.c are shown here.

void TMR1_ISR(void)
{

    // Clear the TMR1 interrupt flag
    PIR4bits.TMR1IF = 0;
    TMR1_WriteTimer(timer1ReloadVal);

    if(TMR1_InterruptHandler)
    {
        TMR1_InterruptHandler();
    }
}

void TMR1_SetInterruptHandler(void (* InterruptHandler)(void)){
    TMR1_InterruptHandler = InterruptHandler;
}

The InterruptHandler function pointer needs to be connected to the myTimerISR() function declared in the main.c file. This is done with a single line in the main.c file right after SYSTEM_Initialize().

TMR1_SetInterruptHandler (myTimerISR);     //Define interrupt Handler

Completed main.c

The final main.c code is shown below with all the Timer1 Interrupt additions to control the LED when a Timer1 interrupt occurs.

#include "mcc_generated_files/mcc.h"

void myTimerISR(void);

/*
                         Main application
 */
void main(void)
{
    // initialize the device
    SYSTEM_Initialize();

   TMR1_SetInterruptHandler (myTimerISR);     //Define interrupt Handler

    // When using interrupts,set the Interrupt Enable bits
    // Use the following macros to:

    // Enable the Global Interrupts
    INTERRUPT_GlobalInterruptEnable();

    // Enable the Peripheral Interrupts
    INTERRUPT_PeripheralInterruptEnable();

    // Disable the Global Interrupts
    //INTERRUPT_GlobalInterruptDisable();

    // Disable the Peripheral Interrupts
    //INTERRUPT_PeripheralInterruptDisable();

    while (1)
    {
        // Add your application code
    }
}

void myTimerISR(void){
   D2_LED_Toggle();
}
/**
 End of File
*/

12

Click on the Build Project (hammer icon) to compile the code and you should see a "BUILD SUCCESSFUL" message in the Output window of MPLAB X.

Main_Build_Project.png
BUILD SUCCESSFUL

13

Make sure your project has the Curiosity Board selected and the USB cable is connected to the board.

Click on Make and Program Device Main Project. This will build the project again and launch the programmer. In the Output window, you should see a series of messages, and if successful, it will end with a "Programming/Verify complete" message.

Main_Program_Target_Project.png
Connecting to Starter Kit on Board...

Currently loaded firmware on Starter Kit on Board
Firmware Suite Version.....01.54.00
Firmware type..............Enhanced Midrange

Target voltage detected
Target device PIC16F18875 found.
Device Revision ID = 2002

Device Erased...

Programming...

The following memory area(s) will be programmed:
program memory: start address = 0x0, end address = 0x7ff
configuration memory
**Programming/Verify complete**



The D2 LED will blink based on the Timer1 interrupt at a 500 millisecond on/off rate for a very accurate blink rate.
proj6finished.png

If it is the first time the programmer is connected to the board, the programming tool may need to download the proper operating firmware for the exact device. You may see a series of processes if this occurs. This should only happen once.

Downloading Firmware…
Downloading bootloader
Bootloader download complete
Programming download…
Downloading RS…
RS download complete
Programming download…
Downloading AP…
AP download complete
Programming download…
Firmware Suite Version…..01.34.11
Firmware type…………..Enhanced Midrange

14

The project can be closed in MPLAB X. The project is saved automatically when it is built, but any changes to files or configuration may ask to be saved before the project is closed. The project can be closed under the File Menu > Close Project.

closeproject.png

Download

If you have any problems with your project, the completed MPLAB X project file can be downloaded from the link below:

File Download
Installation
Instructions
Windows Linux Mac OSX
Project 10 Files
© 2019 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.