Programming the USB module of PIC32MZ without Harmony (5) – Endpoint Configuration
So many details …
The following codes work for my project. I must say the configuration of the endpoints is probably the most important step to make the USB module work on the PICs. However, Microchip’s office PIC32MZ datasheet doesn’t provide detailed instructions. I managed to tune my codes to work through a lot of trial and errors and concluded all my understandings to a note at the end of this post:
void initUSBEndpoints()
{
// Clear all interrupt flags
USBCSR0bits.EP0IF = 0;
USBCSR0bits.EP1TXIF = 0;
USBCSR0bits.EP2TXIF = 0;
USBCSR0bits.EP3TXIF = 0;
USBCSR1bits.EP1RXIF = 0;
USBCSR1bits.EP2RXIF = 0;
// Set the maximum transfer length for each endpoint
// Configure endpoint 0 first.
USBE0CSR0bits.TXMAXP = 64; // Set endpoint 0 buffer size to 64 bytes (can be a maximum of 64 for EP0)
/* Set up endpoint 1 */
USBCSR3bits.ENDPOINT = 1;
// USBFIFOA = 0x00080000;
USBE1CSR0bits.MODE = 0; // EP1 is OUT (OUT from host = in to device = RX)
USBE1CSR1bits.CLRDT = 1;
USBE1CSR0bits.TXMAXP = 512; // Endpoint 1 - Maximum TX payload / transaction (set to 64)
USBE1CSR1bits.RXMAXP = 512; //
USBE1CSR3bits.RXFIFOSZ = 0x9; // Receive FIFO Size bits - 64 bytes
USBE1CSR3bits.TXFIFOSZ = 0x9; // Transmit FIFO Size bits - 64 bytes
USBE1CSR2bits.PROTOCOL = 2; // Endpoint TX - Bulk mode
USBE1CSR3bits.PROTOCOL = 2; // Endpoint RX - Bulk mode
USBE1CSR3bits.SPEED = 1; // Set receive endpoint to High Speed
USBE1CSR0bits.ISO = 0; // Disable Isochronous mode
USBE1CSR1bits.PIDERR = 0; // Clear DISNYET to enable NYET replies
// Set maximum size for each packet before splitting occurs
USBOTGbits.RXFIFOSZ = 0b0110; // 0b0110 = 512 bytes
USBOTGbits.TXFIFOSZ = 0b0110; // 0b0110 = 512 bytes
/* Set up endpoint 2 */
USBCSR3bits.ENDPOINT = 2;
// USBFIFOA = 0x0050010;
USBE2CSR1bits.CLRDT = 1;
USBE2CSR0bits.MODE = 0; // EP2 is OUT (for now, will be IN at times)
USBE2CSR0bits.TXMAXP = 512; // Endpoint 2 - Maximum TX payload / transaction (512 is the maximum, set to 512)
USBE2CSR1bits.RXMAXP = 512; // Endpoint 2 - Maximum RX payload / transaction (512 is the maximum, set to 512)
USBE2CSR3bits.TXFIFOSZ = 0x9; // Transmit FIFO Size bits - 512 bytes
USBE2CSR3bits.RXFIFOSZ = 0x9; // Receive FIFO Size bits - 512 bytes
USBE2CSR2bits.PROTOCOL = 2; // Endpoint 2 TX - Bulk mode
USBE2CSR3bits.PROTOCOL = 2; // Endpoint 2 RX - Bulk mode
USBE2CSR3bits.SPEED = 1; // Set receive endpoint to High Speed
USBE2CSR0bits.ISO = 0; // Disable Isochronous mode
USBE2CSR1bits.PIDERR = 0; // Clear DISNYET to enable NYET replies
// Set maximum size for each packet before splitting occurs
USBOTGbits.RXFIFOSZ = 0b0110; // 0b0110 = 512 bytes
USBOTGbits.TXFIFOSZ = 0b0110; // 0b0110 = 512 bytes
/* Set up endpoint 3 */
USBCSR3bits.ENDPOINT = 3;
// USBFIFOA = 0x0050010;
USBE3CSR1bits.CLRDT = 1;
USBE3CSR0bits.MODE = 1; // EP3 is IN
USBE3CSR0bits.TXMAXP = 512; // Endpoint 2 - Maximum TX payload / transaction (512 is the maximum, set to 512)
USBE3CSR3bits.TXFIFOSZ = 0x9; // Transmit FIFO Size bits - 512 bytes
USBE3CSR2bits.PROTOCOL = 2; // Endpoint 3 TX - Bulk mode
USBE3CSR3bits.SPEED = 1; // Set receive endpoint to High Speed
USBE3CSR0bits.ISO = 0; // Disable Isochronous mode
USBE3CSR1bits.PIDERR = 0; // Clear DISNYET to enable NYET replies
// Set maximum size for each packet before splitting occurs
USBOTGbits.TXFIFOSZ = 0b0110; // 0b0110 = 512 bytes
// Set up endpoint interrupts
// Initially clear all the interrupt enables (IE)
USBCSR1 = 0;
USBCSR2 = 0;
USBCSR1bits.EP0IE = 1; // Endpoint 0 interrupt enable
USBCSR1bits.EP1TXIE = 0; // Endpoint 1 TX interrupt - do not use
USBCSR2bits.EP1RXIE = 1; // Endpoint 1 RX interrupt - enable
USBCSR1bits.EP2TXIE = 0; // Endpoint 2 TX interrupt - do not use
USBCSR2bits.EP2RXIE = 1; // Endpoint 2 RX interrupt enable
USBCSR1bits.EP3TXIE = 1; // Endpoint 2 RX interrupt enable
USBCSR2bits.RESETIE = 1;
USBCSR2bits.RESUMEIE = 1;
USBCSR2bits.SOFIE = 1;
}
Notes:
- Any change of USBIENCSRx register must happen after USBCSR3bits.ENDPOINT is set to the corresponding endpoint. Or alternatively, use USCEnCSRx to directly configure each endpoint. I believe it makes more sense to use USBIENCSRx in a loop or function where the index of the endpoint could be represented as a variable.
- USBOTGbits.RXFIFOSZ (or TXFIFOSZ) must be equal to or larger than the max packet size defined in the endpoint descriptor. Otherwise, the transaction will fail. Once the host enumerates a device, it will break the data into packets per max_packet_size. The host, however, has no idea with regard to how the device will handle the data. Since the USBOTGbits.RXFIFOSZ defines “The maximum packet size to allowed for”, the device will only be able to receive the data coming in with a size no bigger than RXFIFOSZ. In a case where RXFIFOSZ is smaller than the max_packet_size, the device won’t be able to process the large data from the host, thus a USB transaction failure will occur. The host program (PyUSB in my case) will report “timeout error”