add a factory test for metro m7

This commit is contained in:
ladyada 2023-02-04 18:00:22 -05:00
parent 07ea898bf8
commit f57642d673
6 changed files with 745 additions and 0 deletions

View file

@ -0,0 +1,22 @@
OUTNAME = factory_test-$(BOARD)
SRC_C += \
$(PORT_DIR)/boards.c \
$(CURRENT_PATH)/main.c \
$(CURRENT_PATH)/arduino.c \
$(CURRENT_PATH)/usb_descriptors.c \
INC += \
$(TOP)/$(CURRENT_PATH) \
$(TOP)/src
ifeq ($(BOARD),metro_m7_1011)
include ../app.mk
else
all:
@echo This board does not have ESP32 co-processor
endif

View file

@ -0,0 +1,139 @@
#include "arduino.h"
pinmap pinmapping[] = {
{.num=0, .port=GPIO1, .pin=9, .mux={IOMUXC_GPIO_09_GPIOMUX_IO09}},
{.num=1, .port=GPIO1, .pin=10, .mux={IOMUXC_GPIO_10_GPIOMUX_IO10}},
{.num=2, .port=GPIO1, .pin=13, .mux={IOMUXC_GPIO_13_GPIOMUX_IO13}},
{.num=3, .port=GPIO1, .pin=12, .mux={IOMUXC_GPIO_12_GPIOMUX_IO12}},
{.num=4, .port=GPIO2, .pin=0, .mux={IOMUXC_GPIO_SD_00_GPIO2_IO00}},
{.num=5, .port=GPIO2, .pin=1, .mux={IOMUXC_GPIO_SD_01_GPIO2_IO01}},
{.num=6, .port=GPIO2, .pin=2, .mux={IOMUXC_GPIO_SD_02_GPIO2_IO02}},
{.num=7, .port=GPIO1, .pin=11, .mux={IOMUXC_GPIO_11_GPIOMUX_IO11}},
{.num=8, .port=GPIO1, .pin=8, .mux={IOMUXC_GPIO_08_GPIOMUX_IO08}},
{.num=9, .port=GPIO1, .pin=7, .mux={IOMUXC_GPIO_07_GPIOMUX_IO07}},
{.num=10, .port=GPIO1, .pin=6, .mux={IOMUXC_GPIO_06_GPIOMUX_IO06}},
{.num=11, .port=GPIO1, .pin=5, .mux={IOMUXC_GPIO_05_GPIOMUX_IO05}},
{.num=12, .port=GPIO1, .pin=4, .mux={IOMUXC_GPIO_04_GPIOMUX_IO04}},
{.num=13, .port=GPIO1, .pin=3, .mux={IOMUXC_GPIO_03_GPIOMUX_IO03}},
{.num=14, .port=GPIO1, .pin=16, .mux={IOMUXC_GPIO_AD_02_GPIOMUX_IO16}, .adc=ADC1}, // AD0
{.num=15, .port=GPIO1, .pin=15, .mux={IOMUXC_GPIO_AD_01_GPIOMUX_IO15}, .adc=ADC1}, // AD1
{.num=16, .port=GPIO1, .pin=14, .mux={IOMUXC_GPIO_AD_00_GPIOMUX_IO14}, .adc=ADC1}, // AD2
{.num=17, .port=GPIO1, .pin=19, .mux={IOMUXC_GPIO_AD_05_GPIOMUX_IO19}, .adc=ADC1}, // AD3
{.num=18, .port=GPIO1, .pin=24, .mux={IOMUXC_GPIO_AD_10_GPIOMUX_IO24}, .adc=ADC1}, // AD4
{.num=19, .port=GPIO1, .pin=22, .mux={IOMUXC_GPIO_AD_08_GPIOMUX_IO22}, .adc=ADC1}, // AD5
{.num=PIN_SDA, .port=GPIO1, .pin=1, .mux={IOMUXC_GPIO_01_GPIOMUX_IO01}},
{.num=PIN_SCL, .port=GPIO1, .pin=2, .mux={IOMUXC_GPIO_02_GPIOMUX_IO02}},
};
static volatile uint32_t _millis = 0;
void Serial_printf(const char format[], ...) {
char buf[256];
va_list ap;
va_start(ap, format);
vsnprintf(buf, sizeof(buf), format, ap);
tud_cdc_write(buf, strlen(buf));
va_end(ap);
tud_cdc_write_flush();
}
void board_timer_handler(void) {
_millis++;
}
uint32_t millis(void) {
return _millis;
}
void delay(uint32_t ms) {
board_timer_start(1);
uint32_t timestamp = millis();
while ((timestamp+ms) > millis()) {
// Serial_printf("delay %d\n\r", millis());
tud_task();
}
}
static pinmap *getMapping(uint32_t pinnum) {
pinmap *pindeets = NULL;
for (uint32_t i=0; i<sizeof(pinmapping) / sizeof(pinmap); i++) {
pindeets = &pinmapping[i];
//Serial_printf("pin %d -> port %x, pin %d\n\r", pindeets->num, pindeets->port, pindeets->pin);
if (pinnum == pindeets->num) {
//Serial_printf("found!\n\r");
return pindeets;
}
}
return NULL;
}
void digitalWrite(uint32_t pinnum, bool value) {
pinmap *pindeets = getMapping(pinnum);
if (! pindeets) return;
GPIO_PinWrite(pindeets->port, pindeets->pin, value);
}
bool digitalRead(uint32_t pinnum) {
pinmap *pindeets = getMapping(pinnum);
if (! pindeets) return 0;
return GPIO_PinRead(pindeets->port, pindeets->pin);
}
void pinMode(uint32_t pinnum, uint8_t state) {
pinmap *pindeets = getMapping(pinnum);
if (! pindeets) return;
IOMUXC_SetPinMux(pindeets->mux.muxReg, pindeets->mux.muxMode,
pindeets->mux.inputReg, pindeets->mux.idaisy,
pindeets->mux.configReg, 0U);
IOMUXC_SetPinConfig(pindeets->mux.muxReg, pindeets->mux.muxMode,
pindeets->mux.inputReg, pindeets->mux.idaisy,
pindeets->mux.configReg, 0x10B0U);
if (state == OUTPUT) {
gpio_pin_config_t pin_config = { kGPIO_DigitalOutput, 0, kGPIO_NoIntmode };
GPIO_PinInit(pindeets->port, pindeets->pin, &pin_config);
} else {
gpio_pin_config_t pin_config = { kGPIO_DigitalInput, 0, kGPIO_NoIntmode };
GPIO_PinInit(pindeets->port, pindeets->pin, &pin_config);
if (state == INPUT_PULLUP) {
IOMUXC_SetPinConfig(pindeets->mux.muxReg, pindeets->mux.muxMode,
pindeets->mux.inputReg, pindeets->mux.idaisy,
pindeets->mux.configReg, 0xB0B0U);
}
}
}
uint32_t analogRead(uint32_t pinnum) {
pinmap *pindeets = getMapping(pinnum);
if (! pindeets) return -1;
if (! pindeets->adc) return -1;
/*
adc_config_t config = {0};
ADC_GetDefaultConfig(&config);
config.enableLongSample = true;
config.samplePeriodMode = kADC_SamplePeriod8or24Clocks;
ADC_Init(pindeets->adc, &config);
ADC_SetHardwareAverageConfig(pindeets->adc, kADC_HardwareAverageCount32);
ADC_DoAutoCalibration(pindeets->adc);
*/
return 0;
}

View file

@ -0,0 +1,57 @@
#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>
#include <string.h>
#include <stdarg.h>
#include "fsl_common.h"
#include "fsl_gpio.h"
#include "fsl_iomuxc.h"
#include "fsl_lpuart.h"
//#include "fsl_adc12.h"
#include "board_api.h"
#include "tusb.h"
#define INPUT 0
#define OUTPUT 1
#define INPUT_PULLUP 2
#define LOW 0
#define HIGH 1
#define PIN_SDA 30
#define PIN_SCL 31
#define PIN_MOSI 32
#define PIN_MISO 33
#define PIN_SCK 34
#define AD0 14
#define AD1 15
#define AD2 16
#define AD3 17
#define AD4 18
#define AD5 19
typedef struct _pinmux {
uint32_t muxReg;
uint8_t muxMode;
uint8_t inputReg;
uint8_t idaisy;
uint32_t configReg;
} pinmux;
typedef struct _pinmapping {
uint8_t num;
GPIO_Type *port;
uint32_t pin;
pinmux mux;
ADC_Type *adc;
} pinmap;
void Serial_printf(const char format[], ...) __attribute__ ((format (printf, 1, 0)));
uint32_t millis(void);
void delay(uint32_t ms);
void digitalWrite(uint32_t pinnum, bool value);
bool digitalRead(uint32_t pinnum);
void pinMode(uint32_t pinnum, uint8_t state);

View file

@ -0,0 +1,243 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2020 Ha Thach (tinyusb.org) for Adafruit Industries
*
* 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 <stdbool.h>
#include <string.h>
#include <stdarg.h>
#include "fsl_gpio.h"
#include "fsl_iomuxc.h"
#include "fsl_lpuart.h"
#include "board_api.h"
#include "tusb.h"
#include "arduino.h"
uint8_t all_pins[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, PIN_SDA, PIN_SCL};
bool testpins(uint8_t a, uint8_t b, uint8_t *allpins, uint8_t num_allpins);
/* This is an application to test Metro M7
*/
//--------------------------------------------------------------------+
// MACRO TYPEDEF CONSTANT ENUM DECLARATION
//--------------------------------------------------------------------+
// optional API, not included in board_api.h
int board_uart_read(uint8_t* buf, int len);
int main(void)
{
board_init();
board_uart_init(115200);
board_timer_start(1);
board_usb_init();
tusb_init();
uint8_t rgb[3] = { 20, 20, 0 };
board_rgb_write(rgb);
pinMode(13, OUTPUT);
while(1)
{
Serial_printf("\n\r\n\rHello Metro M7 iMX RT1011 Test! %d\n\r", millis());
digitalWrite(13, HIGH);
delay(100);
digitalWrite(13, LOW);
if ( !testpins(0, 2, all_pins, sizeof(all_pins))) continue;
if ( !testpins(1, 3, all_pins, sizeof(all_pins))) continue;
if ( !testpins(4, 6, all_pins, sizeof(all_pins))) continue;
if ( !testpins(5, 7, all_pins, sizeof(all_pins))) continue;
if ( !testpins(8, 10, all_pins, sizeof(all_pins))) continue;
if ( !testpins(9, 11, all_pins, sizeof(all_pins))) continue;
if ( !testpins(12, PIN_SDA, all_pins, sizeof(all_pins))) continue;
if ( !testpins(13, PIN_SCL, all_pins, sizeof(all_pins))) continue;
if ( !testpins(PIN_MOSI, PIN_MISO, all_pins, sizeof(all_pins))) continue;
if ( !testpins(PIN_SCK, AD5, all_pins, sizeof(all_pins))) continue;
Serial_printf("*** TEST OK! ***\n\r");
delay(100);
/*
// USB -> UART
while( tud_cdc_available() )
{
count = tud_cdc_read(serial_buf, sizeof(serial_buf));
board_uart_write(serial_buf, count);
uint8_t rgb[3] = { 10, 0, 0 };
board_rgb_write(rgb);
}
// UART -> USB
count = board_uart_read(serial_buf, sizeof(serial_buf));
if (count)
{
tud_cdc_write(serial_buf, count);
tud_cdc_write_flush();
uint8_t rgb[3] = { 0, 0, 10 };
board_rgb_write(rgb);
}
*/
tud_task();
}
}
//--------------------------------------------------------------------+
// Logger newlib retarget
//--------------------------------------------------------------------+
// Enable only with LOG is enabled (Note: ESP32-S2 has built-in support already)
#if TUF2_LOG // && (CFG_TUSB_MCU != OPT_MCU_ESP32S2)
#if defined(LOGGER_RTT)
#include "SEGGER_RTT.h"
#endif
__attribute__ ((used)) int _write (int fhdl, const void *buf, size_t count)
{
(void) fhdl;
#if defined(LOGGER_RTT)
SEGGER_RTT_Write(0, (char*) buf, (int) count);
return count;
#else
return board_uart_write(buf, count);
#endif
}
#endif
bool testpins(uint8_t a, uint8_t b, uint8_t *allpins, uint8_t num_allpins) {
bool ok = false;
Serial_printf("\tTesting %d and %d\n\r", a, b);
// set both to inputs
pinMode(b, INPUT);
// turn on 'a' pullup
pinMode(a, INPUT_PULLUP);
delay(1);
// verify neither are grounded
if (!digitalRead(a) || !digitalRead(b)) {
Serial_printf("Ground test 1 fail: both pins should not be grounded");
return false;
}
for (int retry=0; retry<3 && !ok; retry++) {
// turn off both pullups
pinMode(a, INPUT);
pinMode(b, INPUT);
// make a an output
pinMode(a, OUTPUT);
digitalWrite(a, LOW);
delay(1);
int ar = digitalRead(a);
int br = digitalRead(b);
delay(5);
// make sure both are low
if (ar || br) {
Serial_printf("Low test fail on pin #");
if (ar) Serial_printf("%d\n\r", a);
if (br) Serial_printf("%d\n\r", b);
ok = false;
continue;
}
ok = true;
}
if (!ok) return false;
ok = false;
for (int retry=0; retry<3 && !ok; retry++) {
//theSerial->println("OK!");
// a is an input, b is an output
pinMode(a, INPUT);
pinMode(b, OUTPUT);
digitalWrite(b, HIGH);
delay(10);
// verify neither are grounded
if (!digitalRead(a)|| !digitalRead(b)) {
Serial_printf("Ground test 2 fail: both pins should not be grounded");
delay(100);
ok = false;
continue;
}
ok = true;
}
if (!ok) return false;
// make sure no pins are shorted to pin a or b
for (uint8_t i = 0; i < num_allpins; i++) {
pinMode(allpins[i], INPUT_PULLUP);
}
pinMode(a, OUTPUT);
digitalWrite(a, LOW);
pinMode(b, OUTPUT);
digitalWrite(b, LOW);
delay(1);
for (uint8_t i = 0; i < num_allpins; i++) {
if ((allpins[i] == a) || (allpins[i] == b)) {
continue;
}
// theSerial->print("Pin #"); theSerial->print(allpins[i]);
// theSerial->print(" -> ");
// theSerial->println(digitalRead(allpins[i]));
if (!digitalRead(allpins[i])) {
Serial_printf("%d is shorted?\n\r", allpins[i]);
return false;
}
}
pinMode(a, INPUT);
pinMode(b, INPUT);
delay(10);
return true;
}

View file

@ -0,0 +1,89 @@
/*
* 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.
*
*/
#ifndef _TUSB_CONFIG_H_
#define _TUSB_CONFIG_H_
#ifdef __cplusplus
extern "C" {
#endif
//--------------------------------------------------------------------
// COMMON CONFIGURATION
//--------------------------------------------------------------------
#ifndef CFG_TUSB_MCU
#error CFG_TUSB_MCU must be defined in board.mk
#endif
#define CFG_TUSB_RHPORT0_MODE (OPT_MODE_DEVICE | OPT_MODE_HIGH_SPEED)
#define CFG_TUSB_OS OPT_OS_NONE
// can be defined by compiler in DEBUG build
#ifndef CFG_TUSB_DEBUG
#define CFG_TUSB_DEBUG 0
#endif
/* 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_CDC 1
#define CFG_TUD_MSC 0
#define CFG_TUD_HID 0
#define CFG_TUD_MIDI 0
#define CFG_TUD_VENDOR 0
// CDC FIFO size of TX and RX
#define CFG_TUD_CDC_RX_BUFSIZE 1024
#define CFG_TUD_CDC_TX_BUFSIZE 1024
#define CFG_TUD_CDC_EP_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)
#ifdef __cplusplus
}
#endif
#endif /* _TUSB_CONFIG_H_ */

View file

@ -0,0 +1,195 @@
/*
* 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 "board_api.h"
#include "tusb.h"
// String Descriptor Index
enum
{
STRID_LANGID = 0,
STRID_MANUFACTURER,
STRID_PRODUCT,
STRID_SERIAL,
STRID_CDC
};
enum
{
ITF_NUM_CDC,
ITF_NUM_CDC_DATA,
ITF_NUM_TOTAL
};
//--------------------------------------------------------------------+
// Device Descriptors
//--------------------------------------------------------------------+
tusb_desc_device_t const desc_device =
{
.bLength = sizeof(tusb_desc_device_t),
.bDescriptorType = TUSB_DESC_DEVICE,
.bcdUSB = 0x0200,
// Use Interface Association Descriptor (IAD) for CDC
// As required by USB Specs IAD's subclass must be common class (2) and protocol must be IAD (1)
.bDeviceClass = TUSB_CLASS_MISC,
.bDeviceSubClass = MISC_SUBCLASS_COMMON,
.bDeviceProtocol = MISC_PROTOCOL_IAD,
.bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,
.idVendor = USB_VID,
.idProduct = 0x8000 | USB_PID, // application PID
.bcdDevice = 0x0100,
.iManufacturer = STRID_MANUFACTURER,
.iProduct = STRID_PRODUCT,
.iSerialNumber = STRID_SERIAL,
.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
//--------------------------------------------------------------------+
#define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_CDC_DESC_LEN)
#define EPNUM_CDC_NOTIF 0x81
#define EPNUM_CDC_DATA 0x02
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, TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 100),
// CDC: Interface number, string index, EP notification address and size, EP data address (out, in) and size.
TUD_CDC_DESCRIPTOR(ITF_NUM_CDC, 4, EPNUM_CDC_NOTIF, 8, EPNUM_CDC_DATA, 0x80 | EPNUM_CDC_DATA, 64)
};
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, TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 100),
// CDC: Interface number, string index, EP notification address and size, EP data address (out, in) and size.
TUD_CDC_DESCRIPTOR(ITF_NUM_CDC, STRID_CDC, EPNUM_CDC_NOTIF, 8, EPNUM_CDC_DATA, 0x80 | EPNUM_CDC_DATA, 512)
};
// 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
// Although we are highspeed, host may be fullspeed.
return (tud_speed_get() == TUSB_SPEED_HIGH) ? desc_hs_configuration : desc_fs_configuration;
}
//--------------------------------------------------------------------+
// String Descriptors
//--------------------------------------------------------------------+
// Serial is 64-bit DeviceID -> 16 chars len
static char desc_str_serial[1+16] = { 0 };
// array of pointer to string descriptors
char const* string_desc_arr [] =
{
(const char[]) { 0x09, 0x04 }, // 0: is supported language is English (0x0409)
USB_MANUFACTURER, // 1: Manufacturer
USB_PRODUCT, // 2: Product
desc_str_serial, // 3: Serials, use default MAC address
"USB to UART", // 4: CDC Interface
};
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;
uint8_t chr_count;
switch (index)
{
case STRID_LANGID:
memcpy(&_desc_str[1], string_desc_arr[0], 2);
chr_count = 1;
break;
case STRID_SERIAL:
{
uint8_t serial_id[16];
uint8_t serial_len;
serial_len = board_usb_get_serial(serial_id);
chr_count = 2*serial_len;
for ( uint8_t i = 0; i < serial_len; i++ )
{
for ( uint8_t j = 0; j < 2; j++ )
{
const char nibble_to_hex[16] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
uint8_t nibble = (serial_id[i] >> (j * 4)) & 0xf;
_desc_str[1 + i * 2 + (1 - j)] = nibble_to_hex[nibble]; // UTF-16-LE
}
}
}
break;
default:
{
// Convert ASCII string into UTF-16
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);
if ( chr_count > 31 ) chr_count = 31;
for(uint8_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] = (TUSB_DESC_STRING << 8 ) | (2*chr_count + 2);
return _desc_str;
}