Skip to content

Commit

Permalink
Merge pull request #2485 from cesanta/ch32v307
Browse files Browse the repository at this point in the history
Add example for ch32v307
  • Loading branch information
cpq authored Nov 22, 2023
2 parents 6386cdf + fa76312 commit f86c14a
Show file tree
Hide file tree
Showing 15 changed files with 9,114 additions and 1,006 deletions.
31 changes: 31 additions & 0 deletions examples/wch/ch32v307-make-baremetal-builtin/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
CFLAGS = -W -Wall -Wextra -Wno-error -Wundef -Wshadow -Wdouble-promotion
CFLAGS += -Wformat-truncation -fno-common -Wconversion -Wno-sign-conversion
CFLAGS += -ffunction-sections -fdata-sections #-fmessage-length=0 -fsigned-char
CFLAGS += -march=rv32imafc -mabi=ilp32f #-msmall-data-limit=8 -mno-save-restore
CFLAGS += -DSYSCLK_FREQ_144MHz_HSE -I. -Ivendor -g3 -Os $(CFLAGS_EXTRA)

LDFLAGS = -T vendor/link.ld -nostartfiles --specs=nano.specs #--specs=nosys.specs
LDFLAGS += -lc -lgcc -Wl,--gc-sections #-Wl,-Map=$@.map

SOURCES = main.c mongoose.c vendor/system_ch32v30x.c vendor/startup_ch32v30x_D8C.S

ifeq ($(OS),Windows_NT)
RM = cmd /C del /Q /F /S
else
RM = rm -rf
endif

all: firmware.bin

firmware.bin: firmware.elf
riscv64-unknown-elf-objcopy -O binary $< $@
ls -l firmware.*

firmware.elf: $(SOURCES) hal.h Makefile
riscv64-unknown-elf-gcc $(SOURCES) $(CFLAGS) $(LDFLAGS) -o $@

flash: firmware.bin
wchisp flash $<

clean:
$(RM) firmware.* *.su
140 changes: 140 additions & 0 deletions examples/wch/ch32v307-make-baremetal-builtin/hal.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
// Copyright (c) 2023 Cesanta Software Limited
// All rights reserved

#pragma once

#include <ch32v30x.h>

#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>

#define BIT(x) (1UL << (x))
#define CLRSET(reg, clear, set) ((reg) = ((reg) & ~(clear)) | (set))
#define PIN(bank, num) ((((bank) - 'A') << 8) | (num))
#define PINNO(pin) (pin & 255)
#define PINBANK(pin) (pin >> 8)

extern uint32_t SystemCoreClock;
extern void SystemInit(void);
extern void SystemCoreClockUpdate(void);

static inline void spin(volatile uint32_t count) {
while (count--) (void) 0;
}

enum {
GPIO_MODE_INPUT,
GPIO_MODE_OUTPUT_10M,
GPIO_MODE_OUTPUT_2M,
GPIO_MODE_OUTPUT_50M
};
enum { GPIO_OTYPE_PP, GPIO_OTYPE_OD, GPIO_OTYPE_AF_PP, GPIO_AF_OD };
enum { GPIO_ITYPE_ANALOG, GPIO_ITYPE_FLOAT, GPIO_ITYPE_PUPD };
#define GPIO(N) ((GPIO_TypeDef *) (GPIOA_BASE + 0x400 * (N)))

static GPIO_TypeDef *gpio_bank(uint16_t pin) {
return GPIO(PINBANK(pin));
}
static inline void gpio_toggle(uint16_t pin) {
GPIO_TypeDef *gpio = gpio_bank(pin);
uint32_t mask = BIT(PINNO(pin));
gpio->BSHR = mask << (gpio->OUTDR & mask ? 16 : 0);
}
static inline bool gpio_read(uint16_t pin) {
return gpio_bank(pin)->INDR & BIT(PINNO(pin)) ? true : false;
}
static inline void gpio_write(uint16_t pin, bool val) {
GPIO_TypeDef *gpio = gpio_bank(pin);
gpio->BSHR = BIT(PINNO(pin)) << (val ? 0 : 16);
}
static inline void gpio_init(uint16_t pin, uint8_t mode, uint8_t cfg) {
GPIO_TypeDef *gpio = gpio_bank(pin);
uint8_t bank = (uint8_t) PINBANK(pin), no = (uint8_t) PINNO(pin);
RCC->APB2PCENR |= BIT(bank + 2); // Enable GPIO clock, section 3.4.7
if (mode != GPIO_MODE_INPUT && cfg == GPIO_OTYPE_AF_PP) {
RCC->APB2PCENR |= BIT(0); // Enable AFIO
}
volatile uint32_t *r = &gpio->CFGLR;
if (no > 7) r = &gpio->CFGHR, no -= 8;
uint8_t v = (mode & 3U) | ((cfg & 3U) << 2);
CLRSET(*r, 15U << (no * 4), v << (no * 4));
}
static inline void gpio_input(uint16_t pin) {
gpio_init(pin, GPIO_MODE_INPUT, GPIO_ITYPE_PUPD);
}
static inline void gpio_output(uint16_t pin) {
gpio_init(pin, GPIO_MODE_OUTPUT_50M, GPIO_OTYPE_PP);
}

