Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add nucleo-g031 baremetal example #2404

Merged
merged 1 commit into from
Oct 4, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions examples/stm32/nucleo-g031-make-baremetal-builtin/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
CFLAGS = -W -Wall -Wextra -Werror -Wundef -Wshadow -Wdouble-promotion
CFLAGS += -Wformat-truncation -fno-common -Wconversion -Wno-sign-conversion
CFLAGS += -g3 -O3 -ffunction-sections -fdata-sections
CFLAGS += -I. -Icmsis_core/CMSIS/Core/Include -Icmsis_g0/Include
CFLAGS += -mcpu=cortex-m0 -mthumb -mfloat-abi=soft $(CFLAGS_EXTRA)
LDFLAGS ?= -Tlink.ld -nostdlib -nostartfiles --specs nano.specs -lc -lgcc -Wl,--gc-sections -Wl,-Map=$@.map

SOURCES = main.c syscalls.c mongoose.c
SOURCES += cmsis_g0/Source/Templates/gcc/startup_stm32g031xx.s # ST startup file. Compiler-dependent!
CFLAGS += -DHTTP_URL=\"http://0.0.0.0/\" # Example-specifig flags

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

all build example: firmware.bin

firmware.bin: firmware.elf
arm-none-eabi-objcopy -O binary $< $@

firmware.elf: cmsis_core cmsis_g0 $(SOURCES) hal.h link.ld Makefile mongoose_custom.h
arm-none-eabi-gcc $(SOURCES) $(CFLAGS) $(LDFLAGS) -o $@

flash: firmware.bin
st-flash --reset write $< 0x8000000

cmsis_core: # ARM CMSIS core headers
git clone --depth 1 -b 5.9.0 https://github.com/ARM-software/CMSIS_5 $@
cmsis_g0: # ST CMSIS headers for STM32H5 series
git clone --depth 1 -b v1.4.3 https://github.com/STMicroelectronics/cmsis_device_g0 $@

# Automated remote test. Requires env variable VCON_API_KEY set. See https://vcon.io/automated-firmware-tests/
DEVICE_URL ?= https://dash.vcon.io/api/v3/devices/10
update: firmware.bin
curl --fail-with-body -su :$(VCON_API_KEY) $(DEVICE_URL)/ota --data-binary @$<

test update: CFLAGS += -DUART_DEBUG=USART1
test: update
curl --fail-with-body -su :$(VCON_API_KEY) $(DEVICE_URL)/tx?t=5 | tee /tmp/output.txt
grep 'READY, IP:' /tmp/output.txt # Check for network init

clean:
$(RM) firmware.* *.su cmsis_core cmsis_g0 mbedtls
186 changes: 186 additions & 0 deletions examples/stm32/nucleo-g031-make-baremetal-builtin/hal.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
// Copyright (c) 2022-2023 Cesanta Software Limited
// All rights reserved
//
// MCU manual: RM0444, board manual: UM2591
// https://www.st.com/resource/en/reference_manual/rm0444-stm32g0x1-advanced-armbased-32bit-mcus-stmicroelectronics.pdf
// https://www.st.com/resource/en/user_manual/um2591-stm32g0-nucleo32-board-mb1455-stmicroelectronics.pdf
// Alternate functions: https://www.st.com/resource/en/datasheet/stm32g031c6.pdf

#pragma once

// #define LED PIN('B', 3)
#define LED PIN('C', 6)
#ifndef UART_DEBUG
#define UART_DEBUG USART2
#endif

#include <stm32g031xx.h>

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

#define BIT(x) (1UL << (x))
#define SETBITS(R, CLEARMASK, SETMASK) (R) = ((R) & ~(CLEARMASK)) | (SETMASK)
#define PIN(bank, num) ((((bank) - 'A') << 8) | (num))
#define PINNO(pin) (pin & 255)
#define PINBANK(pin) (pin >> 8)

#define CPU_FREQUENCY 16000000
#define AHB_FREQUENCY CPU_FREQUENCY
#define APB_FREQUENCY CPU_FREQUENCY
// #define APB1_FREQUENCY (AHB_FREQUENCY / (BIT(PPRE1 - 3)))

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

enum { GPIO_MODE_INPUT, GPIO_MODE_OUTPUT, GPIO_MODE_AF, GPIO_MODE_ANALOG };
enum { GPIO_OTYPE_PUSH_PULL, GPIO_OTYPE_OPEN_DRAIN };
enum { GPIO_SPEED_LOW, GPIO_SPEED_MEDIUM, GPIO_SPEED_HIGH, GPIO_SPEED_INSANE };
enum { GPIO_PULL_NONE, GPIO_PULL_UP, GPIO_PULL_DOWN };

