UART and Peripheral Pin Select (PPS) on PIC32MZ

How to use the PPS functionality and how to set up the UART

It's been a while, so time to blog about something worthwhile! A lot of the cool peripherals (devices) on the PIC32MZ aren't assigned to specific pins, you can choose between a set of pins on which to place them. For today's example, I will be looking at how to set up the PPS pins to give me UART port 5 on pins RF0 and RF1. There are two pins that we want, the receive pin (RX) and the transmit pin (TX). So let's go look at the datasheet and see what it says. PIC32MZ - PPS Input pins!

These are some of the input pins we can set up. We are looking for U5RX, the RX pin for UART number 5. As you can see, it's on the left. The pins on the right are the possible pin numbers we can place that pin on. I want to place it on pin RF1, which is also called RPF1. Looking on the right, I can see that the code to do this is 0100. So now, we go to the code and set U5RX to 0b0100, like this:

U5RXR = 0b0100; // RF1 = U5RX

Simple, if you know how. But wait, there's more! Because this is now an input pin, we need to set up the TRISF register to make that pin an input too.

TRISFbits.TRISF1 = 1; // Make RF1 an input

OK, now let's set up the TX pin. Let's look at the datasheet again.

PIC32MZ - PPS Output pins!

For the output pins, the peripherals are listed on the right and the possible ports on the left. We can see on the right that U5TX has a code of 0011, and as we want it on pin RF0 we set it up as follows:

RPF0R = 0b0011; // RF0 = U5TX

OK, now we've assigned the UART 5 peripheral to pins RF0 and RF1, it's time to initialise it. In this example, I want to set it up at a baud rate of 38400bps so I can communicate with my FTDI cable connected at those pins. I like doing this for debug logs and just communicating with the PC in general. Unfortunately, when we look at the datasheet and the UART section we get to read this:

"This data sheet summarizes the features of the PIC32MZ EF family of devices. It is not intended to be a comprehensive reference source. To complement the information in this data sheet, refer to Section 21. “Universal Asynchronous Receiver Transmitter (UART)”(DS60001107) in the “PIC32 Family Reference Manual”, which is available from the Microchip web site (www.microchip.com/PIC32)."

Wonderful, so we google DS60001107 and find the UART datasheet and down the rabbit hole we go. Nothing is quick and easy to do on the PIC32. Let's look at the speed calculation formula:

PIC32MZ - PPS Output pins!

Very long story short, the UART peripherals are run off of Peripheral Bus Clock 2 (PBCLK2), which we set to 100Mhz in the previous blog post. There is a "standard speed" mode and a "high speed mode". The standard speed mode has a maximum speed of (PBCLK2 / 16) which is 6.25Mbps in our example. The high speed mode has a maximum speed or (PBCLK2 / 4), which is 25Mbps for us. As I'm using a much, much slower speed I'm going to be setting it up in standard mode. Here's the code to initialise UART 5 at 38400bps:

void UART_Init()
{
    int pbClk;

    pbClk = SYS_FREQ / 2; // Our PBCLK2 divider was set to 1, so PBCLK2 is exactly half the speed of the system clock, or 100Mhz

    U5MODE = 0; // Set UART 5 off prior to setting it up
    U5MODEbits.BRGH = 0; // We want standard speed mode. Not necessary as we just set U5MODE to 0 so this is just for explanation's sake
    U5BRG = pbClk / (16 * 38400) - 1;// This is the formula straight from the datasheet
    U5STA = 0; // Disable the TX and RX pins, clear all flags
    U5STAbits.UTXEN = 1; // Enable the TX pin
    U5STAbits.URXEN = 1; // Enable the RX pin
    U5MODEbits.PDSEL = 0; // PDSEL controls how many data bits and how many parity bits we want, this is the default of 8-bit data, no parity bits that most terminals use
    U5MODEbits.STSEL = 0; // STSEL controls how many stop bits we use, let's use the default of 1
    U5MODEbits.ON = 1; // Turn on the UART 5 peripheral
}

Phew. That's actually the hardest part done. Now we can immediately start writing data to UART 5 by writing to U5TXREG, like this:

char c;
c = 'A';
U5TXREG = c;

But there's a much cooler and easier way to do this that allows us to use printf. We need to overwrite the _mon_putc function, which is thankfully really easy:

void _mon_putc (char c)
 {
   while (U5STAbits.UTXBF); // Wait til current transmission is complete
   U5TXREG = c;
}

And that's it! Now we can use printf to send data directly to the serial port UART 5. Remember, when connecting the FTDI cable you need to connect the FTDI's TX pin to the PIC32's RX pin and the FTDI's RX pin to the PIC32's TX pin. I've attached a very basic, but working, program that outputs a counter to the serial port every second.

Here's the code Have fun!

Tags: code

Delays on PIC32MZ

How to make millisecond and microsecond delays

Ever wanted to make an LED toggle on and off every second? It's the dream of every hobbyist, especially when we're dealing with the PIC32MZ. A sign of success, that things are working and clock speeds are right. Unfortunately, the XC32 compiler doesn't give us a delay function to call. A quick google of "PIC32 millisecond timer" brings back countless results relating to other PIC devices, not PIC32, and people saying we should be using Harmony. There are also plenty of results relating to using the Timer peripherals and the calculations look complex and really aren't what we want for this simple task. Oh, and "read the data sheet" of course.

Instead, let's do it the easy way. There's a built-in timer in every PIC32 called the Core Timer. It doesn't need to be set up and it is always running. It runs at half the system frequency, so if you're running at 200Mhz, it'll update at 100Mhz. First, let's add a define for how fast we've set the fuses to.

#define SYS_FREQ 200000000 // Running at 200MHz

This value will be useful for many calculations we'll need to do later. OK, so now how do we use the Core Timer? Well, first we need to calculate how long we need to wait.

<TL;DR>

In our example, the core timer updates at 100Mhz. Let's say we want to wait for 500 microseconds. First, let's calculate how many clock ticks that is. One clock tick takes 1 / 100,000,000 seconds, which is 0.000001 seconds, which is 0.01 microseconds. So this means that for every 100 clock ticks 1 microsecond has passed. So, for 500 microseconds we need to wait 500 * 100 = 50,000 clock ticks.

</TL;DR>

Still with me? Good :) Let's put that knowledge to use and make a microsecond delay function:

void delay_us(unsigned int us)
{
    // Convert microseconds us into how many clock ticks it will take
    us *= SYS_FREQ / 1000000 / 2; // Core Timer updates every 2 ticks

    _CP0_SET_COUNT(0); // Set Core Timer count to 0

    while (us > _CP0_GET_COUNT()); // Wait until Core Timer count reaches the number we calculated earlier
}

First we calculate how many clock ticks we need to wait. Then we set the Core Timer counter to 0 by calling

CP0_SET_COUNT(0)

<TL;DR>

CP0_SET_COUNT(value) is a built-in way to set the Core Timer's value. For the fancy pantses among us, yes, you can use:

_mtc0(9, 0, 0); // Register 9 is the Count register, set it to 0

But it comes down to the same thing, so take your pick.

</TL;DR>

Then we just need to wait until the Core Timer counter reaches the value we set and the delay will be done. Simple! Let's use that to make a millisecond delay now.

void delay_ms(int ms)
{
    delay_us(ms * 1000);
}

Is that cheating? Maybe? Off by a few clock cycles? Probably. As the audience for this blog is going to be tiny (or non-existent) anyway I don't think it matters :) Oh, one more thing. The Core Timer is a 32-bit number, which means it can only count to 4 billion before it'll wrap back around to 0. So don't use delay_ms() for hundreds of seconds. At least up to 20 seconds should be fine. Now let's make that LED dance!

while (1)
{
    LATAINV = 1 << 0; // Toggle LAT A bit 0
    delay_ms(1000); // Delay 1 second
}

LATAINV is an easy way to toggle bits on Port A and requires no work on our part. Hope the TL;DR tags help :)

Here's the code

Tags: code

A PIC32MZ program skeleton

How to start every program (at 200Mhz)

Last time I discussed how to set the PIC32 and its peripherals at maximum clock speeds. This time I'll be going over some common "traps for young players", as Dave on EEVBlog would say. The PIC32 is great but starting out can be a nightmare.

