mirror of
https://github.com/cesanta/mongoose.git
synced 2026-10-12 10:13:29 +00:00
@@ -52,4 +52,4 @@ endif
|
||||
|
||||
# Cleanup. Delete built program and all build artifacts
|
||||
clean:
|
||||
$(DELETE) $(PROG) $(PACK) *.o *.obj *.exe *.dSYM mbedtls
|
||||
$(DELETE) $(PROG) $(PACK) *.o *.obj *.exe *.bin *.dSYM mbedtls
|
||||
|
||||
@@ -4,12 +4,34 @@
|
||||
#include "mongoose.h"
|
||||
#include "net.h"
|
||||
|
||||
#define CONFIG_FILE "settings.bin"
|
||||
|
||||
static int s_sig_num;
|
||||
static void signal_handler(int sig_num) {
|
||||
signal(sig_num, signal_handler);
|
||||
s_sig_num = sig_num;
|
||||
}
|
||||
|
||||
bool web_load_settings(void *buf, size_t len) {
|
||||
bool ok = false;
|
||||
size_t size = 0;
|
||||
void *data = mg_file_read(&mg_fs_posix, CONFIG_FILE, &size);
|
||||
if (data == NULL) {
|
||||
MG_ERROR(("Error reading %s", CONFIG_FILE));
|
||||
} else if (size != len) {
|
||||
MG_ERROR(("%s size != %lu", CONFIG_FILE, len));
|
||||
} else {
|
||||
memcpy(buf, data, len);
|
||||
}
|
||||
free(data);
|
||||
return ok;
|
||||
}
|
||||
|
||||
bool web_save_settings(void *buf, size_t len) {
|
||||
MG_INFO(("Saving to %s", CONFIG_FILE));
|
||||
return mg_file_write(&mg_fs_posix, CONFIG_FILE, buf, len);
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
struct mg_mgr mgr;
|
||||
|
||||
|
||||
@@ -4,12 +4,13 @@
|
||||
#include "net.h"
|
||||
|
||||
// Device settings
|
||||
struct settings {
|
||||
struct device_settings {
|
||||
uint32_t magic;
|
||||
int log_level;
|
||||
bool mqtt_enabled;
|
||||
char *mqtt_server_url;
|
||||
char *mqtt_topic_tx;
|
||||
char *mqtt_topic_rx;
|
||||
char mqtt_server_url[64];
|
||||
char mqtt_topic_tx[16];
|
||||
char mqtt_topic_rx[16];
|
||||
};
|
||||
|
||||
struct conndata {
|
||||
@@ -17,13 +18,21 @@ struct conndata {
|
||||
unsigned long id; // Connection ID waiting for the Modbus response
|
||||
};
|
||||
|
||||
static struct settings s_settings = {3, false, NULL, NULL, NULL};
|
||||
|
||||
static struct device_settings s_settings;
|
||||
static const char *s_json_header =
|
||||
"Content-Type: application/json\r\n"
|
||||
"Cache-Control: no-cache\r\n";
|
||||
static uint64_t s_boot_timestamp = 0; // Updated by SNTP
|
||||
|
||||
static void set_default_settings(struct device_settings *s) {
|
||||
s->magic = SETTINGS_MAGIC;
|
||||
s->log_level = MG_LL_DEBUG;
|
||||
mg_snprintf(s->mqtt_server_url, sizeof(s->mqtt_server_url), "%s",
|
||||
"mqtt://broker.hivemq.com:1883");
|
||||
mg_snprintf(s->mqtt_topic_tx, sizeof(s->mqtt_topic_tx), "%s", "modbus1/tx");
|
||||
mg_snprintf(s->mqtt_topic_rx, sizeof(s->mqtt_topic_rx), "%s", "modbus1/rx");
|
||||
}
|
||||
|
||||
// This is for newlib and TLS (mbedTLS)
|
||||
uint64_t mg_now(void) {
|
||||
return mg_millis() + s_boot_timestamp;
|
||||
@@ -50,25 +59,28 @@ static void timer_sntp_fn(void *param) {
|
||||
mg_sntp_connect(param, "udp://time.google.com:123", sfn, NULL);
|
||||
}
|
||||
|
||||
static void setfromjson(struct mg_str json, const char *jsonpath, char **dst) {
|
||||
static void setfromjson(struct mg_str json, const char *jsonpath, char *buf,
|
||||
size_t len) {
|
||||
char *val = mg_json_get_str(json, jsonpath);
|
||||
if (val != NULL) {
|
||||
free(*dst);
|
||||
*dst = val;
|
||||
}
|
||||
if (val != NULL) mg_snprintf(buf, len, "%s", val);
|
||||
free(val);
|
||||
}
|
||||
|
||||
static void handle_settings_set(struct mg_connection *c, struct mg_str body) {
|
||||
struct settings settings;
|
||||
struct device_settings settings;
|
||||
memset(&settings, 0, sizeof(settings));
|
||||
set_default_settings(&settings);
|
||||
mg_json_get_bool(body, "$.mqtt_enabled", &settings.mqtt_enabled);
|
||||
settings.log_level = mg_json_get_long(body, "$.log_level", MG_LL_INFO);
|
||||
setfromjson(body, "$.mqtt_server_url", &settings.mqtt_server_url);
|
||||
setfromjson(body, "$.mqtt_topic_rx", &settings.mqtt_topic_rx);
|
||||
setfromjson(body, "$.mqtt_topic_tx", &settings.mqtt_topic_tx);
|
||||
setfromjson(body, "$.mqtt_server_url", settings.mqtt_server_url,
|
||||
sizeof(settings.mqtt_server_url));
|
||||
setfromjson(body, "$.mqtt_topic_rx", settings.mqtt_topic_rx,
|
||||
sizeof(settings.mqtt_topic_rx));
|
||||
setfromjson(body, "$.mqtt_topic_tx", settings.mqtt_topic_tx,
|
||||
sizeof(settings.mqtt_topic_tx));
|
||||
|
||||
s_settings = settings; // TODO: save to the device flash
|
||||
bool ok = true;
|
||||
s_settings = settings;
|
||||
bool ok = web_save_settings(&s_settings, sizeof(s_settings));
|
||||
mg_http_reply(c, 200, s_json_header,
|
||||
"{%m:%s,%m:%m}", //
|
||||
MG_ESC("status"), ok ? "true" : "false", //
|
||||
@@ -146,7 +158,7 @@ static struct mg_connection *start_modbus_request(struct mg_mgr *mgr,
|
||||
send16(c, reg); // Start register
|
||||
send16(c, nregs); // Number of registers
|
||||
|
||||
if (func == 16) { // Fill in register values to write
|
||||
if (func == 16) { // Fill in register values to write
|
||||
send8(c, (uint8_t) (nregs * 2)); // Send number of bytes
|
||||
for (uint16_t i = 0; i < nregs; i++) {
|
||||
char path[20];
|
||||
@@ -283,10 +295,8 @@ static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
|
||||
}
|
||||
|
||||
void web_init(struct mg_mgr *mgr) {
|
||||
// Init default settings
|
||||
s_settings.mqtt_server_url = strdup("mqtt://broker.hivemq.com:1883");
|
||||
s_settings.mqtt_topic_tx = strdup("modbus1/tx");
|
||||
s_settings.mqtt_topic_rx = strdup("modbus1/rx");
|
||||
set_default_settings(&s_settings);
|
||||
web_load_settings(&s_settings, sizeof(s_settings));
|
||||
|
||||
mg_http_listen(mgr, HTTP_URL, fn, NULL);
|
||||
mg_http_listen(mgr, HTTPS_URL, fn, (void *) 1);
|
||||
|
||||
@@ -16,7 +16,11 @@ extern "C" {
|
||||
#define HTTPS_URL "https://0.0.0.0:8443"
|
||||
#endif
|
||||
|
||||
void web_init(struct mg_mgr *mgr);
|
||||
void web_init(struct mg_mgr *mgr); // Initialise Web UI
|
||||
bool web_load_settings(void *buf, size_t len); // Load from disk/flash
|
||||
bool web_save_settings(void *buf, size_t len); // Save to disk/flash
|
||||
|
||||
#define SETTINGS_MAGIC 0xaabbccdd
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -298,13 +298,14 @@ function Settings({}) {
|
||||
return html`
|
||||
<div class="m-4 grid grid-cols-1 gap-4 md:grid-cols-2">
|
||||
|
||||
${saveResult && html`<${Notification} ok=${saveResult.status}
|
||||
text=${saveResult.message} close=${() => setSaveResult(null)} />`}
|
||||
|
||||
<div class="py-1 divide-y border rounded bg-white flex flex-col">
|
||||
<div class="font-light uppercase flex items-center text-gray-600 px-4 py-2">
|
||||
MQTT Forwarding
|
||||
<//>
|
||||
<div class="py-2 px-5 flex-1 flex flex-col relative">
|
||||
${saveResult && html`<${Notification} ok=${saveResult.status}
|
||||
text=${saveResult.message} close=${() => setSaveResult(null)} />`}
|
||||
<${Setting} title="Enable MQTT forwarding" value=${settings.mqtt_enabled} setfn=${mksetfn('mqtt_enabled')} type="switch" />
|
||||
<${Setting} title="MQTT Server URL" value=${settings.mqtt_server_url} setfn=${mksetfn('mqtt_server_url')} type="" disabled=${!settings.mqtt_enabled} />
|
||||
<${Setting} title="MQTT Topic RX" value=${settings.mqtt_topic_rx} setfn=${mksetfn('mqtt_topic_rx')} type="" disabled=${!settings.mqtt_enabled} />
|
||||
@@ -335,8 +336,6 @@ function Settings({}) {
|
||||
Console Log
|
||||
<//>
|
||||
<div class="py-2 px-5 flex-1 flex flex-col relative">
|
||||
${saveResult && html`<${Notification} ok=${saveResult.status}
|
||||
text=${saveResult.message} close=${() => setSaveResult(null)} />`}
|
||||
<${Setting} title="Log Level" value=${settings.log_level} setfn=${mksetfn('log_level')} type="select" addonLeft="0-3" options=${logOptions} />
|
||||
<div class="mb-1 mt-3 flex place-content-end"><${Button} icon=${Icons.save} onclick=${onsave} title="Save Settings" /><//>
|
||||
<//>
|
||||
|
||||
@@ -77,7 +77,7 @@ int main(void) {
|
||||
}
|
||||
|
||||
MG_INFO(("Initialising application..."));
|
||||
web_init(&mgr);
|
||||
//web_init(&mgr);
|
||||
|
||||
MG_INFO(("Starting event loop"));
|
||||
for (;;) {
|
||||
|
||||
@@ -1,13 +1,18 @@
|
||||
ROOT ?= $(realpath $(PWD)/../../..)
|
||||
PREFIX ?= riscv-none-elf
|
||||
#PREFIX ?= docker run --platform linux/amd64 --rm -it -v$(ROOT):$(ROOT) -w $(PWD) mdashnet/riscv riscv64-unknown-elf
|
||||
|
||||
CFLAGS = -W -Wall -Wextra -Wno-error -Wundef -Wshadow -Wdouble-promotion
|
||||
CFLAGS += -Wformat-truncation -fno-common -Wconversion -Wno-sign-conversion
|
||||
CFLAGS += -ffunction-sections -fdata-sections
|
||||
CFLAGS += -march=rv32imafc -mabi=ilp32f
|
||||
CFLAGS += -DSYSCLK_FREQ_144MHz_HSE -I. -Ivendor -g3 -Os $(CFLAGS_EXTRA)
|
||||
|
||||
LDFLAGS = -T vendor/link.ld -nostartfiles --specs=nano.specs
|
||||
LDFLAGS += -lc -lgcc -Wl,--gc-sections
|
||||
LDFLAGS = -T link.ld -nostartfiles --specs=nano.specs
|
||||
LDFLAGS += -lc -lgcc -Wl,--gc-sections #-Wl,-Map=$@.map
|
||||
|
||||
SOURCES = main.c mongoose.c net.c packed_fs.c vendor/system_ch32v30x.c vendor/startup_ch32v30x_D8C.S
|
||||
SOURCES = main.c mongoose.c net.c packed_fs.c
|
||||
SOURCES += vendor/system_ch32v30x.c vendor/startup_ch32v30x_D8C.S
|
||||
CFLAGS += -DHTTP_URL=\"http://0.0.0.0/\" -DHTTPS_URL=\"https://0.0.0.0/\"
|
||||
|
||||
ifeq ($(OS),Windows_NT)
|
||||
@@ -19,14 +24,15 @@ endif
|
||||
all: firmware.bin
|
||||
|
||||
firmware.bin: firmware.elf
|
||||
riscv64-unknown-elf-objcopy -O binary $< $@
|
||||
$(PREFIX)-objcopy -O binary $< $@
|
||||
ls -l firmware.*
|
||||
|
||||
firmware.elf: $(SOURCES) hal.h Makefile
|
||||
riscv64-unknown-elf-gcc $(SOURCES) $(CFLAGS) $(LDFLAGS) -o $@
|
||||
firmware.elf: $(SOURCES) link.ld hal.h mongoose_custom.h Makefile
|
||||
$(PREFIX)-gcc $(SOURCES) $(CFLAGS) $(LDFLAGS) -o $@
|
||||
|
||||
flash: firmware.bin
|
||||
wchisp flash $<
|
||||
# @echo; echo "IMPORTANT: configure device to 96k RAM / 224k Flash"
|
||||
|
||||
clean:
|
||||
$(RM) firmware.* *.su
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#define UART_DEBUG USART1
|
||||
|
||||
#include <ch32v30x.h>
|
||||
|
||||
#include <stdbool.h>
|
||||
@@ -58,7 +60,7 @@ static inline void gpio_init(uint16_t pin, uint8_t mode, uint8_t cfg) {
|
||||
}
|
||||
volatile uint32_t *r = &gpio->CFGLR;
|
||||
if (no > 7) r = &gpio->CFGHR, no -= 8;
|
||||
uint8_t v = (mode & 3U) | ((cfg & 3U) << 2);
|
||||
uint8_t v = (uint8_t) ((mode & 3U) | ((cfg & 3U) << 2));
|
||||
CLRSET(*r, 15U << (no * 4), v << (no * 4));
|
||||
}
|
||||
static inline void gpio_input(uint16_t pin) {
|
||||
@@ -127,6 +129,23 @@ static inline void ethernet_init(void) {
|
||||
// NVIC_SetPriority(ETH_IRQn, 0);
|
||||
}
|
||||
|
||||
// opt: 0: 128/192, 1: 96/224, 2: 64/256, 3: 32/288
|
||||
static inline void set_ram_size(uint8_t opt) {
|
||||
// Unlock flash option byte, RM 32.6.1
|
||||
FLASH->KEYR = 0x45670123;
|
||||
FLASH->KEYR = 0xcdef89ab;
|
||||
FLASH->OBKEYR = 0x45670123;
|
||||
FLASH->OBKEYR = 0xcdef89ab;
|
||||
FLASH->CTLR |= 1U << 4; // Enable options byte programming
|
||||
unsigned val = *(uint16_t *) 0x1ffff802;
|
||||
val &= ~(3U << 6);
|
||||
val |= ((opt & 3U)) << 6;
|
||||
FLASH->CTLR |= FLASH_CTLR_PG; // Set programming bit
|
||||
*(uint16_t *) 0x1ffff802 = (uint16_t) val; // Write half-word
|
||||
spin(9999);
|
||||
FLASH->CTLR &= ~(1U << 4);
|
||||
}
|
||||
|
||||
static inline void rng_init(void) {
|
||||
RNG->CR |= RNG_CR_RNGEN;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
ENTRY(_start);
|
||||
MEMORY {
|
||||
flash(rx) : ORIGIN = 0x00000000, LENGTH = 288k
|
||||
sram(rwx) : ORIGIN = 0x20000000, LENGTH = 32k
|
||||
}
|
||||
_estack = ORIGIN(sram) + LENGTH(sram);
|
||||
|
||||
SECTIONS {
|
||||
.init : { _sinit = .; . = ALIGN(4); KEEP(*(SORT_NONE(.init))) . = ALIGN(4); _einit = . ; } > flash
|
||||
.vector : { KEEP(*(.vector)) } > flash
|
||||
.text : { *(.text .text.* .rodata .rodata.*) } > flash
|
||||
.dalign : { . = ALIGN(4); PROVIDE(_data_vma = . ); } > sram AT > flash
|
||||
.dlalign : { . = ALIGN(4); PROVIDE(_data_lma = . ); } > flash AT > flash
|
||||
|
||||
.data : {
|
||||
_sdata = .;
|
||||
*(.data .data.*)
|
||||
. = ALIGN(8);
|
||||
PROVIDE( __global_pointer$ = . + 0x800 );
|
||||
*(.sdata .sdata.* .gnu.linkonce.s.*) /* for weak symbol linking */
|
||||
_edata = .;
|
||||
} > sram AT > flash
|
||||
|
||||
.bss : { _sbss = .; *(.bss .bss.* COMMON) _ebss = .; } > sram
|
||||
. = ALIGN(8);
|
||||
_end = .;
|
||||
|
||||
__stack_size = 2048;
|
||||
.stack ORIGIN(sram) + LENGTH(sram) - __stack_size : {
|
||||
PROVIDE( _heap_end = . );
|
||||
. = ALIGN(4);
|
||||
PROVIDE(_susrstack = . );
|
||||
. = . + __stack_size;
|
||||
PROVIDE( _eusrstack = . );
|
||||
} > sram
|
||||
}
|
||||
@@ -8,7 +8,6 @@
|
||||
#define LED1_PIN PIN('A', 15) // On-board red LED
|
||||
#define LED2_PIN PIN('B', 4) // On-board blue LED
|
||||
#define LED_PIN LED2_PIN
|
||||
#define UART_DEBUG USART1
|
||||
|
||||
#define BLINK_PERIOD_MS 1000 // LED_PIN blinking period in millis
|
||||
|
||||
@@ -29,10 +28,24 @@ void mg_random(void *buf, size_t len) { // Use on-board RNG
|
||||
}
|
||||
}
|
||||
|
||||
// This flash space resides at after the 0-wait 320k area
|
||||
static char *s_flash_space = (char *) (0x8000000 + 320 * 1024);
|
||||
|
||||
bool web_load_settings(void *buf, size_t len) {
|
||||
if (*(uint32_t *) s_flash_space != SETTINGS_MAGIC) return false;
|
||||
memcpy(buf, s_flash_space, len);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool web_save_settings(void *buf, size_t len) {
|
||||
return mg_flash_write(s_flash_space, buf, len);
|
||||
}
|
||||
|
||||
static void timer_fn(void *arg) {
|
||||
gpio_toggle(LED_PIN); // Blink LED_PIN
|
||||
struct mg_tcpip_if *ifp = arg; // And show
|
||||
const char *names[] = {"down", "up", "req", "ready"}; // network stats
|
||||
return;
|
||||
MG_INFO(("Ethernet: %s, IP: %M, rx:%u, tx:%u, dr:%u, er:%u",
|
||||
names[ifp->state], mg_print_ip4, &ifp->ip, ifp->nrecv, ifp->nsent,
|
||||
ifp->ndrop, ifp->nerr));
|
||||
@@ -53,21 +66,20 @@ static void mg_putchar(char ch, void *param) {
|
||||
// https://mongoose.ws/documentation/#2-minute-integration-guide
|
||||
static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
|
||||
if (ev == MG_EV_HTTP_MSG) {
|
||||
// The MG_EV_HTTP_MSG event means HTTP request. `hm` holds parsed request,
|
||||
// see https://mongoose.ws/documentation/#struct-mg_http_message
|
||||
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||
|
||||
// If the requested URI is "/api/hi", send a simple JSON response back
|
||||
if (mg_http_match_uri(hm, "/api/hi")) {
|
||||
// Use mg_http_reply() API function to generate JSON response. It adds a
|
||||
// Content-Length header automatically. In the response, we show
|
||||
// the requested URI and HTTP body:
|
||||
mg_http_reply(c, 200, "", "{%m:%m,%m:%m}\n", // See mg_snprintf doc
|
||||
MG_ESC("uri"), mg_print_esc, hm->uri.len, hm->uri.ptr,
|
||||
MG_ESC("body"), mg_print_esc, hm->body.len, hm->body.ptr);
|
||||
if (mg_http_match_uri(hm, "/api/ram")) {
|
||||
// This endpoint allows to read RAM:
|
||||
// curl IP:8000/api/ram -d '{"addr":"0x20", "len": 32}'
|
||||
char *s = mg_json_get_str(hm->body, "$.addr");
|
||||
void *buf = (void *) (uintptr_t) (s ? strtoul(s, NULL, 0) : 0);
|
||||
size_t len = mg_json_get_long(hm->body, "$.len", 4);
|
||||
mg_hexdump(buf, len);
|
||||
mg_http_reply(c, 200, "", "{%m:%m}\n", MG_ESC("data"), mg_print_hex, len,
|
||||
buf);
|
||||
free(s);
|
||||
} else {
|
||||
// For all other URIs, serve static content
|
||||
mg_http_reply(c, 200, "", "hi tick %llu\n", s_ticks); // See mg_snprintf doc
|
||||
mg_http_reply(c, 200, "", "hi tick %llu\n", s_ticks);
|
||||
}
|
||||
}
|
||||
(void) fn_data;
|
||||
@@ -85,10 +97,19 @@ int main(void) {
|
||||
mg_log_set(MG_LL_DEBUG); // Set log level
|
||||
mg_log_set_fn(mg_putchar, UART_DEBUG);
|
||||
|
||||
ethernet_init(); // Initialise ethernet pins
|
||||
MG_INFO(("Starting, CPU freq %g MHz", (double) SystemCoreClock / 1000000));
|
||||
// MG_INFO(("RCC_RSTSCKR=%#lx", RCC->RSTSCKR));
|
||||
extern char _end[], _heap_end[];
|
||||
MG_INFO(("Heap size: %lu bytes", _heap_end - _end));
|
||||
|
||||
// Print chip RAM/Flash configuration, and set to 64/256
|
||||
const char *sizes[] = {"128/192", "96/224", "64/256", "32/288"};
|
||||
uint32_t mode = (FLASH->OBR >> 8) & 3U;
|
||||
MG_INFO(("RAM/FLASH configuration: %s", sizes[mode]));
|
||||
if (mode != 3) set_ram_size(3), mg_device_reset();
|
||||
|
||||
// Initialise Mongoose network stack
|
||||
ethernet_init(); // Initialise ethernet pins
|
||||
struct mg_tcpip_driver_stm32_data driver_data = {.mdc_cr = 1};
|
||||
struct mg_tcpip_if mif = {.mac = GENERATE_LOCALLY_ADMINISTERED_MAC(),
|
||||
// Uncomment below for static configuration:
|
||||
@@ -117,17 +138,12 @@ int main(void) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Newlib syscalls overrides. IO retargeting, and malloc
|
||||
int _write(int fd, char *ptr, int len) {
|
||||
if (fd == 1 || fd == 2) uart_write_buf(UART_DEBUG, ptr, (size_t) len);
|
||||
return len;
|
||||
}
|
||||
|
||||
// Newlib syscall for malloc
|
||||
void *_sbrk(ptrdiff_t incr) {
|
||||
extern char _end[];
|
||||
extern char _heap_end[];
|
||||
extern char _end[], _heap_end[];
|
||||
static char *curbrk = _end;
|
||||
if ((curbrk + incr < _end) || (curbrk + incr > _heap_end)) return NULL - 1;
|
||||
//MG_INFO(("%p %ld", curbrk, incr));
|
||||
curbrk += incr;
|
||||
return curbrk - incr;
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
// See https://mongoose.ws/documentation/#build-options
|
||||
#define MG_ARCH MG_ARCH_NEWLIB
|
||||
#define MG_OTA MG_OTA_FLASH
|
||||
#define MG_DEVICE MG_DEVICE_CH32V307
|
||||
|
||||
#define MG_ENABLE_TCPIP 1
|
||||
#define MG_ENABLE_CUSTOM_MILLIS 1
|
||||
|
||||
+180
-1
@@ -1 +1,180 @@
|
||||
ENTRY( _start )␍␍__stack_size = 2048;␍␍PROVIDE( _stack_size = __stack_size );␍␍␍MEMORY␍{␍/* CH32V30x_D8C - CH32V305RB-CH32V305FB␍ CH32V30x_D8 - CH32V303CB-CH32V303RB␍*/␍/*␍ FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 128K␍ RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 32K␍*/␍ ␍/* CH32V30x_D8C - CH32V307VC-CH32V307WC-CH32V307RC␍ CH32V30x_D8 - CH32V303VC-CH32V303RC␍ FLASH + RAM supports the following configuration␍ FLASH-192K + RAM-128K␍ FLASH-224K + RAM-96K␍ FLASH-256K + RAM-64K ␍ FLASH-288K + RAM-32K ␍*/␍ FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 288K␍ RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 32K␍}␍␍␍SECTIONS␍{␍␍ .init :␍ {␍ _sinit = .;␍ . = ALIGN(4);␍ KEEP(*(SORT_NONE(.init)))␍ . = ALIGN(4);␍ _einit = .;␍ } >FLASH AT>FLASH␍␍ .vector :␍ {␍ *(.vector);␍ . = ALIGN(64);␍ } >FLASH AT>FLASH␍␍ .text :␍ {␍ . = ALIGN(4);␍ *(.text)␍ *(.text.*)␍ *(.rodata)␍ *(.rodata*)␍ *(.gnu.linkonce.t.*)␍ . = ALIGN(4);␍ } >FLASH AT>FLASH ␍␍ .fini :␍ {␍ KEEP(*(SORT_NONE(.fini)))␍ . = ALIGN(4);␍ } >FLASH AT>FLASH␍␍ PROVIDE( _etext = . );␍ PROVIDE( _eitcm = . ); ␍␍ .preinit_array :␍ {␍ PROVIDE_HIDDEN (__preinit_array_start = .);␍ KEEP (*(.preinit_array))␍ PROVIDE_HIDDEN (__preinit_array_end = .);␍ } >FLASH AT>FLASH ␍ ␍ .init_array :␍ {␍ PROVIDE_HIDDEN (__init_array_start = .);␍ KEEP (*(SORT_BY_INIT_PRIORITY(.init_array.*) SORT_BY_INIT_PRIORITY(.ctors.*)))␍ KEEP (*(.init_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .ctors))␍ PROVIDE_HIDDEN (__init_array_end = .);␍ } >FLASH AT>FLASH ␍ ␍ .fini_array :␍ {␍ PROVIDE_HIDDEN (__fini_array_start = .);␍ KEEP (*(SORT_BY_INIT_PRIORITY(.fini_array.*) SORT_BY_INIT_PRIORITY(.dtors.*)))␍ KEEP (*(.fini_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .dtors))␍ PROVIDE_HIDDEN (__fini_array_end = .);␍ } >FLASH AT>FLASH ␍ ␍ .ctors :␍ {␍ /* gcc uses crtbegin.o to find the start of␍ the constructors, so we make sure it is␍ first. Because this is a wildcard, it␍ doesn't matter if the user does not␍ actually link against crtbegin.o; the␍ linker won't look for a file to match a␍ wildcard. The wildcard also means that it␍ doesn't matter which directory crtbegin.o␍ is in. */␍ KEEP (*crtbegin.o(.ctors))␍ KEEP (*crtbegin?.o(.ctors))␍ /* We don't want to include the .ctor section from␍ the crtend.o file until after the sorted ctors.␍ The .ctor section from the crtend file contains the␍ end of ctors marker and it must be last */␍ KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .ctors))␍ KEEP (*(SORT(.ctors.*)))␍ KEEP (*(.ctors))␍ } >FLASH AT>FLASH ␍ ␍ .dtors :␍ {␍ KEEP (*crtbegin.o(.dtors))␍ KEEP (*crtbegin?.o(.dtors))␍ KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .dtors))␍ KEEP (*(SORT(.dtors.*)))␍ KEEP (*(.dtors))␍ } >FLASH AT>FLASH ␍␍ .dalign :␍ {␍ . = ALIGN(4);␍ PROVIDE(_data_vma = .);␍ } >RAM AT>FLASH ␍␍ .dlalign :␍ {␍ . = ALIGN(4); ␍ PROVIDE(_data_lma = .);␍ } >FLASH AT>FLASH␍␍ .data :␍ {␍ *(.gnu.linkonce.r.*)␍ *(.data .data.*)␍ *(.gnu.linkonce.d.*)␍ . = ALIGN(8);␍ PROVIDE( __global_pointer$ = . + 0x800 );␍ *(.sdata .sdata.*)␍ *(.sdata2.*)␍ *(.gnu.linkonce.s.*)␍ . = ALIGN(8);␍ *(.srodata.cst16)␍ *(.srodata.cst8)␍ *(.srodata.cst4)␍ *(.srodata.cst2)␍ *(.srodata .srodata.*)␍ . = ALIGN(4);␍ PROVIDE( _edata = .);␍ } >RAM AT>FLASH␍␍ .bss :␍ {␍ . = ALIGN(4);␍ PROVIDE( _sbss = .);␍ *(.sbss*)␍ *(.gnu.linkonce.sb.*)␍ *(.bss*)␍ *(.gnu.linkonce.b.*) ␍ *(COMMON*)␍ . = ALIGN(4);␍ PROVIDE( _ebss = .);␍ } >RAM AT>FLASH␍␍ PROVIDE( _end = _ebss);␍ PROVIDE( end = . );␍␍ .stack ORIGIN(RAM) + LENGTH(RAM) - __stack_size :␍ {␍ PROVIDE( _heap_end = . ); ␍ . = ALIGN(4);␍ PROVIDE(_susrstack = . );␍ . = . + __stack_size;␍ PROVIDE( _eusrstack = .);␍ } >RAM ␍␍}␍␍␍␍
|
||||
ENTRY( _start )
|
||||
__stack_size = 2048;
|
||||
PROVIDE( _stack_size = __stack_size );
|
||||
|
||||
|
||||
MEMORY {
|
||||
/* CH32V30x_D8C - CH32V305RB-CH32V305FB
|
||||
CH32V30x_D8 - CH32V303CB-CH32V303RB
|
||||
*/
|
||||
/*
|
||||
FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 128K
|
||||
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 32K
|
||||
*/
|
||||
|
||||
/* CH32V30x_D8C - CH32V307VC-CH32V307WC-CH32V307RC
|
||||
CH32V30x_D8 - CH32V303VC-CH32V303RC
|
||||
FLASH + RAM supports the following configuration
|
||||
FLASH-192K + RAM-128K
|
||||
FLASH-224K + RAM-96K
|
||||
FLASH-256K + RAM-64K
|
||||
FLASH-288K + RAM-32K
|
||||
FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 288K
|
||||
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 32K
|
||||
*/
|
||||
|
||||
FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 288k
|
||||
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 32k
|
||||
}
|
||||
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
|
||||
.init :
|
||||
{
|
||||
_sinit = .;
|
||||
. = ALIGN(4);
|
||||
KEEP(*(SORT_NONE(.init)))
|
||||
. = ALIGN(4);
|
||||
_einit = .;
|
||||
} >FLASH AT>FLASH
|
||||
|
||||
.vector :
|
||||
{
|
||||
*(.vector);
|
||||
. = ALIGN(64);
|
||||
} >FLASH AT>FLASH
|
||||
|
||||
.text :
|
||||
{
|
||||
. = ALIGN(4);
|
||||
*(.text)
|
||||
*(.text.*)
|
||||
*(.rodata)
|
||||
*(.rodata*)
|
||||
*(.gnu.linkonce.t.*)
|
||||
. = ALIGN(4);
|
||||
} >FLASH AT>FLASH
|
||||
|
||||
.fini :
|
||||
{
|
||||
KEEP(*(SORT_NONE(.fini)))
|
||||
. = ALIGN(4);
|
||||
} >FLASH AT>FLASH
|
||||
|
||||
PROVIDE( _etext = . );
|
||||
PROVIDE( _eitcm = . );
|
||||
|
||||
.preinit_array :
|
||||
{
|
||||
PROVIDE_HIDDEN (__preinit_array_start = .);
|
||||
KEEP (*(.preinit_array))
|
||||
PROVIDE_HIDDEN (__preinit_array_end = .);
|
||||
} >FLASH AT>FLASH
|
||||
|
||||
.init_array :
|
||||
{
|
||||
PROVIDE_HIDDEN (__init_array_start = .);
|
||||
KEEP (*(SORT_BY_INIT_PRIORITY(.init_array.*) SORT_BY_INIT_PRIORITY(.ctors.*)))
|
||||
KEEP (*(.init_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .ctors))
|
||||
PROVIDE_HIDDEN (__init_array_end = .);
|
||||
} >FLASH AT>FLASH
|
||||
|
||||
.fini_array :
|
||||
{
|
||||
PROVIDE_HIDDEN (__fini_array_start = .);
|
||||
KEEP (*(SORT_BY_INIT_PRIORITY(.fini_array.*) SORT_BY_INIT_PRIORITY(.dtors.*)))
|
||||
KEEP (*(.fini_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .dtors))
|
||||
PROVIDE_HIDDEN (__fini_array_end = .);
|
||||
} >FLASH AT>FLASH
|
||||
|
||||
.ctors :
|
||||
{
|
||||
/* gcc uses crtbegin.o to find the start of
|
||||
the constructors, so we make sure it is
|
||||
first. Because this is a wildcard, it
|
||||
doesn't matter if the user does not
|
||||
actually link against crtbegin.o; the
|
||||
linker won't look for a file to match a
|
||||
wildcard. The wildcard also means that it
|
||||
doesn't matter which directory crtbegin.o
|
||||
is in. */
|
||||
KEEP (*crtbegin.o(.ctors))
|
||||
KEEP (*crtbegin?.o(.ctors))
|
||||
/* We don't want to include the .ctor section from
|
||||
the crtend.o file until after the sorted ctors.
|
||||
The .ctor section from the crtend file contains the
|
||||
end of ctors marker and it must be last */
|
||||
KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .ctors))
|
||||
KEEP (*(SORT(.ctors.*)))
|
||||
KEEP (*(.ctors))
|
||||
} >FLASH AT>FLASH
|
||||
|
||||
.dtors :
|
||||
{
|
||||
KEEP (*crtbegin.o(.dtors))
|
||||
KEEP (*crtbegin?.o(.dtors))
|
||||
KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .dtors))
|
||||
KEEP (*(SORT(.dtors.*)))
|
||||
KEEP (*(.dtors))
|
||||
} >FLASH AT>FLASH
|
||||
|
||||
.dalign :
|
||||
{
|
||||
. = ALIGN(4);
|
||||
PROVIDE(_data_vma = .);
|
||||
} >RAM AT>FLASH
|
||||
|
||||
.dlalign :
|
||||
{
|
||||
. = ALIGN(4);
|
||||
PROVIDE(_data_lma = .);
|
||||
} >FLASH AT>FLASH
|
||||
|
||||
.data :
|
||||
{
|
||||
*(.gnu.linkonce.r.*)
|
||||
*(.data .data.*)
|
||||
*(.gnu.linkonce.d.*)
|
||||
. = ALIGN(8);
|
||||
PROVIDE( __global_pointer$ = . + 0x800 );
|
||||
*(.sdata .sdata.*)
|
||||
*(.sdata2.*)
|
||||
*(.gnu.linkonce.s.*)
|
||||
. = ALIGN(8);
|
||||
*(.srodata.cst16)
|
||||
*(.srodata.cst8)
|
||||
*(.srodata.cst4)
|
||||
*(.srodata.cst2)
|
||||
*(.srodata .srodata.*)
|
||||
. = ALIGN(4);
|
||||
PROVIDE( _edata = .);
|
||||
} >RAM AT>FLASH
|
||||
|
||||
.bss :
|
||||
{
|
||||
. = ALIGN(4);
|
||||
PROVIDE( _sbss = .);
|
||||
*(.sbss*)
|
||||
*(.gnu.linkonce.sb.*)
|
||||
*(.bss*)
|
||||
*(.gnu.linkonce.b.*)
|
||||
*(COMMON*)
|
||||
. = ALIGN(4);
|
||||
PROVIDE( _ebss = .);
|
||||
} >RAM AT>FLASH
|
||||
|
||||
PROVIDE( _end = _ebss);
|
||||
PROVIDE( end = . );
|
||||
|
||||
.stack ORIGIN(RAM) + LENGTH(RAM) - __stack_size :
|
||||
{
|
||||
PROVIDE( _heap_end = . );
|
||||
. = ALIGN(4);
|
||||
PROVIDE(_susrstack = . );
|
||||
. = . + __stack_size;
|
||||
PROVIDE( _eusrstack = .);
|
||||
} >RAM
|
||||
|
||||
}
|
||||
|
||||
+82
@@ -112,6 +112,88 @@ size_t mg_base64_decode(const char *src, size_t n, char *dst, size_t dl) {
|
||||
return len;
|
||||
}
|
||||
|
||||
#ifdef MG_ENABLE_LINES
|
||||
#line 1 "src/device_ch32v307.c"
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#if MG_DEVICE == MG_DEVICE_CH32V307
|
||||
// RM: https://www.wch-ic.com/downloads/CH32FV2x_V3xRM_PDF.html
|
||||
|
||||
#define FLASH_BASE 0x40022000
|
||||
#define FLASH_ACTLR (FLASH_BASE + 0)
|
||||
#define FLASH_KEYR (FLASH_BASE + 4)
|
||||
#define FLASH_OBKEYR (FLASH_BASE + 8)
|
||||
#define FLASH_STATR (FLASH_BASE + 12)
|
||||
#define FLASH_CTLR (FLASH_BASE + 16)
|
||||
#define FLASH_ADDR (FLASH_BASE + 20)
|
||||
#define FLASH_OBR (FLASH_BASE + 28)
|
||||
#define FLASH_WPR (FLASH_BASE + 32)
|
||||
|
||||
void *mg_flash_start(void) {
|
||||
return (void *) 0x08000000;
|
||||
}
|
||||
size_t mg_flash_size(void) {
|
||||
return 480 * 1024; // First 320k is 0-wait
|
||||
}
|
||||
size_t mg_flash_sector_size(void) {
|
||||
return 4096;
|
||||
}
|
||||
size_t mg_flash_write_align(void) {
|
||||
return 4;
|
||||
}
|
||||
int mg_flash_bank(void) {
|
||||
return 0;
|
||||
}
|
||||
void mg_device_reset(void) {
|
||||
*((volatile uint32_t *) 0xbeef0000) |= 1U << 7; // NVIC_SystemReset()
|
||||
}
|
||||
static void flash_unlock(void) {
|
||||
static bool unlocked;
|
||||
if (unlocked == false) {
|
||||
MG_REG(FLASH_KEYR) = 0x45670123;
|
||||
MG_REG(FLASH_KEYR) = 0xcdef89ab;
|
||||
unlocked = true;
|
||||
}
|
||||
}
|
||||
static void flash_wait(void) {
|
||||
while (MG_REG(FLASH_STATR) & MG_BIT(0)) (void) 0;
|
||||
}
|
||||
|
||||
bool mg_flash_erase(void *addr) {
|
||||
//MG_INFO(("%p", addr));
|
||||
flash_unlock();
|
||||
flash_wait();
|
||||
MG_REG(FLASH_ADDR) = (uint32_t) addr;
|
||||
MG_REG(FLASH_CTLR) |= MG_BIT(1) | MG_BIT(6); // PER | STRT;
|
||||
flash_wait();
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool is_page_boundary(const void *addr) {
|
||||
uint32_t val = (uint32_t) addr;
|
||||
return (val & (mg_flash_sector_size() - 1)) == 0;
|
||||
}
|
||||
|
||||
bool mg_flash_write(void *addr, const void *buf, size_t len) {
|
||||
//MG_INFO(("%p %p %lu", addr, buf, len));
|
||||
//mg_hexdump(buf, len);
|
||||
flash_unlock();
|
||||
const uint16_t *src = (uint16_t *) buf, *end = &src[len / 2];
|
||||
uint16_t *dst = (uint16_t *) addr;
|
||||
MG_REG(FLASH_CTLR) |= MG_BIT(0); // Set PG
|
||||
//MG_INFO(("CTLR: %#lx", MG_REG(FLASH_CTLR)));
|
||||
while (src < end) {
|
||||
if (is_page_boundary(dst)) mg_flash_erase(dst);
|
||||
*dst++ = *src++;
|
||||
flash_wait();
|
||||
}
|
||||
MG_REG(FLASH_CTLR) &= ~MG_BIT(0); // Clear PG
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef MG_ENABLE_LINES
|
||||
#line 1 "src/device_dummy.c"
|
||||
#endif
|
||||
|
||||
+5
-4
@@ -1732,10 +1732,11 @@ MG_IRAM void mg_ota_boot(void); // Bootloader function
|
||||
|
||||
|
||||
|
||||
#define MG_DEVICE_NONE 0 // Dummy system
|
||||
#define MG_DEVICE_STM32H5 1 // STM32 H5
|
||||
#define MG_DEVICE_STM32H7 2 // STM32 H7
|
||||
#define MG_DEVICE_CUSTOM 100 // Custom implementation
|
||||
#define MG_DEVICE_NONE 0 // Dummy system
|
||||
#define MG_DEVICE_STM32H5 1 // STM32 H5
|
||||
#define MG_DEVICE_STM32H7 2 // STM32 H7
|
||||
#define MG_DEVICE_CH32V307 100 // WCH CH32V307
|
||||
#define MG_DEVICE_CUSTOM 1000 // Custom implementation
|
||||
|
||||
#ifndef MG_DEVICE
|
||||
#define MG_DEVICE MG_DEVICE_NONE
|
||||
|
||||
+5
-4
@@ -5,10 +5,11 @@
|
||||
|
||||
#include "arch.h"
|
||||
|
||||
#define MG_DEVICE_NONE 0 // Dummy system
|
||||
#define MG_DEVICE_STM32H5 1 // STM32 H5
|
||||
#define MG_DEVICE_STM32H7 2 // STM32 H7
|
||||
#define MG_DEVICE_CUSTOM 100 // Custom implementation
|
||||
#define MG_DEVICE_NONE 0 // Dummy system
|
||||
#define MG_DEVICE_STM32H5 1 // STM32 H5
|
||||
#define MG_DEVICE_STM32H7 2 // STM32 H7
|
||||
#define MG_DEVICE_CH32V307 100 // WCH CH32V307
|
||||
#define MG_DEVICE_CUSTOM 1000 // Custom implementation
|
||||
|
||||
#ifndef MG_DEVICE
|
||||
#define MG_DEVICE MG_DEVICE_NONE
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
#include "device.h"
|
||||
#include "log.h"
|
||||
|
||||
#if MG_DEVICE == MG_DEVICE_CH32V307
|
||||
// RM: https://www.wch-ic.com/downloads/CH32FV2x_V3xRM_PDF.html
|
||||
|
||||
#define FLASH_BASE 0x40022000
|
||||
#define FLASH_ACTLR (FLASH_BASE + 0)
|
||||
#define FLASH_KEYR (FLASH_BASE + 4)
|
||||
#define FLASH_OBKEYR (FLASH_BASE + 8)
|
||||
#define FLASH_STATR (FLASH_BASE + 12)
|
||||
#define FLASH_CTLR (FLASH_BASE + 16)
|
||||
#define FLASH_ADDR (FLASH_BASE + 20)
|
||||
#define FLASH_OBR (FLASH_BASE + 28)
|
||||
#define FLASH_WPR (FLASH_BASE + 32)
|
||||
|
||||
void *mg_flash_start(void) {
|
||||
return (void *) 0x08000000;
|
||||
}
|
||||
size_t mg_flash_size(void) {
|
||||
return 480 * 1024; // First 320k is 0-wait
|
||||
}
|
||||
size_t mg_flash_sector_size(void) {
|
||||
return 4096;
|
||||
}
|
||||
size_t mg_flash_write_align(void) {
|
||||
return 4;
|
||||
}
|
||||
int mg_flash_bank(void) {
|
||||
return 0;
|
||||
}
|
||||
void mg_device_reset(void) {
|
||||
*((volatile uint32_t *) 0xbeef0000) |= 1U << 7; // NVIC_SystemReset()
|
||||
}
|
||||
static void flash_unlock(void) {
|
||||
static bool unlocked;
|
||||
if (unlocked == false) {
|
||||
MG_REG(FLASH_KEYR) = 0x45670123;
|
||||
MG_REG(FLASH_KEYR) = 0xcdef89ab;
|
||||
unlocked = true;
|
||||
}
|
||||
}
|
||||
static void flash_wait(void) {
|
||||
while (MG_REG(FLASH_STATR) & MG_BIT(0)) (void) 0;
|
||||
}
|
||||
|
||||
bool mg_flash_erase(void *addr) {
|
||||
//MG_INFO(("%p", addr));
|
||||
flash_unlock();
|
||||
flash_wait();
|
||||
MG_REG(FLASH_ADDR) = (uint32_t) addr;
|
||||
MG_REG(FLASH_CTLR) |= MG_BIT(1) | MG_BIT(6); // PER | STRT;
|
||||
flash_wait();
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool is_page_boundary(const void *addr) {
|
||||
uint32_t val = (uint32_t) addr;
|
||||
return (val & (mg_flash_sector_size() - 1)) == 0;
|
||||
}
|
||||
|
||||
bool mg_flash_write(void *addr, const void *buf, size_t len) {
|
||||
//MG_INFO(("%p %p %lu", addr, buf, len));
|
||||
//mg_hexdump(buf, len);
|
||||
flash_unlock();
|
||||
const uint16_t *src = (uint16_t *) buf, *end = &src[len / 2];
|
||||
uint16_t *dst = (uint16_t *) addr;
|
||||
MG_REG(FLASH_CTLR) |= MG_BIT(0); // Set PG
|
||||
//MG_INFO(("CTLR: %#lx", MG_REG(FLASH_CTLR)));
|
||||
while (src < end) {
|
||||
if (is_page_boundary(dst)) mg_flash_erase(dst);
|
||||
*dst++ = *src++;
|
||||
flash_wait();
|
||||
}
|
||||
MG_REG(FLASH_CTLR) &= ~MG_BIT(0); // Clear PG
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
Reference in New Issue
Block a user