#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->BSRR = mask << (gpio->ODR & mask ? 16 : 0);
}
static inline int gpio_read(uint16_t pin) {
return gpio_bank(pin)->IDR & BIT(PINNO(pin)) ? 1 : 0;
}
static inline void gpio_write(uint16_t pin, bool val) {
GPIO_TypeDef *gpio = gpio_bank(pin);
gpio->BSRR = BIT(PINNO(pin)) << (val ? 0 : 16);
}
static inline void gpio_init(uint16_t pin, uint8_t mode, uint8_t type,
uint8_t speed, uint8_t pull, uint8_t af) {
GPIO_TypeDef *gpio = gpio_bank(pin);
uint8_t n = (uint8_t) (PINNO(pin));
RCC->IOPENR |= BIT(PINBANK(pin)); // Enable GPIO clock
SETBITS(gpio->OTYPER, 1UL << n, ((uint32_t) type) << n);
SETBITS(gpio->OSPEEDR, 3UL << (n * 2), ((uint32_t) speed) << (n * 2));
SETBITS(gpio->PUPDR, 3UL << (n * 2), ((uint32_t) pull) << (n * 2));
SETBITS(gpio->AFR[n >> 3], 15UL << ((n & 7) * 4),
((uint32_t) af) << ((n & 7) * 4));
SETBITS(gpio->MODER, 3UL << (n * 2), ((uint32_t) mode) << (n * 2));
}
static inline void gpio_input(uint16_t pin) {
gpio_init(pin, GPIO_MODE_INPUT, GPIO_OTYPE_PUSH_PULL, GPIO_SPEED_HIGH,
GPIO_PULL_NONE, 0);
}
static inline void gpio_output(uint16_t pin) {
gpio_init(pin, GPIO_MODE_OUTPUT, GPIO_OTYPE_PUSH_PULL, GPIO_SPEED_HIGH,
GPIO_PULL_NONE, 0);
}

static inline bool uart_init(USART_TypeDef *uart, unsigned long baud) {
uint8_t af = 1; // Alternate function
uint16_t rx = 0, tx = 0; // pins
uint32_t freq = 0; // Bus frequency

if (uart == USART1) {
freq = CPU_FREQUENCY, RCC->APBENR2 |= RCC_APBENR2_USART1EN;
tx = PIN('A', 9), rx = PIN('A', 10);
} else if (uart == USART2) {
freq = CPU_FREQUENCY, RCC->APBENR1 |= RCC_APBENR1_USART2EN;
tx = PIN('A', 2), rx = PIN('A', 3);
} else {
return false;
}
gpio_init(tx, GPIO_MODE_AF, GPIO_OTYPE_PUSH_PULL, GPIO_SPEED_HIGH, 0, af);
gpio_init(rx, GPIO_MODE_AF, GPIO_OTYPE_PUSH_PULL, GPIO_SPEED_HIGH, 0, af);
uart->CR1 = 0; // Disable UART
uart->BRR = freq / baud; // Set baud rate
uart->CR1 = USART_CR1_RE | USART_CR1_TE; // Set mode to TX & RX
uart->CR1 |= USART_CR1_UE; // Enable UART
return true;
}
static inline void uart_write_byte(USART_TypeDef *uart, uint8_t byte) {
uart->TDR = byte;
while ((uart->ISR & BIT(7)) == 0) 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->ISR & BIT(5); // If RXNE bit is set, data is ready
}
static inline uint8_t uart_read_byte(USART_TypeDef *uart) {
return (uint8_t) (uart->RDR & 255);
}

#ifndef RNG
struct rng {
volatile uint32_t CR, SR, DR;
};
#define RNG ((struct rng *) 0x40025000) // RM0444 2.2.2 Table 6
#endif

static inline void rng_init(void) {
RCC->CCIPR |= 2U << 26U; // RNG clock source. Documented in 5.4.21
RCC->AHBENR |= BIT(18); // RM0444 5.4.25 Table 36
RNG->CR |= BIT(2); // 19.7.1
}
static inline uint32_t rng_read(void) {
while ((RNG->SR & BIT(0)) == 0) (void) 0;
return RNG->DR;
}

// Bit-bang SPI implementation
struct spi {
uint16_t miso, mosi, clk, cs; // Pins
int spin; // Number of NOP spins for bitbanging
};

static inline void spi_begin(struct spi *spi) {
gpio_write(spi->cs, 0);
// printf("%s\n", __func__);
}

static inline void spi_end(struct spi *spi) {
gpio_write(spi->cs, 1);
// printf("%s\n", __func__);
}

static inline void spi_init(struct spi *spi) {
gpio_input(spi->miso);
gpio_output(spi->mosi);
gpio_output(spi->clk);
gpio_output(spi->cs);
gpio_write(spi->cs, 1);
// printf("%s\n", __func__);
}

// Send a byte, and return a received byte
static inline uint8_t spi_txn(struct spi *spi, uint8_t write_byte) {
unsigned count = spi->spin <= 0 ? 9 : (unsigned) spi->spin;
uint8_t rx = 0, tx = write_byte;
for (int i = 0; i < 8; i++) {
gpio_write(spi->mosi, tx & 0x80U); // Set mosi
gpio_write(spi->clk, 1); // Clock high
spin(count); // Wait half cycle
rx <<= 1U; // Shift alreay read bits
if (gpio_read(spi->miso)) rx |= 1U; // Read next bit
gpio_write(spi->clk, 0); // Clock low
spin(count); // Wait half cycle
tx <<= 1U; // Discard written bit
}
// printf("%s %02x %02x\n", __func__, (int) write_byte, (int) rx);
return rx; // Return the received byte
}

