From 8fc012c7617f068f57bdcd7e93b77252e98f5f5d Mon Sep 17 00:00:00 2001 From: Luigi Cruz Date: Thu, 11 Mar 2021 22:50:45 -0300 Subject: [PATCH] Add documentation to apps. --- apps/CMakeLists.txt | 1 + apps/adc_dma_chain/README.md | 37 ++ apps/iperf_server/README.md | 13 + apps/piccolosdr/CMakeLists.txt | 29 ++ apps/piccolosdr/README.md | 15 + apps/piccolosdr/lwipopts.h | 60 ++++ apps/piccolosdr/main.c | 379 +++++++++++++++++++++ apps/piccolosdr/media/gnuradio_example.jpg | Bin 0 -> 72522 bytes apps/piccolosdr/tusb_config.h | 109 ++++++ apps/piccolosdr/usb_descriptors.c | 225 ++++++++++++ apps/tcp_server/CMakeLists.txt | 2 - apps/tcp_server/README.md | 33 ++ apps/tcp_server/main.c | 141 +------- 13 files changed, 917 insertions(+), 127 deletions(-) create mode 100644 apps/adc_dma_chain/README.md create mode 100644 apps/iperf_server/README.md create mode 100644 apps/piccolosdr/CMakeLists.txt create mode 100644 apps/piccolosdr/README.md create mode 100644 apps/piccolosdr/lwipopts.h create mode 100644 apps/piccolosdr/main.c create mode 100644 apps/piccolosdr/media/gnuradio_example.jpg create mode 100644 apps/piccolosdr/tusb_config.h create mode 100644 apps/piccolosdr/usb_descriptors.c create mode 100644 apps/tcp_server/README.md diff --git a/apps/CMakeLists.txt b/apps/CMakeLists.txt index 650b074..98fda2f 100644 --- a/apps/CMakeLists.txt +++ b/apps/CMakeLists.txt @@ -1,3 +1,4 @@ add_subdirectory(iperf_server) add_subdirectory(tcp_server) add_subdirectory(adc_dma_chain) +add_subdirectory(piccolosdr) \ No newline at end of file diff --git a/apps/adc_dma_chain/README.md b/apps/adc_dma_chain/README.md new file mode 100644 index 0000000..a885526 --- /dev/null +++ b/apps/adc_dma_chain/README.md @@ -0,0 +1,37 @@ +# ADC DMA Chain +This is an example of the ADC of the Pico working with chained DMA buffers. This will collect the samples from the ADC using two DMA channels as fast as possible (500ksps). When a DMA is full, the channel will raise an interrupt and start the second channel immediately. + +### Dependencies +None. + +### Usage +This program will start collecting samples when it receives a char from the virtual serial port. It will also output the following messages: + +```txt +Hello from Pi Pico! +Arming DMA. +Start capture. +DMA IRQ 0 [48 47 47] +DMA IRQ 1 [47 47 47] +DMA IRQ 0 [47 47 47] +DMA IRQ 1 [47 47 47] +DMA IRQ 0 [47 47 47] +DMA IRQ 1 [47 47 47] +DMA IRQ 0 [47 47 47] +DMA IRQ 1 [47 47 47] +DMA IRQ 0 [47 47 47] +DMA IRQ 1 [47 47 47] +DMA IRQ 0 [47 47 47] +DMA IRQ 1 [47 47 47] +DMA IRQ 0 [47 47 47] +DMA IRQ 1 [47 47 47] +DMA IRQ 0 [47 47 47] +DMA IRQ 1 [47 47 47] +DMA IRQ 0 [47 47 47] +DMA IRQ 1 [47 47 47] +DMA IRQ 0 [47 47 47] +DMA IRQ 1 [47 47 47] +DMA IRQ 0 [47 47 47] +DMA IRQ 1 [47 47 47] +DMA IRQ 0 [47 47 47] +``` \ No newline at end of file diff --git a/apps/iperf_server/README.md b/apps/iperf_server/README.md new file mode 100644 index 0000000..4781989 --- /dev/null +++ b/apps/iperf_server/README.md @@ -0,0 +1,13 @@ +# IPERF Server +This is a tool to measure the network performance using `iperf2`. This program will make the Pico work like a network router when the device is plugged into a computer USB port using the RNDIS protocol. This was tested on Linux. This example will also create a sample HTTP page on 192.168.7.1. + +### Dependencies +- Patched `pico-sdr` and `pico-extras`. +- [USB Network Stack](/lib/networking) Library. + +### Usage +This program will start automatically. No user interaction with the device is needed. The device won't produce an output. You can measure the network speed with `iperf2` using the command below. Expect speeds between 6-10 Mbps. This is a physical limitation of the Full Speed USB present on the RP2040. + +```bash +$ iperf -c 192.168.7.1 +``` \ No newline at end of file diff --git a/apps/piccolosdr/CMakeLists.txt b/apps/piccolosdr/CMakeLists.txt new file mode 100644 index 0000000..f3a41b1 --- /dev/null +++ b/apps/piccolosdr/CMakeLists.txt @@ -0,0 +1,29 @@ +cmake_minimum_required(VERSION 3.12) + +project(piccolosdr) + +add_executable(piccolosdr + usb_descriptors.c + main.c +) + +target_link_libraries(piccolosdr + pico_stdlib + pico_stdio + tinyusb_host + tinyusb_board + tinyusb_net + hardware_adc + hardware_dma + hardware_irq + lwip +) + +target_include_directories(piccolosdr PRIVATE .) + +pico_enable_stdio_usb(piccolosdr 1) +pico_enable_stdio_uart(piccolosdr 0) + +pico_add_extra_outputs(piccolosdr) + +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) diff --git a/apps/piccolosdr/README.md b/apps/piccolosdr/README.md new file mode 100644 index 0000000..89ccd4b --- /dev/null +++ b/apps/piccolosdr/README.md @@ -0,0 +1,15 @@ +# PiccoloSDR (WIP) +This example is a Raspberry Pico RP2040 working as a basic direct-sampling SDR. The data is sent via USB using the RNDIS protocol to emulate a TCP/IP interface. The ADC speed is limited to 500 ksps. This was tested on Linux but should work fine on Windows. The data can be used with software like the GNU Radio, an example is available [here](/apps/piccolosdr/piccolosdr.grc). It requires an OOT module that can be found [here](https://github.com/ghostop14/gr-grnet). + +### Specifications +- 500 ksps Sample-rate +- 250 kHz Bandwidth + +### Dependencies Device +- Patched `pico-sdr` and `pico-extras`. +- [USB Network Stack](/lib/networking) Library. + +### Usage +This data stream will start when a TCP connection is established. After plugging the device in the USB port of your computer you will be able to open the GNU Radio flowgraph and see the data. + +![GNU Radio Example With PiccoloSDR](/apps/piccolosdr/media/gnuradio_example.jpg) \ No newline at end of file diff --git a/apps/piccolosdr/lwipopts.h b/apps/piccolosdr/lwipopts.h new file mode 100644 index 0000000..755e214 --- /dev/null +++ b/apps/piccolosdr/lwipopts.h @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2001-2003 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Simon Goldschmidt + * + */ +#ifndef __LWIPOPTS_H__ +#define __LWIPOPTS_H__ + +/* Prevent having to link sys_arch.c (we don't test the API layers in unit tests) */ +#define NO_SYS 1 +#define MEM_ALIGNMENT 4 +#define LWIP_RAW 0 +#define LWIP_NETCONN 0 +#define LWIP_SOCKET 0 +#define LWIP_DHCP 0 +#define LWIP_ICMP 1 +#define LWIP_UDP 1 +#define LWIP_TCP 1 +#define ETH_PAD_SIZE 0 +#define LWIP_IP_ACCEPT_UDP_PORT(p) ((p) == PP_NTOHS(67)) +#define LWIP_TCP_KEEPALIVE 1 + +#define TCP_MSS (1500 /*mtu*/ - 20 /*iphdr*/ - 20 /*tcphhr*/) +#define TCP_SND_BUF (2 * TCP_MSS) + +#define ETHARP_SUPPORT_STATIC_ENTRIES 1 + +#define LWIP_HTTPD_CGI 0 +#define LWIP_HTTPD_SSI 0 +#define LWIP_HTTPD_SSI_INCLUDE_TAG 0 + +#define LWIP_SINGLE_NETIF 1 + +#endif /* __LWIPOPTS_H__ */ diff --git a/apps/piccolosdr/main.c b/apps/piccolosdr/main.c new file mode 100644 index 0000000..884efa1 --- /dev/null +++ b/apps/piccolosdr/main.c @@ -0,0 +1,379 @@ +// Initial TinyUSB RNDIS written by Peter Lawrence. +// Modifications were made by Luigi Cruz. + +#include +#include "bsp/board.h" +#include "pico/stdlib.h" +#include "hardware/adc.h" +#include "hardware/dma.h" +#include "hardware/irq.h" + +#include "tusb.h" +#include "dhserver.h" +#include "dnserver.h" + +#include "lwip/init.h" +#include "lwip/timeouts.h" +#include "lwip/api.h" +#include "lwip/sys.h" +#include "lwip/tcp.h" + +/* lwip context */ +static struct netif netif_data; + +/* shared between tud_network_recv_cb() and service_traffic() */ +static struct pbuf *received_frame; + +/* this is used by this code, ./class/net/net_driver.c, and usb_descriptors.c */ +/* ideally speaking, this should be generated from the hardware's unique ID (if available) */ +/* it is suggested that the first byte is 0x02 to indicate a link-local address */ +const uint8_t tud_network_mac_address[6] = {0x02,0x02,0x84,0x6A,0x96,0x00}; + +/* network parameters of this MCU */ +static const ip_addr_t ipaddr = IPADDR4_INIT_BYTES(192, 168, 7, 1); +static const ip_addr_t netmask = IPADDR4_INIT_BYTES(255, 255, 255, 0); +static const ip_addr_t gateway = IPADDR4_INIT_BYTES(0, 0, 0, 0); + +/* database IP addresses that can be offered to the host; this must be in RAM to store assigned MAC addresses */ +static dhcp_entry_t entries[] = { + /* mac ip address lease time */ + { {0}, IPADDR4_INIT_BYTES(192, 168, 7, 2), 24 * 60 * 60 }, + { {0}, IPADDR4_INIT_BYTES(192, 168, 7, 3), 24 * 60 * 60 }, + { {0}, IPADDR4_INIT_BYTES(192, 168, 7, 4), 24 * 60 * 60 }, +}; + +static const dhcp_config_t dhcp_config = { + .router = IPADDR4_INIT_BYTES(0, 0, 0, 0), /* router address (if any) */ + .port = 67, /* listen port */ + .dns = IPADDR4_INIT_BYTES(192, 168, 7, 1), /* dns server (if any) */ + "usb", /* dns suffix */ + TU_ARRAY_SIZE(entries), /* num entry */ + entries /* entries */ +}; +static err_t linkoutput_fn(struct netif *netif, struct pbuf *p) { + (void)netif; + + for (;;) { + /* if TinyUSB isn't ready, we must signal back to lwip that there is nothing we can do */ + if (!tud_ready()) return ERR_USE; + + /* if the network driver can accept another packet, we make it happen */ + if (tud_network_can_xmit()) { + tud_network_xmit(p, 0 /* unused for this example */); + return ERR_OK; + } + + /* transfer execution to TinyUSB in the hopes that it will finish transmitting the prior packet */ + tud_task(); + } +} + +static err_t output_fn(struct netif *netif, struct pbuf *p, const ip_addr_t *addr) { + return etharp_output(netif, p, addr); +} + +static err_t netif_init_cb(struct netif *netif) { + LWIP_ASSERT("netif != NULL", (netif != NULL)); + netif->mtu = CFG_TUD_NET_MTU; + netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_LINK_UP | NETIF_FLAG_UP; + netif->state = NULL; + netif->name[0] = 'E'; + netif->name[1] = 'X'; + netif->linkoutput = linkoutput_fn; + netif->output = output_fn; + return ERR_OK; +} + +static void init_lwip(void) { + struct netif *netif = &netif_data; + + lwip_init(); + + /* the lwip virtual MAC address must be different from the host's; to ensure this, we toggle the LSbit */ + netif->hwaddr_len = sizeof(tud_network_mac_address); + memcpy(netif->hwaddr, tud_network_mac_address, sizeof(tud_network_mac_address)); + netif->hwaddr[5] ^= 0x01; + + netif = netif_add(netif, &ipaddr, &netmask, &gateway, NULL, netif_init_cb, ip_input); + netif_set_default(netif); +} + +/* handle any DNS requests from dns-server */ +bool dns_query_proc(const char *name, ip_addr_t *addr) +{ + if (0 == strcmp(name, "tiny.usb")) + { + *addr = ipaddr; + return true; + } + return false; +} + +bool tud_network_recv_cb(const uint8_t *src, uint16_t size) { + /* this shouldn't happen, but if we get another packet before + parsing the previous, we must signal our inability to accept it */ + if (received_frame) return false; + + if (size) { + struct pbuf *p = pbuf_alloc(PBUF_RAW, size, PBUF_POOL); + + if (p) { + /* pbuf_alloc() has already initialized struct; all we need to do is copy the data */ + memcpy(p->payload, src, size); + + /* store away the pointer for service_traffic() to later handle */ + received_frame = p; + } + } + + return true; +} + +uint16_t tud_network_xmit_cb(uint8_t *dst, void *ref, uint16_t arg) { + struct pbuf *p = (struct pbuf *)ref; + struct pbuf *q; + uint16_t len = 0; + + (void)arg; /* unused for this example */ + + /* traverse the "pbuf chain"; see ./lwip/src/core/pbuf.c for more info */ + for(q = p; q != NULL; q = q->next) { + memcpy(dst, (char *)q->payload, q->len); + dst += q->len; + len += q->len; + if (q->len == q->tot_len) break; + } + + return len; +} + +static void service_traffic(void) +{ + /* handle any packet received by tud_network_recv_cb() */ + if (received_frame) { + ethernet_input(received_frame, &netif_data); + pbuf_free(received_frame); + received_frame = NULL; + tud_network_recv_renew(); + } + + sys_check_timeouts(); +} + +void tud_network_init_cb(void) { + /* if the network is re-initializing and we have a leftover packet, we must do a cleanup */ + if (received_frame) { + pbuf_free(received_frame); + received_frame = NULL; + } +} + +bool streaming = false; +struct tcp_pcb* client; +struct repeating_timer timer; + +//#define DEBUG +#define CAPTURE_CHANNEL 0 +#define CAPTURE_DEPTH 500 + +uint dma_chan_a, dma_chan_b; +uint8_t capture_buf_a[CAPTURE_DEPTH]; +uint8_t capture_buf_b[CAPTURE_DEPTH]; + +void dma_handler(uint8_t* buffer, int id) { +#ifdef DEBUG + char str[64]; + int len = sprintf(str, "DMA IRQ %d [%d %d ... %d]\n", id, buffer[0], buffer[1], buffer[CAPTURE_DEPTH-1]); + tcp_write(client, str, len, 0); + tcp_output(client); +#else + tcp_write(client, buffer, CAPTURE_DEPTH, 0x01); + tcp_output(client); +#endif +} + +void dma_handler_a() { + dma_handler((uint8_t*)&capture_buf_a, 0); + dma_hw->ints0 = 1u << dma_chan_a; + dma_channel_set_write_addr(dma_chan_a, &capture_buf_a, false); +} + +void dma_handler_b() { + dma_handler((uint8_t*)&capture_buf_b, 1); + dma_hw->ints1 = 1u << dma_chan_b; + dma_channel_set_write_addr(dma_chan_b, &capture_buf_b, false); +} + +static void init_adc_dma_chain() { +adc_gpio_init(26 + CAPTURE_CHANNEL); + adc_init(); + adc_select_input(CAPTURE_CHANNEL); + adc_fifo_setup( + true, // Write to FIFO + true, // Enable DREQ + 1, // Trigger DREQ with at least one sample + false, // No ERR bit + true // Shift each sample by 8 bits + ); + adc_set_clkdiv(0); + + dma_channel_config dma_cfg_a, dma_cfg_b; + + dma_chan_a = dma_claim_unused_channel(true); + dma_chan_b = dma_claim_unused_channel(true); + + dma_cfg_a = dma_channel_get_default_config(dma_chan_a); + dma_cfg_b = dma_channel_get_default_config(dma_chan_b); + + channel_config_set_transfer_data_size(&dma_cfg_a, DMA_SIZE_8); + channel_config_set_transfer_data_size(&dma_cfg_b, DMA_SIZE_8); + + channel_config_set_read_increment(&dma_cfg_a, false); + channel_config_set_read_increment(&dma_cfg_b, false); + + channel_config_set_write_increment(&dma_cfg_a, true); + channel_config_set_write_increment(&dma_cfg_b, true); + + channel_config_set_dreq(&dma_cfg_a, DREQ_ADC); + channel_config_set_dreq(&dma_cfg_b, DREQ_ADC); + + channel_config_set_chain_to(&dma_cfg_a, dma_chan_b); + channel_config_set_chain_to(&dma_cfg_b, dma_chan_a); + + dma_channel_configure(dma_chan_a, &dma_cfg_a, + capture_buf_a, // dst + &adc_hw->fifo, // src + CAPTURE_DEPTH, // transfer count + true // start now + ); + + dma_channel_configure(dma_chan_b, &dma_cfg_b, + capture_buf_b, // dst + &adc_hw->fifo, // src + CAPTURE_DEPTH, // transfer count + false // start now + ); + + dma_channel_set_irq0_enabled(dma_chan_a, true); + irq_set_exclusive_handler(DMA_IRQ_0, dma_handler_a); + irq_set_enabled(DMA_IRQ_0, true); + + dma_channel_set_irq1_enabled(dma_chan_b, true); + irq_set_exclusive_handler(DMA_IRQ_1, dma_handler_b); + irq_set_enabled(DMA_IRQ_1, true); + + adc_run(false); +} + +static void start_stream() { + dma_channel_set_write_addr(dma_chan_a, &capture_buf_a, true); + dma_channel_set_write_addr(dma_chan_b, &capture_buf_b, false); + adc_run(true); + streaming = true; +} + +static void stop_stream() { + adc_run(false); + adc_fifo_drain(); + dma_channel_set_write_addr(dma_chan_a, &capture_buf_a, false); + dma_channel_set_write_addr(dma_chan_b, &capture_buf_b, false); + streaming = false; +} + +static void srv_close(struct tcp_pcb *pcb){ + stop_stream(); + + tcp_arg(pcb, NULL); + tcp_sent(pcb, NULL); + tcp_recv(pcb, NULL); + tcp_close(pcb); +} + +static void srv_err(void *arg, err_t err) { + // Probably an indication that the client connection went kaput! Stopping stream... + srv_close(client); +} + +static err_t srv_receive(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err) { + if (err != ERR_OK && p != NULL) { + goto exception; + } + + tcp_recved(pcb, p->tot_len); + tcp_sent(pcb, NULL); + + // The connection is closed if the client sends "X". + if (((char*)p->payload)[0] == 'X') { + srv_close(pcb); + } + +exception: + pbuf_free(p); + return err; +} + +static err_t srv_accept(void * arg, struct tcp_pcb * pcb, err_t err) { + if (err != ERR_OK) { + return err; + } + + tcp_setprio(pcb, TCP_PRIO_MAX); + tcp_recv(pcb, srv_receive); + tcp_err(pcb, srv_err); + tcp_poll(pcb, NULL, 4); + + client = pcb; + start_stream(); + + return err; +} + +bool led_timer(struct repeating_timer *t) { + int status = 1; + if (streaming) { + status = !gpio_get(PICO_DEFAULT_LED_PIN); + } + gpio_put(PICO_DEFAULT_LED_PIN, status); + return true; +} + +int main(void) { + board_init(); + + // Init built-in LED. + gpio_init(PICO_DEFAULT_LED_PIN); + gpio_set_dir(PICO_DEFAULT_LED_PIN, GPIO_OUT); + + // Init ADC DMA chain. + init_adc_dma_chain(); + + // Init network stack. + tusb_init(); + init_lwip(); + + // Startup lwIP stack. + while (!netif_is_up(&netif_data)); + while (dhserv_init(&dhcp_config) != ERR_OK); + while (dnserv_init(&ipaddr, 53, dns_query_proc) != ERR_OK); + + // Start TCP server. + struct tcp_pcb* pcb = tcp_new(); + pcb->so_options |= SOF_KEEPALIVE; + pcb->keep_intvl = 75000000; + tcp_bind(pcb, IP_ADDR_ANY, 7777); + + // Start listening for connections. + struct tcp_pcb* listen = tcp_listen(pcb); + tcp_accept(listen, srv_accept); + + // Start LED indicator. + add_repeating_timer_ms(250, led_timer, NULL, &timer); + + // Listen to events. + while (1) { + tud_task(); + service_traffic(); + } + + return 0; +} \ No newline at end of file diff --git a/apps/piccolosdr/media/gnuradio_example.jpg b/apps/piccolosdr/media/gnuradio_example.jpg new file mode 100644 index 0000000000000000000000000000000000000000..72c32cd71aa2b58b09a048c60a64604e7e4d30ee GIT binary patch literal 72522 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⪚hVTO4>p?b(}ZUuN6gBw28&Cv5z(7Q?B zWHbatJ`WX7L`H!c(Z$zwWQ+ajC~Cbp6Uc5Qr0Nd62v<*1mt7BXDA>d(Cq;P}&2DS* z^czc}r`CIX?UTH$B8zUK`Aq*QPtTFClfcslkEFkJRfkyqeBM}wm~-P~za74!g9!04 zvFvXqk!6;^Kxq&fw5@TOW^sLD-LJh_W&|_CPjnOU zicAFiS_OaPTgOs=ZXwVLY4cDACf1PKgFdXV@yfdx6p2avP{iJlL&nHLQpk|Ky5PZWi@SYA~QG0}~?sUO%TQ z24<{Jj%3=iSbf|mJvd>l0|{V1468>YlbVf(Ym>0!fJ3M0m4bKy7tyYhMczcTG*{EW zz7H1`2N~o8C1Z8nDPkh@gDoE!nb_xRQfiokqa8qFgChpbtUHM-uW2RFCzcJCIof23 znhQU1Mb_GD=y$!b@%MZ$oC}J>5KTax>ah`Qd2DUB^Qq@!>T;!7V`$Vt4r%YbiEym| zK>hmte*!@zxc`ZEyI@y9CUF%pFlVtN0ZkP5Trwy7!fZmE_M*{%JC(*Y8S_sZ&l+*6JMu9v3u!I+GLpw} z4ZA@Ec>wdpeo%w%5gne9a6i^qOA+{A0HXf}R7gRBK!Ks44ihA$CO7^~x&KE=|A3f5 zpv0+O0CfrO{|=mM|2rN%0Wtyz#p)9z8JSUBLWtFPxM}N`$yxaGXRCZS49c694_R6# zU)XV;1pK`ENEG}1@?7XA^H0@xE$a+n@tn!MB|>bMLe*-P>DeV}?(AvSu!d|gjYM64 zlDQcj*A`}^m8Zt`nIVZm%GhjdVa6t~ z%j!5LHCZxW15A?)WOu*j?YMGy3NghT>$vJjze{nT{EAyaaFn%^`sD#Mv=@$~T&ALv z2hlsFg0t0WEhwccTnn_a2~zOgj!4gU{9;B{eQ%s_HUir{|RBHT6}Wg`IY8x zpv5xP!pz^n8WKvg+;YGC?n}Rk?MxCGn)=p0sCxEqGYS@5Jb^|EHnja9y@$IR`YBw0 zS)Y+L`xS-(@7kvm9T^qXZYqL8V%pXKK&}ou*ohw3($}Qz%_Y2rq5=Lw(ST@^Jm`Yu zP2<(am-)ZnNuwip79U;@4JHIQR3Tl>33>gK8Gh(Fhq%^gS5Jx?=%K-x-9=zMeD(tI~KA^y^dWE4a~SDgrM#Z>!GjR2F8e z-|~#MJ|IX((%WRzgo4I^aS33zT8P^$y@pLj$Pq_QxwBFYtQ`$)Mqr(2bF-Ae@|u1p zINeB(i`QfNu5o?pr50m8ZSvQaB_un^>$2~aML&f2zN00VEu4Klz+XQJvwKIb9zcT3 zY1c6q;2>Vf0hci>bt0cap&<)y;_TEI+(d&L^BVwK?&aFw=84(W9z2OQ(3%Y%|&ap@7gdO>dmWitiFV}yx?|zp*`~#3Q83p z{7bNlteq*iIEgk8HDx9hxi{W_>JJ&Fb4~I-VyN?Ts$c3TS!!M;j@|kr_JRGMPTl z4KeL>4lHp=xTGxxReGF2#3tCPSlRh{UTX2CKmIJS;Oyg~=M%;G&AY@?*iQSx(8&mu!033>i3_T}bg$?=OunHMsxTajW= z*X0_E&by>0+olX!0S^X^eKbDXKXn&^gVY_B0VlY)*{Q&alZ!XRxaXCDo?1+yz)V9R zMl>F`6o#qhpOPs5Qwu$>OP{{V)&LiOSiQ^J^KU^#$tvc3Q%P!3I76ewtq;#$a0Jm% zy!bOqe>_q!Y4y1x^RGl_TXM;ua*fvRyw6yb<}jxWk8!s3->dzjz<+2OSzy?IsC_24 zJxi8&l6mf0<(z+NIoyZ0kKHZFbzkwAZp*aDm`Yy=-MniT0FV4rt`|h@?J|M`hv4Ey zyDmq>f!@2I#*z$l#@!%><$o6T4?X`&>-vRUoP!)+79Jj?mibC@@uFH;VPb2jjjv8t zrtrp#IKgpgkOV`2N`LNGuLQ0a9uB)bra&C%1jU1?nfJA~I*KT^zN+z$IP#Jt|I1Jy z`*T#aY9p0lA$};+%kfB>5Lup`RWxyW@~N$-##XXwOKD(s>V>+`pE=t7TNm&T=8CR$ zRuqV5S>IAYgRfX$vr?-| zvC%IlRw`&EGC!6fzd>D6-{iCH0nUldx0%R$T$)+-i=OO{S*amv;ro}#>+k=Vmr6^% z__-L&i~Ajh)xamk;gj6s2c}SxO+%9|mC+S$LjF1a&>~;;q_c~~DE;}U0_aav{nS0= zBw-2sJ#5_!Qf4~HS@nF|!{f1k58IJAfHq5p8suNwsQmW}9{zfPni<@t^yyMC$)QZY z22HHko_}qyk{;w#lnZy<(?YceBbz3=mlf&S{X#Q+QC0*$iq}&o)c**-y+jU3B@0jS z;bg*#H+uy(8ql^U_f{1n2|D;2NUecxnR7Tn|hX zbilp}Csl%o{g1qDoyJF>8Ub1OZKz3_N3>$h4$U@!#5(>{$`42>sy)!1A?k|@svn`5 zaYu$tvoe}_)pL&VT8sc{A}!YYINB*N{Xu1VA23C@!X$^2+Mit0FZp7kNx?`a`CH=TFTZ1Ovy{!*{V-5kTPQiIP+;=f!qfv3JowVZrM(O{ma3!vB)rJgSA!rZ5 zx0fw%X&$`Hf7q74;izidUf@O7U%;rbkViwydlcpzw4S)k_6=%Y1%8VGE3U^#>nQH=8LZu(*Ah@ut77jXp0 zOouU9P4Z&mEu+c|}qn~$kA{x9_c{T)Xg zc{ZZ_?WAM5z?2YTIhbb6^0+$xDIpzWOo=NpZWckt`GL`jVr252uIcH}r8VW1tKd;^R9 z;N4N%rk?34{y9HZDUOHqop%Zlht&BH7E1EN!U*vP_;3(7P4N2{Aly&6z+Tt^@bo3l zgCp&S5^SX#i!zzQc`)~Yg0RSPE1u#|@mmP$cT-!tsaEYN5mAltz0p~W+uV~9w|st< zIe#7TXM}0QKv*3;@(!HQbeuhwg8rh+ci$8B)3su3X;dKDrL{L*E`1(=!D;+@qc)Bx zIMv!bp>E*&wbCM~hb#h3E%fGE4na~{o2ijI8WDfDXI=_U4=cP$NGU!jDP|2BncymP zvwnzqX_0cYCh{$hYh{sKfW7CtDJ6dk5^b_PY(*hLtCf%`#C-L6afZA!;z@T=rv4R} zXT*6kk&uLLGJbZZ`n^_9F0G%r8ytnb2tz9j=MbN@hPn zdci@Okb(UQNuyBsIqSxpzU6w0De%_M(u})pr_z^j;|ZXn1v<|Doc!g$dr{A^2lMLm z(mA1l_6zKfPK=Q8YA%wqK^6;&H-Dl07&@>|`6;9`NTqG6P5z%_|9?84{<9ar0>;io z{|M^XN^8^9voa{h;Bkp%o`w(5>2v_gTT*a|t=jtcsXlgRyrlkjK4{MNqpI52Gz z^^@ffzXIqVQ{;d3P}Sie|FEO`v2=2 z|Jwu8=)z>j^gYs6_7?Z_7ju8(aKD*fOWS7xuY%r7eQUF|vQ4|%G$+@v7nK7>{p~`Y zUfXEvr@N1>y_Ri5+>aYg%G{-`pHr+b&U@aQsG6K%6y6Z`sftW-rfOyso@Q*)fyRLk zs(Y=)mID*hQX}&=6-EwCX@<2wE~Z4R*{$Vu^DegabI%lB^D3|iy?DrRZ5(N#?ca25 zg(P2}WpvVd$Nu#QG2~v}#N%zKkiV(0XxQ3n!SySlscO}Ux2e>oZtl)!-tS@JWw*CU zMa_4OIrqGmXPHw`!`FP0gpc5Rk031!B%1!P$1H7+%WD057`4zs-n26iY5LDz%Q>?3 zo|=KKgplY8G@z$Wcr`X+vxF|j7qieC02|u=%4hjPML7AH)6BL)MxE6iLTdzXm)`RaJkm z@gw`m7^9Pg4Bq|wJx2FPBW?eR``>la7uEbaiA<+xY`6n^IPDpC77i_r+n??Jz{FM5 z;-hLP6VEv9Ids%u^_=Smp3JQ6;a@0|>dqGlRanS_xnq|S*MkO|qvMc`Vl2)HKqt{w zc4Ru=+)d)VUh-MOo~&8bm&lS}w4Jag8!-j;FzFJ5v6#8hi*ATdhzIizj&A>|o_h{9 z+kUr?wjrR6#N`gr67!q`z9~_ErpC`VOby%@c81^AHalG!hY?rGNx*sq98Ld*H?wDB z$a=;IaajkJa}_m&s3IekJ!+Q|zGtijb?N;4rH%T#y8HvP!^8QD@TNz3(;u^v!|KC% zGY?X}__%zHGK0)@RNWwt6YKX zsNPGVBA5aVz_s$tqUZawyXN50zq?S(@s?G7?=eG3WVwkG?E3Nbo}C2fvzNk5^-`eb z=KUJ|>YMMk1sAK4Hjn%(UlXWBLbcVUMwv)|Y|WnDJx1nCwSvlW-l`(AfjA9oJa)P^`EjV){cebAB%Gs_W+G zid#avc*$F+EN^;$vh5$TG$Zq@6uPgBNe zf%eq$75`)NJ6TD+Hr^lQE?%0^rW3Pml1*a(EBmp(+qZt#S9b9Ft}*`r`1h05Uk{i} zR%p3}f2a8UJLY)iaRFJmgG;(V4eS2T#-I@6S7pm^c(juBk7pn!VP<~*Vqt`Qj807& z*@aO>)Kp{rS;phm`sy}`i!ry~{&y&Ed?g~83ACgBjx+x1&sY!r&( zp~GJ8w7M4q<#%^Knq91xCPX_{AM-Cz;eH!p+kG_usMj*d+54dB#*~19uhiio`S;a( zD3hl5{vDqbf5N=TgCB0+8os)Zw2S%oczW(MWr-jncz!+6n3|v2g}s#J&qFLcdBs#z z9^7(ACMA%wocEQl3iy-w``UeH!IDL<)n^ldnHHn(ESEyBpDoe!I(^nvn5l5NRlPR5 zczMjfQ*Qin=b7Uw;cJBBz%7PDSNsy$>5}#0^?~qN3ctH8EAb+$lF% z*=Z1+M^0Q(=bB5K6_1cplJT_s`yU|W`!SOMln={f=^V=L04s=L2nL>Z}AsiHGM7!KN1mH@J_Xj17B#OFn72 zxL0|DrdK~e=Xd@6tB=pkf8Z47g*PPM8vQQi+~9`O+bmK+42@PrnUZySY`Ci33oDM9 zkETtehFGvEgU=IlIpM<1a;WtMpwn{r9ws}@G*pKM>rgo*QZE@aVh~8`GcWo1QltB_ z^nQPxv|mHC!#86V5c9gN1V4k-np9Obe76PqZr9#n!nW+2tv%VoA;awfYJzw)@f4b-xN8E$Fj^85 z%IJE2+?*tebY5+=qc%@!_E0{z4BQEr%EcQcCtmYV9=kUF-g`TzuARZoW9@7WI1hS- z*^I1dgHXb!%;HAvUAG@ap$k5o+|_QE!Wxr5pld>T6PRB2GCFb|WEH0OY{2K+ai!H- zpV&tEKH#0hs3@|MO^HzTKT#2+B%79M3vi+IG_X+At5l(N&!tXc>nkY}pH|u@P@{ES zuCepJK z*8`yRQfBEPC{v>VT&nj2y8C*gFP?yI!w(}lzE%-`QlP|e^WH#)CaGgHr$h^%!MHid zyz$lel*t$5vawr!E)W=ux61c?dF-avX9{|}H@k{yprsN^!4JjY_ieGu*S=Hy`r?Pg z^tQOQUJglu-}hWQ@4ry|UIZDnMZw+ARNMvHnV4MP!rp>fJC!&2I`xBbSK2POpPdVB zTbmLt*+!PW>pCHbX&spkW}S^kOutC%NI4fUD%$r;>QwmnT9XEMs%_2Da>@D#(|b+0 z(izlaoB*3`?xF0m?53uSz_B9kJ41ECKHV^LY$919u_nP)nW3=WXihT*;pS2|gTM*; zV2Tu1^Bg=zu*v8Is`*Boxk|$!B|2`sjT=P)&Q=@l;cIIFi_xhB-cAhe1XTeC6GvV^ zy*Y7%IKWuAnOvo76ut(=-K^(ju?lKTjinfrFs$9IOMPnIU<6dxcb}pVeeu`k{{hNx Bx)1;W literal 0 HcmV?d00001 diff --git a/apps/piccolosdr/tusb_config.h b/apps/piccolosdr/tusb_config.h new file mode 100644 index 0000000..262d4eb --- /dev/null +++ b/apps/piccolosdr/tusb_config.h @@ -0,0 +1,109 @@ +/* + * 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 +#ifndef CFG_TUSB_MCU + #error CFG_TUSB_MCU must be defined +#endif + +// RHPort number used for device can be defined by board.mk, default to port 0 +#ifndef BOARD_DEVICE_RHPORT_NUM + #define BOARD_DEVICE_RHPORT_NUM 0 +#endif + +// RHPort max operational speed can defined by board.mk +// Default to Highspeed for MCU with internal HighSpeed PHY (can be port specific), otherwise FullSpeed +#ifndef BOARD_DEVICE_RHPORT_SPEED + #if (CFG_TUSB_MCU == OPT_MCU_LPC18XX || CFG_TUSB_MCU == OPT_MCU_LPC43XX || CFG_TUSB_MCU == OPT_MCU_MIMXRT10XX || \ + CFG_TUSB_MCU == OPT_MCU_NUC505 || CFG_TUSB_MCU == OPT_MCU_CXD56) + #define BOARD_DEVICE_RHPORT_SPEED OPT_MODE_HIGH_SPEED + #else + #define BOARD_DEVICE_RHPORT_SPEED OPT_MODE_FULL_SPEED + #endif +#endif + +// Device mode with rhport and speed defined by board.mk +#if BOARD_DEVICE_RHPORT_NUM == 0 + #define CFG_TUSB_RHPORT0_MODE (OPT_MODE_DEVICE | BOARD_DEVICE_RHPORT_SPEED) +#elif BOARD_DEVICE_RHPORT_NUM == 1 + #define CFG_TUSB_RHPORT1_MODE (OPT_MODE_DEVICE | BOARD_DEVICE_RHPORT_SPEED) +#else + #error "Incorrect RHPort configuration" +#endif + +#ifndef CFG_TUSB_OS +#define CFG_TUSB_OS OPT_OS_NONE +#endif + +// CFG_TUSB_DEBUG is defined by compiler in DEBUG build +// #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 0 +#define CFG_TUD_MSC 0 +#define CFG_TUD_HID 0 +#define CFG_TUD_MIDI 0 +#define CFG_TUD_VENDOR 0 +#define CFG_TUD_NET 1 + +#ifdef __cplusplus + } +#endif + +#endif /* _TUSB_CONFIG_H_ */ diff --git a/apps/piccolosdr/usb_descriptors.c b/apps/piccolosdr/usb_descriptors.c new file mode 100644 index 0000000..f1c0b47 --- /dev/null +++ b/apps/piccolosdr/usb_descriptors.c @@ -0,0 +1,225 @@ +/* + * 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] NET | VENDOR | MIDI | 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) | _PID_MAP(NET, 5) ) + +// String Descriptor Index +enum +{ + STRID_LANGID = 0, + STRID_MANUFACTURER, + STRID_PRODUCT, + STRID_SERIAL, + STRID_INTERFACE, + STRID_MAC +}; + +enum +{ + ITF_NUM_CDC = 0, + ITF_NUM_CDC_DATA, + ITF_NUM_TOTAL +}; + +enum +{ + CONFIG_ID_RNDIS = 0, + CONFIG_ID_ECM = 1, + CONFIG_ID_COUNT +}; + +//--------------------------------------------------------------------+ +// 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) device class + .bDeviceClass = TUSB_CLASS_MISC, + .bDeviceSubClass = MISC_SUBCLASS_COMMON, + .bDeviceProtocol = MISC_PROTOCOL_IAD, + + .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE, + + .idVendor = 0xCafe, + .idProduct = USB_PID, + .bcdDevice = 0x0101, + + .iManufacturer = STRID_MANUFACTURER, + .iProduct = STRID_PRODUCT, + .iSerialNumber = STRID_SERIAL, + + .bNumConfigurations = CONFIG_ID_COUNT // multiple configurations +}; + +// 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 MAIN_CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_RNDIS_DESC_LEN) +#define ALT_CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_CDC_ECM_DESC_LEN) + +#if CFG_TUSB_MCU == OPT_MCU_LPC175X_6X || CFG_TUSB_MCU == OPT_MCU_LPC177X_8X || CFG_TUSB_MCU == OPT_MCU_LPC40XX + // LPC 17xx and 40xx endpoint type (bulk/interrupt/iso) are fixed by its number + // 0 control, 1 In, 2 Bulk, 3 Iso, 4 In etc ... + #define EPNUM_NET_NOTIF 0x81 + #define EPNUM_NET_OUT 0x02 + #define EPNUM_NET_IN 0x82 + +#elif CFG_TUSB_MCU == OPT_MCU_SAMG + // SAMG doesn't support a same endpoint number with different direction IN and OUT + // e.g EP1 OUT & EP1 IN cannot exist together + #define EPNUM_NET_NOTIF 0x81 + #define EPNUM_NET_OUT 0x02 + #define EPNUM_NET_IN 0x83 + +#else + #define EPNUM_NET_NOTIF 0x81 + #define EPNUM_NET_OUT 0x02 + #define EPNUM_NET_IN 0x82 +#endif + +static uint8_t const rndis_configuration[] = +{ + // Config number (index+1), interface count, string index, total length, attribute, power in mA + TUD_CONFIG_DESCRIPTOR(CONFIG_ID_RNDIS+1, ITF_NUM_TOTAL, 0, MAIN_CONFIG_TOTAL_LEN, 0, 100), + + // Interface number, string index, EP notification address and size, EP data address (out, in) and size. + TUD_RNDIS_DESCRIPTOR(ITF_NUM_CDC, STRID_INTERFACE, EPNUM_NET_NOTIF, 8, EPNUM_NET_OUT, EPNUM_NET_IN, CFG_TUD_NET_ENDPOINT_SIZE), +}; + +static uint8_t const ecm_configuration[] = +{ + // Config number (index+1), interface count, string index, total length, attribute, power in mA + TUD_CONFIG_DESCRIPTOR(CONFIG_ID_ECM+1, ITF_NUM_TOTAL, 0, ALT_CONFIG_TOTAL_LEN, 0, 100), + + // Interface number, description string index, MAC address string index, EP notification address and size, EP data address (out, in), and size, max segment size. + TUD_CDC_ECM_DESCRIPTOR(ITF_NUM_CDC, STRID_INTERFACE, STRID_MAC, EPNUM_NET_NOTIF, 64, EPNUM_NET_OUT, EPNUM_NET_IN, CFG_TUD_NET_ENDPOINT_SIZE, CFG_TUD_NET_MTU), +}; + +// Configuration array: RNDIS and CDC-ECM +// - Windows only works with RNDIS +// - MacOS only works with CDC-ECM +// - Linux will work on both +// Note index is Num-1x +static uint8_t const * const configuration_arr[2] = +{ + [CONFIG_ID_RNDIS] = rndis_configuration, + [CONFIG_ID_ECM ] = ecm_configuration +}; + +// 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) +{ + return (index < CONFIG_ID_COUNT) ? configuration_arr[index] : NULL; +} + +//--------------------------------------------------------------------+ +// String Descriptors +//--------------------------------------------------------------------+ + +// array of pointer to string descriptors +static char const* string_desc_arr [] = +{ + [STRID_LANGID] = (const char[]) { 0x09, 0x04 }, // supported language is English (0x0409) + [STRID_MANUFACTURER] = "TinyUSB", // Manufacturer + [STRID_PRODUCT] = "TinyUSB Device", // Product + [STRID_SERIAL] = "123456", // Serial + [STRID_INTERFACE] = "TinyUSB Network Interface" // Interface Description + + // STRID_MAC index is handled separately +}; + +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; + + unsigned int chr_count = 0; + + if (STRID_LANGID == index) + { + memcpy(&_desc_str[1], string_desc_arr[STRID_LANGID], 2); + chr_count = 1; + } + else if (STRID_MAC == index) + { + // Convert MAC address into UTF-16 + + for (unsigned i=0; i> 4) & 0xf]; + _desc_str[1+chr_count++] = "0123456789ABCDEF"[(tud_network_mac_address[i] >> 0) & 0xf]; + } + } + 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 > (TU_ARRAY_SIZE(_desc_str) - 1)) chr_count = TU_ARRAY_SIZE(_desc_str) - 1; + + // Convert ASCII string into UTF-16 + for (unsigned int i=0; iints0 = 1u << dma_chan_a; - dma_channel_set_write_addr(dma_chan_a, &capture_buf_a, false); -} - -void dma_handler_b() { - dma_handler((uint8_t*)&capture_buf_b, 1); - dma_hw->ints1 = 1u << dma_chan_b; - dma_channel_set_write_addr(dma_chan_b, &capture_buf_b, false); -} - -static void init_adc_dma_chain() { -adc_gpio_init(26 + CAPTURE_CHANNEL); - adc_init(); - adc_select_input(CAPTURE_CHANNEL); - adc_fifo_setup( - true, // Write to FIFO - true, // Enable DREQ - 1, // Trigger DREQ with at least one sample - false, // No ERR bit - true // Shift each sample by 8 bits - ); - adc_set_clkdiv(0); - - dma_channel_config dma_cfg_a, dma_cfg_b; - - dma_chan_a = dma_claim_unused_channel(true); - dma_chan_b = dma_claim_unused_channel(true); - - dma_cfg_a = dma_channel_get_default_config(dma_chan_a); - dma_cfg_b = dma_channel_get_default_config(dma_chan_b); - - channel_config_set_transfer_data_size(&dma_cfg_a, DMA_SIZE_8); - channel_config_set_transfer_data_size(&dma_cfg_b, DMA_SIZE_8); - - channel_config_set_read_increment(&dma_cfg_a, false); - channel_config_set_read_increment(&dma_cfg_b, false); - - channel_config_set_write_increment(&dma_cfg_a, true); - channel_config_set_write_increment(&dma_cfg_b, true); - - channel_config_set_dreq(&dma_cfg_a, DREQ_ADC); - channel_config_set_dreq(&dma_cfg_b, DREQ_ADC); - - channel_config_set_chain_to(&dma_cfg_a, dma_chan_b); - channel_config_set_chain_to(&dma_cfg_b, dma_chan_a); - - dma_channel_configure(dma_chan_a, &dma_cfg_a, - capture_buf_a, // dst - &adc_hw->fifo, // src - CAPTURE_DEPTH, // transfer count - true // start now - ); - - dma_channel_configure(dma_chan_b, &dma_cfg_b, - capture_buf_b, // dst - &adc_hw->fifo, // src - CAPTURE_DEPTH, // transfer count - false // start now - ); - - dma_channel_set_irq0_enabled(dma_chan_a, true); - irq_set_exclusive_handler(DMA_IRQ_0, dma_handler_a); - irq_set_enabled(DMA_IRQ_0, true); - - dma_channel_set_irq1_enabled(dma_chan_b, true); - irq_set_exclusive_handler(DMA_IRQ_1, dma_handler_b); - irq_set_enabled(DMA_IRQ_1, true); - - adc_run(false); -} - -static void start_stream() { - dma_channel_set_write_addr(dma_chan_a, &capture_buf_a, true); - dma_channel_set_write_addr(dma_chan_b, &capture_buf_b, false); - adc_run(true); - streaming = true; -} - -static void stop_stream() { - adc_run(false); - adc_fifo_drain(); - dma_channel_set_write_addr(dma_chan_a, &capture_buf_a, false); - dma_channel_set_write_addr(dma_chan_b, &capture_buf_b, false); - streaming = false; + return true; } static void srv_close(struct tcp_pcb *pcb){ - stop_stream(); + // Cancel send timer. + cancel_repeating_timer(&timer); tcp_arg(pcb, NULL); tcp_sent(pcb, NULL); @@ -323,33 +227,19 @@ static err_t srv_accept(void * arg, struct tcp_pcb * pcb, err_t err) { tcp_poll(pcb, NULL, 4); client = pcb; - start_stream(); + + // Start send timer. + add_repeating_timer_ms(50, send_timer, NULL, &timer); return err; } -bool led_timer(struct repeating_timer *t) { - int status = 1; - if (streaming) { - status = !gpio_get(PICO_DEFAULT_LED_PIN); - } - gpio_put(PICO_DEFAULT_LED_PIN, status); - return true; -} - int main(void) { - board_init(); - - // Init built-in LED. - gpio_init(PICO_DEFAULT_LED_PIN); - gpio_set_dir(PICO_DEFAULT_LED_PIN, GPIO_OUT); - - // Init ADC DMA chain. - init_adc_dma_chain(); - // Init network stack. + board_init(); tusb_init(); init_lwip(); + adc_init(); // Startup lwIP stack. while (!netif_is_up(&netif_data)); @@ -366,8 +256,9 @@ int main(void) { struct tcp_pcb* listen = tcp_listen(pcb); tcp_accept(listen, srv_accept); - // Start LED indicator. - add_repeating_timer_ms(250, led_timer, NULL, &timer); + // Start ADC. + adc_set_temp_sensor_enabled(true); + adc_select_input(4); // Listen to events. while (1) {