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;
}