合并TinyUSB仓库

This commit is contained in:
2025-10-10 15:24:53 +08:00
parent 12f3c23968
commit 99a4282aca
371 changed files with 151941 additions and 0 deletions

View File

@@ -0,0 +1,150 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Ha Thach (tinyusb.org)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "bsp/board_api.h"
#include "tusb.h"
#include "usbtmc_app.h"
//--------------------------------------------------------------------+
// MACRO CONSTANT TYPEDEF PROTYPES
//--------------------------------------------------------------------+
/* Blink pattern
* - 250 ms : device not mounted
* - 0 ms : device mounted
* - 2500 ms : device is suspended
*/
enum {
BLINK_NOT_MOUNTED = 250,
BLINK_MOUNTED = 0,
BLINK_SUSPENDED = 2500,
};
static uint32_t blink_interval_ms = BLINK_NOT_MOUNTED;
void led_blinking_task(void);
/*------------- MAIN -------------*/
int main(void)
{
board_init();
// init device stack on configured roothub port
tusb_rhport_init_t dev_init = {
.role = TUSB_ROLE_DEVICE,
.speed = TUSB_SPEED_AUTO
};
tusb_init(BOARD_TUD_RHPORT, &dev_init);
if (board_init_after_tusb) {
board_init_after_tusb();
}
while (1)
{
tud_task(); // tinyusb device task
led_blinking_task();
usbtmc_app_task_iter();
}
}
//--------------------------------------------------------------------+
// Device callbacks
//--------------------------------------------------------------------+
// Invoked when device is mounted
void tud_mount_cb(void)
{
blink_interval_ms = BLINK_MOUNTED;
}
// Invoked when device is unmounted
void tud_umount_cb(void)
{
blink_interval_ms = BLINK_NOT_MOUNTED;
}
// Invoked when usb bus is suspended
// remote_wakeup_en : if host allow us to perform remote wakeup
// Within 7ms, device must draw an average of current less than 2.5 mA from bus
void tud_suspend_cb(bool remote_wakeup_en)
{
(void) remote_wakeup_en;
blink_interval_ms = BLINK_SUSPENDED;
}
// Invoked when usb bus is resumed
void tud_resume_cb(void)
{
blink_interval_ms = tud_mounted() ? BLINK_MOUNTED : BLINK_NOT_MOUNTED;
}
//--------------------------------------------------------------------+
// BLINKING TASK + Indicator pulse
//--------------------------------------------------------------------+
volatile uint8_t doPulse = false;
// called from USB context
void led_indicator_pulse(void) {
doPulse = true;
}
void led_blinking_task(void)
{
static uint32_t start_ms = 0;
static bool led_state = false;
if(blink_interval_ms == BLINK_MOUNTED) // Mounted
{
if(doPulse)
{
led_state = true;
board_led_write(true);
start_ms = board_millis();
doPulse = false;
}
else if (led_state == true)
{
if ( board_millis() - start_ms < 750) //Spec says blink must be between 500 and 1000 ms.
{
return; // not enough time
}
led_state = false;
board_led_write(false);
}
}
else
{
// Blink every interval ms
if ( board_millis() - start_ms < blink_interval_ms) return; // not enough time
start_ms += blink_interval_ms;
board_led_write(led_state);
led_state = 1 - led_state; // toggle
}
}

View File

@@ -0,0 +1,5 @@
#ifndef MAIN_H
#define MAIN_H
void led_indicator_pulse(void);
#endif

View File

@@ -0,0 +1,85 @@
/*
* tusb_config.h
*
* Created on: Sep 5, 2019
* Author: nconrad
*/
#ifndef TUSB_CONFIG_H_
#define TUSB_CONFIG_H_
#ifdef __cplusplus
extern "C" {
#endif
//--------------------------------------------------------------------+
// Board Specific Configuration
//--------------------------------------------------------------------+
// RHPort number used for device can be defined by board.mk, default to port 0
#ifndef BOARD_TUD_RHPORT
#define BOARD_TUD_RHPORT 0
#endif
// RHPort max operational speed can defined by board.mk
#ifndef BOARD_TUD_MAX_SPEED
#define BOARD_TUD_MAX_SPEED OPT_MODE_DEFAULT_SPEED
#endif
//--------------------------------------------------------------------
// Common Configuration
//--------------------------------------------------------------------
// defined by compiler flags for flexibility
#ifndef CFG_TUSB_MCU
#error CFG_TUSB_MCU must be defined
#endif
#ifndef CFG_TUSB_OS
#define CFG_TUSB_OS OPT_OS_NONE
#endif
#ifndef CFG_TUSB_DEBUG
#define CFG_TUSB_DEBUG 0
#endif
// Enable Device stack
#define CFG_TUD_ENABLED 1
// Default is max speed that hardware controller could support with on-chip PHY
#define CFG_TUD_MAX_SPEED BOARD_TUD_MAX_SPEED
/* USB DMA on some MCUs can only access a specific SRAM region with restriction on alignment.
* Tinyusb use follows macros to declare transferring memory so that they can be put
* into those specific section.
* e.g
* - CFG_TUSB_MEM SECTION : __attribute__ (( section(".usb_ram") ))
* - CFG_TUSB_MEM_ALIGN : __attribute__ ((aligned(4)))
*/
#ifndef CFG_TUSB_MEM_SECTION
#define CFG_TUSB_MEM_SECTION
#endif
#ifndef CFG_TUSB_MEM_ALIGN
#define CFG_TUSB_MEM_ALIGN __attribute__ ((aligned(4)))
#endif
//--------------------------------------------------------------------
// DEVICE CONFIGURATION
//--------------------------------------------------------------------
#ifndef CFG_TUD_ENDPOINT0_SIZE
#define CFG_TUD_ENDPOINT0_SIZE 64
#endif
//------------- CLASS -------------//
#define CFG_TUD_USBTMC 1
#define CFG_TUD_USBTMC_ENABLE_INT_EP 1
#define CFG_TUD_USBTMC_ENABLE_488 1
#ifdef __cplusplus
}
#endif
#endif /* TUSB_CONFIG_H_ */

View File

@@ -0,0 +1,241 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Ha Thach (tinyusb.org)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
#include "bsp/board_api.h"
#include "tusb.h"
#include "class/usbtmc/usbtmc.h"
#include "class/usbtmc/usbtmc_device.h"
/* A combination of interfaces must have a unique product id, since PC will save device driver after the first plug.
* Same VID/PID with different interface e.g MSC (first), then CDC (later) will possibly cause system error on PC.
*
* Auto ProductID layout's Bitmap:
* [MSB] HID | MSC | CDC [LSB]
*/
#define _PID_MAP(itf, n) ( (CFG_TUD_##itf) << (n) )
#define USB_PID (0x4000 | _PID_MAP(CDC, 0) | _PID_MAP(MSC, 1) | _PID_MAP(HID, 2) | \
_PID_MAP(MIDI, 3) | _PID_MAP(VENDOR, 4) )
#define USB_VID 0xcafe
#define USB_BCD 0x0200
//--------------------------------------------------------------------+
// Device Descriptors
//--------------------------------------------------------------------+
tusb_desc_device_t const desc_device =
{
.bLength = sizeof(tusb_desc_device_t),
.bDescriptorType = TUSB_DESC_DEVICE,
.bcdUSB = USB_BCD,
.bDeviceClass = TUSB_CLASS_UNSPECIFIED,
.bDeviceSubClass = 0x00,
.bDeviceProtocol = 0x00,
.bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,
.idVendor = USB_VID,
.idProduct = USB_PID,
.bcdDevice = 0x0100,
.iManufacturer = 0x01,
.iProduct = 0x02,
.iSerialNumber = 0x03,
.bNumConfigurations = 0x01
};
// Invoked when received GET DEVICE DESCRIPTOR
// Application return pointer to descriptor
uint8_t const * tud_descriptor_device_cb(void)
{
return (uint8_t const *) &desc_device;
}
//--------------------------------------------------------------------+
// Configuration Descriptor
//--------------------------------------------------------------------+
#if defined(CFG_TUD_USBTMC)
# define TUD_USBTMC_DESC_MAIN(_itfnum,_bNumEndpoints, _bulkMaxPacketLength) \
TUD_USBTMC_IF_DESCRIPTOR(_itfnum, _bNumEndpoints, /*_stridx = */ 4u, TUD_USBTMC_PROTOCOL_USB488), \
TUD_USBTMC_BULK_DESCRIPTORS(/* OUT = */0x01, /* IN = */ 0x81, /* packet size = */_bulkMaxPacketLength)
#if CFG_TUD_USBTMC_ENABLE_INT_EP
// USBTMC Interrupt xfer always has length of 2, but we use epMaxSize=8 for
// compatibility with mcus that only allow 8, 16, 32 or 64 for FS endpoints
# define TUD_USBTMC_DESC(_itfnum, _bulkMaxPacketLength) \
TUD_USBTMC_DESC_MAIN(_itfnum, /* _epCount = */ 3, _bulkMaxPacketLength), \
TUD_USBTMC_INT_DESCRIPTOR(/* INT ep # */ 0x82, /* epMaxSize = */ 8, /* bInterval = */16u )
# define TUD_USBTMC_DESC_LEN (TUD_USBTMC_IF_DESCRIPTOR_LEN + TUD_USBTMC_BULK_DESCRIPTORS_LEN + TUD_USBTMC_INT_DESCRIPTOR_LEN)
#else
# define TUD_USBTMC_DESC(_itfnum, _bulkMaxPacketLength) \
TUD_USBTMC_DESC_MAIN(_itfnum, /* _epCount = */ 2u, _bulkMaxPacketLength)
# define TUD_USBTMC_DESC_LEN (TUD_USBTMC_IF_DESCRIPTOR_LEN + TUD_USBTMC_BULK_DESCRIPTORS_LEN)
#endif /* CFG_TUD_USBTMC_ENABLE_INT_EP */
#else
# define USBTMC_DESC_LEN (0)
#endif /* CFG_TUD_USBTMC */
enum
{
ITF_NUM_USBTMC,
ITF_NUM_TOTAL
};
#define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_USBTMC_DESC_LEN)
uint8_t const desc_fs_configuration[] =
{
// Config number, interface count, string index, total length, attribute, power in mA
TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100),
TUD_USBTMC_DESC(ITF_NUM_USBTMC, /* _bulkMaxPacketLength = */ 64),
};
#if TUD_OPT_HIGH_SPEED
uint8_t const desc_hs_configuration[] =
{
// Config number, interface count, string index, total length, attribute, power in mA
TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100),
TUD_USBTMC_DESC(ITF_NUM_USBTMC, /* _bulkMaxPacketLength = */ 512),
};
// other speed configuration
uint8_t desc_other_speed_config[CONFIG_TOTAL_LEN];
// device qualifier is mostly similar to device descriptor since we don't change configuration based on speed
tusb_desc_device_qualifier_t const desc_device_qualifier =
{
.bLength = sizeof(tusb_desc_device_qualifier_t),
.bDescriptorType = TUSB_DESC_DEVICE_QUALIFIER,
.bcdUSB = USB_BCD,
.bDeviceClass = TUSB_CLASS_UNSPECIFIED,
.bDeviceSubClass = 0x00,
.bDeviceProtocol = 0x00,
.bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,
.bNumConfigurations = 0x01,
.bReserved = 0x00
};
// Invoked when received GET DEVICE QUALIFIER DESCRIPTOR request
// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete.
// device_qualifier descriptor describes information about a high-speed capable device that would
// change if the device were operating at the other speed. If not highspeed capable stall this request.
uint8_t const* tud_descriptor_device_qualifier_cb(void)
{
return (uint8_t const*) &desc_device_qualifier;
}
#endif
// Invoked when received GET CONFIGURATION DESCRIPTOR
// Application return pointer to descriptor
// Descriptor contents must exist long enough for transfer to complete
uint8_t const * tud_descriptor_configuration_cb(uint8_t index)
{
(void) index; // for multiple configurations
#if TUD_OPT_HIGH_SPEED
// Although we are highspeed, host may be fullspeed.
return (tud_speed_get() == TUSB_SPEED_HIGH) ? desc_hs_configuration : desc_fs_configuration;
#else
return desc_fs_configuration;
#endif
}
//--------------------------------------------------------------------+
// String Descriptors
//--------------------------------------------------------------------+
// String Descriptor Index
enum {
STRID_LANGID = 0,
STRID_MANUFACTURER,
STRID_PRODUCT,
STRID_SERIAL,
};
// array of pointer to string descriptors
char const *string_desc_arr[] =
{
(const char[]) { 0x09, 0x04 }, // 0: is supported language is English (0x0409)
"TinyUSB", // 1: Manufacturer
"TinyUSB Device", // 2: Product
NULL, // 3: Serials will use unique ID if possible
"TinyUSB USBTMC", // 4: USBTMC
};
static uint16_t _desc_str[32 + 1];
// Invoked when received GET STRING DESCRIPTOR request
// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete
uint16_t const *tud_descriptor_string_cb(uint8_t index, uint16_t langid) {
(void) langid;
size_t chr_count;
switch ( index ) {
case STRID_LANGID:
memcpy(&_desc_str[1], string_desc_arr[0], 2);
chr_count = 1;
break;
case STRID_SERIAL:
chr_count = board_usb_get_serial(_desc_str + 1, 32);
break;
default:
// Note: the 0xEE index string is a Microsoft OS 1.0 Descriptors.
// https://docs.microsoft.com/en-us/windows-hardware/drivers/usbcon/microsoft-defined-usb-descriptors
if ( !(index < sizeof(string_desc_arr) / sizeof(string_desc_arr[0])) ) return NULL;
const char *str = string_desc_arr[index];
// Cap at max char
chr_count = strlen(str);
size_t const max_count = sizeof(_desc_str) / sizeof(_desc_str[0]) - 1; // -1 for string type
if ( chr_count > max_count ) chr_count = max_count;
// Convert ASCII string into UTF-16
for ( size_t i = 0; i < chr_count; i++ ) {
_desc_str[1 + i] = str[i];
}
break;
}
// first byte is length (including header), second byte is string type
_desc_str[0] = (uint16_t) ((TUSB_DESC_STRING << 8) | (2 * chr_count + 2));
return _desc_str;
}

View File

@@ -0,0 +1,320 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Nathan Conrad
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
#include <string.h>
#include <stdlib.h> /* atoi */
#include "tusb.h"
#include "bsp/board_api.h"
#include "main.h"
#if (CFG_TUD_USBTMC_ENABLE_488)
static usbtmc_response_capabilities_488_t const
#else
static usbtmc_response_capabilities_t const
#endif
tud_usbtmc_app_capabilities =
{
.USBTMC_status = USBTMC_STATUS_SUCCESS,
.bcdUSBTMC = USBTMC_VERSION,
.bmIntfcCapabilities =
{
.listenOnly = 0,
.talkOnly = 0,
.supportsIndicatorPulse = 1
},
.bmDevCapabilities = {
.canEndBulkInOnTermChar = 0
},
#if (CFG_TUD_USBTMC_ENABLE_488)
.bcdUSB488 = USBTMC_488_VERSION,
.bmIntfcCapabilities488 =
{
.supportsTrigger = 1,
.supportsREN_GTL_LLO = 0,
.is488_2 = 1
},
.bmDevCapabilities488 =
{
.SCPI = 1,
.SR1 = 0,
.RL1 = 0,
.DT1 =0,
}
#endif
};
#define IEEE4882_STB_QUESTIONABLE (0x08u)
#define IEEE4882_STB_MAV (0x10u)
#define IEEE4882_STB_SER (0x20u)
#define IEEE4882_STB_SRQ (0x40u)
static const char idn[] = "TinyUSB,ModelNumber,SerialNumber,FirmwareVer123456\r\n";
//static const char idn[] = "TinyUSB,ModelNumber,SerialNumber,FirmwareVer and a bunch of other text to make it longer than a packet, perhaps? lets make it three transfers...\n";
static volatile uint8_t status;
// 0=not query, 1=queried, 2=delay,set(MAV), 3=delay 4=ready?
// (to simulate delay)
static volatile uint16_t queryState = 0;
static volatile uint32_t queryDelayStart;
static volatile uint32_t bulkInStarted;
static volatile uint32_t idnQuery;
static uint32_t resp_delay = 125u; // Adjustable delay, to allow for better testing
static size_t buffer_len;
static size_t buffer_tx_ix; // for transmitting using multiple transfers
static uint8_t buffer[225]; // A few packets long should be enough.
void tud_usbtmc_open_cb(uint8_t interface_id)
{
(void)interface_id;
tud_usbtmc_start_bus_read();
}
#if (CFG_TUD_USBTMC_ENABLE_488)
usbtmc_response_capabilities_488_t const *
#else
usbtmc_response_capabilities_t const *
#endif
tud_usbtmc_get_capabilities_cb()
{
return &tud_usbtmc_app_capabilities;
}
bool tud_usbtmc_msg_trigger_cb(usbtmc_msg_generic_t* msg) {
(void)msg;
// Let trigger set the SRQ
status |= IEEE4882_STB_SRQ;
return true;
}
bool tud_usbtmc_msgBulkOut_start_cb(usbtmc_msg_request_dev_dep_out const * msgHeader)
{
(void)msgHeader;
buffer_len = 0;
if(msgHeader->TransferSize > sizeof(buffer))
{
return false;
}
return true;
}
bool tud_usbtmc_msg_data_cb(void *data, size_t len, bool transfer_complete)
{
// If transfer isn't finished, we just ignore it (for now)
if(len + buffer_len < sizeof(buffer))
{
memcpy(&(buffer[buffer_len]), data, len);
buffer_len += len;
}
else
{
return false; // buffer overflow!
}
queryState = transfer_complete;
idnQuery = 0;
if ( transfer_complete && (len >= 4) &&
(!strncmp("*idn?", data, 4) || !strncmp("*IDN?", data, 4)) )
{
idnQuery = 1;
}
if ( transfer_complete &&
(!strncmp("delay ", data, 5) || !strncmp("DELAY ", data, 5)) )
{
queryState = 0;
int d = atoi((char*)data + 5);
if(d > 10000)
d = 10000;
if(d<0)
d=0;
resp_delay = (uint32_t)d;
}
tud_usbtmc_start_bus_read();
return true;
}
bool tud_usbtmc_msgBulkIn_complete_cb()
{
if((buffer_tx_ix == buffer_len) || idnQuery) // done
{
status &= (uint8_t)~(IEEE4882_STB_MAV); // clear MAV
queryState = 0;
bulkInStarted = 0;
buffer_tx_ix = 0;
}
tud_usbtmc_start_bus_read();
return true;
}
static unsigned int msgReqLen;
bool tud_usbtmc_msgBulkIn_request_cb(usbtmc_msg_request_dev_dep_in const * request)
{
msgReqLen = request->TransferSize;
#ifdef xDEBUG
uart_tx_str_sync("MSG_IN_DATA: Requested!\r\n");
#endif
if(queryState == 0 || (buffer_tx_ix == 0))
{
TU_ASSERT(bulkInStarted == 0);
bulkInStarted = 1;
// > If a USBTMC interface receives a Bulk-IN request prior to receiving a USBTMC command message
// that expects a response, the device must NAK the request (*not stall*)
}
else
{
size_t txlen = tu_min32(buffer_len-buffer_tx_ix,msgReqLen);
tud_usbtmc_transmit_dev_msg_data(&buffer[buffer_tx_ix], txlen,
(buffer_tx_ix+txlen) == buffer_len, false);
buffer_tx_ix += txlen;
}
// Always return true indicating not to stall the EP.
return true;
}
void usbtmc_app_task_iter(void) {
switch(queryState) {
case 0:
break;
case 1:
queryDelayStart = board_millis();
queryState = 2;
break;
case 2:
if( (board_millis() - queryDelayStart) > resp_delay) {
queryDelayStart = board_millis();
queryState=3;
status |= 0x10u; // MAV
status |= 0x40u; // SRQ
}
break;
case 3:
if( (board_millis() - queryDelayStart) > resp_delay) {
queryState = 4;
}
break;
case 4: // time to transmit;
if(bulkInStarted && (buffer_tx_ix == 0)) {
if(idnQuery)
{
tud_usbtmc_transmit_dev_msg_data(idn, tu_min32(sizeof(idn)-1,msgReqLen),true,false);
queryState = 0;
bulkInStarted = 0;
}
else
{
buffer_tx_ix = tu_min32(buffer_len,msgReqLen);
tud_usbtmc_transmit_dev_msg_data(buffer, buffer_tx_ix, buffer_tx_ix == buffer_len, false);
}
// MAV is cleared in the transfer complete callback.
}
break;
default:
TU_ASSERT(false,);
}
}
bool tud_usbtmc_initiate_clear_cb(uint8_t *tmcResult)
{
*tmcResult = USBTMC_STATUS_SUCCESS;
queryState = 0;
bulkInStarted = false;
status = 0;
return true;
}
bool tud_usbtmc_check_clear_cb(usbtmc_get_clear_status_rsp_t *rsp)
{
queryState = 0;
bulkInStarted = false;
status = 0;
buffer_tx_ix = 0u;
buffer_len = 0u;
rsp->USBTMC_status = USBTMC_STATUS_SUCCESS;
rsp->bmClear.BulkInFifoBytes = 0u;
return true;
}
bool tud_usbtmc_initiate_abort_bulk_in_cb(uint8_t *tmcResult)
{
bulkInStarted = 0;
*tmcResult = USBTMC_STATUS_SUCCESS;
return true;
}
bool tud_usbtmc_check_abort_bulk_in_cb(usbtmc_check_abort_bulk_rsp_t *rsp)
{
(void)rsp;
tud_usbtmc_start_bus_read();
return true;
}
bool tud_usbtmc_initiate_abort_bulk_out_cb(uint8_t *tmcResult)
{
*tmcResult = USBTMC_STATUS_SUCCESS;
return true;
}
bool tud_usbtmc_check_abort_bulk_out_cb(usbtmc_check_abort_bulk_rsp_t *rsp)
{
(void)rsp;
tud_usbtmc_start_bus_read();
return true;
}
void tud_usbtmc_bulkIn_clearFeature_cb(void)
{
}
void tud_usbtmc_bulkOut_clearFeature_cb(void)
{
tud_usbtmc_start_bus_read();
}
// Return status byte, but put the transfer result status code in the rspResult argument.
uint8_t tud_usbtmc_get_stb_cb(uint8_t *tmcResult)
{
uint8_t old_status = status;
status = (uint8_t)(status & ~(IEEE4882_STB_SRQ)); // clear SRQ
*tmcResult = USBTMC_STATUS_SUCCESS;
// Increment status so that we see different results on each read...
return old_status;
}
bool tud_usbtmc_indicator_pulse_cb(tusb_control_request_t const * msg, uint8_t *tmcResult)
{
(void)msg;
led_indicator_pulse();
*tmcResult = USBTMC_STATUS_SUCCESS;
return true;
}

View File

@@ -0,0 +1,7 @@
#ifndef USBTMC_APP_H
#define USBTMC_APP_H
void usbtmc_app_task_iter(void);
#endif

View File

@@ -0,0 +1,212 @@
#!/usr/bin/env python3
import pyvisa
import time
import sys
def test_idn():
idn = inst.query("*idn?");
assert (idn == "TinyUSB,ModelNumber,SerialNumber,FirmwareVer123456\r\n")
assert (inst.is_4882_compliant)
def test_echo(m,n):
longstr = "0123456789abcdefghijklmnopqrstuvwxyz" * 50
t0 = time.monotonic()
#Next try echo from 1 to 175 characters (200 is max buffer size on DUT)
for i in range(m,n):
#print(i)
x = longstr[0:i]
xt = x + inst.write_termination
y = inst.query(x)
#print(x)
#print (":".join("{:02x}".format(ord(c)) for c in xt))
#print (":".join("{:02x}".format(ord(c)) for c in y))
assert(xt == y), f"failed i={i}"
#inst.read_stb();# Just to make USB logging easier by sending a control query (bad thing is that this made things slow)
t = time.monotonic() - t0
print(f" elapsed: {t:0.3} sec")
def test_trig():
# clear SRQ
inst.read_stb()
assert (inst.read_stb() == 0)
inst.assert_trigger()
time.sleep(0.3) # SRQ may have some delay
assert (inst.read_stb() & 0x40), "SRQ not set after 0.3 seconds"
assert (inst.read_stb() == 0)
def test_mav():
inst.write("delay 50")
inst.read_stb() # clear STB
assert (inst.read_stb() == 0)
inst.write("123")
time.sleep(0.3)
assert (inst.read_stb() & 0x10), "MAV not set after 0.5 seconds"
rsp = inst.read()
assert(rsp == "123\r\n")
def test_srq():
assert (inst.read_stb() == 0)
inst.write("123")
#inst.enable_event(pyvisa.constants.VI_EVENT_SERVICE_REQ, pyvisa.constants.VI_QUEUE)
#waitrsp = inst.wait_on_event(pyvisa.constants.VI_EVENT_SERVICE_REQ, 5000)
#inst.discard_events(pyvisa.constants.VI_EVENT_SERVICE_REQ, pyvisa.constants.VI_QUEUE)
#inst.wait_for_srq()
time.sleep(0.3)
stb = inst.read_stb()
msg = "SRQ not set after 0.5 seconds, was {:02x}".format(stb)
assert (stb == 0x50),msg
assert (inst.read_stb() == 0x10), "SRQ set at second read!"
rsp = inst.read()
assert(rsp == "123\r\n")
def test_read_timeout():
inst.timeout = 500
# First read with no MAV
inst.read_stb()
assert (inst.read_stb() == 0)
inst.write("delay 500")
t0 = time.monotonic()
try:
rsp = inst.read()
assert(False), "Read should have resulted in timeout"
except pyvisa.VisaIOError:
print(" Got expected exception")
t = time.monotonic() - t0
assert ((t*1000.0) > (inst.timeout - 300))
assert ((t*1000.0) < (inst.timeout + 300))
print(f"Delay was {t:0.3}")
# Response is still in queue, so send a clear (to be more helpful to the next test)
inst.clear()
time.sleep(0.3)
assert(0 == (inst.read_stb() & 0x10)), "MAV not reset after clear"
def test_abort_in():
inst.timeout = 200
# First read with no MAV
inst.read_stb()
assert (inst.read_stb() == 0)
inst.write("delay 500")
inst.write("xxx")
t0 = time.monotonic()
try:
rsp = inst.read()
assert(False), "Read should have resulted in timeout"
except pyvisa.VisaIOError:
print(" Got expected exception")
t = time.monotonic() - t0
assert ((t*1000.0) > (inst.timeout - 300))
assert ((t*1000.0) < (inst.timeout + 300))
print(f" Delay was {t:0.3}")
# Response is still in queue, so read it out (to be more helpful to the next test)
inst.timeout = 800
y = inst.read()
assert(y == "xxx\r\n")
def test_indicate():
# perform indicator pulse
usb_iface = inst.get_visa_attribute(pyvisa.constants.VI_ATTR_USB_INTFC_NUM)
retv = inst.control_in(request_type_bitmap_field=0xA1, request_id=64, request_value=0x0000, index=usb_iface, length=0x0001)
# pyvisa used to return (statuscode,bytes), but now only returns bytes, so we need to handle both cases
if(isinstance(retv,bytes)):
assert(retv == b'\x01')
else:
assert((retv[1] == pyvisa.constants.StatusCode(0)) and (retv[0] == b'\x01')), f"indicator pulse failed: retv={retv}"
def test_multi_read():
old_chunk_size = inst.chunk_size
longstr = "0123456789abcdefghijklmnopqrstuvwxyz" * 10
timeout = 10
x = longstr[0:174]
inst.chunk_size = 50 # Seems chunk size only applies to read but not write
inst.write(x)
# I'm not sure how to request just the remaining bit using a max count... so just read it all.
y = inst.read()
assert (x + "\r\n" == y)
#inst.chunk_size = old_chunk_size
def test_stall_ep0():
usb_iface = inst.get_visa_attribute(pyvisa.constants.VI_ATTR_USB_INTFC_NUM)
inst.read_stb()
# This is an invalid request, should create stall.
try:
retv = inst.control_in(request_type_bitmap_field=0xA1, request_id=60, request_value=0x0000, index=usb_iface, length=0x0001)
assert(False)
except pyvisa.VisaIOError:
pass
assert (inst.read_stb() == 0)
rm = pyvisa.ResourceManager()
reslist = rm.list_resources("USB?::?*::INSTR")
print(reslist)
if (len(reslist) == 0):
sys.exit()
inst = rm.open_resource(reslist[0]);
inst.timeout = 3000
inst.clear()
print("+ IDN")
test_idn()
print("+test abort in")
test_abort_in()
inst.timeout = 2000
print("+ multi read")
test_multi_read()
print("+ echo delay=0")
inst.write("delay 0")
test_echo(1,175)
print("+ echo delay=2")
inst.write("delay 2")
test_echo(1,175)
print("+ echo delay=150")
inst.write("delay 150")
test_echo(53,76)
test_echo(165,170)
print("+ Read timeout (no MAV)")
test_read_timeout()
print("+ Test EP0 stall recovery")
test_stall_ep0()
print("+ MAV")
test_mav()
print("+ SRQ")
test_srq()
print("+ indicate")
test_indicate()
print("+ TRIG")
test_trig()
# Untested:
# abort bulk out
# LLO, GTL, etc
# Throughput rate?
# Transmitting a message using multiple transfers
inst.close()
print("Test complete")