#define UUID ((uint32_t *) UID_BASE) // Unique 96-bit chip ID. TRM 59.1

// Helper macro for MAC generation, byte reads not allowed
#define GENERATE_LOCALLY_ADMINISTERED_MAC() \
{ \
2, UUID[0] & 255, (UUID[0] >> 10) & 255, (UUID[0] >> 19) & 255, \
UUID[1] & 255, UUID[2] & 255 \
}
30 changes: 30 additions & 0 deletions examples/stm32/nucleo-g031-make-baremetal-builtin/link.ld
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
ENTRY(Reset_Handler);
MEMORY {
flash(rx) : ORIGIN = 0x08000000, LENGTH = 64k
sram(rwx) : ORIGIN = 0x20000000, LENGTH = 8k
}
_estack = ORIGIN(sram) + LENGTH(sram); /* End of RAM. stack points here */

SECTIONS {
.vectors : { KEEP(*(.isr_vector)) } > flash
.text : { *(.text* .text.*) } > flash
.rodata : { *(.rodata*) } > flash

.data : {
_sdata = .;
*(.first_data)
*(.ram)
*(.data SORT(.data.*))
_edata = .;
} > sram AT > flash
_sidata = LOADADDR(.data);

.bss : {
_sbss = .;
*(.bss SORT(.bss.*) COMMON)
_ebss = .;
} > sram

. = ALIGN(8);
_end = .;
}
111 changes: 111 additions & 0 deletions examples/stm32/nucleo-g031-make-baremetal-builtin/main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
// Copyright (c) 2023 Cesanta Software Limited
// All rights reserved

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

#define BLINK_PERIOD_MS 1000 // LED blinking period in millis
static struct spi spi_pins = {
.miso = PIN('B', 4),
.mosi = PIN('A', 0),
.clk = PIN('A', 1),
.cs = PIN('A', 4),
};
uint32_t SystemCoreClock = CPU_FREQUENCY;

void SystemInit(void) { // Called automatically by startup code
rng_init();
SysTick_Config(CPU_FREQUENCY / 1000); // Sys tick every 1ms
}

static volatile uint64_t s_ticks; // Milliseconds since boot
void SysTick_Handler(void) { // SyStick IRQ handler, triggered every 1ms
s_ticks++;
}

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

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

static void timer_fn(void *arg) {
gpio_toggle(LED); // Blink LED
struct mg_tcpip_if *ifp = arg; // And show
MG_INFO(("Ethernet: %d, IP: %M, rx:%u, tx:%u, dr:%u, er:%u", ifp->state,
mg_print_ip4, &ifp->ip, ifp->nrecv, ifp->nsent, ifp->ndrop,
ifp->nerr));
}

static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
struct mg_tcpip_if *ifp = (struct mg_tcpip_if *) fn_data;
if (ev == MG_EV_HTTP_MSG) {
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
if (mg_http_match_uri(hm, "/api/hello")) { // Request to /api/hello
mg_http_reply(c, 200, "", "{%m:%u,%m:%u,%m:%u,%m:%u,%m:%u}\n",
MG_ESC("eth"), ifp->state, MG_ESC("frames_received"),
ifp->nrecv, MG_ESC("frames_sent"), ifp->nsent,
MG_ESC("frames_dropped"), ifp->ndrop,
MG_ESC("interface_errors"), ifp->nerr);
} else if (mg_http_match_uri(hm, "/")) { // Index page
mg_http_reply(
c, 200, "", "%s",
"<html><head><link rel='icon' href='data:;base64,='></head><body>"
"<h1>Welcome to Mongoose</h1>"
"See <a href=/api/hello>/api/hello</a> for REST example"
"</body></html>");
} else { // All other URIs
mg_http_reply(c, 404, "", "Not Found\n");
}
}
}

int main(void) {
gpio_output(LED); // Setup green LED
uart_init(UART_DEBUG, 115200); // Initialise debug printf

// ethernet_init(); // Initialise ethernet pins
MG_INFO(("Starting, CPU freq %g MHz", (double) SystemCoreClock / 1000000));

struct mg_mgr mgr; // Initialise
mg_mgr_init(&mgr); // Mongoose event manager
mg_log_set(MG_LL_DEBUG); // Set log level

// Initialise Mongoose network stack
spi_init(&spi_pins);
struct mg_tcpip_spi spi = {
.begin = (void (*)(void *)) spi_begin,
.end = (void (*)(void *)) spi_end,
.txn = (uint8_t(*)(void *, uint8_t)) spi_txn,
.spi = &spi_pins,
};
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_w5500,
.driver_data = &spi};
mg_tcpip_init(&mgr, &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_timer_add(&mgr, BLINK_PERIOD_MS, MG_TIMER_REPEAT, timer_fn, &mif);
mg_http_listen(&mgr, "http://0.0.0.0:80", fn, &mif);

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

return 0;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#pragma once

#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_IO_SIZE 128
#define MG_ENABLE_LINES 1
Loading