static inline void uart_init(USART_TypeDef *uart, unsigned baud) {
uint16_t rx = 0, tx = 0; // pins
uint32_t freq = 0; // Bus frequency. UART1 is on APB2, rest on APB1

if (uart == USART1) freq = SystemCoreClock, RCC->APB2PCENR |= BIT(14);
if (uart == USART2) freq = SystemCoreClock, RCC->APB1PCENR |= BIT(17);
if (uart == USART3) freq = SystemCoreClock, RCC->APB1PCENR |= BIT(18);

if (uart == USART1) tx = PIN('A', 9), rx = PIN('A', 10);
// if (uart == USART1) tx = PIN('B', 6), rx = PIN('B', 7);
if (uart == USART2) tx = PIN('A', 2), rx = PIN('A', 3);
if (uart == USART3) tx = PIN('B', 10), rx = PIN('B', 11);

gpio_init(tx, GPIO_MODE_OUTPUT_50M, GPIO_OTYPE_AF_PP);
gpio_init(rx, GPIO_MODE_INPUT, GPIO_ITYPE_PUPD);
uart->CTLR1 = 0; // Disable this UART
unsigned div = freq / baud * 100 / 16; // 18.3
uart->BRR = (uint16_t) (((div / 100) << 4) | (div * 16 / 100));
uart->CTLR1 = BIT(13) | BIT(2) | BIT(3); // Set UE, RE, TE
}
static inline void uart_write_byte(USART_TypeDef *uart, uint8_t byte) {
uart->DATAR = byte;
volatile int timeout = 999;
while ((uart->STATR & BIT(7)) == 0 && timeout--) spin(1);
}
static inline void uart_write_buf(USART_TypeDef *uart, char *buf, size_t len) {
while (len-- > 0) uart_write_byte(uart, *(uint8_t *) buf++);
}
static inline int uart_read_ready(USART_TypeDef *uart) {
return uart->STATR & BIT(5); // If RXNE bit is set, data is ready
}
static inline uint8_t uart_read_byte(USART_TypeDef *uart) {
return (uint8_t) (uart->DATAR & 255U);
}

static inline void ethernet_init(void) {
// Set 60MHz ethernet clock
RCC->CTLR &= ~BIT(28); // PLL3 off
CLRSET(RCC->CFGR2, 15U << 4, 1U << 4); // 3.4.12: PREDIV2 = 2
CLRSET(RCC->CFGR2, 15U << 12, 13U << 12); // 3.4.12: PLL3MUL = 15
RCC->CTLR |= BIT(28); // PLL3 on

EXTEN->EXTEN_CTR |= EXTEN_ETH_10M_EN; // Enable built-in 10M PHY
#if 0
// RMII mode
// 27.1.3 : init pins
uint16_t pins[] = {PIN('A', 1), PIN('A', 2), PIN('A', 7),
PIN('B', 11), PIN('B', 12), PIN('B', 13),
PIN('C', 1), PIN('C', 4), PIN('C', 5)};
for (size_t i = 0; i < sizeof(pins) / sizeof(pins[0]); i++) {
gpio_init(pins[i], GPIO_MODE_OUTPUT_50M, GPIO_OTYPE_AF_PP);
}
AFIO->PCFR1 |= BIT(23); // Use RMII
#endif
RCC->AHBPCENR |= BIT(14) | BIT(15) | BIT(16); // Enable MAC, TX, RX
NVIC_EnableIRQ(ETH_IRQn);
// NVIC_SetPriority(ETH_IRQn, 0);
}

static inline void rng_init(void) {
RNG->CR |= RNG_CR_RNGEN;
}
static inline uint32_t rng_read(void) {
return RNG->DR;
}

