mirror of
https://github.com/cesanta/mongoose.git
synced 2026-11-13 17:00:19 +00:00
Add radio simulation
This commit is contained in:
1 parent
9337a78cd0
commit
d39519b92f
66 files changed
+5659
-1000
No files matched your search
@@ -0,0 +1,61 @@
|
||||
# Environment setup: https://mongoose.ws/documentation/tutorials/tools/
|
||||
|
||||
MGDIR = ../../../..
|
||||
|
||||
# Flash over USB: the board's Adafruit UF2 bootloader does serial DFU. The
|
||||
# firmware resets into it on a 1200 baud touch, see usb.c. PORT: the board's
|
||||
# USB serial port. adafruit-nrfutil: pip install adafruit-nrfutil
|
||||
PORT ?= /dev/cu.usbmodem11301
|
||||
NRFUTIL ?= adafruit-nrfutil
|
||||
|
||||
CFLAGS = -W -Wall -Wextra -Werror -Wshadow -Wdouble-promotion
|
||||
CFLAGS += -Wformat-truncation -fno-common -Wno-conversion -Wno-sign-conversion
|
||||
CFLAGS += -g3 -Os -ffunction-sections -fdata-sections
|
||||
CFLAGS += -I. -I$(MGDIR) -Icmsis_core/CMSIS/Core/Include
|
||||
CFLAGS += -Inrfx/mdk -Inrfx -Inrfx/hal -Inrfx/templates -Inrfx/drivers/src -Itinyusb/src
|
||||
CFLAGS += -Wno-cast-align -Wno-shadow -Wno-unused-parameter
|
||||
CFLAGS += -DNRF52840_XXAA -D__STARTUP_CLEAR_BSS -D__START=main -D__HEAP_SIZE=0
|
||||
CFLAGS += -mcpu=cortex-m4 -mthumb -mfloat-abi=hard -mfpu=fpv4-sp-d16
|
||||
CFLAGS += $(CFLAGS_EXTRA)
|
||||
|
||||
LDFLAGS ?= -Tlink.ld -nostdlib -nostartfiles --specs=nano.specs -lc -lm -lgcc -Wl,--gc-sections
|
||||
|
||||
SOURCES = main.c hal.c usb.c $(MGDIR)/mongoose.c
|
||||
SOURCES += $(MGDIR)/tutorials/mqtt/mqtt-client/mongoose_mqtt.c
|
||||
SOURCES += nrfx/mdk/system_nrf52840.c nrfx/mdk/gcc_startup_nrf52840.S
|
||||
|
||||
# TinyUSB, for the USB serial console: the board has no USB-UART chip
|
||||
SOURCES += tinyusb/src/tusb.c tinyusb/src/common/tusb_fifo.c
|
||||
SOURCES += tinyusb/src/device/usbd.c tinyusb/src/device/usbd_control.c
|
||||
SOURCES += tinyusb/src/class/cdc/cdc_device.c
|
||||
SOURCES += tinyusb/src/portable/nordic/nrf5x/dcd_nrf5x.c
|
||||
|
||||
all build: firmware.hex
|
||||
|
||||
firmware.hex: firmware.elf
|
||||
arm-none-eabi-objcopy -O ihex $< $@
|
||||
arm-none-eabi-size --format=berkeley $<
|
||||
|
||||
firmware.elf: cmsis_core nrfx tinyusb $(SOURCES) $(wildcard *.h) link.ld Makefile
|
||||
arm-none-eabi-gcc $(SOURCES) $(CFLAGS) $(LDFLAGS) -o $@
|
||||
|
||||
firmware.zip: firmware.hex
|
||||
$(NRFUTIL) dfu genpkg --dev-type 0x0052 --sd-req 0xFFFE --application $< $@
|
||||
|
||||
flash: firmware.zip
|
||||
$(NRFUTIL) dfu serial --package $< -p $(PORT) -b 115200 --singlebank --touch 1200
|
||||
|
||||
cmsis_core:
|
||||
git clone -q -c advice.detachedHead=false --depth 1 -b 5.9.0 https://github.com/ARM-software/CMSIS_5 $@
|
||||
|
||||
# nRF52840 device headers and SystemInit(), nrf_* register helpers for TinyUSB
|
||||
nrfx:
|
||||
git clone -q -c advice.detachedHead=false --depth 1 -b v2.11.0 https://github.com/NordicSemiconductor/nrfx $@
|
||||
|
||||
tinyusb:
|
||||
git clone -q -c advice.detachedHead=false --depth 1 -b 0.14.0 https://github.com/hathach/tinyusb $@
|
||||
|
||||
clean:
|
||||
rm -rf firmware.* *.dSYM tinyusb nrfx cmsis_core
|
||||
|
||||
.PHONY: all build flash clean
|
||||
@@ -0,0 +1,141 @@
|
||||
// Copyright (c) 2026 Cesanta Software Limited
|
||||
// All rights reserved
|
||||
|
||||
#include "hal.h"
|
||||
|
||||
// Added to minimise the bin size: avoid pulling printf and friends
|
||||
void __assert_func(const char *s, int line, const char *fn, const char *expr) {
|
||||
(void) s, (void) line, (void) fn, (void) expr;
|
||||
for (;;) (void) 0; // or trigger the fault handler
|
||||
}
|
||||
|
||||
bool hal_timer_expired(volatile uint64_t *t, uint64_t period, uint64_t now) {
|
||||
uint64_t diff = now - *t; // Wrap-safe elapsed time since last expiry
|
||||
if (period == 0) return false; // Avoid division by zero
|
||||
if (diff < period) return false; // Period has not elapsed yet
|
||||
*t += (diff / period) * period; // Preserve cadence, skip missed periods
|
||||
return true;
|
||||
}
|
||||
|
||||
static volatile uint64_t s_ticks; // Milliseconds since boot
|
||||
void SysTick_Handler(void) { // SyStick IRQ handler, triggered every 1ms
|
||||
s_ticks++;
|
||||
}
|
||||
|
||||
uint64_t hal_get_tick(void) {
|
||||
return s_ticks;
|
||||
};
|
||||
|
||||
struct stat;
|
||||
__attribute__((weak)) int _fstat(int fd, struct stat *st) {
|
||||
(void) fd, (void) st;
|
||||
return -1;
|
||||
}
|
||||
|
||||
extern unsigned char _end[]; // End of data section, start of heap. See link.ld
|
||||
static unsigned char *s_current_heap_end = _end;
|
||||
|
||||
size_t hal_ram_used(void) {
|
||||
return (size_t) (s_current_heap_end - _end);
|
||||
}
|
||||
|
||||
size_t hal_ram_free(void) {
|
||||
unsigned char endofstack;
|
||||
return (size_t) (&endofstack - s_current_heap_end);
|
||||
}
|
||||
|
||||
void *_sbrk(int incr) {
|
||||
unsigned char *prev_heap;
|
||||
unsigned char *heap_end = (unsigned char *) ((size_t) &heap_end - 256);
|
||||
prev_heap = s_current_heap_end;
|
||||
// Check how much space we got from the heap end to the stack end
|
||||
if (s_current_heap_end + incr > heap_end) return (void *) -1;
|
||||
s_current_heap_end += incr;
|
||||
return prev_heap;
|
||||
}
|
||||
|
||||
__attribute__((weak)) int _open(const char *path) {
|
||||
(void) path;
|
||||
return -1;
|
||||
}
|
||||
|
||||
__attribute__((weak)) int _close(int fd) {
|
||||
(void) fd;
|
||||
return -1;
|
||||
}
|
||||
|
||||
__attribute__((weak)) int _isatty(int fd) {
|
||||
(void) fd;
|
||||
return 1;
|
||||
}
|
||||
|
||||
__attribute__((weak)) int _lseek(int fd, int ptr, int dir) {
|
||||
(void) fd, (void) ptr, (void) dir;
|
||||
return 0;
|
||||
}
|
||||
|
||||
__attribute__((weak)) void _exit(int status) {
|
||||
(void) status;
|
||||
for (;;) asm volatile("BKPT #0");
|
||||
}
|
||||
|
||||
__attribute__((weak)) void _kill(int pid, int sig) {
|
||||
(void) pid, (void) sig;
|
||||
}
|
||||
|
||||
__attribute__((weak)) int _getpid(void) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
__attribute__((weak)) int _write(int fd, char *ptr, int len) {
|
||||
(void) fd, (void) ptr, (void) len;
|
||||
return -1;
|
||||
}
|
||||
|
||||
__attribute__((weak)) int _read(int fd, char *ptr, int len) {
|
||||
(void) fd, (void) ptr, (void) len;
|
||||
return -1;
|
||||
}
|
||||
|
||||
__attribute__((weak)) int _link(const char *a, const char *b) {
|
||||
(void) a, (void) b;
|
||||
return -1;
|
||||
}
|
||||
|
||||
__attribute__((weak)) int _unlink(const char *a) {
|
||||
(void) a;
|
||||
return -1;
|
||||
}
|
||||
|
||||
__attribute__((weak)) int _stat(const char *path, struct stat *st) {
|
||||
(void) path, (void) st;
|
||||
return -1;
|
||||
}
|
||||
|
||||
__attribute__((weak)) int mkdir(const char *path, int mode) {
|
||||
(void) path, (void) mode;
|
||||
return -1;
|
||||
}
|
||||
|
||||
__attribute__((weak)) void _init(void) {
|
||||
}
|
||||
|
||||
uint64_t mg_micros(void);
|
||||
// SysTick runs at the core clock and reloads every millisecond. Sample
|
||||
// the tick counter and the sub-millisecond counter in one critical section.
|
||||
uint64_t mg_micros(void) {
|
||||
uint32_t primask = __get_PRIMASK(), val, pending;
|
||||
uint64_t ms;
|
||||
__disable_irq();
|
||||
ms = hal_get_tick();
|
||||
pending = SCB->ICSR & SCB_ICSR_PENDSTSET_Msk;
|
||||
val = SysTick->VAL;
|
||||
if (pending != (SCB->ICSR & SCB_ICSR_PENDSTSET_Msk)) {
|
||||
pending = SCB_ICSR_PENDSTSET_Msk;
|
||||
val = SysTick->VAL;
|
||||
}
|
||||
__set_PRIMASK(primask);
|
||||
if (pending != 0 && val != 0) ms++;
|
||||
return ms * 1000 + ((uint64_t) (SysTick->LOAD - val) * 1000) /
|
||||
(SysTick->LOAD + 1);
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
#pragma once
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include "nrf.h"
|
||||
uint64_t hal_get_tick(void);
|
||||
bool hal_timer_expired(volatile uint64_t *t, uint64_t period, uint64_t now);
|
||||
size_t hal_ram_used(void);
|
||||
size_t hal_ram_free(void);
|
||||
void usb_init(void);
|
||||
void usb_poll(void);
|
||||
void usb_log(char ch, void *param);
|
||||
|
||||
#define BIT(x) (1UL << (x))
|
||||
#define PIN(port, num) (((port) << 5) | (num))
|
||||
#define PINNO(pin) ((pin) & 31)
|
||||
#define PINPORT(pin) ((pin) >> 5)
|
||||
|
||||
static inline NRF_GPIO_Type *hal_gpio_bank(uint16_t pin) {
|
||||
return PINPORT(pin) == 0 ? NRF_P0 : NRF_P1;
|
||||
}
|
||||
|
||||
static inline void hal_gpio_write(uint16_t pin, bool val) {
|
||||
NRF_GPIO_Type *gpio = hal_gpio_bank(pin);
|
||||
if (val) {
|
||||
gpio->OUTSET = BIT(PINNO(pin));
|
||||
} else {
|
||||
gpio->OUTCLR = BIT(PINNO(pin));
|
||||
}
|
||||
}
|
||||
|
||||
static inline void hal_gpio_toggle(uint16_t pin) {
|
||||
hal_gpio_write(pin, (hal_gpio_bank(pin)->OUT & BIT(PINNO(pin))) == 0);
|
||||
}
|
||||
|
||||
static inline void hal_gpio_output(uint16_t pin) {
|
||||
hal_gpio_bank(pin)->PIN_CNF[PINNO(pin)] =
|
||||
(GPIO_PIN_CNF_DIR_Output << GPIO_PIN_CNF_DIR_Pos) |
|
||||
(GPIO_PIN_CNF_INPUT_Disconnect << GPIO_PIN_CNF_INPUT_Pos);
|
||||
}
|
||||
|
||||
static inline void hal_system_init(void) {
|
||||
extern uint32_t __isr_vector[];
|
||||
SCB->VTOR = (uint32_t) __isr_vector; // Application vectors above the bootloader
|
||||
__DSB();
|
||||
__ISB();
|
||||
__enable_irq();
|
||||
NRF_CLOCK->EVENTS_HFCLKSTARTED = 0;
|
||||
NRF_CLOCK->TASKS_HFCLKSTART = 1;
|
||||
while (!NRF_CLOCK->EVENTS_HFCLKSTARTED) (void) 0;
|
||||
SystemCoreClockUpdate();
|
||||
SysTick_Config(SystemCoreClock / 1000);
|
||||
}
|
||||
|
||||
static inline void hal_rng_init(void) {
|
||||
NRF_RNG->CONFIG = 1;
|
||||
NRF_RNG->TASKS_START = 1;
|
||||
}
|
||||
|
||||
static inline uint32_t hal_rng_read(void) {
|
||||
uint32_t value = 0;
|
||||
for (unsigned i = 0; i < 4; i++) {
|
||||
while (!NRF_RNG->EVENTS_VALRDY) (void) 0;
|
||||
value |= (uint32_t) NRF_RNG->VALUE << (i * 8);
|
||||
NRF_RNG->EVENTS_VALRDY = 0;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
/* nRF52840 on the XIAO nRF52840, with the Adafruit UF2 bootloader. Flash:
|
||||
* MBR and SoftDevice S140 below 0x27000, the bootloader from 0xED000 up: the
|
||||
* application in between. The SoftDevice is not enabled, so all RAM is ours,
|
||||
* except the first 24k it would take: kept free, as the Arduino core does */
|
||||
ENTRY(Reset_Handler);
|
||||
MEMORY {
|
||||
flash(rx) : ORIGIN = 0x00027000, LENGTH = 0xC6000
|
||||
sram(rwx) : ORIGIN = 0x20006000, LENGTH = 0x3A000
|
||||
}
|
||||
|
||||
SECTIONS {
|
||||
.vectors : { KEEP(*(.isr_vector)) . = ALIGN(4); } > flash
|
||||
.text : { *(.text* .text.*) } > flash
|
||||
.rodata : { *(.rodata*) . = ALIGN(4); } > flash
|
||||
|
||||
.data : {
|
||||
_sdata = .;
|
||||
*(.first_data)
|
||||
*(.iram .iram.*) /* Mongoose MG_IRAM functions */
|
||||
*(.data SORT(.data.*))
|
||||
. = ALIGN(4);
|
||||
_edata = .;
|
||||
} > sram AT > flash
|
||||
__data_start = _sdata;
|
||||
__data_end = _edata;
|
||||
__data_load_start = LOADADDR(.data);
|
||||
/* Unused data regions expected by Nordic's standard startup. */
|
||||
__sdata_start = _edata;
|
||||
__sdata_end = _edata;
|
||||
__sdata_load_start = _edata;
|
||||
__tdata_start = _edata;
|
||||
__tdata_end = _edata;
|
||||
__tdata_load_start = _edata;
|
||||
__fast_start = _edata;
|
||||
__fast_end = _edata;
|
||||
__fast_load_start = _edata;
|
||||
|
||||
.bss : {
|
||||
_sbss = .;
|
||||
*(.bss SORT(.bss.*) COMMON)
|
||||
. = ALIGN(4);
|
||||
_ebss = .;
|
||||
} > sram
|
||||
__bss_start__ = _sbss;
|
||||
__bss_end__ = _ebss;
|
||||
__tbss_start__ = _ebss;
|
||||
__tbss_end__ = _ebss;
|
||||
__sbss_start__ = _ebss;
|
||||
__sbss_end__ = _ebss;
|
||||
|
||||
/* Health record, see MG_HEALTH_RAM: not cleared, survives a warm reset */
|
||||
.mg_health (NOLOAD) : { KEEP(*(.mg_health .mg_health*)) } > sram
|
||||
|
||||
. = ALIGN(8);
|
||||
_end = .; /* Start of heap, see _sbrk() in hal.c */
|
||||
|
||||
/* Nordic startup reserves its default 16 KiB stack at the top of RAM. */
|
||||
.stack ORIGIN(sram) + LENGTH(sram) - 0x4000 (NOLOAD) : {
|
||||
KEEP(*(.stack))
|
||||
} > sram
|
||||
ASSERT(_end <= __StackLimit, "RAM sections overlap the stack")
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
// Copyright (c) 2026 Cesanta Software Limited
|
||||
// All rights reserved
|
||||
|
||||
#include "hal.h"
|
||||
#include "mongoose.h"
|
||||
|
||||
#define LED1 PIN(0, 26) // P0.26, user LED, active low
|
||||
|
||||
static void log_fn(char ch, void *param) {
|
||||
usb_log(ch, param);
|
||||
}
|
||||
|
||||
static void blink_task(void) {
|
||||
static uint64_t blink_timer = 0;
|
||||
if (hal_timer_expired(&blink_timer, 500, hal_get_tick())) {
|
||||
hal_gpio_toggle(LED1);
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t mg_millis(void) {
|
||||
return hal_get_tick();
|
||||
}
|
||||
|
||||
bool mg_random(void *buf, size_t len) {
|
||||
for (size_t n = 0; n < len; n += sizeof(uint32_t)) {
|
||||
uint32_t r = hal_rng_read();
|
||||
memcpy((char *) buf + n, &r, n + sizeof(r) > len ? len - n : sizeof(r));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Self-signed P-256 certificate and key for the HTTPS listener. Created by:
|
||||
// openssl ecparam -genkey -name prime256v1 -noout -out key.pem
|
||||
// openssl req -x509 -key key.pem -out cert.pem -days 3650 -subj /CN=mg
|
||||
#define TLS_CERT \
|
||||
"-----BEGIN CERTIFICATE-----\n" \
|
||||
"MIIBgjCCASegAwIBAgIUIJOq77Qn7lHbH6WseMemeqorR50wCgYIKoZIzj0EAwIw\n" \
|
||||
"FjEUMBIGA1UEAwwLbnVjbGVvLWg3MjMwHhcNMjYxMDEyMTUwMjE5WhcNMzYxMDA5\n" \
|
||||
"MTUwMjE5WjAWMRQwEgYDVQQDDAtudWNsZW8taDcyMzBZMBMGByqGSM49AgEGCCqG\n" \
|
||||
"SM49AwEHA0IABB6IQJNmzrsdQL+2BnZYS7GYOaj7sz8f6cjBmz3n8D9Em19XodlU\n" \
|
||||
"uy4ybIvyhr766lL0ERoTmMUivb/lGmzx9C+jUzBRMB0GA1UdDgQWBBQqxMuc4gj7\n" \
|
||||
"MTMzreWlRMNI3QMqTzAfBgNVHSMEGDAWgBQqxMuc4gj7MTMzreWlRMNI3QMqTzAP\n" \
|
||||
"BgNVHRMBAf8EBTADAQH/MAoGCCqGSM49BAMCA0kAMEYCIQDY4xaJpuxq6b3rHK3b\n" \
|
||||
"lOIQyfkQX+orCRqQUkseplqpqwIhAKswZFNn+2zUNRRNY8+cHmdIwmdpsmxtoWYP\n" \
|
||||
"BCjX++LI\n" \
|
||||
"-----END CERTIFICATE-----\n"
|
||||
|
||||
#define TLS_KEY \
|
||||
"-----BEGIN EC PRIVATE KEY-----\n" \
|
||||
"MHcCAQEEIJ/GBl0TlD52q9PMg1KWaNIdXqp8b/HyIsABmBpqC+O1oAoGCCqGSM49\n" \
|
||||
"AwEHoUQDQgAEHohAk2bOux1Av7YGdlhLsZg5qPuzPx/pyMGbPefwP0SbX1eh2VS7\n" \
|
||||
"LjJsi/KGvvrqUvQRGhOYxSK9v+UabPH0Lw==\n" \
|
||||
"-----END EC PRIVATE KEY-----\n"
|
||||
|
||||
static void http_ev_handler(struct mg_connection *c, int ev, void *ev_data) {
|
||||
if (ev == MG_EV_ACCEPT && c->is_tls) { // Accepted on the https:// listener
|
||||
struct mg_tls_opts opts = {.cert = mg_str(TLS_CERT),
|
||||
.key = mg_str(TLS_KEY)};
|
||||
mg_tls_init(c, &opts);
|
||||
} else if (ev == MG_EV_HTTP_MSG) {
|
||||
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||
if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) {
|
||||
mg_http_reply(c, 200, "", "{%m:%llu}\n", MG_ESC("tick"), hal_get_tick());
|
||||
} else if (mg_match(hm->uri, mg_str("/api/crash"), NULL)) {
|
||||
__asm volatile("udf #0"); // Trigger HardFault
|
||||
} else {
|
||||
mg_http_reply(c, 200, "",
|
||||
"Hi from Mongoose!\n"
|
||||
"tick: %llu\n"
|
||||
"ram used: %zu\n"
|
||||
"ram free: %zu\n",
|
||||
hal_get_tick(), hal_ram_used(), hal_ram_free());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
MG_HEALTH_INIT();
|
||||
|
||||
hal_system_init();
|
||||
usb_init();
|
||||
mg_log_set_fn(log_fn, NULL);
|
||||
hal_rng_init();
|
||||
hal_gpio_write(LED1, true);
|
||||
hal_gpio_output(LED1);
|
||||
|
||||
struct mg_mgr mgr;
|
||||
mg_mgr_init(&mgr);
|
||||
mg_http_listen(&mgr, "http://0.0.0.0", http_ev_handler, NULL);
|
||||
mg_http_listen(&mgr, "https://0.0.0.0", http_ev_handler, NULL);
|
||||
mg_mqtt_init(&mgr);
|
||||
|
||||
MG_INFO(("Initialised. CPU clock: %lu MHz", SystemCoreClock / 1000000));
|
||||
MG_INFO(("RAM used: %zu, free: %zu", hal_ram_used(), hal_ram_free()));
|
||||
MG_INFO(("Crash record: %M", mg_print_crash_record));
|
||||
|
||||
for (;;) {
|
||||
usb_poll();
|
||||
mg_mgr_poll(&mgr, 0);
|
||||
mg_mqtt_poll(&mgr);
|
||||
blink_task();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
#include "nrf.h"
|
||||
#define MG_ARCH MG_ARCH_ARMGCC
|
||||
#define MG_ENABLE_TCPIP 1
|
||||
#define MG_ENABLE_DRIVER_NRF52 1
|
||||
#define MG_ENABLE_TCPIP_TCPRTX 1
|
||||
#define MG_ENABLE_CUSTOM_MILLIS 1
|
||||
#define MG_ENABLE_CUSTOM_RANDOM 1
|
||||
#define MG_TLS MG_TLS_BUILTIN
|
||||
#define MQTT_CLIENT_ID "xiao-nrf52840"
|
||||
|
||||
#ifndef MQTT_SERVER_URL
|
||||
#define MQTT_SERVER_URL "mqtt://broker.emqx.io:1883"
|
||||
#endif
|
||||
|
||||
// Stable, locally administered MAC derived from the factory device ID.
|
||||
#define MG_SET_MAC_ADDRESS(mac) \
|
||||
do { \
|
||||
uint32_t id_[2] = {NRF_FICR->DEVICEID[0], NRF_FICR->DEVICEID[1]}; \
|
||||
uint8_t hash_[32]; \
|
||||
mg_sha256(hash_, (uint8_t *) id_, sizeof(id_)); \
|
||||
memcpy(mac, hash_, 6); \
|
||||
(mac)[0] = ((mac)[0] & 0xfc) | 2; \
|
||||
} while (0)
|
||||
@@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
#define CFG_TUSB_MCU OPT_MCU_NRF5X
|
||||
#define CFG_TUSB_OS OPT_OS_NONE
|
||||
#define CFG_TUSB_RHPORT0_MODE OPT_MODE_DEVICE
|
||||
#define CFG_TUD_ENDPOINT0_SIZE 64
|
||||
#define CFG_TUD_CDC 1
|
||||
#define CFG_TUD_CDC_RX_BUFSIZE 256
|
||||
#define CFG_TUD_CDC_TX_BUFSIZE 4096
|
||||
#define CFG_TUD_CDC_EP_BUFSIZE 64
|
||||
@@ -0,0 +1,99 @@
|
||||
#include "hal.h"
|
||||
#include "tusb.h"
|
||||
extern void tusb_hal_nrf_power_event(uint32_t event);
|
||||
void USBD_IRQHandler(void) {
|
||||
tud_int_handler(0);
|
||||
}
|
||||
void POWER_CLOCK_IRQHandler(void) {
|
||||
if (NRF_POWER->EVENTS_USBDETECTED) {
|
||||
NRF_POWER->EVENTS_USBDETECTED = 0;
|
||||
tusb_hal_nrf_power_event(0);
|
||||
}
|
||||
if (NRF_POWER->EVENTS_USBREMOVED) {
|
||||
NRF_POWER->EVENTS_USBREMOVED = 0;
|
||||
tusb_hal_nrf_power_event(1);
|
||||
}
|
||||
if (NRF_POWER->EVENTS_USBPWRRDY) {
|
||||
NRF_POWER->EVENTS_USBPWRRDY = 0;
|
||||
tusb_hal_nrf_power_event(2);
|
||||
}
|
||||
}
|
||||
void usb_init(void) {
|
||||
NRF_POWER->INTENSET = POWER_INTENSET_USBDETECTED_Msk |
|
||||
POWER_INTENSET_USBREMOVED_Msk |
|
||||
POWER_INTENSET_USBPWRRDY_Msk;
|
||||
NVIC_SetPriority(POWER_CLOCK_IRQn, 3);
|
||||
NVIC_SetPriority(USBD_IRQn, 3);
|
||||
if (NRF_POWER->USBREGSTATUS & POWER_USBREGSTATUS_VBUSDETECT_Msk)
|
||||
tusb_hal_nrf_power_event(0);
|
||||
if (NRF_POWER->USBREGSTATUS & POWER_USBREGSTATUS_OUTPUTRDY_Msk)
|
||||
tusb_hal_nrf_power_event(2);
|
||||
NRF_POWER->EVENTS_USBDETECTED = 0;
|
||||
NRF_POWER->EVENTS_USBPWRRDY = 0;
|
||||
NRF_POWER->EVENTS_USBREMOVED = 0;
|
||||
NVIC_ClearPendingIRQ(POWER_CLOCK_IRQn);
|
||||
tusb_init();
|
||||
NVIC_EnableIRQ(POWER_CLOCK_IRQn);
|
||||
}
|
||||
void usb_poll(void) {
|
||||
tud_task();
|
||||
tud_cdc_write_flush();
|
||||
}
|
||||
void usb_log(char ch, void *param) {
|
||||
(void) param;
|
||||
if (tud_cdc_connected()) tud_cdc_write_char(ch);
|
||||
}
|
||||
void tud_cdc_line_state_cb(uint8_t itf, bool dtr, bool rts) {
|
||||
cdc_line_coding_t lc;
|
||||
(void) rts;
|
||||
tud_cdc_n_get_line_coding(itf, &lc);
|
||||
if (!dtr && lc.bit_rate == 1200) {
|
||||
NRF_POWER->GPREGRET = 0x57;
|
||||
NVIC_SystemReset();
|
||||
}
|
||||
}
|
||||
static const tusb_desc_device_t s_dev = {
|
||||
.bLength = sizeof(tusb_desc_device_t),
|
||||
.bDescriptorType = TUSB_DESC_DEVICE,
|
||||
.bcdUSB = 0x0200,
|
||||
.bDeviceClass = TUSB_CLASS_MISC,
|
||||
.bDeviceSubClass = MISC_SUBCLASS_COMMON,
|
||||
.bDeviceProtocol = MISC_PROTOCOL_IAD,
|
||||
.bMaxPacketSize0 = 64,
|
||||
.idVendor = 0xcafe,
|
||||
.idProduct = 0x4001,
|
||||
.bcdDevice = 0x0100,
|
||||
.iManufacturer = 1,
|
||||
.iProduct = 2,
|
||||
.iSerialNumber = 3,
|
||||
.bNumConfigurations = 1};
|
||||
uint8_t const *tud_descriptor_device_cb(void) {
|
||||
return (const uint8_t *) &s_dev;
|
||||
}
|
||||
static const uint8_t s_cfg[] = {
|
||||
TUD_CONFIG_DESCRIPTOR(1, 2, 0, TUD_CONFIG_DESC_LEN + TUD_CDC_DESC_LEN, 0,
|
||||
100),
|
||||
TUD_CDC_DESCRIPTOR(0, 0, 0x81, 8, 0x02, 0x82, 64)};
|
||||
uint8_t const *tud_descriptor_configuration_cb(uint8_t index) {
|
||||
(void) index;
|
||||
return s_cfg;
|
||||
}
|
||||
uint16_t const *tud_descriptor_string_cb(uint8_t index, uint16_t langid) {
|
||||
static uint16_t buf[32];
|
||||
const char *strings[] = {"", "Cesanta", "XIAO nRF52840", "mongoose-nrf52840"};
|
||||
size_t n = 0;
|
||||
(void) langid;
|
||||
if (index == 0) {
|
||||
buf[1] = 0x0409;
|
||||
n = 1;
|
||||
} else if (index < 4) {
|
||||
const char *s = strings[index];
|
||||
while (s[n] && n < 31) {
|
||||
buf[n + 1] = s[n];
|
||||
n++;
|
||||
}
|
||||
} else
|
||||
return NULL;
|
||||
buf[0] = (uint16_t) ((TUSB_DESC_STRING << 8) | (2 * n + 2));
|
||||
return buf;
|
||||
}
|
||||
Reference in new issue
Block a user