USB on the PIC32MZ


In the USB_device_description, change the line called /* bDeviceClass */ from 0x00 to 0x02 otherwise enumeration won't complete

Hello again all, he said to no one. Great news! I've finally gotten the USB port on the PIC32MZ working without Harmony. It's been a multi-year on-again-off-again project but this time I managed to get it done. Well, it works on all my development boards anyway.
What I've gotten to work so far is USB CDC (virtual COM port), which means I no longer have to use a USB-UART chip on my PIC32MZ development boards. From here I want to move on to getting HID working and then the ultimate goal of MSD. But for now, I'm uploading the CDC code and hopefully if anyone reads this they can see if it works on their boards :)

The documentation is severely lacking on USB-related stuff and to give credit where credit is due, here are all the sites I referenced when making my code:
* Microchip's own OTG datasheet
* Microchip's AN1166 on USB CDC
* The massively long CDC spec 1.2
* MARKDINGST.BLOGSPOT.COM that helped me understand the get/set line coding better
* WWW.XMOS.AI that helped me understand what the various endpoints were
* AIJU.DE that really helped explain some of the intricacies of the flags I needed to set
* WWW.BEYONDLOGIC.ORG for their simple explanataion of the USB setup packets
* WWW.SILABS.COM for a better explanation of USB-CDC

And then the big ones. Actual examples of USB-CDC for the PIC32MZ. I couldn't get either of these to work on my dev board but I used them as skeletons to base my own code off of (with a fair bit of copy and pasting!). Thanks to both of these people without whom I'd never have had any hope of getting this done:

What you will notice missing there is MPLAB Harmony, because it continues to be a bloated, slow, useless mess that is so cryptic that it is useless and provides no hints of how to do anything yourself. The Harmony approach of one library for all devices may be great for corporations who don't care about how anything works but it is atrocious for a hobbyist like me trying to understand how to use the PIC32MZ series.

Anyway, I will go into a brief explanation of USB and CDC in a future post but for now I just wanted to share the code.

Please note that while I have started splitting up the code into a few different files the work isn't done yet. I also have a lot more to learn about USB so some of the naming or things I'm doing may well be wrong :) Point is, it works on my board and I think having the code out there is better than not having it out there. As always, YMMV!

Here's the code. I hope it works on your boards and, if not, leave a comment and I'll try and help out.

Tags: code

A quick update!

An update on my site

Hi there everyone and no one. I haven't updated this site for quite some time (10 months or so). Certainly the COVID-19 pandemic has changed the way we do work and changed the world but apart from that I really just started this site to share the knowledge of PIC32MZ that I had gained through years of trial and error. I was sick of seeing useless "read the manual" and "work it out for yourself" posts by arrogant and selfish people on the microchip forums and annoyed by the tons of errors I had found in the datasheets. But now that I've written about all the stuff I wanted to share I really don't know what more to write about :)

I'm keeping the site up and I do hope it can provide a useful reference to the few people that need it. I'll also be uploading some of the schematics of stuff I've been working on in the next week or two.

Works and life have kept me busy and so I've been putting off replying to posts ("I'll get to that tomorrow") but I'll get to them today and hopefully stop being so lazy :)

Apologies if I miss out on replying to anyone's older questions the notification system here sucks!

Tags: rambling

Choosing which pins to use on a PIC32MZ project

How I choose which pins to use on a PIC32MZ project

I got an interesting question in my comments yesterday asking how I go about choosing which pins to use when I do a PIC32MZ project. My answer is a bit long and I think it might be useful to other noobs like me so I'm releasing it as a post today. Please note: This isn't based on anything but my own experience with PIC32MZ projects, it is certainly not the only way to to things. It's just the way I do it myself.

Basically, I go through the following steps:

  • List all peripherals I will need for the project
  • Check which hidden secrets the peripherals have
  • Check errata to make sure they all work
  • Check which pins each peripheral needs
  • Actually choose the pins

List all peripherals I will need for the project

In the example I was given, it was 2 x I2S (using an internally generated reference clock), 3 x UART, 2 x I2C and 2 x SPI on a 144-pin PIC32MZ EFG chip. Furthermore, while I2C and SPI peripherals can both be connected to multiple devices, in the interests of speed (and for the explanation) I'm going to connect each device to a separate SPI and I2C peripheral. The aim will be simple: to map these peripherals to GPIO pins in a way that makes layout easy.

Check which hidden secrets the peripherals have

All the peripherals have certain things that must be kept in mind when they are used. They're not really secrets at all but they may not be immediately obvious when you first set out to use them. For the example, the "hidden secrets" are as follows:

I2C peripherals are fixed and cannot be moved. Furthermore, I2C SDA and SCL pins require a 4.7k pullup resistor in order for them to meet the I2C standard and work reliably.

I2S either works off of PBCLK2 or off of REFCLKO1 because REFCLKO1 controls all SPI (which I2S is a subset of). It cannot just use any reference clock. For audio and the frequencies it uses, REFCLKO1 is much more useful. Furthermore, REFCLKO1 is set up by PPS, which further limits out options to the following:


Although most pins for SPI peripherals can be moved around at will, the clock pins (SCK1 ~ SCK6) cannot be moved. Luckily, if we control the Chip Select (CS) pin of the device ourselves, we can gain a bit more flexibility.

As I detailed in a post in the past, in order to extract 50Mhz from the SPI port you can only use SPI2 or SPI3 and even then only on specific pins, as follows:

PIC32MZ - SPI speed trick

Because of this, I'm going to be putting the SD card on SPI2 (but you could put the LCD there for 50Mhz too if you wish).

Check errata to make sure they all work