TL;DR warning: To be honest, I just want to start writing about how to code this thing but the PIC32MZ has so many traps and pitfalls that I think need to be discussed first. Just download the code at the end :)

Let's take a simple example: I want to turn on an LED on RA0 (aka PORT A bit 0). So, coming from other microcontrollers, I know I need to set the pin to an output. So I make this incredibly simple program:

void main()
{
    set_performance_mode(); // Set everything to maximum speed

    TRISACLR = 1 << 0; // Clear bit 0 of TRISA. which makes PORT A bit 0 an output

    LATASET = 1 << 0; // Set bit 0 of PORTA to 1
}

I connect a 330 ohm resistor from RA5 to the LED, and the cathode of the LED to ground. Laughing to myself at how easy that was, I compile and run it and... why isn't the LED turning on? Did I connect it to the wrong pin? No, I checked that. Ah yes, now I remember. The PIC32 starts all ports out as analog and not digital so we have to set them up. We do that by setting

ANSELA = 0;

There are 32 bits in ANSELA, and each of them corresponds to a bit on PORT A. Setting a bit to 0 means we want to use it for digital I/O and setting it to a 1 means we want analog I/O. I usually just set the whole register to 0 and in those rare cases I need to use the ADC I'll set the appropriate bit. So let's add that in.

void main()
{
    set_performance_mode(); // Set everything to maximum speed

    ANSELA = 0; // Set all of PORT A's pins to digital

    TRISACLR = 1 << 0; // Clear bit 0 of TRISA. which makes PORT A bit 0 an output

    LATASET = 1 << 0; // Set bit 0 of PORT A to 1
}

Haha! That's it, it has to work this time! Compile, run and... what... the... heck? Maybe the oscillator wasn't configured correctly? Nope, went into that in excruciating detail last time. Maybe the pin is broken, because that's likely. Let's give PORT B a go:

void main()
{
    set_performance_mode(); // Set everything to maximum speed

    ANSELB = 0; // Set all of PORT B's pins to digital

    TRISBCLR = 1 << 0; // Clear bit 0 of TRISB. which makes PORT B bit 0 an output

    LATBSET = 1 << 0; // Set bit 0 of PORT B to 1
}

Compile, run and it works! But why? Does this mean I can't use RA0? Welcome to PIC32 Hell. You can google about and maybe get the answer, scanning forums full of people who know what's going on but enjoy taunting noobs. "Read the datasheet" is some awesome advice that's thrown around an awful lot. So let's do that.

PIC32MZ - Read the datasheet!

There are two arrows there, red and green. The red arrow is pointing to the bad RA0 that didn't work and green is pointing to the good RB0 that worked fine. If you look next to RA0 you'll see 3 little letters that can mean hours of frustration. TMS. There are more, TCK, TDI and TDO. So what gives?

As we've established by now, Microchip's idea of a default configuration is somewhat different from what a hobbyist wants. This means that every peripheral may be set to the wrong clock speed, everything is set to analog and interfaces like JTAG are enabled by default. What is JTAG? For most of us, it's something that takes away our ability to use the ports that we want :) To prevent many many hours of "reading the datasheet", please ensure you have these fuses set:

#pragma config FWDTEN = OFF             // Watchdog Timer Enable (WDT Disabled) - Enable for random resets
#pragma config FDMTEN = OFF             // Deadman Timer Enable (Deadman Timer is disabled) - See above
#pragma config JTAGEN = OFF             // JTAG Enable (JTAG Disabled) - Enable to lose four port outputs
#pragma config DEBUG = OFF              // Background Debugger Enable (Debugger is disabled) - Set if off and MPLAB will override it when you debug
#pragma config CP = OFF                 // Code Protect (Protection Disabled) - This will encrypt your PIC32 if enabled
#pragma config PMDL1WAY = OFF           // Peripheral Module Disable Configuration (Allow multiple reconfigurations) - Enable to only be allowed to make one change to peripheral pin select settings
#pragma config IOL1WAY = OFF            // Peripheral Pin Select Configuration (Allow multiple reconfigurations) - Enable to only be allowed to make one change to peripheral pin select settings

