Is a SEREMU or CDC interface required for every Teensy USB Device?

mborgerson

Well-known member
I'm working on the development of a USB Serial Bus Test and Measurement Class (USBTMC) device as a way for a T41 Host to have one or more other Teensies as data collection devices which send their data to the host for analysis, display, and storage. The USBTMC interface is fairly simple: a control interface and one each of USB Bulk IN and Bulk OUT interfaces. I modified usb_desc.h to add a simple interface with just the minimum USBTMC interface as described.

However, on compilation of a basic blink program with the new interface, it fails with an error that indicates "Serial was not declared" and the error originates in the core Print.cpp file. If I compile with other USB interfaces, such as joystick, mouse, etc., the program compiles successfully.

If I add the USBTMC interface in place of Sertial2 in the USB Dual Serial interface, the blink program compiles properly.

I wanted to use only the USBTMC interface to simplify the descriptors required for the device, as adding a CDC interface greatly complicates the descriptors I have to sort through when enumerating and claiming the device at the host driver level.

My initial guess is that every Teensy device has to have either a SEREMU or CDC interface so that the boot loader can connect over USB.
 
What if you provide an alternative implementation of _write() that doesn't reference Serial? It's declared as weak in Print.cpp so applications can override it with their own if necessary.
 
My initial guess is that every Teensy device has to have either a SEREMU or CDC interface so that the boot loader can connect over USB.

If you create a USB type with neither of these USB interfaces, you will need to press the pushbutton on Teensy for every new code upload. Also you will get no Serial.print() to the Arduino Serial Monitor, but hopefully that's obvious.
 
If you create a USB type with neither of these USB interfaces, you will need to press the pushbutton on Teensy for every new code upload. Also you will get no Serial.print() to the Arduino Serial Monitor, but hopefully that's obvious.
I'll try overriding Print long enough to test enumeration and see how far I get with the driver.

Another question: Since there are no other devices derived from the USBTMC class, where do I define the DeviceClass and DeviceSubClass values? I searched the whole Teensy4 core folder and found lots of references to the macros, but never a place where they were assigned values except in the USB_SERIAL_HID and USB_EVERYTHING definitions in USB_desc.h and if CDC_AD_DESCRIPTOR is defined in USB_desc.c. Do those few tests cover setting the class and subclass for all other devices such as mice, Or do all others need to have class and subclass descriptors in an interface since they have the default DeviceClass set to zero?

I guess I'll have to add code to set the DeviceClass to 0xFE and the SUBCLASS to 0x03 if USBTMC is defined. I'm wondering if the USBTMC class can get by without having to set up the interfaces to have the device class and subclass--in which case the Claim() function will claim the whole device without worrying about multiple interfaces.
 
I'm confused.

