Monday, November 24, 2014

PIC24FJ64GB004, first successful USB data transfer

I just got my first successful data transfer through to my PIC.The first transfer in a enumeration process is the "get device descriptor" transfer, which consists of three transfers, setup, in and out transfer. Here is the UART output of the transfer:

***************************

Welcome to MIDI2VC+
Version 0.15
Build date Nov 24 2014

***************************
0x01bf> USB device attached.
0x01bf> Waiting for device power stabilization
0x01ca> Device power stabilized
0x01ca> Full speed device found
0x01ca> Device reset initiation
0x01d3> Device reset complete
0x01d3> Setup token sent
0x01d3> Setup token transfer complete
0x01d3> Handshake was: 0002
0x01d3> In token sent
0x01d3> In token transfer complete
0x01d3> Handshake was: 000b

***************************
Selected register dump:
***************************
0x01d3> U1CON   = 0x0089
0x01d3> U1ADDR  = 0x0000
0x01d3> U1TOK   = 0x0090
0x01d3> U1BDTP1 = 0x0012
0x01d3> U1EP0   = 0x000d
0x01d3> U1CNFG1 = 0x0000
0x01d3> BDT[0]  = 0x6c12
0x01d3> BDT[1]  = 0x0800
0x01d3> BDT[2]  = 0x0808
0x01d3> BDT[3]  = 0x0840
0x01d3> &BDT[0] = 0x1200
0x01d3> &ep0TxBuffer[0] = 0x0840
0x01d3> ep0TxBuffer[0] = 0x0680
0x01d3> &ep0RxBuffer[0] = 0x0800
0x01d3> ep0RxBuffer[0] = 0x0112
0x01d3> U1IR = 0x0044
0x01d3> U1STAT = 0x0000
***************************
***************************


***************************
ep0RxBuffer dump:
***************************
0x01d3> ep0RxBuffer = 0x0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0100 0201 0150 0121 0944 4000 0000 0110 0112 
***************************
***************************

0x01d3> Out token sent
0x01d3> Out token transfer complete
0x01d3> Handshake was: 000e

Now, everything is not great here. The last transfer has an 0xe handshake, which is a stall handshake. This can't be right since I have disabled stalling. The second thing that is fishy is the content of the RxBuffer. I analyzed the USB data transfer of my MicroKey keyboard, by using  Device Monitoring Studio (demo version). Here I can see that the transferred data should actually be:

12 01 10 01 00 00 00 40 44 09 21 01 50 01 01 02 00 01

As you can see, bytes are in the wrong order. I'll look into these issues next...
I'm really happy for this road mark, it means that I get the hardware and it's working! 

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:

 [setup]  
   
 [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.DMPULDWN = 1;  
 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  
   
   
   
 [enumeration]  
 after attach interrupt is thrown:  
 wait 100ms  
   
 if device is low speed:  
           U1ADDRbits.LSPDEN = 1;//module operates at low speed  
           U1EP0bits.LSPD = 1;  
 else:  
           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  
   
 //send GET_DEVICE_DESCRIPTOR  
 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) {  
   
     pushUARTTXQueue(str[i]);  
     i++;  
   }  
   return;  
 }  
   
   
   
 //*********************************************************  
 //*******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];  
   UARTTXQueueLength--;  
   
   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)) {  
     return;  
   }  
   
   UARTTXQueueHead = (UARTTXQueueHead + 1) & (UART_TXQueue_SIZE - 1);  
   UARTTXQueue[UARTTXQueueHead] = UARTVal;  
   UARTTXQueueLength++;  
   
   return;  
 }  
   
 //*********************************************************  
 //*******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("\r\n***************************\r\n");  
   string2UARTTXQueue("Selected register dump:\r\n");  
   string2UARTTXQueue("***************************\r\n");  
   
   string2UARTTXQueue(timeStamp());  
   string2UARTTXQueue("U1CON  = 0x");  
   string2UARTTXQueue(uint16_t2String(U1CON));  
   string2UARTTXQueue("\r\n");  
    
   string2UARTTXQueue(timeStamp());  
   string2UARTTXQueue("U1ADDR = 0x");  
   string2UARTTXQueue(uint16_t2String(U1ADDR));  
   string2UARTTXQueue("\r\n");  
   
   string2UARTTXQueue(timeStamp());  
   string2UARTTXQueue("U1TOK  = 0x");  
   string2UARTTXQueue(uint16_t2String(U1TOK));  
   string2UARTTXQueue("\r\n");  
   
   string2UARTTXQueue(timeStamp());  
   string2UARTTXQueue("U1BDTP1 = 0x");  
   string2UARTTXQueue(uint16_t2String(U1BDTP1));  
   string2UARTTXQueue("\r\n");  

   
   string2UARTTXQueue("***************************\r\n");  
   string2UARTTXQueue("***************************\r\n\r\n");  
   return;  
 }