KurtE
Senior Member+
As I mentioned in the USB Mouse thread (https://forum.pjrc.com/threads/45740-USB-Host-Mouse-Driver?p=159888&viewfull=1#post159888) , I have been playing around now with adding RAW Hide support.
More details about it in the other thread. Support is still in my own fork and branch: https://github.com/KurtE/USBHost_t36/tree/RAWHID2
Question to self and Paul and ... - Wonder if we should have top level threads for the USB Host support for different types with the first message showing the status of the different USB types. Example USB Mouse - Has been tried on X, Y, Z...
It is still a WIP, but thought I would try to do something with it to get an idea of how well it worked (or not).
So I wrote a Quick and dirty app that updates an ILI9341 display (currently using my ili9341_t3n library could use _t3 library). It draws a couple of screens and then waits for data on the RAWHID object. It assumes packet size is 64, the first 4 bytes are Frame number, and packet within frame followed by 30 pixels of data... When it receives the data, it updates the display...
Current program:
I then created a USB host program using the RAW hid support, that when it receives Serial data, it draws a new page. Currently just cycles through whole color displays...
The programs Run! and the display does update. Not as fast as I would like: Note: my test apps, also actually setup second Raw Hid device that the Serial.print outputs use. My host app receives those and outputs the messages to it's Serial object...
Some output:
The frame completion time is not correct... Need to check the output, but from Logic analyzer I believe it is taking about 2.5 seconds.
To send a full frame we send: 2560 packets
From Logic Analyzer, you see that the first two raw packets queue more or less instantaneously, then the remaining ones have to wait until one completes before it can actually queue the data. Next up see if I can somehow speed it up...
Also then see if my implementation makes sense and if so to issue Pull Request.
But if anyone wants to play along...
Edit: Updated the first program to output the correct delta time
More details about it in the other thread. Support is still in my own fork and branch: https://github.com/KurtE/USBHost_t36/tree/RAWHID2
Question to self and Paul and ... - Wonder if we should have top level threads for the USB Host support for different types with the first message showing the status of the different USB types. Example USB Mouse - Has been tried on X, Y, Z...
It is still a WIP, but thought I would try to do something with it to get an idea of how well it worked (or not).
So I wrote a Quick and dirty app that updates an ILI9341 display (currently using my ili9341_t3n library could use _t3 library). It draws a couple of screens and then waits for data on the RAWHID object. It assumes packet size is 64, the first 4 bytes are Frame number, and packet within frame followed by 30 pixels of data... When it receives the data, it updates the display...
Current program:
Code:
// To use Kurt's Flexiboard uncomment the line below:
#define KURTS_FLEXI
#include <ILI9341_t3n.h>
#ifdef KURTS_FLEXI
#define TFT_DC 22
#define TFT_CS 15
#define TFT_RST -1
#define TFT_SCK 14
#define TFT_MISO 12
#define TFT_MOSI 7
#define DEBUG_PIN 13
#else
#define TFT_DC 9
#define TFT_CS 10
#define TFT_RST 7
#define TFT_SCK 13
#define TFT_MISO 12
#define TFT_MOSI 11
#endif
uint8_t buffer[64]; // Raw hid buffer.
uint16_t *pixel_data = (uint16_t*)(&buffer[4]);
uint16_t frame_number = 0; // What frame number are we on.
uint16_t frame_packet_number = 0xffff; // Which packet within a frame are we?
const uint16_t packets_per_frame = 320 * 240 * 2 / 60; // We will use 60 data bytes per packet
elapsedMillis msFrameTimeout;
elapsedMillis msFrame;
#define FRAME_TIMEOUT 10
ILI9341_t3n tft = ILI9341_t3n(TFT_CS, TFT_DC, TFT_RST, TFT_MOSI, TFT_SCK, TFT_MISO);
// Size of the color selection boxes and the paintbrush size
void setup(void) {
pinMode(0, OUTPUT);
digitalWrite(0, LOW);
while (!Serial && (millis() <= 1000));
Serial.begin(9600);
Serial.println(F("RawHID remote display"));
tft.begin();
tft.setRotation(1);
tft.fillScreen(ILI9341_RED);
delay(250);
tft.fillScreen(ILI9341_GREEN);
delay(250);
tft.fillScreen(ILI9341_BLACK);
delay(250);
tft.drawRect(0, 0, tft.width(), tft.height(), ILI9341_BLUE);
tft.setCursor(10, tft.height() / 2);
tft.setTextColor(ILI9341_WHITE);
tft.print("Display Ready!");
Serial.println("Remote display ready");
}
void loop()
{
int n = RawHID.recv(buffer, 0); // 0 timeout = do not wait
if (n > 0) {
digitalWrite(0, HIGH);
// Lets extract the frame and packet numbers
uint16_t buffer_frame_number = buffer[0] + (uint16_t)(buffer[1] << 8);
uint16_t buffer_frame_packet_number = buffer[2] + (uint16_t)(buffer[3] << 8);
if (buffer_frame_number != frame_number) {
frame_number = buffer_frame_number;
Serial.printf("New frame: %d %d\n", frame_number, buffer_frame_packet_number);
msFrame = 0;
} else {
if (buffer_frame_packet_number != (frame_packet_number + 1)) {
Serial.printf("Sequence error: %d != %d+1\n", buffer_frame_packet_number, frame_packet_number);
}
}
frame_packet_number = buffer_frame_packet_number;
// first pass, we will take the data sent to us and convert the packet number into starting
// X, Y positions.
uint32_t starting_pixel_number = buffer_frame_packet_number * 30;
uint16_t y = starting_pixel_number / tft.width();
uint16_t x = starting_pixel_number % tft.width();
// Now see if we can draw fits on the same row
if ((x + 30) <= tft.width()) {
tft.writeRect(x, y, 30, 1, pixel_data);
} else {
uint16_t w = tft.width() - x;
tft.writeRect(x, y, w, 1, pixel_data); // draw part on the calculated line
tft.writeRect(0, y + 1, 30 - w, 1, &pixel_data[w]); // draw rest on next line
}
// See if that was the last expected packet
if (frame_packet_number == (packets_per_frame - 1)) {
uint32_t frame_time = msFrame;
Serial.printf("Frame Completed in %u ms\n", frame_time);
frame_packet_number = 0xffff;
}
msFrameTimeout = 0;
digitalWrite(0, LOW);
} else if ((frame_packet_number != 0xffff) && (msFrameTimeout > FRAME_TIMEOUT)) {
Serial.printf("Frame Timeout: %d %d\n", frame_number, frame_packet_number);
frame_packet_number = 0xffff; // only error once.
}
}
I then created a USB host program using the RAW hid support, that when it receives Serial data, it draws a new page. Currently just cycles through whole color displays...
Code:
// Simple test of USB Host Mouse/Keyboard
//
// This example is in the public domain
#include "USBHost_t36.h"
USBHost myusb;
USBHub hub1(myusb);
USBHIDParser hid1(myusb);
USBHIDParser hid2(myusb);
USBHIDParser hid3(myusb);
RawHIDController rawhid1(myusb);
RawHIDController rawhid2(myusb, 0xffc90004);
USBDriver *drivers[] = {&hub1, &hid1, &hid2, &hid3,};
#define CNT_DEVICES (sizeof(drivers)/sizeof(drivers[0]))
const char * driver_names[CNT_DEVICES] = {"Hub1", "HID1", "HID2",};
bool driver_active[CNT_DEVICES] = {false, false};
// Lets also look at HID Input devices
USBHIDInput *hiddrivers[] = { &rawhid1, &rawhid2};
#define CNT_HIDDEVICES (sizeof(hiddrivers)/sizeof(hiddrivers[0]))
const char * hid_driver_names[CNT_DEVICES] = {"RawHid1", "RawHid2"};
bool hid_driver_active[CNT_DEVICES] = {false, false};
void setup()
{
pinMode(0, OUTPUT);
digitalWrite(0, LOW);
while (!Serial) ; // wait for Arduino Serial Monitor
Serial.println("\n\nUSB Host Testing");
Serial.println(sizeof(USBHub), DEC);
myusb.begin();
rawhid1.attachReceive(OnReceiveHidData);
rawhid2.attachReceive(OnReceiveHidData);
}
void loop()
{
myusb.Task();
CheckUSBDeviceStatus();
// If we get input from user, then lets try updating remote display
if (Serial.available()) {
while (Serial.read() != -1) ; // get rid of the input data.
UpdateRAWHidDisplay();
}
}
// Color definitions
#define ILI9341_BLACK 0x0000 /* 0, 0, 0 */
#define ILI9341_WHITE 0xFFFF /* 255, 255, 255 */
#define ILI9341_RED 0xF800 /* 255, 0, 0 */
#define ILI9341_GREEN 0x07E0 /* 0, 255, 0 */
#define ILI9341_BLUE 0x001F /* 0, 0, 255 */
#define ILI9341_YELLOW 0xFFE0 /* 255, 255, 0 */
#define ILI9341_ORANGE 0xFD20 /* 255, 165, 0 */
#define ILI9341_LIGHTGREY 0xC618 /* 192, 192, 192 */
uint16_t frame_number = 0;
uint16_t colors[] = {ILI9341_RED, ILI9341_GREEN, ILI9341_BLUE, ILI9341_BLACK, ILI9341_WHITE, ILI9341_BLUE, ILI9341_YELLOW, ILI9341_ORANGE};
// first pass simple just full colors.
uint16_t buffer[32]; // 64 byte buffer.
#define PACKET_TIMEOUT 10
void UpdateRAWHidDisplay() {
frame_number++;
elapsedMillis msPacket;
uint16_t color = colors[frame_number & 0x7]; // choose a color
buffer[0] = frame_number;
for (uint8_t i = 2; i < (sizeof(buffer)/sizeof(buffer[0])); i++) {
buffer[i] = color;
}
bool packet_sent;
for (buffer[1] = 0; buffer[1] < (320*240/30); buffer[1]++) {
// Let try to output this buffer to the Raw Hid person...
msPacket = 0;
digitalWrite(0, HIGH);
while (!(packet_sent = rawhid1.sendPacket((uint8_t*)buffer)) && msPacket < (PACKET_TIMEOUT)) ;
digitalWrite(0, LOW);
}
if (!packet_sent) {
Serial.printf("Packet Timeout: %d %d\n", buffer[0], buffer[1]);
}
}
bool OnReceiveHidData(uint32_t usage, const uint8_t *data, uint32_t len) {
// Called for maybe both HIDS for rawhid basic test. One is for the Teensy
// to output to Serial. while still having Raw Hid...
if (usage == 0xffc90004) {
// Lets trim off trailing null characters.
while ((len > 0) && (data[len - 1] == 0)) {
len--;
}
if (len) {
Serial.print("RawHid Serial: ");
Serial.write(data, len);
}
} else {
Serial.print("RawHID data: ");
Serial.println(usage, HEX);
while (len) {
uint8_t cb = (len > 16) ? 16 : len;
const uint8_t *p = data;
uint8_t i;
for (i = 0; i < cb; i++) {
Serial.printf("%02x ", *p++);
}
Serial.print(": ");
for (i = 0; i < cb; i++) {
Serial.write(((*data >= ' ') && (*data <= '~')) ? *data : '.');
data++;
}
len -= cb;
Serial.println();
}
}
return true;
}
void CheckUSBDeviceStatus() {
for (uint8_t i = 0; i < CNT_DEVICES; i++) {
if (*drivers[i] != driver_active[i]) {
if (driver_active[i]) {
Serial.printf("*** Device %s - disconnected ***\n", driver_names[i]);
driver_active[i] = false;
} else {
Serial.printf("*** Device %s %x:%x - connected ***\n", driver_names[i], drivers[i]->idVendor(), drivers[i]->idProduct());
driver_active[i] = true;
const uint8_t *psz = drivers[i]->manufacturer();
if (psz && *psz) Serial.printf(" manufacturer: %s\n", psz);
psz = drivers[i]->product();
if (psz && *psz) Serial.printf(" product: %s\n", psz);
psz = drivers[i]->serialNumber();
if (psz && *psz) Serial.printf(" Serial: %s\n", psz);
}
}
}
for (uint8_t i = 0; i < CNT_HIDDEVICES; i++) {
if (*hiddrivers[i] != hid_driver_active[i]) {
if (hid_driver_active[i]) {
Serial.printf("*** HID Device %s - disconnected ***\n", hid_driver_names[i]);
hid_driver_active[i] = false;
} else {
Serial.printf("*** HID Device %s %x:%x - connected ***\n", hid_driver_names[i], hiddrivers[i]->idVendor(), hiddrivers[i]->idProduct());
hid_driver_active[i] = true;
const uint8_t *psz = hiddrivers[i]->manufacturer();
if (psz && *psz) Serial.printf(" manufacturer: %s\n", psz);
psz = hiddrivers[i]->product();
if (psz && *psz) Serial.printf(" product: %s\n", psz);
psz = hiddrivers[i]->serialNumber();
if (psz && *psz) Serial.printf(" Serial: %s\n", psz);
}
}
}
}
The programs Run! and the display does update. Not as fast as I would like: Note: my test apps, also actually setup second Raw Hid device that the Serial.print outputs use. My host app receives those and outputs the messages to it's Serial object...
Some output:
Code:
*** HID Device RawHid2 16c0:486 - connected ***
manufacturer: Teensyduino
product: Teensyduino RawHID
Serial: 2416110
RawHid Serial: New frame: 1 0
RawHid Serial: Frame Completed in 536810320 ms
New frame: 2 0
RawHid Serial: Frame Completed in 536810320 ms
New frame: 3 0
RawHid Serial: Frame Completed in 536810320 ms
RawHid Serial: New frame: 4 0
RawHid Serial: Frame Completed in 536810320 ms
The frame completion time is not correct... Need to check the output, but from Logic analyzer I believe it is taking about 2.5 seconds.
To send a full frame we send: 2560 packets
From Logic Analyzer, you see that the first two raw packets queue more or less instantaneously, then the remaining ones have to wait until one completes before it can actually queue the data. Next up see if I can somehow speed it up...
Also then see if my implementation makes sense and if so to issue Pull Request.
But if anyone wants to play along...
Edit: Updated the first program to output the correct delta time
Last edited: