Lab 2. Using Peripheral Pin Select (PPS)
Purpose
This lab uses the MPLAB® X Integrated Development Environment (IDE) tools through their integration with Microsoft® VS Code®. In this lab exercise, you will create and build a new standalone dsPIC33A MPLAB X IDE project through its integration with Microsoft VS Code that demonstrates the Peripheral Pin Select (PPS) functionality of the dsPIC33A IO port. PPS lets you map digital peripheral functions to almost any remappable pin. This makes it easy to assign functions like Universal Asynchronous Receiver/Transmitter (UART) or interrupts to the pins that best fit your design.
To learn more about the dsPIC33A IO port, please visit the dsPIC33A I/O Ports Peripheral page, or refer to the dsPIC33AK512MPS512 Family Data Sheet.
Overview
After creating a new MPLAB X IDE project in VS Code, you will modify main.c with a code example that assigns the UART1 TX function to pin RH0/RP113, and the UART1 RX function to pin RD10/RP59. The code example also enables the use of the printf() function using UART1. The main loop simply echoes characters typed in the serial terminal application.
Here is a 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®
In this exercise, you will create a new project and verify that it builds successfully.
Follow the procedures covered in Lab 0 and Lab 1 to create a new MPLAB X IDE project called lab2 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 simple PPS initialization and usage on a dsPIC33A device. The code assigns the UART1 TX function to pin RH0/RP113, and the UART1 RX function to pin RD10/RP59. The code example also enables the use of the printf() function using UART1. The main loop simply echoes characters typed in the serial terminal application.
Replace the default application with the provided example 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
* File: main.c
* Project: lab2
*
* Description:
*
* Configures UART1 and PPS to map RH0/RP113 and RD10/RP59 to U1TX/U1RX functions
* Enables printf() use with UART1 (9600, 8N1)
*
* Main loop echoes characters entered from a serial terminal
*
*/
#include <xc.h>
#include <stdio.h>
#define FCPU 8000000U
#define FPB_FAST FCPU
#define FPB_STD FCPU/2
#define FPB_SLO FCPU/4
#define UART_BAUD 9600 // Options: 9600, 19200, 38400
#pragma config FWDT_WDTEN = SW
void clocksInit(void);
void pinsInit(void);
void uartInit(void);
void uartWrite(unsigned char txData);
unsigned char uartRead(void);
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()
INTCON1bits.GIE = 1; // enable global interrupts
printf("\r\ndsPIC33A Hands On Labs - Lab 2\r\n\r\n");
printf("Enter some text: ");
while(1)
{
uartWrite(uartRead());
}
}
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;
}
// 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'll 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:

Set the COM port to the one enumerated by the on-board PICKit 4 Virtual COM Port.
Set Baud rate to 9600. Toggle Terminal Mode. Press Start Monitoring:

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

Begin typing at the keyboard. The characters should be echoed back to the Serial Monitor window as shown:

Results
Re: Using Peripheral Pin Select (PPS) on dsPIC33A Devices
Digital-only peripherals, such as UART, Serial Peripheral Interface (SPI), Timer Clock Input, Input Capture, Output Compare (OC), etc., are not connected to any GPIO pins by default. Several PPS registers need to be correctly initialized to map pins to peripheral functions.
Input Mapping Registers (RPINRx)
PPS inputs are mapped based on the basis of the peripheral. Each peripheral input function is associated with an RPINRx register, containing an 8-bit field that maps a re-programmable IO pin (RPx) to the input function.
From the dsPIC33AK512MPS512 data sheet, the UART1 RX input function mapping is controlled by the U1RXR bitfield in the RPINR13 register. To map reprogrammable pin RD10/RP59 to this input function, either of the following statements may be executed:
- RPINR13bits.U1RXR = 59;
- _U1RXR = 59;
Output Mapping Registers (RPORx)
PPS outputs are mapped on the basis of the pin. Each reprogrammable pin (RPx) is associated with an RPORx control register, containing a 6-bit field that maps a specific peripheral output to that pin.
From the dsPIC33AK512MPS512 data sheet, the UART1 TX output function ID number is 19, and the reprogrammable pin RP113 output mapping is controlled by RPOR28.RP113R. Therefore, to map UART1 TX output function to pin RH0/RP113, either of the following statements may be executed:
- RPOR28bits.RP113R = 19;
- _RP113R = 19;
Next Project
- Start Lab 3 to learn how to set up Timer1 interrupts on dsPIC33A to create a simple Tick interface that helps create non-blocking delays.