All these features are great and all but sheesh setting up a PIC32MZ before you can even do anything is a lot of work. That's why I use a skeleton and just copy it over each time. Another long winded explanation done, next time some actual code!

Here's the code

Tags: code

Configuration without Harmony 1

How to configure the PIC32MZ to run at 200Mhz

TL;DR warning: This goes into quite a lot of detail and you can just download the code at the bottom of the page and it'll work fine. However, the reason for having this blog was to share why we set things to the values we do so here goes!

One of the biggest problems with the PIC32 series is that the startup configuration sucks. Everything seems to be set to low speeds, all the digital ports are set to analog and nothing is how you generally want it to be. So we need to set up the configuration "fuses" (just another name for setting) before we even begin to code. Today I'm purely going to be focussing on how to get the chip running at the frequency we want because it's long enough that it deserves a post of its own. For my example, I'm using a PIC32MZ2048EFF144, but this configuration applies to any PIC32MZ chip, and is very similar to PIC32MX configuration settings. In my example, I'm using a 24Mhz External Clock and I want the chip to run 200Mhz so I set the relevant fuses as follows:

#pragma config FPLLIDIV = DIV_3         // System PLL Input Divider (3x Divider)
#pragma config FPLLRNG = RANGE_5_10_MHZ // System PLL Input Range (5-10 MHz Input)
#pragma config FPLLICLK = PLL_POSC      // System PLL Input Clock Selection (POSC is input to the System PLL)
#pragma config FPLLMULT = MUL_50        // System PLL Multiplier (PLL Multiply by 50)
#pragma config FPLLODIV = DIV_2         // System PLL Output Clock Divider (2x Divider)
#pragma config POSCMOD = EC             // Primary Oscillator Configuration (External clock mode)
#pragma config FNOSC = SPLL             // Oscillator Selection Bits (System PLL)

While it initially seems like a lot to take in, it's not that bad. Let's look at it line by line.

#pragma config FPLLIDIV = DIV_3 // System PLL Input Divider (3x Divider)

I start with my 24Mhz clock frequency and I set the input divider to 3. This means the PLL input frequency will be 24 / 3 = 8Mhz.

#pragma config FPLLRNG = RANGE_5_10_MHZ // System PLL Input Range (5-10 MHz Input)

This next line tells the PIC32MZ what range this frequency is in, and 8Mhz fits nicely into the 5 to 10Mhz range.

#pragma config FPLLICLK = PLL_POSC // System PLL Input Clock Selection (POSC is input to the System PLL)

This tells the PIC32MZ where the clock signal is coming from. If you have connected an external clock or a crystal, this will be set to POSC (which is short for Primary Oscillator).

#pragma config FPLLMULT = MUL_50 // System PLL Multiplier (PLL Multiply by 50)

This is where the magic begins. The PIC uses something called a PLL (or Phase Locked Loop) to multiply the frequency we put into it, allowing us to run at very high speeds with a lower input frequency. So I've set the multiplier to 50, which means by 8Mhz will now be 400Mhz. However, this is too high for the PIC32MZ chip I use, which can only do 200Mhz maximum, so we need to do one final configuration for frequency.

#pragma config FPLLODIV = DIV_2 // System PLL Output Clock Divider (2x Divider)

This will tell the PIC32 to divide whatever frequency is coming out of the PLL (which was 400Mhz in our example) and divide it by 2, giving us the frequency we wanted, which is 200Mhz.

#pragma config POSCMOD = EC // Primary Oscillator Configuration (External clock mode)

This tells the PIC32MZ that I'm using an External Clock. If you're using a crystal, this value will be XT or HS, depending on whether you're using a lower speed (3 to 10Mhz) crystal (XT) or a higher speed (10 to 20Mhz) crystal (HS). However, I strongly recommend using an external clock with the PIC32MZ as there are still errata related to the crystal and I've had issues where things didn't work right. So while the external clock is a little bit more expensive ($0.50 or so more) I'd say go for it by all means.

#pragma config FNOSC = SPLL // Oscillator Selection Bits (System PLL)

