Electronic – Manually communicate on an USB bus

microcontrollersignal processingusbusb-host

I'm using an MBED LPC1768 as a UC and I'm trying to communicate with a USB Device (a random hub) just to read the device descriptor.

I'm quite a novice in electronics.

I've connected + and – to UC 5V and GND, and D+ / D- to standards Digital/IO pins. I've also hook them up with 10K resistors to ground.
When I plug in the device, I can see D+ going up, so it means this is a high speed device.

I've try to send reset (for 20ms), then some manually encoded frames, but after sending two frames (sync / data / eop), and changing the IO pins to "input", nothings happens: bus stays in idle state (D+ up, D- down).

Here is my code:

#include "mbed.h"
#include "crc.h"

Serial pc(USBTX, USBRX);
DigitalInOut dplus(p21);
DigitalInOut dminus(p22);

void sendBits(int bits, int length);
void waitNbit(int n);
void sendPacket();
void sendEOP();
void sendReset();
void sendSync();
void setJ();
void setK();
void sendCRC(uint8_t crc);

int lastSentK; // For NRZI
int sentOneCounter; // For bit stuffing

void send0()
{
    sentOneCounter = 0;
    if (lastSentK) setJ();
    else setK();
    lastSentK = !lastSentK;
}

void send1()
{
    sentOneCounter++;
    waitNbit(1);
    if (sentOneCounter == 6) // Bit stuffing
    {
        send0();
    }
}

void sendCRC16(uint16_t crc)
{
    // send msb first
    for (int i = 0; i < 16; i++)
    {
        if (crc & (1 << (16 - i))) send1();
        else send0();
    }
}

void sendCRC(uint8_t crc)
{
    // send msb first
    for (int i = 0; i < 5; i++)
    {
        if (crc & (1 << (4 - i))) send1();
        else send0();
    }
}

void sendBytes(uint8_t* bytes, int length)
{
    // Send lsb first
    for (int i = 0; i < length; i++)
    {
        //printf("Sending %02X\r\n", bytes[i]);
        sendBits(bytes[i], 8);
    }
}

void sendBits(int bits, int length)
{
    // Send lsb first
    for (int i = 0; i < length; i++)
    {
        if (bits & 0x01) send1();
        else send0();
        bits = bits >> 1;
    }
}

void waitNbit(int n)
{
    wait_ns(n * 83); // 83 ns per byte (12MHz)
}

void sendEOP()
{
    dplus = 0;
    dminus = 0;
    waitNbit(2);
    setJ();
}

void sendReset()
{
    dplus = 0;
    dminus = 0;
    wait_us(20 * 1000); // > 10ms
}

void sendSync()
{
    // KJKJKJKK
    setK();
    setJ();
    setK();
    setJ();
    setK();
    setJ();
    setK();
    setK();
    lastSentK = 1;
}

void setJ()
{
    dplus = 1;
    dminus = 0;
    waitNbit(1);
}

void setK()
{
    dplus = 0;
    dminus = 1;
    waitNbit(1);
}

void sendTokenPacket()
{
    sendSync();
    //Send packet data

    sendBits(0b00101101, 8); // SETUP PID
    sendBits(0, 7); // Address
    sendBits(0, 4); // Endpoint
    sendCRC(0x08);

    sendEOP();
}

void sendSetupDataPacket()
{
    sendSync();
    //Send packet data

    uint8_t data[] = {
        0x80, // request type (direction=device to host?? type=standard recipient=device)
        0x06, // bRequest GET_DESCRIPTOR
        0x00, // Descriptor index
        0x01, // Descriptor type (DEVICE)
        0x00, // Language id
        0x00, // Language id
        0x12, // Length (allow device to send 18 bytes)
        0x00  // ?
    };
    uint16_t crc = compute_crc16(data, sizeof(data));

    uint8_t finalData[20];

    finalData[0] = 0b11000011; // Data0 pid
    memcpy(finalData + 1, data, sizeof(data));

    sendBytes(finalData, sizeof(data) + 1); // Data
    sendCRC16(crc);

    sendEOP();
}

void listen()
{
    dplus.input();
    dminus.input();
    uint8_t old_dplus = dplus.read();
    uint8_t old_dminus = dminus.read();
    //pc.printf("Status (%x ; %x).\r\n", old_dminus, old_dplus);
    while (true)
    {
        if (dminus.read() != old_dminus || dplus.read() != old_dplus)
        {
            pc.printf("Change (%x->%x %x->%x).\r\n", old_dminus, dminus.read(), old_dplus, dplus.read());
            old_dminus = dminus.read();
            old_dplus = dplus.read();
        }
        waitNbit(1);
    }
}

int main() {
    dplus.input();
    dminus.input();
    pc.printf("Started\r\n");

    while (true)
    {
        wait_us(1000 * 500); //0.5s

        if (dplus.read() == 1) // Pulled high by full speed device
        {
            dplus.output();
            dminus.output();
            sendReset();
            sendTokenPacket();
            sendSetupDataPacket();
            listen();
            break;
        }
    }
}

What could be off, and how can I debug that?

Best Answer

I've try to send reset (for 20ms), then some manually encoded frames, but after sending two frames (sync / data / eop), and changing the IO pins to "input", nothings happens: bus stays in idle state (D+ up, D- down).

To begin, your way of "manually control" serial interface is called "bit-banging". FYI.

Then, you need to do quite a bit more than what you described.

First, you have to start a CONTINUOUS stream of frame packets on 1 ms interval, with incremented frame counter.

Second, to get any response from a USB device, you need to send a valid USB request. The first request is usually the "GET_DESCRIPTOR" from default control pipe (address 0 endpoint 0).

This would be your first challenge.

So far I have not heard of any bit-bang implementation of full-speed (12 mbps) USB protocol on ARM-level processors. There are LS implementations though.

The only successful bit-banging implementation of 12-mbps USB I know of is on Parallax/Propeller MCU, which is a symmetrical 8-core 32-bit processor running at 80 MHz, with special IO access features.

I can see D+ going up, so it means this is a high speed device.

No, it means that it could be either FS or HS device, depending on whether the device asserts Chirp-K during USB bus reset. FYI.