diff --git a/examples/irda_rx/Makefile b/examples/irda_rx/Makefile new file mode 100644 index 0000000..aaabed0 --- /dev/null +++ b/examples/irda_rx/Makefile @@ -0,0 +1,24 @@ +# Keep this first line. +GOSSAMER_PATH = ../.. + +# If your firmware targets a specific board, specify it here, +# or omit it and provide it on the command line (make BOARD=foo). +BOARD=circuitplayground + +# enable USB +TINYUSB_CDC=1 + +# Leave this line here. +include $(GOSSAMER_PATH)/make.mk + +INCLUDES += \ + -I../drivers/tinyusb/src \ + +# Add all your source files to compile after the app.c line. +SRCS += \ + ./app.c \ + ./usb_descriptors.c \ + ./usb_cdc.c \ + +# Finally, leave this line at the bottom of the file. +include $(GOSSAMER_PATH)/rules.mk diff --git a/examples/irda_rx/app.c b/examples/irda_rx/app.c new file mode 100644 index 0000000..de3417d --- /dev/null +++ b/examples/irda_rx/app.c @@ -0,0 +1,47 @@ +#include +#include +#include "app.h" +#include "uart.h" +#include "delay.h" +#include "usb.h" +#include "usb_cdc.h" + +void app_init(void) { +} + +void app_setup(void) { + usb_init(); + usb_enable(); + HAL_GPIO_IR_ANALOG_in(); + HAL_GPIO_IR_ANALOG_pullup(); + HAL_GPIO_IR_ANALOG_pmuxen(HAL_GPIO_PMUX_SERCOM_ALT); + uart_init_instance(0, UART_TXPO_NONE, UART_RXPO_0, 300); + uart_set_irda_mode_instance(0, true); + uart_enable_instance(0); +} + +bool app_loop(void) { + uint8_t buf[32]; + size_t bytes_read = uart_read_instance(0, &buf, 32); + if (bytes_read) { + buf[31] = 0; + // dump as hex + for(int i = 0; i < bytes_read; i++) { + printf("%c", buf[i]); + } + } + + yield(); + + return false; +} + +void irq_handler_sercom0(void); +void irq_handler_sercom0(void) { + uart_irq_handler(0); +} + +void yield(void) { + tud_task(); + cdc_task(); +} diff --git a/examples/irda_rx/usb_cdc.c b/examples/irda_rx/usb_cdc.c new file mode 100644 index 0000000..0053a72 --- /dev/null +++ b/examples/irda_rx/usb_cdc.c @@ -0,0 +1,138 @@ +/* + * MIT License + * + * Copyright (c) 2020 Joey Castillo + * Copyright (c) 2023 Edward Shin + * + * 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 +#include "usb_cdc.h" +#include "tusb.h" + +/* + * Implement a circular buffer for the USB CDC Serial read buffer. + * The size of the buffer must be a power of two for this circular buffer + * implementation to work. + */ + +// Size of the circular buffer. Must be a power of two. +#define CDC_WRITE_BUF_SZ (1024) +// Macro function to perform modular arithmetic on an index. +// eg. (63 + 2) & (64 - 1) -> 1 +#define CDC_WRITE_BUF_IDX(x) ((x) & (CDC_WRITE_BUF_SZ - 1)) +static char s_write_buf[CDC_WRITE_BUF_SZ] = {0}; +static size_t s_write_buf_pos = 0; +static size_t s_write_buf_len = 0; + +#define CDC_READ_BUF_SZ (256) +#define CDC_READ_BUF_IDX(x) ((x) & (CDC_READ_BUF_SZ - 1)) +static char s_read_buf[CDC_READ_BUF_SZ] = {0}; +static size_t s_read_buf_pos = 0; +static size_t s_read_buf_len = 0; + +int _write(int file, char *ptr, int len) { + (void) file; + + if (ptr == NULL || len <= 0) { + return -1; + } + + int bytes_written = 0; + + for (int i = 0; i < len; i++) { + s_write_buf[s_write_buf_pos] = ptr[i]; + s_write_buf_pos = CDC_WRITE_BUF_IDX(s_write_buf_pos + 1); + if (s_write_buf_len < CDC_WRITE_BUF_SZ) { + s_write_buf_len++; + } + bytes_written++; + } + + return bytes_written; +} + +int _read(int file, char *ptr, int len) { + (void) file; + + if (ptr == NULL || len <= 0 || s_read_buf_len == 0) { + return -1; + } + + // Clamp to the length of the read buffer + if ((size_t) len > s_read_buf_len) { + len = s_read_buf_len; + } + + // Calculate the start of the circular buffer, and iterate from there + const size_t start_pos = CDC_READ_BUF_IDX(s_read_buf_pos - len); + for (size_t i = 0; i < (size_t) len; i++) { + const size_t idx = CDC_READ_BUF_IDX(start_pos + i); + ptr[i] = s_read_buf[idx]; + s_read_buf[idx] = 0; + } + + // Update circular buffer position and length + s_read_buf_len -= len; + s_read_buf_pos = CDC_READ_BUF_IDX(s_read_buf_pos - len); + + return len; +} + +static void prv_handle_reads(void) { + while (tud_cdc_available()) { + int c = tud_cdc_read_char(); + if (c < 0) { + continue; + } + s_read_buf[s_read_buf_pos] = c; + s_read_buf_pos = CDC_READ_BUF_IDX(s_read_buf_pos + 1); + if (s_read_buf_len < CDC_READ_BUF_SZ) { + s_read_buf_len++; + } + } +} + +static void prv_handle_writes(void) { + if (s_write_buf_len > 0) { + const size_t start_pos = + CDC_WRITE_BUF_IDX(s_write_buf_pos - s_write_buf_len); + for (size_t i = 0; i < (size_t) s_write_buf_len; i++) { + const size_t idx = CDC_WRITE_BUF_IDX(start_pos + i); + if (tud_cdc_available() > 0) { + // If we receive data while doing a large write, we need to + // fully service it before continuing to write, or the + // stack will crash. + prv_handle_reads(); + } + if (tud_cdc_write_available()) { + tud_cdc_write(&s_write_buf[idx], 1); + } + s_write_buf[idx] = 0; + s_write_buf_len--; + } + tud_cdc_write_flush(); + } +} + +void cdc_task(void) { + prv_handle_reads(); + prv_handle_writes(); +} diff --git a/examples/irda_rx/usb_cdc.h b/examples/irda_rx/usb_cdc.h new file mode 100644 index 0000000..4559ff7 --- /dev/null +++ b/examples/irda_rx/usb_cdc.h @@ -0,0 +1,30 @@ +/* + * MIT License + * + * Copyright (c) 2020 Joey Castillo + * Copyright (c) 2023 Edward Shin + * + * 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. + */ + +#pragma once + +int _write(int file, char *ptr, int len); +int _read(int file, char *ptr, int len); +void cdc_task(void); diff --git a/examples/irda_rx/usb_descriptors.c b/examples/irda_rx/usb_descriptors.c new file mode 100644 index 0000000..cf203ff --- /dev/null +++ b/examples/irda_rx/usb_descriptors.c @@ -0,0 +1,154 @@ +/* + * 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" + +//--------------------------------------------------------------------+ +// 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 = 0x1209, + .idProduct = 0x2151, + .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 +//--------------------------------------------------------------------+ + +static char serialnum[32 * 2 + 1] = {'\0'}; // 2 chars per hexnumber + '\0' + +// array of pointer to string descriptors +char const* string_desc_arr [] = { + (const char[]) { 0x09, 0x04 }, // 0: is supported language is English (0x0409) + "Oddly Specific Objects", // 1: Manufacturer + "Sensor Watch", // 2: Product (we're using the Sensor Watch PID for this, shhh!) + serialnum, // 3: Serial number + "TinyUSB CDC", // 4: CDC Interface +}; + +static uint16_t _desc_str[32]; +static bool serial_number_written = false; + +// 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 (!serial_number_written) { + // Generate serial number based on chip unique ID + uint32_t chipid[4]; + chipid[0] = *((uint32_t *)0x0080A00C); + chipid[1] = *((uint32_t *)0x0080A040); + chipid[2] = *((uint32_t *)0x0080A044); + chipid[3] = *((uint32_t *)0x0080A048); + + sprintf(serialnum, "%08lX%08lX%08lX%08lX", chipid[0], chipid[1], chipid[2], chipid[3]); + serial_number_written = true; + } + + 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 +#include +#include "app.h" +#include "uart.h" +#include "delay.h" + +void app_init(void) { +} + +void app_setup(void) { + HAL_GPIO_TX_LED_out(); + HAL_GPIO_TX_LED_clr(); + HAL_GPIO_RX_LED_out(); + HAL_GPIO_RX_LED_clr(); + HAL_GPIO_TX_out(); + + HAL_GPIO_TX_pmuxen(HAL_GPIO_PMUX_SERCOM); + uart_init_instance(0, UART_TXPO_2, UART_RXPO_NONE, 300); + uart_set_irda_mode_instance(0, true); + uart_enable_instance(0); +} + +bool app_loop(void) { + char hello[] = "Hello, world!\r\n"; + HAL_GPIO_TX_LED_toggle(); + uart_write_instance(0, hello, strlen(hello)); + delay_ms(50); + HAL_GPIO_TX_LED_toggle(); + delay_ms(950); + + return false; +} + +void irq_handler_sercom0(void); +void irq_handler_sercom0(void) { + uart_irq_handler(0); +} \ No newline at end of file