Finally, seting Oscillator Selection to System PLL. You could set this to an external clock but then why did we set all that up in the beginning?

Note: The procedure for setting up the 8Mhz internal "FRC" clock is very similar, you just need to make the following changes:

#pragma config FPLLIDIV = DIV_1         // System PLL Input Divider (1x Divider)
#pragma config FPLLRNG = RANGE_5_10_MHZ // System PLL Input Range (5-10 MHz Input)
#pragma config FPLLICLK = PLL_FRC       // System PLL Input Clock Selection (FRC is input to the System PLL)
#pragma config FPLLMULT = MUL_50        // System PLL Multiplier (PLL Multiply by 50)
#pragma config FPLLODIV = DIV_2         // System PLL Output Clock Divider (2x Divider)
#pragma config POSCMOD = OFF            // Primary Oscillator Configuration (Primary Oscillator disabled)

I'm using an external clock because the FRC is sadly not accurate enough to allow for using the USB module. It's perfectly fine for most projects not using USB, however.

So we're done right? 200Mhz set, time to go! Sadly, no. That's the easy part. We now need to set up the frequency of the various peripheral clocks, turn on caching and turn on pre-fetch. When we had the PIC32MX series and used PLIB, this was done by simply calling SYSTEMConfigPerformance(80000000UL). Thanks to Harmony, we don't have access to this any more and have to write it for ourselves. Fun. There are eight peripheral clocks on the PIC32MZ EF series, and if you look in the data sheet you get this helpful diagram. PIC32MZ Clock Distribution This is a very important, if initially confusing, diagram telling us which of the Peripheral Bus Clocks (PBCLKs) are responsible for which peripherals. I will refer to this diagram a lot when discussing various peripherals later on, it's well worth keeping in mind. Even later in the datasheet, you'll come across this

0000001 = PBCLKx is SYSCLK divided by 2 (default value for x ~~=~~ 7)
0000000 = PBCLKx is SYSCLK divided by 1 (default value for x = 7)

on the page about PBxDIV. What this essentially means is that all of the peripheral bus clocks are limited to a maximum speed of our system clock (SYSCLK, which is 200Mhz in my example) divided by 2. Except for PBCLK4 and PBCLK7, which are responsible for the Ports and CPU and the Deadman Timer respectively. For us hobbyists, we often just want stuff to run as fast as it can, so here's the function I wrote to set everything to maximum.

void set_performance_mode()
{   
    unsigned int cp0;

    // Unlock Sequence
    asm volatile("di"); // Disable all interrupts
    SYSKEY = 0xAA996655;
    SYSKEY = 0x556699AA;  

    // PB1DIV
    // Peripheral Bus 1 cannot be turned off, so there's no need to turn it on
    PB1DIVbits.PBDIV = 1; // Peripheral Bus 1 Clock Divisor Control (PBCLK1 is SYSCLK divided by 2)

    // PB2DIV
    PB2DIVbits.ON = 1; // Peripheral Bus 2 Output Clock Enable (Output clock is enabled)
    PB2DIVbits.PBDIV = 1; // Peripheral Bus 2 Clock Divisor Control (PBCLK2 is SYSCLK divided by 2)

    // PB3DIV
    PB3DIVbits.ON = 1; // Peripheral Bus 2 Output Clock Enable (Output clock is enabled)
    PB3DIVbits.PBDIV = 1; // Peripheral Bus 3 Clock Divisor Control (PBCLK3 is SYSCLK divided by 2)

    // PB4DIV
    PB4DIVbits.ON = 1; // Peripheral Bus 4 Output Clock Enable (Output clock is enabled)
    while (!PB4DIVbits.PBDIVRDY); // Wait until it is ready to write to
    PB4DIVbits.PBDIV = 0; // Peripheral Bus 4 Clock Divisor Control (PBCLK4 is SYSCLK divided by 1)

    // PB5DIV
    PB5DIVbits.ON = 1; // Peripheral Bus 5 Output Clock Enable (Output clock is enabled)
    PB5DIVbits.PBDIV = 1; // Peripheral Bus 5 Clock Divisor Control (PBCLK5 is SYSCLK divided by 2)

    // PB7DIV
    PB7DIVbits.ON = 1; // Peripheral Bus 7 Output Clock Enable (Output clock is enabled)
    PB7DIVbits.PBDIV = 0; // Peripheral Bus 7 Clock Divisor Control (PBCLK7 is SYSCLK divided by 1)

    // PB8DIV
    PB8DIVbits.ON = 1; // Peripheral Bus 8 Output Clock Enable (Output clock is enabled)
    PB8DIVbits.PBDIV = 1; // Peripheral Bus 8 Clock Divisor Control (PBCLK8 is SYSCLK divided by 2)

    // PRECON - Set up prefetch
    PRECONbits.PFMSECEN = 0; // Flash SEC Interrupt Enable (Do not generate an interrupt when the PFMSEC bit is set)
    PRECONbits.PREFEN = 0b11; // Predictive Prefetch Enable (Enable predictive prefetch for any address)
    PRECONbits.PFMWS = 0b010; // PFM Access Time Defined in Terms of SYSCLK Wait States (Two wait states)

    // Set up caching
    cp0 = _mfc0(16, 0);
    cp0 &= ~0x07;
    cp0 |= 0b011; // K0 = Cacheable, non-coherent, write-back, write allocate
    _mtc0(16, 0, cp0);  

    // Lock Sequence
    SYSKEY = 0x33333333;
    asm volatile("ei"); // Enable all interrupts
}

