PIC32MX QuickstartPart 4 - Peripheral Pin Select (PPS)Anyone starting out with the MX150, that is used to working with other chips, are likely to look at the pin out diagram and think it has hardly any peripherals. They couldn't be more wrong.
One of the biggest problems with microcontrollers is how to bring all of the peripherals out to pins without making them mutually exclusive, for instance if the SPI module and the Timer/Counter module share a common pin, you can use one or the other but not both.
Things get even worse the less pins you have to work with and the MX150 only has 28 PINS.
To compound the issue further this chip packs a lot of Peripherals but only 21 of the 28 Pins are usable for actual I/O.
This sounds like a recipe for disaster, but it is not thanks to PPS.
Lets have a look at it, you will need the
PIC32MX1XX/2XX Family Data Sheet again. Download it if you haven't already.
TABLE 11-1: of the Datasheet shows the possible INPUT PIN SELECTION and TABLE 11-2: the OUTPUT PIN SELECTION.
At first glance these look pretty daunting. You can either select a Peripheral and see which pins it can use or you can select a pin and see which peripherals are available. The tables are also separated into 4 groups of 8 Pins.
For Inputs the the Peripheral is listed on the left and the available pins on the right.
For outputs it is the other way round.
So lets have a look at how these tables are used.
Possibly the most used peripheral in any microcontroller is the UART.
Even if the target application has no need to talk to a PC, having some kind of console I/O is incredibly useful for debugging. Often when I have the ICD3 and the Logic / Protocol analyzer connected I still have a console session going at the same time. So lets have a look at connecting a UART to the outside world via PPS.
The MX150 has two UART’s so lets have a look and see where we can connect them.
First we will consider UART1 so we look at TABLE 11-1 Left hand column and look for U1RX.
We find that RPA2, RPB6, RPA4, RPB13, RPB2, RPC6, RPC1 and RPC3 can all be used.
Next we locate them physically on the package, to do this you need to cross reference the pinout diagram on page 4. (I always print out the diagram for the package I am working with).
At this point we can see a problem with using UART1 in this application.
The dark shaded PINS on the diagram are 5V tolerant, but none of the available pins for U1RX fall in this category. This interface is likely to be used with a 5V FTDI cable so a 5V tolerant input is essential.
Lets have a look for U2RX instead. We find it can be used on RPA1, RPB5, RPB1, RPB11, RPB8, RPA8, RPC8, and RPA9.
BINGO: RPB5, RPB11 and RPB8 are all 5V tolerant so UART2 will be our choice.
Now we look at TABLE 11-2 Right hand column and find U2TX.
The Pins available are: RPA3, RPB14, RPB0, RPB10, RPB9, RPC9, RPC2 and RPC4.
Just in case the cable is connected in reverse we should probably make our TX Pin 5V tolerant too, so we can narrow the selection to: RPB10 and RPB9.
Select a pair, any pair.
Well not quite.
We also have to consider any other Peripherals we may need to use, especially the Analogue ones as Analogue Peripherals can not be remapped by PPS, they have dedicated I/O.
Once you have weeded out the pins that are unavailable due to conflicts, the final decision can be determined by PCB routing. If you can simplify the routing or save a layer on the board by choosing a different Pin that is an added bonus.
In this case I will just select RPB10 and RPB11 as they are adjacent.
The Paperwork is almost done, but remember the 4 groups of pins? they are relevant.
We have selected to use U2RX on RPB11 and that was in the 2nd Section of TABLE 11-1 and U2TX on RPB10 in the 4th section of TABLE 11-2.
lets get back to creating code, and Plib again comes to the rescue, it avoids us having to directly set bits in registers by providing functions to do it for us.
PPSInput() and PPSOutput()
The arguments are in the order we read them from the tables so to set U2TX we say:
PPSOutput(4, RPB10, U2TX);
And to to set U2RX we say:
PPSInput (2, U2RX, RPB11);
Being able to assign pins like this in Software, whilst powerful, is also potentially dangerous.
If your program crashes or you mess up some pointer arithmetic in C you could start writing random data all over the memory map. If that happens there is the possibility of it inadvertently changing a pin allocation and blowing up external hardware or the CPU. In order to prevent that, PPS has a locking mechanism that requires certain values to be written in a certain sequence before the registers can be changed.
Fortunately Plib handles that for us too.
So we now say:
PPSUnLock;
PPSOutput(4, RPB10, U2TX); // MAP Tx to PB10
PPSInput (2, U2RX, RPB11); // MAP Rx to PB11
PPSLock;
the indentation is irrelevant, I just use it to show the blocking.
There is one more thing to consider.
The PPS functions set up the mapping but they do not setup the actual port.
That has to be done separately and with the usual consideration for turning off any Analogue functions.
mSETPORTBDigitalOut(RPB10);
and
mSETPORTBDigitalIn(RPB11);
takes care of that for us.
I need my bed so that is it for today, tomorrow we will look at actually using the UART.
Thanks for following along.
Cheers
Chris