USB Code for the PIC32MZ Family
Here is some minimal USB code for the MZ processor written specifically for the hobbyist or developer
that does not want to use Harmony.
My code uses Microsoft OS Descriptors 1.0 to load a Winusb driver. When a USB device enumerates for WinUSB, the process starts as soon as the device is plugged in and the host detects its presence on the bus. The device first responds with basic descriptors—like the device descriptor—so Windows can identify its vendor ID, product ID, and supported USB version. Windows then requests configuration and interface descriptors, where the device advertises that it uses the WinUSB-compatible interface. If Microsoft OS descriptors are present, the device can explicitly signal that WinUSB is the preferred driver. Once Windows recognizes that the interface is WinUSB-compatible, it loads the WinUSB.sys driver, creates a device node, and exposes the endpoints for user-mode applications through the WinUSB API. The device will be listed as 'MainBrain MZ' in Device Manager under 'Universal Serial Bus Devices'
/*********************************************************************
#pragma config DEBUG = OFF
#pragma config JTAGEN = OFF
#pragma config ICESEL = ICS_PGx2
#pragma config TRCEN = OFF
#pragma config BOOTISA = MIPS32
#pragma config FECCCON = OFF_UNLOCKED
#pragma config FSLEEP = OFF
#pragma config DBGPER = PG_ALL
#pragma config SMCLR = MCLR_NORM
#pragma config SOSCGAIN = GAIN_2X
#pragma config SOSCBOOST = ON
#pragma config POSCGAIN = GAIN_2X
#pragma config POSCBOOST = OFF
#pragma config EJTAGBEN = NORMAL
#pragma config CP = OFF
#pragma config FNOSC = SPLL
#pragma config DMTINTV = WIN_127_128
#pragma config FSOSCEN = ON
#pragma config IESO = OFF
#pragma config POSCMOD = EC
#pragma config OSCIOFNC = OFF
#pragma config FCKSM = CSECMD
#pragma config WDTPS = PS1048576
#pragma config WDTSPGM = STOP
#pragma config FWDTEN = OFF
#pragma config WINDIS = NORMAL
#pragma config FWDTWINSZ = WINSZ_25
#pragma config DMTCNT = DMT31
#pragma config FDMTEN = OFF
#pragma config FPLLIDIV = DIV_3
#pragma config FPLLRNG = RANGE_5_10_MHZ
#pragma config FPLLICLK = PLL_POSC
#pragma config FPLLMULT = MUL_58
#pragma config FPLLODIV = DIV_2
#pragma config UPLLFSEL = FREQ_24MHZ
#pragma config USERID = 0xffff
#pragma config FMIIEN = OFF
#pragma config FETHIO = OFF
#pragma config PGL1WAY = OFF
#pragma config PMDL1WAY = OFF
#pragma config IOL1WAY = OFF
#pragma config FUSBIDIO = OFF
#include <xc.h>
#include "usb.h"
bool SetFreqPOSC(uint8_t f);
void SystemSetup(void);
int main(void)
{
while(CLKSTATbits.POSCRDY == 0);
SystemSetup();
__builtin_enable_interrupts();
while(1)
{
PORTCbits.RC15 = !PORTCbits.RC15;
}
}
void SystemSetup(void)
{
__builtin_disable_interrupts();
SYSKEY = 0xAA996655;
SYSKEY = 0x556699AA;
USB_init();
INTCONbits.MVEC = 1;
unsigned int cp0;
USBCRCONbits.USBIDOVEN = 1;
USBCRCONbits.USBIDVAL = 1;
PRISS = 0x76543210;
PB1DIVbits.PBDIV = 1;
PB2DIVbits.PBDIV = 1;
PB2DIVbits.ON = 1;
while (!PB2DIVbits.PBDIVRDY);
PB3DIVbits.PBDIV = 0;
PB3DIVbits.ON = 1;
while (!PB3DIVbits.PBDIVRDY);
PB4DIVbits.PBDIV = 1;
PB4DIVbits.ON = 1;
while (!PB4DIVbits.PBDIVRDY);
PB5DIVbits.PBDIV = 1;
PB5DIVbits.ON = 1;
while (!PB5DIVbits.PBDIVRDY);
PB7DIVbits.PBDIV = 0;
PB7DIVbits.ON = 1;
while (!PB7DIVbits.PBDIVRDY);
PB8DIVbits.PBDIV = 1;
PB8DIVbits.ON = 1;
while (!PB8DIVbits.PBDIVRDY);
PRECONbits.PFMSECEN = 0;
PRECONbits.PREFEN = 3;
PRECONbits.PFMWS = 2;
CFGCONbits.USBSSEN = 1;
cp0 = _mfc0(16, 0);
cp0 &= ~0x07;
cp0 |= 0b011;
_mtc0(16, 0, cp0);
while(CLKSTATbits.DIVSPLLRDY == 0);
}
void __attribute__((nomips16)) _general_exception_handler(void)
{
unsigned int exccode = (_CP0_GET_CAUSE() & 0x7C) >> 2;
while(1)
{
}
}
/*********************************************************************
#ifndef USB_H
#define USB_H
#include <stdbool.h>
#include <stdint.h>
typedef enum
{
CONNECTED = 0x00,
DISCONNECTED = 0x01,
} DEVICE_STATE;
typedef struct
{
volatile unsigned char bmRequestType;
volatile unsigned char bRequest;
volatile unsigned short wValue;
volatile unsigned short wIndex;
volatile unsigned short wLength;
} USB_TRANSACTION;
typedef struct
{
volatile unsigned short rx_num_bytes;
volatile unsigned short tx_num_bytes;
volatile unsigned char tx_buffer[512];
volatile unsigned char rx_buffer[512];
} USB_ENDPOINT;
void USB_init(void);
#endif
/*********************************************************************
\SYSTEM\CurrentControlSet\Control\usbflags
\SYSTEM\CurrentControlSet\Enum\USB\VID_1209\MainBrain_MZ
#include <xc.h>
#include <string.h>
#include <stdbool.h>
#include "usb.h"
bool isConnected = false;
USB_TRANSACTION USB_transaction;
USB_ENDPOINT EP[3];
uint8_t device_descriptor[] =
{
0x12,
0x01,
0x00,0x02,
0x00,
0x00,
0x00,
0x40,
0x09,0x12,
0x80,0x10,
0x00,0x10,
0x01,
0x02,
0x02,
0x01
};
uint8_t config_descriptor[] =
{
0x09,
0x02,
0x20,0x00,
0x01,
0x01,
0x00,
0xc0,
0x32,
0x09,
0x04,
0x00,
0x00,
0x02,
0x00,
0x00,
0x00,
0x00,
0x07,
0x05,
0x01,
0x02,
0x40,0x00,
0x00,
0x07,
0x05,
0x82,
0x02,
0x40,0x00,
0x00
};
uint8_t device_qualifier[] =
{
0x0a,
0x06,
0x00, 0x02,
0x00,
0x00,
0x00,
0x40,
0x01,
0x00
};
uint8_t MSOSDescriptor[] =
{
0x12,
0x03,
'M',0,'S',0,'F',0,'T',0,'1',0,'0',0,'0',0,
0xee,
0x00
};
uint8_t ExtCompatIDFeatureDescriptor[] =
{
0x28, 0x00, 0x00, 0x00,
0x00, 0x01,
0x04, 0x00,
0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00,
0x01,
0x57, 0x49, 0x4E, 0x55, 0x53, 0x42, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
uint8_t ExtPropertyFeatureDescriptor[] =
{
0x8e, 0x00, 0x00, 0x00,
0x00, 0x01,
0x05, 0x00,
0x01, 0x00,
0x84, 0x00, 0x00, 0x00,
0x01,0x00, 0x00, 0x00,
0x28, 0x00,
'D', 0, 'e', 0, 'v', 0, 'i', 0, 'c', 0, 'e', 0, 'I', 0, 'n', 0, 't', 0, 'e', 0, 'r', 0, 'f', 0, 'a', 0, 'c', 0,
'e', 0, 'G', 0, 'U', 0, 'I', 0, 'D', 0, 0x00, 0x00,
0x4e, 0x00, 0x00, 0x00,
'{', 0, '2', 0, 'b', 0, '8', 0, 'a', 0, '8', 0, '2', 0, '1', 0, '6', 0, '-', 0, 'c', 0, '8', 0, '2', 0, 'a', 0,
'-', 0, '4', 0, 'a', 0, '9', 0, '1', 0, '-', 0, 'a', 0, '8', 0, 'b', 0, 'c', 0, '-', 0, 'a', 0, '1', 0, '2',
0, '1', 0, '2', 0, '9', 0, 'd', 0, '2', 0, 'd', 0, '7', 0, '0', 0, 'b', 0, '}', 0, 0x00, 0x00
};
uint8_t string0[] = {4, 0x03, 0x09, 0x04};
uint8_t string1[] = {28, 3, 'A', 0, 'n', 0, 't', 0, 'i', 0, 'm', 0, 'a', 0, 't', 0, 't', 0, 'e', 0, 'r', 0, '.', 0, 'm', 0, 'e', 0};
uint8_t string2[] = {26, 3, 'M', 0, 'a', 0, 'i', 0, 'n', 0, 'B', 0, 'r', 0, 'a', 0, 'i', 0, 'n', 0, ' ', 0, 'M', 0, 'Z', 0};
uint8_t string3[] = {10, 3, '0', 0, '0', 0, '0', 0, '1', 0};
void EP0ControTransaction(void);
void USB_queue_EP0(uint8_t *buffer, int size, int max_size);
void EP0_RX(int length);
void EP0_TX(void);
void Host_CMDs(void);
int EP1_RX(void);
int EP2_TX(volatile uint8_t *tx_buffer);
int EP0_Wait_TXRDY(void);
int EP2_Wait_TXRDY(void);
volatile uint8_t usbAddress;
volatile bool SetAddress = true;
volatile uint8_t DeviceState;
volatile bool ep0_in_data_phase = false;
void USB_init(void)
{
USBCSR0bits.SOFTCONN = 0;
DeviceState = DISCONNECTED;
USBE0CSR0bits.TXMAXP = 64;
USBCSR3bits.ENDPOINT = 1;
USBOTGbits.RXFIFOSZ = 0x06;
USBIENCSR1bits.RXMAXP = 64;
USBIENCSR3bits.RXFIFOSZ = 0x09;
USBFIFOAbits.RXFIFOAD = 0x0280;
USBIENCSR3bits.PROTOCOL = 0x02;
USBIENCSR3bits.TEP = 0x01;
USBIENCSR1bits.FLUSH = 1;
USBE1CSR0bits.MODE = 0;
USBCSR3bits.ENDPOINT = 2;
USBOTGbits.TXFIFOSZ = 0x06;
USBIENCSR0bits.TXMAXP = 64;
USBIENCSR3bits.TXFIFOSZ = 0x09;
USBFIFOAbits.TXFIFOAD = 0x0080;
USBIENCSR2bits.PROTOCOL = 0x02;
USBIENCSR2bits.TEP = 0x02;
USBIENCSR0bits.FLUSH = 1;
USBE2CSR0bits.MODE = 0;
usbAddress = 0;
USBCSR0bits.FUNC = 0;
USBCRCONbits.VBUSMONEN = 1;
USBCSR2bits.RESETIE = 1;
IEC4bits.USBIE = 1;
USBCRCONbits.USBIE = 1;
IFS4bits.USBIF = 0;
IPC33bits.USBIP = 7;
IPC33bits.USBIS = 3;
USBE1CSR1bits.PIDERR = 1;
USBCSR0bits.HSEN = 1;
USBCSR0bits.SOFTCONN = 1;
}
void __attribute__((vector(_USB_VECTOR), interrupt(ipl7srs), nomips16)) USB_handler()
{
if(USBCSR2bits.RESETIF)
{
USBE1CSR0bits.MODE = 1;
USBE0CSR0bits.TXMAXP = 64;
USBE0CSR2bits.SPEED = 1;
USBCSR2bits.RESETIF = 0;
}
if (USBCSR0bits.EP0IF)
{
if (SetAddress)
{
USBCSR0bits.FUNC = usbAddress & 0x7F;
SetAddress = false;
}
if (USBE0CSR0bits.RXRDY)
{
EP0_RX(USBE0CSR2bits.RXCNT);
USB_transaction.bmRequestType = EP[0].rx_buffer[0];
USB_transaction.bRequest = EP[0].rx_buffer[1];
USB_transaction.wValue = ((uint16_t)EP[0].rx_buffer[3] << 8) |
EP[0].rx_buffer[2];
USB_transaction.wIndex = ((uint16_t)EP[0].rx_buffer[5] << 8) |
EP[0].rx_buffer[4];
USB_transaction.wLength = ((uint16_t)EP[0].rx_buffer[7] << 8) |
EP[0].rx_buffer[6];
ep0_in_data_phase = false;
EP0ControTransaction();
if ((USB_transaction.bmRequestType & 0x80) &&
(EP[0].tx_num_bytes > 0))
{
ep0_in_data_phase = true;
}
if (USB_transaction.wLength == 0)
{
USBE0CSR0bits.DATAEND = 1;
}
}
if (ep0_in_data_phase &&
!USBE0CSR0bits.TXRDY)
{
EP0_TX();
if (EP[0].tx_num_bytes == 0)
{
ep0_in_data_phase = false;
}
}
if (USBE0CSR0bits.SETEND)
{
USBE0CSR0bits.SETENDC = 1;
ep0_in_data_phase = false;
}
USBCSR0bits.EP0IF = 0;
}
if(USBCSR1bits.EP1RXIF == 1)
{
EP1_RX();
Host_CMDs();
USBCSR1bits.EP1RXIF = 0;
}
IFS4bits.USBIF = 0;
}
void EP0ControTransaction()
{
uint16_t length;
if ((USB_transaction.bmRequestType == 0xC0) && (USB_transaction.bRequest == 0xee) && USB_transaction.wIndex == 0x04)
{
length = USB_transaction.wLength;
if (length > sizeof(ExtCompatIDFeatureDescriptor))
{
length = sizeof(ExtCompatIDFeatureDescriptor);
}
USB_queue_EP0(ExtCompatIDFeatureDescriptor, sizeof(ExtCompatIDFeatureDescriptor), length);
return;
}
if(USB_transaction.bmRequestType == 0xc1)
{
if(USB_transaction.bRequest == 0xee)
{
if(USB_transaction.wIndex == 0x05)
{
length = sizeof(ExtPropertyFeatureDescriptor);
if(USB_transaction.wLength < length)
{
length = USB_transaction.wLength;
}
USB_queue_EP0(ExtPropertyFeatureDescriptor, sizeof(ExtPropertyFeatureDescriptor), length);
USBE0CSR0bits.TXRDY = 1;
return;
}
}
}
switch (USB_transaction.bRequest)
{
case 0xC:
{
USBE0CSR0bits.STALL = 1;
break;
}
case 0x0:
{
if (USB_transaction.bmRequestType == 0x80)
USB_queue_EP0(device_descriptor, 0, 0);
if (USB_transaction.bmRequestType == 0x00)
USB_queue_EP0(device_descriptor, 0, 0);
break;
}
case 0x5:
{
USBE0CSR0bits.RXRDYC = 1;
usbAddress = EP[0].rx_buffer[2];
SetAddress = true;
break;
}
case 0x6:
{
switch (USB_transaction.wValue >> 8)
{
case 0x1:
{
USB_queue_EP0(device_descriptor, sizeof(device_descriptor), USB_transaction.wLength);
break;
}
case 0x2:
{
USB_queue_EP0(config_descriptor, sizeof(config_descriptor), USB_transaction.wLength);
break;
}
case 0x3:
{
switch (USB_transaction.wValue & 0xff)
{
case 0x0:
{
USB_queue_EP0(string0, sizeof(string0), USB_transaction.wLength);
break;
}
case 0x1:
{
USB_queue_EP0(string1, sizeof(string1), USB_transaction.wLength);
break;
}
case 0x2:
{
USB_queue_EP0(string2, sizeof(string2), USB_transaction.wLength);
break;
}
case 0x3:
{
USB_queue_EP0(string3, sizeof(string3), USB_transaction.wLength);
break;
}
case 0xee:
{
USB_queue_EP0(MSOSDescriptor, sizeof(MSOSDescriptor), USB_transaction.wLength);
break;
}
break;
}
break;
}
case 0x04:
if (USB_transaction.wIndex == 0x0004)
{
USB_queue_EP0(ExtCompatIDFeatureDescriptor, sizeof(ExtCompatIDFeatureDescriptor), USB_transaction.wLength);
}
break;
case 0x05:
if (USB_transaction.wIndex == 0x0005)
{
USB_queue_EP0(ExtPropertyFeatureDescriptor, sizeof(ExtPropertyFeatureDescriptor), USB_transaction.wLength);
}
break;
case 0x6:
{
USB_queue_EP0(device_qualifier, sizeof(device_qualifier), USB_transaction.wLength);
break;
}
}
break;
}
case 0x9:
{
USBCSR2bits.EP1RXIE = 1;
USBCSR1bits.EP2TXIE = 1;
break;
}
default:
{
USBE0CSR0bits.STALL = 1;
break;
}
}
}
void USB_queue_EP0(uint8_t *buffer, int size, int max_size)
{
int cnt;
if (max_size < size)
size = max_size;
EP[0].tx_num_bytes = size;
for (cnt = 0; cnt < size; cnt++)
{
EP[0].tx_buffer[cnt] = buffer[cnt];
}
EP0_TX();
}
void EP0_RX(int length)
{
int cnt;
uint8_t *FIFO_buffer;
EP[0].rx_num_bytes = USBE0CSR2bits.RXCNT;
FIFO_buffer = (uint8_t *)&USBFIFO0;
for(cnt = 0; cnt < length; cnt++)
{
EP[0].rx_buffer[cnt] = *(FIFO_buffer + (cnt & 3));
}
USBE0CSR0bits.RXRDYC = 1;
}
int EP0_Wait_TXRDY()
{
int timeout;
timeout = 0;
while (USBE0CSR0bits.TXRDY)
{
timeout++;
if (timeout > 5000)
{
return 1;
}
};
return 0;
}
void EP0_TX(void)
{
static uint16_t ep0_tx_cnt = 0;
uint8_t *FIFO = (uint8_t *)&USBFIFO0;
uint16_t bytes_this_pkt = 0;
while (ep0_tx_cnt < EP[0].tx_num_bytes && bytes_this_pkt < 64)
{
*FIFO = EP[0].tx_buffer[ep0_tx_cnt++];
bytes_this_pkt++;
}
USBE0CSR0bits.TXRDY = 1;
if (ep0_tx_cnt >= EP[0].tx_num_bytes)
{
USBE0CSR0bits.DATAEND = 1;
ep0_tx_cnt = 0;
EP[0].tx_num_bytes = 0;
}
}
int EP2_TX(volatile uint8_t* tx_buffer)
{
int cnt = 0;
EP[2].tx_num_bytes = 64;
for (cnt = 0; cnt < 64; cnt++)
{
EP[2].tx_buffer[cnt] = tx_buffer[cnt];
}
uint8_t *FIFO_buffer;
FIFO_buffer = (uint8_t *)&USBFIFO2;
if (EP2_Wait_TXRDY())
{
return 0;
}
cnt = 0;
while (cnt < EP[2].tx_num_bytes)
{
*FIFO_buffer = EP[2].tx_buffer[cnt];
cnt++;
if ((cnt > 0) && (cnt % 64 == 0))
{
USBE2CSR0bits.TXPKTRDY = 1;
if(EP2_Wait_TXRDY())
{
return 0;
}
}
}
USBE2CSR0bits.TXPKTRDY = 1;
}
int EP1_RX()
{
unsigned char *FIFO_buffer;
int cnt;
int rx_bytes;
rx_bytes = USBE1CSR2bits.RXCNT;
FIFO_buffer = (unsigned char *)&USBFIFO1;
for(cnt = 0; cnt < rx_bytes; cnt++)
{
EP[1].rx_buffer[cnt] = *(FIFO_buffer + (cnt & 3));
}
USBE1CSR1bits.RXPKTRDY = 0;
return rx_bytes;
}
int EP2_Wait_TXRDY()
{
int timeout;
timeout = 0;
while (USBE2CSR0bits.TXPKTRDY)
{
timeout++;
if (timeout > 5000)
{
return 1;
}
};
return 0;
}
void Host_CMDs()
{
if(EP[1].rx_buffer[0] == 0x0)
{
DeviceState = CONNECTED;
}
else
{
DeviceState = DISCONNECTED;
}
EP[2].tx_buffer[0] = EP[1].rx_buffer[1];
EP2_TX(EP[2].tx_buffer);
}