Unlike the 32MZ EC series, which was a horrible mess full of bugs, the EF series has relatively few errata. There is, however, one that looks concerning. It's listed as "The I2C module does not function reliably under certain conditions."
It turns out that this problem occurs when we use I2C at a rate of more than 100kHz and have constant I2C transfers of more than 500 bytes. The solution from Microchip, as it has been since the PIC32MX days, is to "bit bang". Which means to implement I2C by yourself and not use their peripheral. That's pretty poor.
In practice I haven't encountered this yet but it is something to bear in mind.

Amusingly, the UART has the following errata: "The UART automatic baud rate feature is intended to set the baud rate during run-time based on external data input. However, this feature does not function." Great work, Microchip. Great work.

Check which pins each peripheral needs

Straightforward stuff here. I'll list the ones relevant to my example.

Each SPI needs:

  • MISO (SDI)
  • MOSI (SDO)
  • CS
  • CLK

Each I2C needs:

  • SDA
  • SCL

Each UART needs:

  • RX
  • TX

Each I2S needs:

  • MOSI (SDO)
  • SCK
  • LRCK (for which I use Slave Select (SS))

Actually choose the pins

OK, now the part that we actually wanted to do. I like to keep the peripherals separated if possible to make layout easier as I routinely use double-sided boards. For this step I look at two parts of the datasheet, namely the pinout of the device and the PPS Input and Output sections. Bearing that in mind, for this example I'd use something like this:

First, on the one "side" of the PIC32MZ EF 144-pin devices, we have the RH and RK ports, which are totally useless from a PPS perspective. There are also no I2C peripherals on that particular side. However, there is a potential output for REFCLKO1 and three SPI peripherals, so I've put the two I2S ports there like this:

SPI3 (I2S):

  • MOSI3 = RD14
  • SCK3 = RB14
  • SS3 = RF12 (used as LRCK)
  • REFOCLK1 = RD15

SPI5 (I2S):

  • MOSI5 = RB9
  • SCK5 = RF13
  • SS5 = RB8 (used as LRCK)
  • REFOCLK1 = RD15

As noted earlier, I need two SPI peripherals and one of them must be on SPI2, using RB3 and RB5. There is one I2C peripheral on this same side. I've laid it out like this:

SPI2 (50Mhz):

  • SCK2 = RG6
  • MISO2 = RB3
  • MOSI2 = RB5
  • SS2 = RG9 (Chip Select)


  • SDA4 = RG7
  • SCL4 = RG8

We still need one more I2C and one more SPI peripheral, which I've done as follows:


  • SDA2 = RA3
  • SCL2 = RA2


  • SCK4 = RD10
  • MISO4 = RD11
  • MOSI4 = RD0
  • SS4 = RH12 [See below]

I've chosen RH12 as SS4 (or Chip Select) but it cannot be used as SS4 in the PPS setting. I've done this because I plan to use this for the LCD, which needs me to manually control the Chip Select (CS) line and there are also no convenient PPS mappings for SS4 nearby.

OK, mostly done now. We now need 3 UARTs, which I've done as follows:


  • U3TX = RD6
  • U3RX = RD7


  • U4TX = RD4
  • U4RX = RD5


  • U5TX = RF0
  • U5RX = RF1

In the end, it looks like this in Eagle (click to zoom):

PIC32MZ - Choosing pins PIC32MZ - Choosing pins

Hope this will be of use to someone :)

Tags: hardware, planning, circuits

Shared pins on the PIC32MZ's ADC

Cautionary note about shared analog inputs when choosing which analog inputs to use

Last time I briefly went over how to use the alternative analog inputs on ADCs 0 to 4. It went like this:

  • set SH0ALT to 0 / 1 to use AN0 / AN45 on ADC0
  • set SH1ALT to 0 / 1 to use AN1 / AN46 on ADC1,
  • set SH2ALT to 0 / 1 to use AN2 / AN47 on ADC2,
  • set SH3ALT to 0 / 1 to use AN3 / AN48 on ADC3 or
  • set SH4ALT to 0 / 1 to use AN4 / AN49 on ADC4 respectively.

At the time I didn't realise the full implications of that. I was working on a project yesterday and saw this:

PIC32MZ ADC MPLAB X Code Complete!

I was trying to use AN45, but there didn't seem to be a flag to check to see if the ADC conversion had completed, so I went to check the datasheet. Sure enough, I found this:


What I was expecting was ADCDATA45, ADCDATA46, ADCDATA47, ADCDATA48 and ADCDATA49, but it ended at ADCDATA44. Then suddenly I remember the pins could be used as alternative inputs and went back to look at the ADC block diagram again:

PIC32MZ ADC - Alternative Inputs Block Diagram!

That confirmed it for me. You can use AN45 as an input OR you can use AN0 as an input, and they will both work on ADC0. However, you can not use them at the same time!

Yes, I'm sure you could keeping setting and clearing SH0ALT to use them both but that is not what I'm referring to. I'm referring to a situation where you look at the datasheet and think you can attach signal A to AN0 and signal B to AN45 and read them in at the same time.

So how to use AN45 then? Well, you set SH0ALT to 1 and then write the code as if you are actually using AN0. That is, you still use all the ADC0 bit settings and flags (like ADCDATA0, ARDY0, TRGSRC0, etc.) as if you were using AN0 but the PIC32MZ will know that you are actually using AN45 instead of AN0 because you've set SH0ALT to 1.

TL;DR ADC inputs AN45, AN46, AN47, AN48 and AN49 are shared with ADC inputs AN0, AN1, AN2, AN3 and AN4 respectively. You can use, for example, either AN0 or AN45 but you can not use both at the same time for different signals.

And yes, it seems obvious now but I think this is a potentially very dangerous design trap that you might fall into if you don't know about it so I'm uploading it in the hopes that someone who needs this will find it.

Tags: code, ADC