Lab 3. Tick Interface Using Timer1
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:
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
{
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:

This lab exercise is divided into the following sections:
- Part A. Create a New dsPIC33A Project in VS Code
- Part B. Update main.c With a Code Example
- Part C. Run the Application
- Results
- Next Project
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).
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:

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:

Verify that the program operation was successful:

Test the application using the Serial Monitor.
Click on the SERIAL MONITOR tab in the output window:

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

Press the MCLR button on the development board to reset the application. You should see the following 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:

Results
Re: Using Interrupts on dsPIC33A Devices
The following interrupt configuration process was demonstrated in this simple example:
- #include standard headers.
- Provide an ISR.
- Disable global interrupts in your initialization firmware until all peripherals are initialized.
- Configure the peripheral and its interrupt settings in your peripheral initialization routine.
- Enable global interrupts after all peripherals are initialized.
These steps are outlined in more detail in the "dsPIC33A Interrupt and Exception Usage" page.
Next Project
- Start Lab 4 to learn how to configure a 200 MHz CPU clock using PLL1.