kopia lustrzana https://github.com/luigifcruz/pico-stuff
Add documentation to apps.
rodzic
6239c6e53c
commit
8fc012c761
|
@ -1,3 +1,4 @@
|
||||||
add_subdirectory(iperf_server)
|
add_subdirectory(iperf_server)
|
||||||
add_subdirectory(tcp_server)
|
add_subdirectory(tcp_server)
|
||||||
add_subdirectory(adc_dma_chain)
|
add_subdirectory(adc_dma_chain)
|
||||||
|
add_subdirectory(piccolosdr)
|
|
@ -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]
|
||||||
|
```
|
|
@ -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
|
||||||
|
```
|
|
@ -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)
|
|
@ -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.
|
||||||
|
|
||||||
|

|
|
@ -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__ */
|
|
@ -0,0 +1,379 @@
|
||||||
|
// Initial TinyUSB RNDIS written by Peter Lawrence.
|
||||||
|
// Modifications were made by Luigi Cruz.
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#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;
|
||||||
|
}
|
Plik binarny nie jest wyświetlany.
Po Szerokość: | Wysokość: | Rozmiar: 71 KiB |
|
@ -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_ */
|
|
@ -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<sizeof(tud_network_mac_address); i++)
|
||||||
|
{
|
||||||
|
_desc_str[1+chr_count++] = "0123456789ABCDEF"[(tud_network_mac_address[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; 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;
|
||||||
|
}
|
|
@ -14,8 +14,6 @@ target_link_libraries(tcp_server
|
||||||
tinyusb_board
|
tinyusb_board
|
||||||
tinyusb_net
|
tinyusb_net
|
||||||
hardware_adc
|
hardware_adc
|
||||||
hardware_dma
|
|
||||||
hardware_irq
|
|
||||||
lwip
|
lwip
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,33 @@
|
||||||
|
# TCP Server
|
||||||
|
This is a TCP Server that will work with the USB Network Stack library to provide a TCP/IP connection between the host (computer) and the device (Pico). This example is a demonstration of how to send high-frequency data to the host using a TCP connection.
|
||||||
|
|
||||||
|
### Dependencies
|
||||||
|
- Patched `pico-sdr` and `pico-extras`.
|
||||||
|
- [USB Network Stack](/lib/networking) Library.
|
||||||
|
|
||||||
|
### Usage
|
||||||
|
This program will start when a TCP connection is established. For this example, we are going to use Netcat. The command bellow should connect the host with the device. When the connection is opened, the device will start to stream data from the internal temperature sensor with ASCII characters. Binary data can also be sent.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ netcat 192.168.7.1 7777
|
||||||
|
TEMP: 23.393365 °C
|
||||||
|
TEMP: 23.393365 °C
|
||||||
|
TEMP: 23.393365 °C
|
||||||
|
TEMP: 23.393365 °C
|
||||||
|
TEMP: 23.393365 °C
|
||||||
|
TEMP: 23.393365 °C
|
||||||
|
TEMP: 23.393365 °C
|
||||||
|
TEMP: 23.393365 °C
|
||||||
|
TEMP: 23.393365 °C
|
||||||
|
TEMP: 23.393365 °C
|
||||||
|
TEMP: 23.393365 °C
|
||||||
|
TEMP: 23.393365 °C
|
||||||
|
TEMP: 23.393365 °C
|
||||||
|
TEMP: 23.393365 °C
|
||||||
|
TEMP: 23.393365 °C
|
||||||
|
TEMP: 23.393365 °C
|
||||||
|
TEMP: 23.393365 °C
|
||||||
|
TEMP: 23.393365 °C
|
||||||
|
TEMP: 23.393365 °C
|
||||||
|
TEMP: 23.393365 °C
|
||||||
|
```
|
|
@ -5,8 +5,8 @@
|
||||||
#include "bsp/board.h"
|
#include "bsp/board.h"
|
||||||
#include "pico/stdlib.h"
|
#include "pico/stdlib.h"
|
||||||
#include "hardware/adc.h"
|
#include "hardware/adc.h"
|
||||||
#include "hardware/dma.h"
|
|
||||||
#include "hardware/irq.h"
|
#include "hardware/irq.h"
|
||||||
|
#include "hardware/gpio.h"
|
||||||
|
|
||||||
#include "tusb.h"
|
#include "tusb.h"
|
||||||
#include "dhserver.h"
|
#include "dhserver.h"
|
||||||
|
@ -168,120 +168,24 @@ void tud_network_init_cb(void) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool streaming = false;
|
|
||||||
struct tcp_pcb* client;
|
struct tcp_pcb* client;
|
||||||
struct repeating_timer timer;
|
struct repeating_timer timer;
|
||||||
|
|
||||||
//#define DEBUG
|
bool send_timer(struct repeating_timer *t) {
|
||||||
#define CAPTURE_CHANNEL 0
|
const float conversion_factor = 3.3f / (1 << 12);
|
||||||
#define CAPTURE_DEPTH 500
|
float ADC_voltage = adc_read() * conversion_factor;
|
||||||
|
|
||||||
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];
|
char str[64];
|
||||||
int len = sprintf(str, "DMA IRQ %d [%d %d ... %d]\n", id, buffer[0], buffer[1], buffer[CAPTURE_DEPTH-1]);
|
int len = sprintf(str, "TEMP: %f °C\n", 27 - (ADC_voltage - 0.706) / 0.001721);
|
||||||
tcp_write(client, str, len, 0);
|
tcp_write(client, str, len, 0);
|
||||||
tcp_output(client);
|
tcp_output(client);
|
||||||
#else
|
|
||||||
tcp_write(client, buffer, CAPTURE_DEPTH, 0x01);
|
|
||||||
tcp_output(client);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void dma_handler_a() {
|
return true;
|
||||||
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){
|
static void srv_close(struct tcp_pcb *pcb){
|
||||||
stop_stream();
|
// Cancel send timer.
|
||||||
|
cancel_repeating_timer(&timer);
|
||||||
|
|
||||||
tcp_arg(pcb, NULL);
|
tcp_arg(pcb, NULL);
|
||||||
tcp_sent(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);
|
tcp_poll(pcb, NULL, 4);
|
||||||
|
|
||||||
client = pcb;
|
client = pcb;
|
||||||
start_stream();
|
|
||||||
|
// Start send timer.
|
||||||
|
add_repeating_timer_ms(50, send_timer, NULL, &timer);
|
||||||
|
|
||||||
return err;
|
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) {
|
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.
|
// Init network stack.
|
||||||
|
board_init();
|
||||||
tusb_init();
|
tusb_init();
|
||||||
init_lwip();
|
init_lwip();
|
||||||
|
adc_init();
|
||||||
|
|
||||||
// Startup lwIP stack.
|
// Startup lwIP stack.
|
||||||
while (!netif_is_up(&netif_data));
|
while (!netif_is_up(&netif_data));
|
||||||
|
@ -366,8 +256,9 @@ int main(void) {
|
||||||
struct tcp_pcb* listen = tcp_listen(pcb);
|
struct tcp_pcb* listen = tcp_listen(pcb);
|
||||||
tcp_accept(listen, srv_accept);
|
tcp_accept(listen, srv_accept);
|
||||||
|
|
||||||
// Start LED indicator.
|
// Start ADC.
|
||||||
add_repeating_timer_ms(250, led_timer, NULL, &timer);
|
adc_set_temp_sensor_enabled(true);
|
||||||
|
adc_select_input(4);
|
||||||
|
|
||||||
// Listen to events.
|
// Listen to events.
|
||||||
while (1) {
|
while (1) {
|
||||||
|
|
Ładowanie…
Reference in New Issue