Lab 3. Tick Interface Using Timer1

Last modified by Microchip on 2026/04/09 14:39

Purpose

In this lab exercise, you will create and build a new standalone dsPIC33A project using MPLAB® Tools for Microsoft® Visual Studio Code (VS Code®) that demonstrates how to set up and use Timer1 interrupts on the dsPIC33A to implement a tick interface, which enables the use of non-blocking delays in your applications. 

To learn the functionality of the interrupt controller in the dsPIC33A, please visit the dsPIC33A Interrupt Controller training.

To learn more details about configuring the dsPIC33A Interrupt Controller, please visit the "dsPIC33A Interrupt and Exception Usage" page, or refer to the "dsPIC33AK512MPS512 Family Data Sheet".

Overview

After creating a new project using MPLAB tools for VS Code, you will modify main.c with a code example that configures Timer1 to produce interrupts every 1 ms. The application also configures a tick interface (and function, tickGet()) that may be used to implement non-blocking delays in your applications, for example, toggling an LED every 100 ms:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
int main(void)
{
    ledTimeout = tickGet() + TICK_SECOND/10; // set next 100mS LED toggle timeout value
 
   while(1)
    {
       // is it time to toggle the led ?
       if((int32_t)(tickGet() - ledTimeout) > (int32_t)0)
        {
          LATDbits.LATD9 ^= 1;    // toggle the led
         ledTimeout = tickGet() + TICK_SECOND/10; // set next 100mS LED toggle timeout value
       }
       // not yet, so run other tasks here!
   }
   
}

Here is the simplified connection diagram for this lab exercise:

simplified connection diagram

This lab exercise is divided into the following sections:

Procedure

Part A. Create a New dsPIC33A Project in VS Code

This exercise will have you create a new project and verify that it builds successfully.

Follow the procedures covered in Lab 0 and Lab 1 to create a new project called lab3 in the C:\projects\dspic33a folder.

Ensure you are running the VS Code profile created in Lab 0 (MCHP Dev).

Create a basic empty project having a default main.c file.

Create Program Project and Debug Project launch configurations in launch.json.

Perform a build of this project (Ctrl + Shift + B), and verify a successful build.

Part B. Update main.c With a Code Example

The following code example demonstrates how to initialize a peripheral (Timer1) to produce regular interrupts every 1 ms.

The Timer Interrupt Service Routine (ISR) increments a 1 ms tick counter, which is periodically polled by a tickGet() function in the main loop to implement non-blocking 100 ms delays in-between toggling the LED, and 1000 ms delays in-between sending data to the serial terminal.

Replace the default application with the provided code.

Open main.c and replace the default application code with the contents below (copy/paste).

  

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
/*
 * File:        main.c
 * Project:     lab3
 *
 * Description:
 *
 * Create a simple "Tick" interface that enables non-blocking delays using 1mS Timer1
 * interrupts.
 *
 * The main application toggles the Red RGB LED every 100mS and prints a status
 * message to the terminal every 1000mS in a non-blocking fashion.
 *
 * "Tick" Interface Description
 *   - Defines a TICK type (uint32_t), providing delays from 1mS to ~49 days
 *   - Each TICK increment is 1mS, performed in the Timer 1 ISR
 *   - Defines a macro TICK_SECOND
 *   - Defines a retrieval function, "tickGet()"
 *
 * Using the interface (see main() below):
 *  - Store next timeout value using tickGet()
 *  - Compare tickGet() to set timeout
 *  - Use TICK_SECOND for easy reference
 *
 */


#include <xc.h>
#include
<stdio.h>
#include
<stdint.h>

#define FCPU        8000000U
#define FPB_FAST    FCPU
#define FPB_STD     FCPU/2
#define FPB_SLO     FCPU/4
#define UART_BAUD   9600        
// Options @ 8MHz FCPU: 9600, 19200, 38400
#define TICKS_PER_SECOND 1000   // 32-bit counter driven by 1mS Timer1 interrupt
#define TICK_SECOND ((uint64_t)TICKS_PER_SECOND) // represents one second in Ticks

#pragma config FWDT_WDTEN = SW

typedef uint32_t TICK;              // all TICKS are stored as 32-bit unsigned integers
TICK ledTimeout, printTimeout;      // define TICK timeout variables
static volatile TICK oneMsCounter;  // TICK counter incremented in Timer1 ISR

void clocksInit(void);
void pinsInit(void);
void uartInit(void);
void uartWrite(unsigned char txData);
unsigned char uartRead(void);
void timer1Init(void);
TICK tickGet(void);

// Timer1 ISR function - increments the global TICK counter every 1mS
void __attribute__((interrupt)) _T1Interrupt(void)
{
    IFS1bits.T1IF = 0;      // acknowledge/clear interrupt flag
   oneMsCounter++;         // increment the global TICK counter
}

