Thursday, November 13, 2014

PIC24FJ64GB004, enumeration process

Last post I wrote about the first steps of the enumeration process on the USB host, finding an attached device. The whole process includes quite a number of steps, which are not that easy to wrap your head around.
Firstly, I hadn't configured the clocks properly. The USB host mode is quite picky about the clocks, so getting it right is essential. One problem was that I haven't designed in a crystal as oscillator. The data sheet says the internal RC-clock isn't precise enough. This also means that even if everything actually seems to be working with the RC-clock, the instructions don't address this configuration. The setup is available in the source code.
When I troubleshooted the enumeration process, I made a pseudocode version of it, and I thought it would be suitable to post that here. I'm getting as far as to get a transfer complete-interrupt, which probably is a good step on the way to get through the whole enumeration. So here it is:

 [global variables]  
 static uint16_t __attribute__((aligned(512))) BDT[4];  
 volatile uint16_t ep0RxBuffer[32];  
 volatile uint16_t ep0TxBuffer[32];  
 [in main()]  
 //USB1 host mode setup  
 U1CONbits.HOSTEN = 1; //enable host mode  
 U1OTGCONbits.DPPULDWN = 1; //enable pulldowns on lanes  
 U1OTGCONbits.DPPULUP = 0;  
 U1OTGCONbits.DMPULUP = 0;  
 U1IRbits.ATTACHIF = 0; //setup and enable attach interrupt  
 U1IEbits.ATTACHIE = 1;  
 IEC5bits.USB1IE = 1;  
 U1EP0 = 0x4d; //EP0 Setup for bidirectional control transfers  
 U1CNFG1 = 0x00; // PING-PONG disabled  
 U1BDTP1 = (uint16_t) (&BDT) >> 8; //set BDT upper byte  
 BDT[1] = (uint16_t) & ep0RxBuffer; //point first BDT entry, second byte, to RX buffer adress  
 BDT[3] = (uint16_t) & ep0TxBuffer;//point second BDT entry, second byte, to RX buffer adress  
 after attach interrupt is thrown:  
 wait 100ms  
 if device is low speed:  
           U1ADDRbits.LSPDEN = 1;//module operates at low speed  
           U1EP0bits.LSPD = 1;  
           U1ADDRbits.LSPDEN = 0; //module operates at full speed  
           U1EP0bits.LSPD = 0;  
 U1CONbits.USBRST = 1; //reset device  
 U1CONbits.SOFEN = 1; //enable start of frame generation  
 wait 60ms  
 U1CONbits.USBRST = 0;//release reset  
 wait 10ms  
 ep0TxBuffer[0] = 0b0000 0110 1000 0000; //GET_DESCRIPTOR  
 ep0TxBuffer[1] = 0x01; //DEVICE  
 ep0TxBuffer[2] = 0x00; //NO LANGUAGE ID  
 ep0TxBuffer[3] = 0x40; //DESCRIPTOR LENGTH  
 U1ADDR = 0x00; //device adress, 0 until enumeration is finsished  
 BDT[2] = 0x8008; //Byte count=8 and BD1STAT handed over to USB module  
 U1TOK = 0xd0; //send setup token  

UART output functions

Also, I have done quite a bit of work in functions to output important data on the UART. This way I can program the PIC with the PICKIT 2 tool and use the same tool for reading the UART output. Even more useful than the debug tool available when using the PICKIT 3. Here are the functions:

 //*******Function timeStamp**********************  
 //Returns a string that begins with the hex value of the  
 //10ms system tick followed by bracket and space: "0xabcd> "  
 char* timeStamp() {  
   extern volatile uint16_t ms10Tick;  
   static char hexChars[9];  
   char* tempChars;  
   uint16_t i;  
   hexChars[0] = '0';  
   hexChars[1] = 'x';  
   hexChars[6] = '>';  
   hexChars[7] = 0x20; //space  
   hexChars[8] = 0;  
   tempChars = uint16_t2String(ms10Tick);  
   for (i = 0; i < 4; i++) {  
     hexChars[i + 2] = tempChars[i];  
   return hexChars;  
 //*******Function string2UARTTXQueue**********************  
 //Takes a c-string and sends it to the UART transmit Queue  
 void string2UARTTXQueue(char str[]) {  
   uint16_t i = 0;  
   while (str[i] != 0) {  
 //*******Function popUARTTXQueue**********************  
 //Returns next value in UART transmit Queue and  
 //sets queue values apropriately  
 char popUARTTXQueue() {  
   char UARTVal;  
   extern volatile uint16_t UARTTXQueueTail, UARTTXQueueHead, UARTTXQueueLength;  
   extern volatile char UARTTXQueue[UART_TXQueue_SIZE];  
   if (UARTTXQueueLength == 0) {  
     return 0;  
   UARTTXQueueTail = (UARTTXQueueTail + 1) & (UART_TXQueue_SIZE - 1);  
   UARTVal = UARTTXQueue[UARTTXQueueTail];  
   return UARTVal;  
 //*******Function pushUARTTXQueue**********************  
 //Puts a char to the UART transmit Queue and  
 //sets queue values apropriately  
 void pushUARTTXQueue(char UARTVal) {  
   extern volatile uint16_t UARTTXQueueTail, UARTTXQueueHead, UARTTXQueueLength;  
   extern volatile char UARTTXQueue[UART_TXQueue_SIZE];  
   if (UARTTXQueueLength == (UART_TXQueue_SIZE - 1)) {  
   UARTTXQueueHead = (UARTTXQueueHead + 1) & (UART_TXQueue_SIZE - 1);  
   UARTTXQueue[UARTTXQueueHead] = UARTVal;  
 //*******Function uint16_t2String**********************  
 //converts a uint16_t to a string in hex format without 0x  
 char* uint16_t2String(uint16_t buffer) {  
   static char hexChars[5];  
   const uint16_t asciiTable[16] = {0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66};  
   uint16_t i;  
   for (i = 4; i > 0; i--) {  
     hexChars[i - 1] = (char) asciiTable[((0xf000 >> 4 * (i - 1)) & buffer) >> 4 * (4 - i)];  
   hexChars[4] = 0;  
   return hexChars;  
 void dumpRegisters() {  
   string2UARTTXQueue("Selected register dump:\r\n");  
   string2UARTTXQueue("U1CON  = 0x");  
   string2UARTTXQueue("U1ADDR = 0x");  
   string2UARTTXQueue("U1TOK  = 0x");  
   string2UARTTXQueue("U1BDTP1 = 0x");  


No comments:

Post a Comment