Are you asking about TMC for USB device mode (where Teensy emulates test gear and connects to software on your PC like Labview) or TMC for USB host mode (where you could connect real test gear to Teensy's USB host port)?

USB device and USB host are completely different. Need to be clear which one we're talking about.
 
I'm confused.

Are you asking about TMC for USB device mode (where Teensy emulates test gear and connects to software on your PC like Labview) or TMC for USB host mode (where you could connect real test gear to Teensy's USB host port)?

USB device and USB host are completely different. Need to be clear which one we're talking about.
I'm actually asking about both. I want to set up one T4.x to be TMC device. It will collect data from various sensors and forward that data to a host. The Host T4.1 will have a USBTMC driver that will enumerate and connect to one or more USBTMC devices. The host T4.1 will collect sensor data from them, and process that data and either save the data or use it to control various processes. This will allow the peripheral data collection bandwidth of multiple T4.X boards to be consolidated into a single data stream.

The USBTMC host and device are ripe for experimentation in my mind, because I have learned about the host side while developing the FLIR Boson driver, which allows me to collect video data sent through a bulk endpoint with minimal foreground CPU overhead. Setting up the TMC device to enumerate properly is this week's excercise.

I have determined that I am stuck with defining the USB DeviceClass in an interface, as the USBTMC Device can't be defined at the device level, but only at the interface level,
 
I've gotten the pseudo TMC device to enumerate:

Code:
SERENUM + USBTMC configuration during enumeration

Configuration Descriptor:
  09 02 40 00 02 01 00 C0 32
    NumInterfaces = 2
    ConfigurationValue = 1
  Interface = 1
    Number of endpoints = 2
    Class/Subclass/Protocol = 3(HID) / 0 / 0
    Data:09 04 01 00 02 03 00 00 00
      HID, 1 report descriptor    Data:09 21 11 01 00 01 22 21 00
  Endpoint = 2 IN
    Type = Interrupt
    Max Size = 64
    Polling Interval = 1
    Data:07 05 82 03 40 00 01

  Endpoint = 2 OUT
    Type = Interrupt
    Max Size = 32
    Polling Interval = 2
    Data:07 05 02 03 20 00 02

  Interface = 0
    Number of endpoints = 2
    Class/Subclass/Protocol = 254 / 3 / 0  // Ap-Specific, USBTMC, T&M Dev.
    Data:09 04 00 00 02 FE 03 00 04

  Endpoint = 3 IN
    Type = Bulk
    Max Size = 512
    Polling Interval = 0
    Data:07 05 83 02 00 02 00

  Endpoint = 3 OUT
    Type = Bulk
    Max Size = 512
    Polling Interval = 0
    Data:07 05 03 02 00 02 00

This is a simple enough configuration that I think I can put together a skeleton USBTMC driver which I can use to write and test a Claim function. After that, I can work with the lowest level Read and Write functions, both for the device end and the host driver.

One thing I found both annoying and time-consuming is the chain of #ifdef ... #elseif constructs in USB_desc.h. The annoying part is that my C?CPP editor, Notepad++, will not fold code at #elseif. As a result, I spent way too much time scrolling through that 969-line file. I finally realized that the various device descriptors are position-independent, so I moved my USBTMC and SEREMU sections to the top of the file.

I also had to add some lines to usb_desc.c:

Code:
// at about line 6543:
// Same as MTP, but without event endpoint
#define USBTMC_INTERFACE_DESC_POS        MTP_INTERFACE_DESC_POS+MTP_INTERFACE_DESC_SIZE
#ifdef  USBTMC_INTERFACE
#define USBTMC_INTERFACE_DESC_SIZE        9+7+7
#else
#define USBTMC_INTERFACE_DESC_SIZE    0
#endif

//At about line 1396.
#ifdef USBTMC_INTERFACE
/************************************/
    //SEREMU + USBTMC configuration--TMC part
    // configuration for 480 Mbit/sec speed
        // interface descriptor, USB spec 9.6.5, page 267-269, Table 9-12
        9,                                      // bLength
        4,                                      // bDescriptorType
        USBTMC_INTERFACE,                         // bInterfaceNumber
        0,                                      // bAlternateSetting
        2,                                      // bNumEndpoints----  no event EP
        0xFE,                                   // bInterfaceClass (0xFE  App. Specific)
        0x03,                                   // bInterfaceSubClass  USBMTC
        0x00,                                   // bInterfaceProtocol 0 = USBMTC
        4,                                      // Interface
        // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13
        7,                                      // bLength
        5,                                      // bDescriptorType
        USBTMC_TX_ENDPOINT | 0x80,                 // bEndpointAddress
        0x02,                                   // bmAttributes (0x02=bulk)
        LSB(USBTMC_TX_SIZE_480),MSB(USBTMC_TX_SIZE_480), // wMaxPacketSize  512
        0,                                      // bInterval
        // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13
        7,                                      // bLength
        5,                                      // bDescriptorType
        USBTMC_RX_ENDPOINT,                        // bEndpointAddress
        0x02,                                   // bmAttributes (0x02=bulk)
        LSB(USBTMC_RX_SIZE_480),MSB(USBTMC_RX_SIZE_480), // wMaxPacketSize
        0,                                      // bInterval

#endif // USBTMC_INTERFACE

Thankfully, usb_desc.c makes more use of #ifdef ... #endif, so code folding in Notepad++ helps a lot.

Perhaps I'll one day have the time to refactor the USB core files in a fashion that will allow addition of new functionality without editing the core files---or without having to duplicate most of the core files in a user library.
 
Back
Top