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:

  1. 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.
  2. 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”

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.