Before we are even allowed to change these settings, we should disable interrupts and we also need to tell the PIC32MZ to unlock them as they are part of a group of special settings and can't normally be written to. We do this by setting the SYSKEY to a certain magic number series, which is AA996655 hex and then 0x556699AA hex. If you look at it in binary, this sequence is:

10101010100110010110011001010101
01010101011001101001100110101010

In other words, the first number and the second number are the logical opposite of each other. It's just a safety feature, nothing to stress about. From then on, we can change these protected settings. So for each of Peripheral Bus Clocks, I'm setting them ON, so we can use them, and I'm setting the PBDIV register for each of them to 1, which means SYSCLK div 2. That is, except for Peripheral Bus Clocks 4 and 7, which I'm setting to 0 so they'll be running at SYSCLK div 1, or 200Mhz in our example. If you look at Peripheral Bus Clock 4 (PBCLK4) you can see there's yet another additional step, and that is to wait until the PB4DIV register is ready to write to by checking the PB4DIVbits.PBDIVRDY) bit.

We then move on to setting up the pre-fetch, which speeds up the PIC32MZ's operation a lot. We do this by setting the PREFEN register to binary 11 and the PFMWS register to binary 010. Why? More digging through the datasheet reveals this:

PIC32MZ Wait States Configuration

So there we get the value 0b11 for PREFEN, which means we want prefetch enabled for anything and everything, the value 0b010 for `PFMWS', meaning we want two wait states and the value 0b011 for the final configuration, which is the cache.

    cp0 = _mfc0(16, 0);
    cp0 &= ~0x07;
    cp0 |= 0b011; // K0 = Cacheable, non-coherent, write-back, write allocate
    _mtc0(16, 0, cp0);  

Look at that mess. Why isn't there an easy way to do this? I don't know to be honest :) But this is how we access the CP0 (System Control Coprocessor) register number 16 which has the settings for the caching. In the data sheet, it looks like this: PIC32MZ CP0 Register 16 - CONFIG

The last 3 bits of this CONFIG register are first set to 0 by going cp0 &= ~0x07;, which is saying cp0 = cp0 AND 11111111111111111111111111111000. We do this to only clearly the last 3 bits and leave the rest as is (very important to not mess up other configuration settings). Then we can set those last 3 bits to 0b011 by simply ORing them in. A look at the above image shows that 0b011 means Cacheable, non-coherent, write-back, write allocate. Which is basically telling the cache module to turn off any and all verification and to just work as fast as it possibly can. You can play around with the values if weird stuff is happening or you're doing something critical but for all of my projects the setting has been fine. Finally, lock the settings back up again by writing some other number to the SYSKEY register and re-enable interrupts. Phew.

Here's the code

Tags: code