]> git.earman.xyz Git - sensor-watch.git/commitdiff
implement USB console
authorJoey Castillo <jose.castillo@gmail.com>
Sat, 28 Aug 2021 17:35:52 +0000 (13:35 -0400)
committerJoey Castillo <jose.castillo@gmail.com>
Sat, 28 Aug 2021 17:55:17 +0000 (13:55 -0400)
Sensor Watch Starter Project/make/Makefile
watch-library/hw/atmel_start_pins.h
watch-library/main.c
watch-library/watch/tusb_config.h [new file with mode: 0644]
watch-library/watch/watch_gpio.c
watch-library/watch/watch_gpio.h
watch-library/watch/watch_private.c
watch-library/watch/watch_private.h
watch-library/watch/watch_uart.c

index 5f811620688d6c182e2ee62e28ef9833cfbfbd0d..e9a581d55a3443fabbcc57b3129532270c9c853e 100755 (executable)
@@ -31,6 +31,7 @@ LDFLAGS += -Wl,--script=../../watch-library/linker/saml22j18.ld
 # ../drivers/\r
 INCLUDES += \\r
   -I../ \\r
+  -I../../tinyusb/src \\r
   -I../../watch-library/include \\r
   -I../../watch-library/hal/ \\r
   -I../../watch-library/hal/documentation/ \\r
@@ -66,6 +67,12 @@ INCLUDES += \
 # ../drivers/st25dv.c\r
 SRCS += \\r
   ../app.c \\r
+  ../../tinyusb/src/tusb.c \\r
+  ../../tinyusb/src/common/tusb_fifo.c \\r
+  ../../tinyusb/src/class/cdc/cdc_device.c \\r
+  ../../tinyusb/src/device/usbd.c \\r
+  ../../tinyusb/src/device/usbd_control.c \\r
+  ../../tinyusb/src/portable/microchip/samd/dcd_samd.c \\r
   ../../watch-library/main.c \\r
   ../../watch-library/startup_saml22.c \\r
   ../../watch-library/hw/driver_init.c \\r
index 7a3a7d78bdd388dfeede7cbfd07257326aa6b8da..14df95e1d84e9fc4a053edea103a0ff2da839965 100644 (file)
@@ -22,7 +22,7 @@
 #define GPIO_PIN_FUNCTION_H 7
 #define GPIO_PIN_FUNCTION_I 8
 
-#define VBUS_DET GPIO(GPIO_PORTA, 2)
+#define VBUS_DET GPIO(GPIO_PORTA, 3)
 #define SEG1 GPIO(GPIO_PORTA, 4)
 #define SEG2 GPIO(GPIO_PORTA, 5)
 #define SEG3 GPIO(GPIO_PORTA, 6)
index c23ceac6a75d2dc47fb412ae0ae3c11502a9edc1..886ab8514391e51abcd4490625bc3146d5f63fef 100755 (executable)
@@ -32,6 +32,7 @@
 #include "hal_init.h"
 #include "atmel_start_pins.h"
 #include "watch.h"