// Helper macro for MAC generation
#define ROM_MAC ((uint8_t *) (0X1ffff7e8 + 5))
#define GENERATE_LOCALLY_ADMINISTERED_MAC() \
{ 2, ROM_MAC[0], ROM_MAC[1], ROM_MAC[2], ROM_MAC[3], ROM_MAC[4] }
131 changes: 131 additions & 0 deletions examples/wch/ch32v307-make-baremetal-builtin/main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
// Copyright (c) 2023 Cesanta Software Limited

#include "hal.h"
#include "mongoose.h"

#define BTN_PIN PIN('B', 3) // On-board user button
#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

static volatile uint64_t s_ticks; // Milliseconds since boot
__attribute__((interrupt())) void SysTick_Handler(void) {
s_ticks++;
SysTick->SR = 0;
}

uint64_t mg_millis(void) { // Let Mongoose use our uptime function
return s_ticks; // Return number of milliseconds since boot
}

void mg_random(void *buf, size_t len) { // Use on-board RNG
for (size_t n = 0; n < len; n += sizeof(uint32_t)) {
uint32_t r = rng_read();
memcpy((char *) buf + n, &r, n + sizeof(r) > len ? len - n : sizeof(r));
}
}

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
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));
}

void SysTick_Init(void) {
SysTick->CMP = SystemCoreClock / 1000 - 1;
SysTick->CNT = 0;
SysTick->SR = 0;
SysTick->CTLR = BIT(0) | BIT(1) | BIT(2) | BIT(3);
NVIC_EnableIRQ(SysTicK_IRQn);
}

static void mg_putchar(char ch, void *param) {
uart_write_byte(param, ch);
}

// 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);
} else {
// For all other URIs, serve static content
mg_http_reply(c, 200, "", "hi tick %llu\n", s_ticks); // See mg_snprintf doc
}
}
(void) fn_data;
}

int main(void) {
SystemCoreClockUpdate();
SysTick_Init();

gpio_output(LED_PIN); // Setup LED
uart_init(UART_DEBUG, 115200); // Initialise debug printf

struct mg_mgr mgr; // Initialise
mg_mgr_init(&mgr); // Mongoose event manager
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));

// Initialise Mongoose network stack
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:
// .ip = mg_htonl(MG_U32(192, 168, 0, 223)),
// .mask = mg_htonl(MG_U32(255, 255, 255, 0)),
// .gw = mg_htonl(MG_U32(192, 168, 0, 1)),
.driver = &mg_tcpip_driver_stm32,
.driver_data = &driver_data};
mg_tcpip_init(&mgr, &mif);
mg_timer_add(&mgr, BLINK_PERIOD_MS, MG_TIMER_REPEAT, timer_fn, &mif);

MG_INFO(("MAC: %M. Waiting for IP...", mg_print_mac, mif.mac));
while (mif.state != MG_TCPIP_STATE_READY) {
mg_mgr_poll(&mgr, 0);
}

MG_INFO(("Initialising application..."));
mg_http_listen(&mgr, "http://0.0.0.0", fn, NULL);

MG_INFO(("Starting event loop"));
for (;;) {
mg_mgr_poll(&mgr, 0);
}

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;
}

void *_sbrk(ptrdiff_t incr) {
extern char _end[];
extern char _heap_end[];
static char *curbrk = _end;
if ((curbrk + incr < _end) || (curbrk + incr > _heap_end)) return NULL - 1;
curbrk += incr;
return curbrk - incr;
}
1 change: 1 addition & 0 deletions examples/wch/ch32v307-make-baremetal-builtin/mongoose.c
1 change: 1 addition & 0 deletions examples/wch/ch32v307-make-baremetal-builtin/mongoose.h
12 changes: 12 additions & 0 deletions examples/wch/ch32v307-make-baremetal-builtin/mongoose_custom.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#pragma once

// See https://mongoose.ws/documentation/#build-options
#define MG_ARCH MG_ARCH_NEWLIB

#define MG_ENABLE_TCPIP 1
#define MG_ENABLE_CUSTOM_MILLIS 1
#define MG_ENABLE_CUSTOM_RANDOM 1
//#define MG_ENABLE_PACKED_FS 1
#define MG_ENABLE_FILE 0
#define MG_ENABLE_DRIVER_STM32 1
#define MG_ENABLE_LINES 1
Loading

0 comments on commit f86c14a

Please sign in to comment.