diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 967207bb..4fdaf4ba 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -98,6 +98,7 @@ jobs: - path: stm32/stm32-nucleo-f429z - path: stm32/stm32-nucleo-h743z - path: stm32/nucleo-f429zi-baremetal + target: test - path: stm32/nucleo-f429zi-freertos-mip - path: stm32/nucleo-f429zi-usb-rndis - path: stm32/nucleo-f746zg-baremetal @@ -135,7 +136,7 @@ jobs: - uses: actions/checkout@v3 - run: sudo apt -y install gcc-arm-none-eabi - name: ${{ matrix.example.path }} - run: make -C examples/${{ matrix.example.path }} build + run: make -C examples/${{ matrix.example.path }} ${{ matrix.example.target || 'build' }} VCON_API_KEY=${{secrets.VCON_API_KEY}} zephyr_examples: runs-on: ubuntu-latest steps: diff --git a/examples/device-dashboard/net.c b/examples/device-dashboard/net.c index 21782a76..61f1be75 100644 --- a/examples/device-dashboard/net.c +++ b/examples/device-dashboard/net.c @@ -109,6 +109,7 @@ static void mqtt_fn(struct mg_connection *c, int ev, void *ev_data, void *fnd) { c->is_hexdumping = 1; mg_mqtt_sub(s_mqtt, mg_str(s_config.sub), 2); send_notification(c->mgr, "{%Q:%Q,%Q:null}", "name", "config", "data"); + MG_INFO(("MQTT connected, server %s", MQTT_SERVER)); } else if (ev == MG_EV_MQTT_MSG) { struct mg_mqtt_message *mm = ev_data; send_notification(c->mgr, "{%Q:%Q,%Q:{%Q: %.*Q, %Q: %.*Q, %Q: %d}}", "name", diff --git a/examples/stm32/nucleo-f429zi-baremetal/Makefile b/examples/stm32/nucleo-f429zi-baremetal/Makefile index 25baa026..98e3787b 100644 --- a/examples/stm32/nucleo-f429zi-baremetal/Makefile +++ b/examples/stm32/nucleo-f429zi-baremetal/Makefile @@ -1,42 +1,35 @@ -TARGET = firmware -ROOT ?= $(realpath $(CURDIR)/../../..) -D ?= docker -DOCKER ?= $(D) run --rm -v $(ROOT):$(ROOT) -w $(CURDIR) mdashnet/armgcc -CROSS ?= arm-none-eabi CFLAGS ?= -W -Wall -Wextra -Werror -Wundef -Wshadow -Wdouble-promotion \ -Wformat-truncation -fno-common -Wconversion \ - -g3 -Os -ffunction-sections -fdata-sections -I. -I$(ROOT) \ - -DMG_ARCH=MG_ARCH_NEWLIB -DMIP_DEBUG=1 -DMG_ENABLE_PACKED_FS=1 \ - -DMG_ENABLE_CUSTOM_MILLIS=1 -DxMG_ENABLE_LINES=1 -DMG_ENABLE_MIP=1 \ + -g3 -Os -ffunction-sections -fdata-sections -I. \ -mcpu=cortex-m4 -mthumb -mfloat-abi=hard -mfpu=fpv4-sp-d16 $(EXTRA_CFLAGS) LDFLAGS ?= -Tlink.ld -nostartfiles -nostdlib --specs nano.specs -lc -lgcc -Wl,--gc-sections -Wl,-Map=$@.map -SOURCES = boot.c main.c syscalls.c \ - $(ROOT)/mongoose.c \ - $(ROOT)/examples/device-dashboard/packed_fs.c \ - $(ROOT)/examples/device-dashboard/net.c +SOURCES = boot.c main.c syscalls.c -all build example: $(TARGET).bin +# Add Mongoose-specific flags and source files +CFLAGS += -I../../.. -DMG_ARCH=MG_ARCH_NEWLIB -DMG_ENABLE_CUSTOM_MILLIS=1 -DMG_ENABLE_MIP=1 -DMG_ENABLE_PACKED_FS=1 +SOURCES += ../../../mongoose.c ../../device-dashboard/net.c ../../device-dashboard/packed_fs.c -$(TARGET).bin: $(TARGET).elf - $(DOCKER) $(CROSS)-objcopy -O binary $< $@ +all build example: firmware.bin -$(TARGET).elf: $(SOURCES) mcu.h - $(DOCKER) $(CROSS)-gcc $(SOURCES) $(CFLAGS) $(LDFLAGS) -o $@ +firmware.bin: firmware.elf + arm-none-eabi-objcopy -O binary $< $@ -# Build on Windows. Download Win32 Zip from ARM GCC downloads, unzip to c:\armgcc -ARMGCC ?= c:/armgcc -windows: $(TARGET).bin -windows: CROSS = $(ARMGCC)/bin/arm-none-eabi -windows: DOCKER = - -# Show top 10 stack-hungry functions -su: CFLAGS += -fstack-usage -su: $(TARGET).elf - cat *.su | sort -rnk2 | head -10 +firmware.elf: $(SOURCES) mcu.h + arm-none-eabi-gcc $(SOURCES) $(CFLAGS) $(LDFLAGS) -o $@ # Note: on "unknown chip id" flash error, wire BOOT0 to VDD and st-flash erase -flash: $(TARGET).bin - st-flash --reset write $(TARGET).bin 0x8000000 +flash: firmware.bin + st-flash --reset write $< 0x8000000 + +# Requires env variable VCON_API_KEY set +DEVICE_URL ?= https://dash.vcon.io/api/v3/devices/2 +test: update + curl --fail -su :$(VCON_API_KEY) $(DEVICE_URL)/tx?t=5 | tee /tmp/output.txt + grep 'Ethernet: up' /tmp/output.txt + grep 'MQTT connected' /tmp/output.txt + +update: firmware.bin + curl --fail -su :$(VCON_API_KEY) $(DEVICE_URL)/ota --data-binary @$< clean: - @rm -rf $(TARGET).* *.su + @rm -rf firmware.* *.su diff --git a/examples/stm32/nucleo-f429zi-baremetal/main.c b/examples/stm32/nucleo-f429zi-baremetal/main.c index d8553c6f..4ceb4f8e 100644 --- a/examples/stm32/nucleo-f429zi-baremetal/main.c +++ b/examples/stm32/nucleo-f429zi-baremetal/main.c @@ -4,19 +4,14 @@ #include "mcu.h" #include "mongoose.h" -#define LED1 PIN('B', 0) // On-board LED pin (green) -#define LED2 PIN('B', 7) // On-board LED pin (blue) -#define LED3 PIN('B', 14) // On-board LED pin (red) -#define BTN1 PIN('C', 13) // On-board user button +#define LED1 PIN('B', 0) // On-board LED pin (green) +#define LED2 PIN('B', 7) // On-board LED pin (blue) +#define LED3 PIN('B', 14) // On-board LED pin (red) +#define BTN1 PIN('C', 13) // On-board user button +#define BLINK_PERIOD_MS 1000 // LED blinking period in millis static uint64_t s_ticks, s_exti; // Counters, increased by IRQ handlers -static void blink_cb(void *arg) { // Blink periodically - // MG_INFO(("ticks: %u", (unsigned) s_ticks)); - gpio_toggle(LED2); - (void) arg; -} - uint64_t mg_millis(void) { // Declare our own uptime function return s_ticks; // Return number of milliseconds since boot } @@ -36,15 +31,22 @@ void EXTI15_10_IRQHandler(void) { // External interrupt handler gpio_write(LED1, gpio_read(BTN1)); // No debounce. Turn LED if button pressed } +static void timer_cb(void *arg) { + gpio_toggle(LED2); // Blink LED + bool up = ((struct mip_if *) arg)->state == MIP_STATE_READY; + MG_INFO(("Ethernet: %s", up ? "up" : "down")); // Show network status +} + int main(void) { - static struct uart *uart = UART3; // Use UART3 - its attached to debug - clock_init(); // Set clock to 180MHz - systick_init(FREQ / 1000); // Increment s_ticks every ms - gpio_output(LED1); // Setup green LED - gpio_output(LED2); // Setup blue LED - gpio_input(BTN1); // Set button to input - irq_exti_attach(BTN1); // Attach BTN1 to exti - uart_init(uart, 115200); // It is wired to the debug port + clock_init(); // Set clock to 180MHz + systick_init(SYS_FREQUENCY / 1000); // Increment s_ticks every ms + gpio_output(LED1); // Setup green LED + gpio_output(LED2); // Setup blue LED + gpio_input(BTN1); // Set button to input + irq_exti_attach(BTN1); // Attach BTN1 to exti + uart_init(UART1, 115200); // Initialise debug printf + + MG_INFO(("Starting, CPU freq %g MHz", (double) SYS_FREQUENCY / 1000000)); // Initialise Ethernet. Enable MAC GPIO pins, see // https://www.farnell.com/datasheets/2014265.pdf section 6.10 @@ -65,23 +67,30 @@ int main(void) { struct mg_mgr mgr; // Initialise Mongoose event manager mg_mgr_init(&mgr); // and attach it to the MIP interface mg_log_set(MG_LL_DEBUG); // Set log level - mg_timer_add(&mgr, 500, MG_TIMER_REPEAT, blink_cb, &mgr); // Initialise Mongoose network stack // Specify MAC address, and IP/mask/GW in network byte order for static // IP configuration. If IP/mask/GW are unset, DHCP is going to be used - struct mip_driver_stm32_data driver_data = {.mdc_cr = 4}; // See driver_stm32.h - struct mip_if mif = { - .mac = {2, 0, 1, 2, 3, 5}, - .driver = &mip_driver_stm32, - .driver_data = &driver_data, - }; + struct mip_driver_stm32_data driver_data = {.mdc_cr = 4}; // driver_stm32.h + struct mip_if mif = {.mac = {2, 0, 1, 2, 3, 5}, + .driver = &mip_driver_stm32, + .driver_data = &driver_data}; mip_init(&mgr, &mif); + mg_timer_add(&mgr, BLINK_PERIOD_MS, MG_TIMER_REPEAT, timer_cb, &mif); - MG_INFO(("Init done, starting main loop")); + MG_INFO(("Waiting until network is up...")); + while (mif.state != MIP_STATE_READY) { + mg_mgr_poll(&mgr, 0); + } + + MG_INFO(("Initialising application...")); extern void device_dashboard_fn(struct mg_connection *, int, void *, void *); mg_http_listen(&mgr, "http://0.0.0.0", device_dashboard_fn, &mgr); - for (;;) mg_mgr_poll(&mgr, 0); // Infinite event loop + + MG_INFO(("Starting event loop")); + for (;;) { + mg_mgr_poll(&mgr, 0); + } return 0; } diff --git a/examples/stm32/nucleo-f429zi-baremetal/mcu.h b/examples/stm32/nucleo-f429zi-baremetal/mcu.h index 213feafa..8cda5c45 100644 --- a/examples/stm32/nucleo-f429zi-baremetal/mcu.h +++ b/examples/stm32/nucleo-f429zi-baremetal/mcu.h @@ -20,10 +20,10 @@ // 33.4: The AHB clock must be at least 25 MHz when Ethernet is used enum { APB1_PRE = 5 /* AHB clock / 4 */, APB2_PRE = 4 /* AHB clock / 2 */ }; enum { PLL_HSI = 16, PLL_M = 8, PLL_N = 180, PLL_P = 2 }; // Run at 180 Mhz -//#define PLL_FREQ PLL_HSI -#define PLL_FREQ (PLL_HSI * PLL_N / PLL_M / PLL_P) #define FLASH_LATENCY 5 -#define FREQ (PLL_FREQ * 1000000) +#define SYS_FREQUENCY ((PLL_HSI * PLL_N / PLL_M / PLL_P) * 1000000) +#define APB2_FREQUENCY (SYS_FREQUENCY / (BIT(APB2_PRE - 3))) +#define APB1_FREQUENCY (SYS_FREQUENCY / (BIT(APB1_PRE - 3))) static inline void spin(volatile uint32_t count) { while (count--) asm("nop"); @@ -93,13 +93,11 @@ struct gpio { }; #define GPIO(N) ((struct gpio *) (0x40020000 + 0x400 * (N))) -static struct gpio *gpio_bank(uint16_t pin) { - return GPIO(PINBANK(pin)); -} +static struct gpio *gpio_bank(uint16_t pin) { return GPIO(PINBANK(pin)); } static inline void gpio_toggle(uint16_t pin) { struct gpio *gpio = gpio_bank(pin); uint32_t mask = BIT(PINNO(pin)); - gpio->BSRR |= mask << (gpio->ODR & mask ? 16 : 0); + 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; @@ -163,10 +161,11 @@ static inline void uart_init(struct uart *uart, unsigned long baud) { // https://www.st.com/resource/en/datasheet/stm32f429zi.pdf uint8_t af = 7; // Alternate function uint16_t rx = 0, tx = 0; // pins + uint32_t freq = 0; // Bus frequency. UART1 is on APB2, rest on APB1 - if (uart == UART1) RCC->APB2ENR |= BIT(4); - if (uart == UART2) RCC->APB1ENR |= BIT(17); - if (uart == UART3) RCC->APB1ENR |= BIT(18); + if (uart == UART1) freq = APB2_FREQUENCY, RCC->APB2ENR |= BIT(4); + if (uart == UART2) freq = APB1_FREQUENCY, RCC->APB1ENR |= BIT(17); + if (uart == UART3) freq = APB1_FREQUENCY, RCC->APB1ENR |= BIT(18); if (uart == UART1) tx = PIN('A', 9), rx = PIN('A', 10); if (uart == UART2) tx = PIN('A', 2), rx = PIN('A', 3); @@ -174,9 +173,8 @@ static inline void uart_init(struct uart *uart, unsigned long baud) { 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 this UART - uart->BRR = FREQ / 4 / baud; // Baud rate, "4" is APBx prescaler, different from APBx_PRE - // TODO(): make this configurable ? + uart->CR1 = 0; // Disable this UART + uart->BRR = freq / baud; // Set baud rate uart->CR1 |= BIT(13) | BIT(2) | BIT(3); // Set UE, RE, TE } static inline void uart_write_byte(struct uart *uart, uint8_t byte) { diff --git a/examples/stm32/nucleo-f429zi-baremetal/syscalls.c b/examples/stm32/nucleo-f429zi-baremetal/syscalls.c index 3b16ca78..4a16522c 100644 --- a/examples/stm32/nucleo-f429zi-baremetal/syscalls.c +++ b/examples/stm32/nucleo-f429zi-baremetal/syscalls.c @@ -53,7 +53,7 @@ int _getpid(void) { int _write(int fd, char *ptr, int len) { (void) fd, (void) ptr, (void) len; - if (fd == 1) uart_write_buf(UART3, ptr, (size_t) len); + if (fd == 1) uart_write_buf(UART1, ptr, (size_t) len); return -1; }