int main(void)
{
    INTCON1bits.GIE = 0;        // prevents interrupt flooding since reset state is a '1'
   clocksInit();               // initialize all clocks
   pinsInit();                 // initialize all pins
   uartInit();                 // initialize uart peripheral for printf()
   timer1Init();               // initialize Timer1 for use with "Tick" API
   INTCON1bits.GIE = 1;        // enable global interrupts
    
    printf("\r\ndsPIC33A Hands On Labs - Lab 3\r\n\r\n");

    ledTimeout = tickGet() + TICK_SECOND/10;    // set next 100mS LED toggle timeout value
   printTimeout = tickGet() + TICK_SECOND/1;   // set next 1000mS printf() timeout value
   
   while(1)
    {
       // is it time to toggle the led ?
       if((int32_t)(tickGet() - ledTimeout) > (int32_t)0)
        {
          LATDbits.LATD9 ^= 1;    // toggle the led
         ledTimeout = tickGet() + TICK_SECOND/10; // set next 100mS LED toggle timeout value
       }

       // is it time to print the next message ?
       if((int32_t)(tickGet() - printTimeout) > (int32_t)0)
        {
          printf("Tick: %d\r\n", (TICK)tickGet());  // print the current TICK value
         printTimeout = tickGet() + TICK_SECOND/1; // set next 1000mS printf() timeout value
       }
    }
   
}

void clocksInit(void)
{
   /*** System Clocks Initialization ***/

   // The FRC provides the source clock
   // Instruction Clock, Fcpu = 8 MHz
   // Peripheral Bus Clocks: Fpb_fast = 8 MHz, Fpb_std = 4 MHz, Fpb_slow = 2 MHz

   // Clock settings left at defaults listed above

}

void pinsInit(void)
{
   /*** Digital I/O Initialization ***/
   
   // Initialize RGB Red LED pin
   LATDbits.LATD9 = 0;
    TRISDbits.TRISD9 = 0;

   // Initialize Switch S2 input
   ANSELFbits.ANSELF0 = 0;         // disable analog function
   TRISFbits.TRISF0 = 1;           // make pin digital input

   /*** PPS Initialization ***/
   
   // UART1 TX Assigned to RH0/RP113 (P102_UART_PKOB_TX)
   RPOR28bits.RP113R = 19;
   
   // UART1 RX Assigned to RD10/RP59 (P100_UART_PKOB_RX)
   RPINR13bits.U1RXR = 59;
   
}

void uartInit(void)
{
   unsigned long brg;
    brg = (FCPU/UART_BAUD/4-1)/2;
    U1BRG = (unsigned short)brg;    
    U1CON = 0;
    U1STAT = 0;
    U1CONbits.BRGS = 1;   
    U1CONbits.TXEN = 1;
    U1CONbits.RXEN = 1;
    U1CONbits.ON = 1;
}

void uartWrite(unsigned char txData){
   while(U1STATbits.TXBE == 0);
    U1TXB = (unsigned long)txData;
}

unsigned char uartRead(){
while(U1STATbits.RXBE != 0);
   return U1RXB;
}

void timer1Init(void)
{
   // Initialize Timer 1 to generate priority level 1 interrupts every 1 mS
   T1CON = 0x0;
    TMR1 = 0x0;
    PR1 = FPB_STD/1000;     // set interrupt period to 1mS, given Timer1 CLK
   IPC6bits.T1IP = 1;      // set interrupt priority = 1
   IFS1bits.T1IF = 0;      // clear interrupt flag
   IEC1bits.T1IE = 1;      // enable Timer1 interrupts
   T1CONbits.ON = 1;       // turn on Timer1
}

TICK tickGet(void)
{
 TICK temp;
    IEC1bits.T1IE = 0;          // disable the Timer1 interrupt
   temp = (TICK)oneMsCounter;  // get a clean copy of counter variable
   IEC1bits.T1IE = 1;          // re-enable Timer1 interrupt
return temp;                // return the current value of the 1mS TICK counter
}

// support for printf()

int __attribute__((__section__(".libc.write"))) write(int handle, void *buffer, unsigned int len){
   unsigned char *pData = (unsigned char*)buffer;
   unsigned short count;
   for(count=0; count<len; count++)
    {
        uartWrite(*pData++);
    }
   
   return count;
}

int __attribute__((__section__(".libc.read"))) read(int handle, void *buffer, unsigned int len){
   unsigned char *pData = (unsigned char*)buffer;
   unsigned short count;

   for(count=0; count<len; count++)
    {        
       *pData++ = uartRead();
    }
   
   return count;
}

Press Ctrl + Shift + B to build the project. 

Fix any syntax errors, ensuring that the project builds successfully:

Terminal

Back to Top

Part C. Run the Application

Next, we will program the project into the board and test it using the Microsoft Serial Monitor extension, which you installed in Lab 0.

Program the application into the target.

Launch the program configuration by navigating to Run and Debug and selecting Program Project:

Program Project

Verify that the program operation was successful:

Successful program operation

 

 


Test the application using the Serial Monitor.

Click on the SERIAL MONITOR tab in the output window:

Serial Monitor Tab

 

Select the PICKit™ 4 COM port. Set the Baud rate to 9600. Press Start Monitoring:

Start Monitoring

 

Press the MCLR button on the development board to reset the application. You should see the following messages:

Messages

The value of the 1 ms tick counter should be periodically displayed in the serial terminal window.

You should also see the red RGB LED toggling every 100 ms:

RED RGB LED toggling

Back to Top

Results

Re: Using Interrupts on dsPIC33A Devices

The following interrupt configuration process was demonstrated in this simple example:

  1. #include standard headers.
  2. Provide an ISR.
  3. Disable global interrupts in your initialization firmware until all peripherals are initialized.
  4. Configure the peripheral and its interrupt settings in your peripheral initialization routine.
  5. Enable global interrupts after all peripherals are initialized.

These steps are outlined in more detail in the "dsPIC33A Interrupt and Exception Usage" page.

Back to Top

Next Project

  • Start Lab 4 to learn how to configure a 200 MHz CPU clock using PLL1.