Difference between revisions of "Teensy 4.1 Computer"
(Add USB image but I gtg meet someone now byeeeeeeeee) |
(Add documentation for the USB circuit) |
||
Line 78: | Line 78: | ||
[[File:Teensy4.1_computer_USB_schematic.png|thumb|Teensy 4.1 Computer USB circuit]] |
[[File:Teensy4.1_computer_USB_schematic.png|thumb|Teensy 4.1 Computer USB circuit]] |
||
+ | |||
+ | The Teensy 4.1's builtin USB port is connected directly to a TUSB2046 IC, which is a USB hub and provides four downstream ports. Each port has overcurrent protection, with a maximum allowed current of 1.5A. There are also TVS diodes on all USB data lines on the downstream ports, provided by three TPD3E001DRLR ICs. Other than that, each downstream USB port has two 15k pulldowns - one on each data line. |
||
+ | |||
+ | '''Example Code:''' |
||
+ | |||
+ | The best example is found in the Mouse_KeyboardBT example from the USBHost_t36 library, which is included in the TeensyDuino package. For convenience, I have included a stripped down version of it below: |
||
+ | <pre> |
||
+ | #include "USBHost_t36.h" |
||
+ | |||
+ | USBHost myusb; |
||
+ | USBHub hub1(myusb); |
||
+ | USBHub hub2(myusb); |
||
+ | KeyboardController keyboard1(myusb); |
||
+ | KeyboardController keyboard2(myusb); |
||
+ | USBHIDParser hid1(myusb); |
||
+ | USBHIDParser hid2(myusb); |
||
+ | USBHIDParser hid3(myusb); |
||
+ | USBHIDParser hid4(myusb); |
||
+ | USBHIDParser hid5(myusb); |
||
+ | MouseController mouse1(myusb); |
||
+ | |||
+ | BluetoothController bluet(myusb); // version assumes it already was paired |
||
+ | |||
+ | USBDriver *drivers[] = {&hub1, &hub2, &keyboard1, &keyboard2, &bluet, &hid1, &hid2, &hid3, &hid4, &hid5}; |
||
+ | |||
+ | #define CNT_DEVICES (sizeof(drivers)/sizeof(drivers[0])) |
||
+ | const char * driver_names[CNT_DEVICES] = {"Hub1", "Hub2", "KB1", "KB2", "Bluet", "HID1" , "HID2", "HID3", "HID4", "HID5"}; |
||
+ | |||
+ | bool driver_active[CNT_DEVICES] = {false, false, false, false, false, false, false, false, false, false}; |
||
+ | |||
+ | // Lets also look at HID Input devices |
||
+ | USBHIDInput *hiddrivers[] = {&mouse1}; |
||
+ | |||
+ | #define CNT_HIDDEVICES (sizeof(hiddrivers)/sizeof(hiddrivers[0])) |
||
+ | const char * hid_driver_names[CNT_HIDDEVICES] = {"Mouse1"}; |
||
+ | |||
+ | bool hid_driver_active[CNT_HIDDEVICES] = {false}; |
||
+ | |||
+ | BTHIDInput *bthiddrivers[] = {&keyboard1, &mouse1}; |
||
+ | #define CNT_BTHIDDEVICES (sizeof(bthiddrivers)/sizeof(bthiddrivers[0])) |
||
+ | const char * bthid_driver_names[CNT_BTHIDDEVICES] = {"Keyboard(BT)", "Mouse(BT)"}; |
||
+ | bool bthid_driver_active[CNT_BTHIDDEVICES] = {false}; |
||
+ | |||
+ | |||
+ | |||
+ | bool show_changed_only = false; |
||
+ | |||
+ | uint8_t joystick_left_trigger_value = 0; |
||
+ | uint8_t joystick_right_trigger_value = 0; |
||
+ | uint64_t joystick_full_notify_mask = (uint64_t) - 1; |
||
+ | |||
+ | void setup() { |
||
+ | while (!Serial); // wait for Arduino Serial Monitor |
||
+ | Serial.begin(115200); |
||
+ | myusb.begin(); |
||
+ | keyboard1.attachPress(OnPress); |
||
+ | keyboard2.attachPress(OnPress); |
||
+ | } |
||
+ | |||
+ | |||
+ | void loop() { |
||
+ | // check to see if the device list has changed: |
||
+ | UpdateActiveDeviceInfo(); |
||
+ | |||
+ | myusb.Task(); |
||
+ | |||
+ | if (mouse1.available()) { |
||
+ | Serial.print("Mouse: buttons = "); |
||
+ | Serial.print(mouse1.getButtons()); |
||
+ | Serial.print(", mouseX = "); |
||
+ | Serial.print(mouse1.getMouseX()); |
||
+ | Serial.print(", mouseY = "); |
||
+ | Serial.print(mouse1.getMouseY()); |
||
+ | Serial.print(", wheel = "); |
||
+ | Serial.print(mouse1.getWheel()); |
||
+ | Serial.print(", wheelH = "); |
||
+ | Serial.print(mouse1.getWheelH()); |
||
+ | Serial.println(); |
||
+ | mouse1.mouseDataClear(); |
||
+ | } |
||
+ | } |
||
+ | |||
+ | void OnPress(int key) |
||
+ | { |
||
+ | Serial.print("key '"); |
||
+ | switch (key) { |
||
+ | case KEYD_UP : Serial.print("UP"); break; |
||
+ | case KEYD_DOWN : Serial.print("DN"); break; |
||
+ | case KEYD_LEFT : Serial.print("LEFT"); break; |
||
+ | case KEYD_RIGHT : Serial.print("RIGHT"); break; |
||
+ | case KEYD_INSERT : Serial.print("Ins"); break; |
||
+ | case KEYD_DELETE : Serial.print("Del"); break; |
||
+ | case KEYD_PAGE_UP : Serial.print("PUP"); break; |
||
+ | case KEYD_PAGE_DOWN: Serial.print("PDN"); break; |
||
+ | case KEYD_HOME : Serial.print("HOME"); break; |
||
+ | case KEYD_END : Serial.print("END"); break; |
||
+ | case KEYD_F1 : Serial.print("F1"); break; |
||
+ | case KEYD_F2 : Serial.print("F2"); break; |
||
+ | case KEYD_F3 : Serial.print("F3"); break; |
||
+ | case KEYD_F4 : Serial.print("F4"); break; |
||
+ | case KEYD_F5 : Serial.print("F5"); break; |
||
+ | case KEYD_F6 : Serial.print("F6"); break; |
||
+ | case KEYD_F7 : Serial.print("F7"); break; |
||
+ | case KEYD_F8 : Serial.print("F8"); break; |
||
+ | case KEYD_F9 : Serial.print("F9"); break; |
||
+ | case KEYD_F10 : Serial.print("F10"); break; |
||
+ | case KEYD_F11 : Serial.print("F11"); break; |
||
+ | case KEYD_F12 : Serial.print("F12"); break; |
||
+ | default: Serial.print((char)key); break; |
||
+ | } |
||
+ | Serial.print("' "); |
||
+ | Serial.println(key); |
||
+ | } |
||
+ | |||
+ | //============================================================================= |
||
+ | // UpdateActiveDeviceInfo |
||
+ | //============================================================================= |
||
+ | void UpdateActiveDeviceInfo() { |
||
+ | 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); |
||
+ | |||
+ | if (drivers[i] == &bluet) { |
||
+ | const uint8_t *bdaddr = bluet.myBDAddr(); |
||
+ | // remember it... |
||
+ | Serial.printf(" BDADDR: %x:%x:%x:%x:%x:%x\n", bdaddr[0], bdaddr[1], bdaddr[2], bdaddr[3], bdaddr[4], bdaddr[5]); |
||
+ | } |
||
+ | } |
||
+ | } |
||
+ | } |
||
+ | 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); |
||
+ | } |
||
+ | } |
||
+ | } |
||
+ | // Then Bluetooth devices |
||
+ | for (uint8_t i = 0; i < CNT_BTHIDDEVICES; i++) { |
||
+ | if (*bthiddrivers[i] != bthid_driver_active[i]) { |
||
+ | if (bthid_driver_active[i]) { |
||
+ | Serial.printf("*** BTHID Device %s - disconnected ***\n", bthid_driver_names[i]); |
||
+ | bthid_driver_active[i] = false; |
||
+ | } else { |
||
+ | Serial.printf("*** BTHID Device %s - connected ***\n", bthid_driver_names[i]); Serial.flush(); |
||
+ | bthid_driver_active[i] = true; |
||
+ | #if 0 |
||
+ | |||
+ | const uint8_t *psz = bthiddrivers[i]->manufacturer(); |
||
+ | if (psz && *psz) Serial.printf(" manufacturer: %s\n", psz); |
||
+ | psz = bthiddrivers[i]->product(); |
||
+ | if (psz && *psz) Serial.printf(" product: %s\n", psz); |
||
+ | psz = bthiddrivers[i]->serialNumber(); |
||
+ | if (psz && *psz) Serial.printf(" Serial: %s\n", psz); |
||
+ | #endif |
||
+ | } |
||
+ | } |
||
+ | } |
||
+ | } |
||
+ | </pre> |
||
+ | |||
+ | '''Known Issues:''' |
||
+ | * Version 1.1 of the board leaves TUSB2046's reset line floating. This can be remedied by connecting the TUSB2046's reset line to some other circuit, although this is error prone. |
Revision as of 19:59, 13 June 2024
The Teensy 4.1 Computer is more or less described by its name: Its a carrier board for the Teensy 4.1 which provides four USB ports, an Ethernet port, Hi-Fi audio, and several convenient expansion ports.
Detailed Specifications
USB:
- TUSB2046 USB Hub, providing 4 USB A ports
- Each port has a maximum of 1.5A output - the port will shutdown due to overcurrent above this
- Board power provides a maximum of 2A to the 5v supply
- one should be aware of this when considering the power used by USB devices
Audio:
- I2S control connected to Teensy I2S 2
- PCM5102 ADC for providing stereo sound
- OPA1688 for the amplifier
- 3.5mm audio jack, maximum 75mA per channel
- Separate ground, connected to main ground through a ferrite bead
- Separate 3.3v and 5v supplies from linear regulators to reduce noise
- Note: 5v line will come from main 5v rail if only powered from USB
Power:
- 2A 5v buck regulator (Exact part is the VR20S05)
- PMOS switches to prevent regulator backfeeding from USB and to control power flow (U16, U17, and U18 on the PCB)
- External VIN accepted from 8v to 20v
- Separate 5v and 3.3v supplies for audio circutry
- Barrel jack and screw terminal connections for VIN
Expansion Ports
- I2S, SPI, I2C, and Serial expansion ports
- 5v-capable 8-bit parallel bus port, with 3-bit address, WR, RD, select, and WAIT lines
- Two larger expansion ports, each providing a 3.3v compatible 8-bit parallel bus, various protocols like I2C and SPI, power, and GPIO
The Audio Circuit
The Teensy 4.1 is connected to the audio circuit via the Teensy's I2S 2 port. This connection goes to the PCM5102 ADC. As is recommended in the PCM5102's datasheet, filtering is added to the output, however the filter used on this board is a bit stronger, using a 4.7nF capacitor instead of the 2.2nF capacitor recommended in the datasheet. The filter cutoff is still far above human perception, at 72kHz, and should filter high frequency noise a bit better. The connection to the OPA1688 is DC, not AC, coupled, but this is according to the recommendations in the OPA1688 datasheet. The gain of the amplifier is configured to be 1, so the output voltage of the amp should match the ADC's output voltage, and be in the range of 0v to 3.3v. A voltage divider is used to activate the PCM5102's soft mute function when the 5v line drops, which is designed to prevent any pops in the audio when the power is disconnected.
The circuit was designed with audio quality, but not energy efficiency, in mind. The circuit may sink as much as 400mA from VIN, and an additional 40mA from the main 5v line. However, when the audio circuit is idle, it should be less than 50mA all together. The 3.5mm audio jack can provide a maximum of 75mA per channel.
Example Code:
#include <Audio.h> #include <SD.h> // GUItool: begin automatically generated code AudioPlaySdWav playSdWav1; //xy=719,389 AudioOutputI2S2 i2s2_1; //xy=892,388 AudioConnection patchCord1(playSdWav1, 0, i2s2_1, 0); AudioConnection patchCord2(playSdWav1, 1, i2s2_1, 1); // GUItool: end automatically generated code void setup() { Serial.begin(115200); AudioMemory(10); if (!(SD.begin(BUILTIN_SDCARD))) { // stop here, but print a message repetitively while (1) { Serial.println("Unable to access the SD card"); delay(500); } } Serial.println("Playing audio.wav..."); playSdWav1.play("audio.wav"); } void loop() { // Do nothing forever }
USB Ports
The Teensy 4.1's builtin USB port is connected directly to a TUSB2046 IC, which is a USB hub and provides four downstream ports. Each port has overcurrent protection, with a maximum allowed current of 1.5A. There are also TVS diodes on all USB data lines on the downstream ports, provided by three TPD3E001DRLR ICs. Other than that, each downstream USB port has two 15k pulldowns - one on each data line.
Example Code:
The best example is found in the Mouse_KeyboardBT example from the USBHost_t36 library, which is included in the TeensyDuino package. For convenience, I have included a stripped down version of it below:
#include "USBHost_t36.h" USBHost myusb; USBHub hub1(myusb); USBHub hub2(myusb); KeyboardController keyboard1(myusb); KeyboardController keyboard2(myusb); USBHIDParser hid1(myusb); USBHIDParser hid2(myusb); USBHIDParser hid3(myusb); USBHIDParser hid4(myusb); USBHIDParser hid5(myusb); MouseController mouse1(myusb); BluetoothController bluet(myusb); // version assumes it already was paired USBDriver *drivers[] = {&hub1, &hub2, &keyboard1, &keyboard2, &bluet, &hid1, &hid2, &hid3, &hid4, &hid5}; #define CNT_DEVICES (sizeof(drivers)/sizeof(drivers[0])) const char * driver_names[CNT_DEVICES] = {"Hub1", "Hub2", "KB1", "KB2", "Bluet", "HID1" , "HID2", "HID3", "HID4", "HID5"}; bool driver_active[CNT_DEVICES] = {false, false, false, false, false, false, false, false, false, false}; // Lets also look at HID Input devices USBHIDInput *hiddrivers[] = {&mouse1}; #define CNT_HIDDEVICES (sizeof(hiddrivers)/sizeof(hiddrivers[0])) const char * hid_driver_names[CNT_HIDDEVICES] = {"Mouse1"}; bool hid_driver_active[CNT_HIDDEVICES] = {false}; BTHIDInput *bthiddrivers[] = {&keyboard1, &mouse1}; #define CNT_BTHIDDEVICES (sizeof(bthiddrivers)/sizeof(bthiddrivers[0])) const char * bthid_driver_names[CNT_BTHIDDEVICES] = {"Keyboard(BT)", "Mouse(BT)"}; bool bthid_driver_active[CNT_BTHIDDEVICES] = {false}; bool show_changed_only = false; uint8_t joystick_left_trigger_value = 0; uint8_t joystick_right_trigger_value = 0; uint64_t joystick_full_notify_mask = (uint64_t) - 1; void setup() { while (!Serial); // wait for Arduino Serial Monitor Serial.begin(115200); myusb.begin(); keyboard1.attachPress(OnPress); keyboard2.attachPress(OnPress); } void loop() { // check to see if the device list has changed: UpdateActiveDeviceInfo(); myusb.Task(); if (mouse1.available()) { Serial.print("Mouse: buttons = "); Serial.print(mouse1.getButtons()); Serial.print(", mouseX = "); Serial.print(mouse1.getMouseX()); Serial.print(", mouseY = "); Serial.print(mouse1.getMouseY()); Serial.print(", wheel = "); Serial.print(mouse1.getWheel()); Serial.print(", wheelH = "); Serial.print(mouse1.getWheelH()); Serial.println(); mouse1.mouseDataClear(); } } void OnPress(int key) { Serial.print("key '"); switch (key) { case KEYD_UP : Serial.print("UP"); break; case KEYD_DOWN : Serial.print("DN"); break; case KEYD_LEFT : Serial.print("LEFT"); break; case KEYD_RIGHT : Serial.print("RIGHT"); break; case KEYD_INSERT : Serial.print("Ins"); break; case KEYD_DELETE : Serial.print("Del"); break; case KEYD_PAGE_UP : Serial.print("PUP"); break; case KEYD_PAGE_DOWN: Serial.print("PDN"); break; case KEYD_HOME : Serial.print("HOME"); break; case KEYD_END : Serial.print("END"); break; case KEYD_F1 : Serial.print("F1"); break; case KEYD_F2 : Serial.print("F2"); break; case KEYD_F3 : Serial.print("F3"); break; case KEYD_F4 : Serial.print("F4"); break; case KEYD_F5 : Serial.print("F5"); break; case KEYD_F6 : Serial.print("F6"); break; case KEYD_F7 : Serial.print("F7"); break; case KEYD_F8 : Serial.print("F8"); break; case KEYD_F9 : Serial.print("F9"); break; case KEYD_F10 : Serial.print("F10"); break; case KEYD_F11 : Serial.print("F11"); break; case KEYD_F12 : Serial.print("F12"); break; default: Serial.print((char)key); break; } Serial.print("' "); Serial.println(key); } //============================================================================= // UpdateActiveDeviceInfo //============================================================================= void UpdateActiveDeviceInfo() { 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); if (drivers[i] == &bluet) { const uint8_t *bdaddr = bluet.myBDAddr(); // remember it... Serial.printf(" BDADDR: %x:%x:%x:%x:%x:%x\n", bdaddr[0], bdaddr[1], bdaddr[2], bdaddr[3], bdaddr[4], bdaddr[5]); } } } } 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); } } } // Then Bluetooth devices for (uint8_t i = 0; i < CNT_BTHIDDEVICES; i++) { if (*bthiddrivers[i] != bthid_driver_active[i]) { if (bthid_driver_active[i]) { Serial.printf("*** BTHID Device %s - disconnected ***\n", bthid_driver_names[i]); bthid_driver_active[i] = false; } else { Serial.printf("*** BTHID Device %s - connected ***\n", bthid_driver_names[i]); Serial.flush(); bthid_driver_active[i] = true; #if 0 const uint8_t *psz = bthiddrivers[i]->manufacturer(); if (psz && *psz) Serial.printf(" manufacturer: %s\n", psz); psz = bthiddrivers[i]->product(); if (psz && *psz) Serial.printf(" product: %s\n", psz); psz = bthiddrivers[i]->serialNumber(); if (psz && *psz) Serial.printf(" Serial: %s\n", psz); #endif } } } }
Known Issues:
- Version 1.1 of the board leaves TUSB2046's reset line floating. This can be remedied by connecting the TUSB2046's reset line to some other circuit, although this is error prone.