+#include "tusb.h"
 
 int main(void) {
     // ASF code. Initialize the MCU with configuration options from Atmel Studio.
@@ -54,12 +55,27 @@ int main(void) {
     // Watch library code. Set initial parameters for the device and enable the RTC.
     _watch_init();
 
+    // check if we are plugged into USB power.
+    watch_enable_digital_input(VBUS_DET);
+    watch_enable_pull_down(VBUS_DET);
+    // IF WE ARE:
+    if (watch_get_pin_level(VBUS_DET)) {
+        // Ramp up to 16 MHz (seems necessary for USB to work)...
+        hri_oscctrl_write_OSC16MCTRL_reg(OSCCTRL, OSCCTRL_OSC16MCTRL_ONDEMAND | OSCCTRL_OSC16MCTRL_FSEL_16 | OSCCTRL_OSC16MCTRL_ENABLE);
+        // ...and enable USB functionality.
+        _watch_enable_usb();
+    }
+    watch_disable_digital_input(VBUS_DET);
+
     // User code. Give the app a chance to enable and set up peripherals.
     app_setup();
 
     while (1) {
         bool can_sleep = app_loop();
-        if (can_sleep) {
+        if (hri_usbdevice_get_CTRLA_ENABLE_bit(USB)) {
+            // if USB is enabled, do not sleep, and handle any pending TinyUSB tasks.
+            tud_task();
+        } else if (can_sleep) {
             app_prepare_for_sleep();
             sleep(4);
             app_wake_from_sleep();
diff --git a/watch-library/watch/tusb_config.h b/watch-library/watch/tusb_config.h
new file mode 100644 (file)
index 0000000..fa981f3
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ * 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
+//--------------------------------------------------------------------
+
+// defined by board.mk
+#define CFG_TUSB_MCU OPT_MCU_SAML22
+
+#define BOARD_DEVICE_RHPORT_NUM     0
+#define BOARD_DEVICE_RHPORT_SPEED   OPT_MODE_FULL_SPEED
+#define CFG_TUSB_RHPORT0_MODE     (OPT_MODE_DEVICE | OPT_MODE_FULL_SPEED)
+
+#define CFG_TUSB_OS                 OPT_OS_NONE
+
+// Do not use TinyUSB debug, as our printf method prints stuff to the USB console.
+// if you must, modify _write in watch_private.c to use an alternate destination like a UART.
+#define CFG_TUSB_DEBUG           0
+
+/* 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   (64)
+#define CFG_TUD_CDC_TX_BUFSIZE   (64)
+
+// CDC Endpoint transfer buffer size, more is faster
+#define CFG_TUD_CDC_EP_BUFSIZE   (64)
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif /* _TUSB_CONFIG_H_ */
index 460a38df94bbaf7d6173f41dff9d5159566af58b..a9bc139d566376c827003bdc3c78210bdec4985f 100644 (file)
     gpio_set_pin_function(pin, GPIO_PIN_FUNCTION_OFF);
 }
 
+void watch_disable_digital_input(const uint8_t pin) {
+    gpio_set_pin_direction(pin, GPIO_DIRECTION_OFF);
+    gpio_set_pin_pull_mode(pin, GPIO_PULL_OFF);
+    gpio_set_pin_function(pin, GPIO_PIN_FUNCTION_OFF);
+}
+
 void watch_enable_pull_up(const uint8_t pin) {
     gpio_set_pin_pull_mode(pin, GPIO_PULL_UP);
 }
index f7678f6fcd62801f53e7c6e121678f61408987d0..640686e0861b51d04b24d683af89fdec084b1837 100644 (file)
   */
 void watch_enable_digital_input(const uint8_t pin);
 
+/** @brief Disables any digital input, along with any pull-up or pull-down configuration.
+  * @param pin The pin that you wish to disable.
+  */
+void watch_disable_digital_input(const uint8_t pin);
+
 /** @brief Enables a pull-up resistor on the selected pin.
   * @param pin The pin that you wish to configure.
   */
index cd9c2baa9165972b6209643eca639b94261921de..7645fb298e4fd3040b1ee03af64a585a7bed8257 100644 (file)
@@ -22,6 +22,8 @@
  * SOFTWARE.
  */
 
+#include "tusb.h"
+
 void _watch_init() {
     // disable the LED pin (it may have been enabled by the bootloader)
     watch_disable_digital_output(RED);
@@ -42,3 +44,226 @@ void _watch_init() {
     a2_callback = NULL;
     a4_callback = NULL;
 }
+
+void _watch_enable_usb() {
+    // disable USB, just in case.
+    hri_usb_clear_CTRLA_ENABLE_bit(USB);
+
+    // reset flags and disable DFLL
+    OSCCTRL->INTFLAG.reg = OSCCTRL_INTFLAG_DFLLRDY;
+    OSCCTRL->DFLLCTRL.reg = 0;
+    while (!(OSCCTRL->STATUS.reg & OSCCTRL_STATUS_DFLLRDY));
+
+    // set the coarse and fine values to speed up frequency lock.
+    uint32_t coarse =(*((uint32_t *)NVMCTRL_OTP5)) >> 26;
+    OSCCTRL->DFLLVAL.reg = OSCCTRL_DFLLVAL_COARSE(coarse) |
+                           OSCCTRL_DFLLVAL_FINE(0x200);
+    // set coarse and fine steps, and multiplier (48 MHz = 32768 Hz * 1465)
+    OSCCTRL->DFLLMUL.reg = OSCCTRL_DFLLMUL_CSTEP( 1 ) |
+                           OSCCTRL_DFLLMUL_FSTEP( 1 ) |
+                           OSCCTRL_DFLLMUL_MUL( 1465 );
+    // set closed loop mode, chill cycle disable and USB clock recovery mode, and enable the DFLL.
+    OSCCTRL->DFLLCTRL.reg = OSCCTRL_DFLLCTRL_MODE | OSCCTRL_DFLLCTRL_CCDIS | OSCCTRL_DFLLCTRL_ONDEMAND | OSCCTRL_DFLLCTRL_RUNSTDBY | OSCCTRL_DFLLCTRL_USBCRM | OSCCTRL_DFLLCTRL_ENABLE;
+    while (!(OSCCTRL->STATUS.reg & OSCCTRL_STATUS_DFLLRDY));
+
+    // assign DFLL to GCLK1
+    GCLK->GENCTRL[1].reg = GCLK_GENCTRL_SRC(GCLK_GENCTRL_SRC_DFLL48M) | GCLK_GENCTRL_DIV(1) | GCLK_GENCTRL_GENEN;// | GCLK_GENCTRL_OE;
+    while (GCLK->SYNCBUSY.bit.GENCTRL1);
+
+    // assign GCLK1 to USB
+    hri_gclk_write_PCHCTRL_reg(GCLK, USB_GCLK_ID, GCLK_PCHCTRL_GEN_GCLK1_Val | GCLK_PCHCTRL_CHEN);
+    hri_mclk_set_AHBMASK_USB_bit(MCLK);
+    hri_mclk_set_APBBMASK_USB_bit(MCLK);
+
+    // USB Pin Init
+    gpio_set_pin_direction(PIN_PA24, GPIO_DIRECTION_OUT);
+    gpio_set_pin_level(PIN_PA24, false);
+    gpio_set_pin_pull_mode(PIN_PA24, GPIO_PULL_OFF);
+    gpio_set_pin_direction(PIN_PA25, GPIO_DIRECTION_OUT);
+    gpio_set_pin_level(PIN_PA25, false);
+    gpio_set_pin_pull_mode(PIN_PA25, GPIO_PULL_OFF);
+
+    gpio_set_pin_function(PIN_PA24, PINMUX_PA24G_USB_DM);
+    gpio_set_pin_function(PIN_PA25, PINMUX_PA25G_USB_DP);
+
+    tusb_init();
+}
+
+// this function ends up getting called by printf to log stuff to the USB console.
+int _write(int file, char *ptr, int len) {
+    (void)file;
+    if (hri_usbdevice_get_CTRLA_ENABLE_bit(USB)) {
+        tud_cdc_n_write(0, (void const*)ptr, len);
+        return len;
+    }
+
+    return 0;
+}
+
+// this method could be overridden to read stuff from the USB console? but no need rn.
+int _read() {
+    return 0;
+}
+
+void USB_Handler(void) {
+    tud_int_handler(0);
+}
+
+
+// USB Descriptors and tinyUSB callbacks follow.
+
+/*
+ * 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 "tusb.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) )
+
+//--------------------------------------------------------------------+
+// 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           = 0xCafe,
+    .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
+//--------------------------------------------------------------------+
+
+enum {
+  ITF_NUM_CDC = 0,
+  ITF_NUM_CDC_DATA,
+  ITF_NUM_TOTAL
+};
+
+#define CONFIG_TOTAL_LEN    (TUD_CONFIG_DESC_LEN + TUD_CDC_DESC_LEN)
+
+#define EPNUM_CDC_NOTIF   0x81
+#define EPNUM_CDC_OUT     0x02
+#define EPNUM_CDC_IN      0x82
+
+
+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),
+
+  // 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_OUT, EPNUM_CDC_IN, 64),
+};
+
+// 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
+    return desc_fs_configuration;
+}
+
+//--------------------------------------------------------------------+
+// String Descriptors
+//--------------------------------------------------------------------+
+
+// 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
+  "123456",                      // 3: Serials, should use chip ID
+  "TinyUSB CDC",                 // 4: CDC Interface
+};
+
+static uint16_t _desc_str[32];
+
+// 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;
+
+    if ( index == 0) {
+        memcpy(&_desc_str[1], string_desc_arr[0], 2);
+        chr_count = 1;
+    } else {
+        // 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);
+        if ( chr_count > 31 ) chr_count = 31;
+
+        // Convert ASCII string into UTF-16
+        for(uint8_t i=0; i<chr_count; i++)
+        {
+            _desc_str[1+i] = str[i];
+        }
+    }
+
+    // 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;
+}
index 99171126db5eae5d711df082b37beca247badf4f..51c78ae1aedf10bacefeb6d926235256280c8d73 100644 (file)
@@ -24,3 +24,6 @@
 
 /// Called by main.c while setting up the app. You should not call this from your app.
 void _watch_init();
+
+/// Called by main.c if plugged in to USB. You should not call this from your app.
+void _watch_enable_usb();
index 4cbac89b182e6d26e95e56d43f666ed526c7e340..68ac872e045ff6cfe0cb953338e4ef7396cde1f0 100644 (file)
@@ -53,6 +53,7 @@
 #include "peripheral_clk_config.h"
 
 void watch_enable_debug_uart(uint32_t baud) {
+    /// FIXME: UART baud rate calculation will be incorrect if plugged into USB / running at 16 MHz
     uint64_t br = (uint64_t)65536 * (CONF_CPU_FREQUENCY - 16 * baud) / CONF_CPU_FREQUENCY;
 
     gpio_set_pin_direction(D1, GPIO_DIRECTION_IN);