├── .github └── workflows │ └── test.yml ├── .gitignore ├── LICENSE ├── README.md ├── README_tr-TR.md ├── README_zh-CN.md ├── images ├── breadboard.webp ├── codespace-terminal.png ├── codespace.png ├── hero.svg ├── mcu.svg ├── mem.svg ├── mem2.svg ├── moder.png ├── ota.svg ├── ozone1.png ├── ozone2.png ├── ozone3.png ├── ozone4.png ├── ozone5.png ├── ozone6.png ├── ozone7.png ├── ozone8.png ├── ozone9.png ├── screenshot.webp └── systick.svg ├── steps ├── step-0-minimal │ ├── Makefile │ ├── link.ld │ └── main.c ├── step-1-blinky │ ├── Makefile │ ├── link.ld │ └── main.c ├── step-2-systick │ ├── Makefile │ ├── link.ld │ └── main.c ├── step-3-uart │ ├── Makefile │ ├── link.ld │ └── main.c ├── step-4-printf │ ├── Makefile │ ├── hal.h │ ├── link.ld │ ├── main.c │ ├── startup.c │ └── syscalls.c ├── step-5-cmsis │ ├── Makefile │ ├── hal.h │ ├── link.ld │ ├── main.c │ └── syscalls.c ├── step-6-clock │ ├── Makefile │ ├── hal.h │ ├── link.ld │ ├── main.c │ ├── syscalls.c │ └── sysinit.c └── step-7-webserver │ ├── ek-tm4c1294xl │ ├── Makefile │ ├── README.md │ ├── hal.h │ ├── include │ │ ├── TM4C129.h │ │ ├── TM4C1294NCPDT.h │ │ ├── cmsis_compiler.h │ │ ├── cmsis_gcc.h │ │ ├── cmsis_version.h │ │ ├── core_cm4.h │ │ ├── mpu_armv7.h │ │ └── system_TM4C129.h │ ├── link.ld │ ├── main.c │ ├── mongoose.c │ ├── mongoose.h │ ├── mongoose_custom.h │ ├── net.c │ ├── packed_fs.c │ ├── startup.c │ └── syscalls.c │ ├── nucleo-f429zi │ ├── Makefile │ ├── hal.h │ ├── include │ │ ├── cmsis_compiler.h │ │ ├── cmsis_gcc.h │ │ ├── cmsis_version.h │ │ ├── core_cm4.h │ │ ├── mpu_armv7.h │ │ ├── stm32f429xx.h │ │ └── system_stm32f4xx.h │ ├── link.ld │ ├── main.c │ ├── mongoose.c │ ├── mongoose.h │ ├── mongoose_custom.h │ ├── net.c │ ├── packed_fs.c │ ├── startup.c │ └── syscalls.c │ └── pico-w5500 │ ├── Makefile │ ├── hal.h │ ├── link.ld │ ├── main.c │ ├── mongoose.c │ ├── mongoose.h │ ├── startup.c │ ├── syscalls.c │ └── tools │ └── bin2uf2.c └── templates ├── blinky ├── esp32-c3 │ ├── Makefile │ ├── README.md │ ├── hal.h │ ├── link.ld │ ├── main.c │ └── startup.c ├── frdm-mcxw71 │ ├── Makefile │ ├── hal.h │ ├── link.ld │ ├── main.c │ ├── startup.c │ └── syscalls.c ├── nRF52840dk │ ├── Makefile │ ├── hal.h │ ├── link.ld │ └── main.c ├── nucleo-f429zi │ ├── Makefile │ ├── hal.h │ ├── link.ld │ ├── main.c │ └── syscalls.c ├── nucleo-l432kc │ ├── Makefile │ ├── hal.h │ ├── link.ld │ ├── main.c │ └── syscalls.c ├── nucleo-u5a5zj-q │ ├── Makefile │ ├── hal.h │ ├── link.ld │ ├── main.c │ └── syscalls.c └── same54-xplained │ ├── Makefile │ ├── hal.h │ ├── link.ld │ ├── main.c │ ├── startup.c │ └── syscalls.c ├── cli ├── nucleo-f429zi │ ├── Makefile │ ├── hal.h │ ├── link.ld │ ├── main.c │ ├── syscalls.c │ └── sysinit.c └── nucleo-l432kc │ ├── Makefile │ ├── hal.h │ ├── link.ld │ ├── main.c │ ├── syscalls.c │ └── sysinit.c └── lfs ├── nucleo-f303k8 ├── Makefile ├── hal.h ├── link.ld ├── main.c ├── sys │ └── dirent.h └── syscalls.c ├── nucleo-h563zi ├── Makefile ├── flash_stm32h5.h ├── hal.h ├── link.ld ├── main.c ├── retarget_lfs.c ├── sys │ └── dirent.h └── syscalls.c └── nucleo-l432kc ├── Makefile ├── hal.h ├── link.ld ├── main.c ├── sys └── dirent.h └── syscalls.c /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: build 2 | on: [push, pull_request] 3 | jobs: 4 | linux: 5 | runs-on: ubuntu-latest 6 | steps: 7 | - uses: actions/checkout@v3 8 | - run: sudo apt-get update 9 | - run: sudo apt -y install gcc-arm-none-eabi make 10 | - run: make -C steps/step-0-minimal 11 | - run: make -C steps/step-1-blinky 12 | - run: make -C steps/step-2-systick 13 | - run: make -C steps/step-3-uart 14 | - run: make -C steps/step-4-printf 15 | - run: make -C steps/step-5-cmsis 16 | - run: make -C steps/step-6-clock 17 | - run: make -C steps/step-7-webserver/nucleo-f429zi 18 | - run: make -C steps/step-7-webserver/pico-w5500 test VCON_API_KEY=${{secrets.VCON_API_KEY}} 19 | - run: make -C templates/blinky/nucleo-f429zi test VCON_API_KEY=${{secrets.VCON_API_KEY}} 20 | - run: make -C templates/blinky/nucleo-l432kc test VCON_API_KEY=${{secrets.VCON_API_KEY}} 21 | - run: make -C templates/cli/nucleo-f429zi test VCON_API_KEY=${{secrets.VCON_API_KEY}} 22 | - run: make -C templates/cli/nucleo-l432kc test VCON_API_KEY=${{secrets.VCON_API_KEY}} 23 | macos: 24 | runs-on: macos-latest 25 | steps: 26 | - uses: actions/checkout@v3 27 | - run: HOMEBREW_NO_AUTO_UPDATE=1 brew install gcc-arm-embedded make 28 | - run: make -C steps/step-0-minimal 29 | - run: make -C steps/step-1-blinky 30 | - run: make -C steps/step-2-systick 31 | - run: make -C steps/step-3-uart 32 | - run: make -C steps/step-4-printf 33 | - run: make -C steps/step-5-cmsis 34 | - run: make -C steps/step-6-clock 35 | - run: make -C steps/step-7-webserver/nucleo-f429zi 36 | - run: make -C steps/step-7-webserver/pico-w5500 37 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /**/build/ 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Cesanta Software Limited 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /images/breadboard.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cpq/bare-metal-programming-guide/4468d1f2cd9c870f1a5b120134363f35220dbb3f/images/breadboard.webp -------------------------------------------------------------------------------- /images/codespace-terminal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cpq/bare-metal-programming-guide/4468d1f2cd9c870f1a5b120134363f35220dbb3f/images/codespace-terminal.png -------------------------------------------------------------------------------- /images/codespace.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cpq/bare-metal-programming-guide/4468d1f2cd9c870f1a5b120134363f35220dbb3f/images/codespace.png -------------------------------------------------------------------------------- /images/mcu.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |
VCC
VCC
GND
GND
Text is not SVG - cannot display
-------------------------------------------------------------------------------- /images/moder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cpq/bare-metal-programming-guide/4468d1f2cd9c870f1a5b120134363f35220dbb3f/images/moder.png -------------------------------------------------------------------------------- /images/ozone1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cpq/bare-metal-programming-guide/4468d1f2cd9c870f1a5b120134363f35220dbb3f/images/ozone1.png -------------------------------------------------------------------------------- /images/ozone2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cpq/bare-metal-programming-guide/4468d1f2cd9c870f1a5b120134363f35220dbb3f/images/ozone2.png -------------------------------------------------------------------------------- /images/ozone3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cpq/bare-metal-programming-guide/4468d1f2cd9c870f1a5b120134363f35220dbb3f/images/ozone3.png -------------------------------------------------------------------------------- /images/ozone4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cpq/bare-metal-programming-guide/4468d1f2cd9c870f1a5b120134363f35220dbb3f/images/ozone4.png -------------------------------------------------------------------------------- /images/ozone5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cpq/bare-metal-programming-guide/4468d1f2cd9c870f1a5b120134363f35220dbb3f/images/ozone5.png -------------------------------------------------------------------------------- /images/ozone6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cpq/bare-metal-programming-guide/4468d1f2cd9c870f1a5b120134363f35220dbb3f/images/ozone6.png -------------------------------------------------------------------------------- /images/ozone7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cpq/bare-metal-programming-guide/4468d1f2cd9c870f1a5b120134363f35220dbb3f/images/ozone7.png -------------------------------------------------------------------------------- /images/ozone8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cpq/bare-metal-programming-guide/4468d1f2cd9c870f1a5b120134363f35220dbb3f/images/ozone8.png -------------------------------------------------------------------------------- /images/ozone9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cpq/bare-metal-programming-guide/4468d1f2cd9c870f1a5b120134363f35220dbb3f/images/ozone9.png -------------------------------------------------------------------------------- /images/screenshot.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cpq/bare-metal-programming-guide/4468d1f2cd9c870f1a5b120134363f35220dbb3f/images/screenshot.webp -------------------------------------------------------------------------------- /steps/step-0-minimal/Makefile: -------------------------------------------------------------------------------- 1 | CFLAGS ?= -W -Wall -Wextra -Werror -Wundef -Wshadow -Wdouble-promotion \ 2 | -Wformat-truncation -fno-common -Wconversion \ 3 | -g3 -Os -ffunction-sections -fdata-sections -I. \ 4 | -mcpu=cortex-m4 -mthumb -mfloat-abi=hard -mfpu=fpv4-sp-d16 $(EXTRA_CFLAGS) 5 | LDFLAGS ?= -Tlink.ld -nostartfiles -nostdlib --specs nano.specs -lc -lgcc -Wl,--gc-sections -Wl,-Map=$@.map 6 | SOURCES = main.c 7 | 8 | ifeq ($(OS),Windows_NT) 9 | RM = cmd /C del /Q /F 10 | else 11 | RM = rm -f 12 | endif 13 | 14 | build: firmware.bin 15 | 16 | firmware.elf: $(SOURCES) 17 | arm-none-eabi-gcc $(SOURCES) $(CFLAGS) $(LDFLAGS) -o $@ 18 | 19 | firmware.bin: firmware.elf 20 | arm-none-eabi-objcopy -O binary $< $@ 21 | 22 | flash: firmware.bin 23 | st-flash --reset write $< 0x8000000 24 | 25 | clean: 26 | $(RM) firmware.* 27 | -------------------------------------------------------------------------------- /steps/step-0-minimal/link.ld: -------------------------------------------------------------------------------- 1 | ENTRY(_reset); 2 | MEMORY { 3 | flash(rx) : ORIGIN = 0x08000000, LENGTH = 2048k 4 | sram(rwx) : ORIGIN = 0x20000000, LENGTH = 192k /* remaining 64k in a separate address space */ 5 | } 6 | _estack = ORIGIN(sram) + LENGTH(sram); /* stack points to end of SRAM */ 7 | 8 | SECTIONS { 9 | .vectors : { KEEP(*(.vectors)) } > flash 10 | .text : { *(.text*) } > flash 11 | .rodata : { *(.rodata*) } > flash 12 | 13 | .data : { 14 | _sdata = .; /* .data section start */ 15 | *(.first_data) 16 | *(.data SORT(.data.*)) 17 | _edata = .; /* .data section end */ 18 | } > sram AT > flash 19 | _sidata = LOADADDR(.data); 20 | 21 | .bss : { 22 | _sbss = .; /* .bss section start */ 23 | *(.bss SORT(.bss.*) COMMON) 24 | _ebss = .; /* .bss section end */ 25 | } > sram 26 | 27 | . = ALIGN(8); 28 | _end = .; /* for cmsis_gcc.h */ 29 | } 30 | -------------------------------------------------------------------------------- /steps/step-0-minimal/main.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 Cesanta Software Limited 2 | // All rights reserved 3 | 4 | int main(void) { 5 | return 0; 6 | } 7 | 8 | // Startup code 9 | __attribute__((naked, noreturn)) void _reset(void) { 10 | // memset .bss to zero, and copy .data section to RAM region 11 | extern long _sbss, _ebss, _sdata, _edata, _sidata; 12 | for (long *dst = &_sbss; dst < &_ebss; dst++) *dst = 0; 13 | for (long *dst = &_sdata, *src = &_sidata; dst < &_edata;) *dst++ = *src++; 14 | 15 | main(); // Call main() 16 | for (;;) (void) 0; // Infinite loop in the case if main() returns 17 | } 18 | 19 | extern void _estack(void); // Defined in link.ld 20 | 21 | // 16 standard and 91 STM32-specific handlers 22 | __attribute__((section(".vectors"))) void (*const tab[16 + 91])(void) = { 23 | _estack, _reset}; 24 | -------------------------------------------------------------------------------- /steps/step-1-blinky/Makefile: -------------------------------------------------------------------------------- 1 | CFLAGS ?= -W -Wall -Wextra -Werror -Wundef -Wshadow -Wdouble-promotion \ 2 | -Wformat-truncation -fno-common -Wconversion \ 3 | -g3 -Os -ffunction-sections -fdata-sections -I. \ 4 | -mcpu=cortex-m4 -mthumb -mfloat-abi=hard -mfpu=fpv4-sp-d16 $(EXTRA_CFLAGS) 5 | LDFLAGS ?= -Tlink.ld -nostartfiles -nostdlib --specs nano.specs -lc -lgcc -Wl,--gc-sections -Wl,-Map=$@.map 6 | SOURCES = main.c 7 | 8 | ifeq ($(OS),Windows_NT) 9 | RM = cmd /C del /Q /F 10 | else 11 | RM = rm -f 12 | endif 13 | 14 | build: firmware.bin 15 | 16 | firmware.elf: $(SOURCES) 17 | arm-none-eabi-gcc $(SOURCES) $(CFLAGS) $(LDFLAGS) -o $@ 18 | 19 | firmware.bin: firmware.elf 20 | arm-none-eabi-objcopy -O binary $< $@ 21 | 22 | flash: firmware.bin 23 | st-flash --reset write $< 0x8000000 24 | 25 | clean: 26 | $(RM) firmware.* 27 | -------------------------------------------------------------------------------- /steps/step-1-blinky/link.ld: -------------------------------------------------------------------------------- 1 | ENTRY(_reset); 2 | MEMORY { 3 | flash(rx) : ORIGIN = 0x08000000, LENGTH = 2048k 4 | sram(rwx) : ORIGIN = 0x20000000, LENGTH = 192k /* remaining 64k in a separate address space */ 5 | } 6 | _estack = ORIGIN(sram) + LENGTH(sram); /* stack points to end of SRAM */ 7 | 8 | SECTIONS { 9 | .vectors : { KEEP(*(.vectors)) } > flash 10 | .text : { *(.text*) } > flash 11 | .rodata : { *(.rodata*) } > flash 12 | 13 | .data : { 14 | _sdata = .; /* .data section start */ 15 | *(.first_data) 16 | *(.data SORT(.data.*)) 17 | _edata = .; /* .data section end */ 18 | } > sram AT > flash 19 | _sidata = LOADADDR(.data); 20 | 21 | .bss : { 22 | _sbss = .; /* .bss section start */ 23 | *(.bss SORT(.bss.*) COMMON) 24 | _ebss = .; /* .bss section end */ 25 | } > sram 26 | 27 | . = ALIGN(8); 28 | _end = .; /* for cmsis_gcc.h */ 29 | } 30 | -------------------------------------------------------------------------------- /steps/step-1-blinky/main.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 Cesanta Software Limited 2 | // All rights reserved 3 | 4 | #include 5 | #include 6 | 7 | #define BIT(x) (1UL << (x)) 8 | #define PIN(bank, num) ((((bank) - 'A') << 8) | (num)) 9 | #define PINNO(pin) (pin & 255) 10 | #define PINBANK(pin) (pin >> 8) 11 | 12 | struct rcc { 13 | volatile uint32_t CR, PLLCFGR, CFGR, CIR, AHB1RSTR, AHB2RSTR, AHB3RSTR, 14 | RESERVED0, APB1RSTR, APB2RSTR, RESERVED1[2], AHB1ENR, AHB2ENR, AHB3ENR, 15 | RESERVED2, APB1ENR, APB2ENR, RESERVED3[2], AHB1LPENR, AHB2LPENR, 16 | AHB3LPENR, RESERVED4, APB1LPENR, APB2LPENR, RESERVED5[2], BDCR, CSR, 17 | RESERVED6[2], SSCGR, PLLI2SCFGR; 18 | }; 19 | #define RCC ((struct rcc *) 0x40023800) 20 | 21 | struct gpio { 22 | volatile uint32_t MODER, OTYPER, OSPEEDR, PUPDR, IDR, ODR, BSRR, LCKR, AFR[2]; 23 | }; 24 | #define GPIO(bank) ((struct gpio *) (0x40020000 + 0x400 * (bank))) 25 | 26 | // Enum values are per datasheet: 0, 1, 2, 3 27 | enum { GPIO_MODE_INPUT, GPIO_MODE_OUTPUT, GPIO_MODE_AF, GPIO_MODE_ANALOG }; 28 | 29 | static inline void gpio_set_mode(uint16_t pin, uint8_t mode) { 30 | struct gpio *gpio = GPIO(PINBANK(pin)); // GPIO bank 31 | int n = PINNO(pin); // Pin number 32 | gpio->MODER &= ~(3U << (n * 2)); // Clear existing setting 33 | gpio->MODER |= (mode & 3U) << (n * 2); // Set new mode 34 | } 35 | 36 | static inline void gpio_write(uint16_t pin, bool val) { 37 | struct gpio *gpio = GPIO(PINBANK(pin)); 38 | gpio->BSRR = (1U << PINNO(pin)) << (val ? 0 : 16); 39 | } 40 | 41 | static inline void spin(volatile uint32_t count) { 42 | while (count--) asm("nop"); 43 | } 44 | 45 | int main(void) { 46 | uint16_t led = PIN('B', 7); // Blue LED 47 | RCC->AHB1ENR |= BIT(PINBANK(led)); // Enable GPIO clock for LED 48 | gpio_set_mode(led, GPIO_MODE_OUTPUT); // Set blue LED to output mode 49 | for (;;) { 50 | gpio_write(led, true); 51 | spin(999999); 52 | gpio_write(led, false); 53 | spin(999999); 54 | } 55 | return 0; 56 | } 57 | 58 | // Startup code 59 | __attribute__((naked, noreturn)) void _reset(void) { //naked=don't generate prologue/epilogue //noreturn=it will never exit 60 | // memset .bss to zero, and copy .data section to RAM region 61 | extern long _sbss, _ebss, _sdata, _edata, _sidata; //linker symbols declared in the .ld file 62 | for (long *dst = &_sbss; dst < &_ebss; dst++) *dst = 0; 63 | for (long *dst = &_sdata, *src = &_sidata; dst < &_edata;) *dst++ = *src++; 64 | 65 | main(); // Call main() 66 | for (;;) (void) 0; // Infinite loop in the case if main() returns 67 | } 68 | 69 | extern void _estack(void); // Defined in link.ld 70 | 71 | // 16 standard and 91 STM32-specific handlers 72 | __attribute__((section(".vectors"))) void (*const tab[16 + 91])(void) = { // defines the interrupt vector table and dumps it into .vectors via the linker script 73 | _estack, _reset}; 74 | -------------------------------------------------------------------------------- /steps/step-2-systick/Makefile: -------------------------------------------------------------------------------- 1 | CFLAGS ?= -W -Wall -Wextra -Werror -Wundef -Wshadow -Wdouble-promotion \ 2 | -Wformat-truncation -fno-common -Wconversion \ 3 | -g3 -Os -ffunction-sections -fdata-sections -I. \ 4 | -mcpu=cortex-m4 -mthumb -mfloat-abi=hard -mfpu=fpv4-sp-d16 $(EXTRA_CFLAGS) 5 | LDFLAGS ?= -Tlink.ld -nostartfiles -nostdlib --specs nano.specs -lc -lgcc -Wl,--gc-sections -Wl,-Map=$@.map 6 | SOURCES = main.c 7 | 8 | ifeq ($(OS),Windows_NT) 9 | RM = cmd /C del /Q /F 10 | else 11 | RM = rm -f 12 | endif 13 | 14 | build: firmware.bin 15 | 16 | firmware.elf: $(SOURCES) 17 | arm-none-eabi-gcc $(SOURCES) $(CFLAGS) $(LDFLAGS) -o $@ 18 | 19 | firmware.bin: firmware.elf 20 | arm-none-eabi-objcopy -O binary $< $@ 21 | 22 | flash: firmware.bin 23 | st-flash --reset write $< 0x8000000 24 | 25 | clean: 26 | $(RM) firmware.* 27 | -------------------------------------------------------------------------------- /steps/step-2-systick/link.ld: -------------------------------------------------------------------------------- 1 | ENTRY(_reset); 2 | MEMORY { 3 | flash(rx) : ORIGIN = 0x08000000, LENGTH = 2048k 4 | sram(rwx) : ORIGIN = 0x20000000, LENGTH = 192k /* remaining 64k in a separate address space */ 5 | } 6 | _estack = ORIGIN(sram) + LENGTH(sram); /* stack points to end of SRAM */ 7 | 8 | SECTIONS { 9 | .vectors : { KEEP(*(.vectors)) } > flash 10 | .text : { *(.text*) } > flash 11 | .rodata : { *(.rodata*) } > flash 12 | 13 | .data : { 14 | _sdata = .; /* .data section start */ 15 | *(.first_data) 16 | *(.data SORT(.data.*)) 17 | _edata = .; /* .data section end */ 18 | } > sram AT > flash 19 | _sidata = LOADADDR(.data); 20 | 21 | .bss : { 22 | _sbss = .; /* .bss section start */ 23 | *(.bss SORT(.bss.*) COMMON) 24 | _ebss = .; /* .bss section end */ 25 | } > sram 26 | 27 | . = ALIGN(8); 28 | _end = .; /* for cmsis_gcc.h */ 29 | } 30 | -------------------------------------------------------------------------------- /steps/step-2-systick/main.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 Cesanta Software Limited 2 | // All rights reserved 3 | 4 | #include 5 | #include 6 | 7 | #define BIT(x) (1UL << (x)) 8 | #define PIN(bank, num) ((((bank) - 'A') << 8) | (num)) 9 | #define PINNO(pin) (pin & 255) 10 | #define PINBANK(pin) (pin >> 8) 11 | 12 | struct systick { 13 | volatile uint32_t CTRL, LOAD, VAL, CALIB; 14 | }; 15 | #define SYSTICK ((struct systick *) 0xe000e010) // 2.2.2 16 | 17 | struct rcc { 18 | volatile uint32_t CR, PLLCFGR, CFGR, CIR, AHB1RSTR, AHB2RSTR, AHB3RSTR, 19 | RESERVED0, APB1RSTR, APB2RSTR, RESERVED1[2], AHB1ENR, AHB2ENR, AHB3ENR, 20 | RESERVED2, APB1ENR, APB2ENR, RESERVED3[2], AHB1LPENR, AHB2LPENR, 21 | AHB3LPENR, RESERVED4, APB1LPENR, APB2LPENR, RESERVED5[2], BDCR, CSR, 22 | RESERVED6[2], SSCGR, PLLI2SCFGR; 23 | }; 24 | #define RCC ((struct rcc *) 0x40023800) 25 | 26 | static inline void systick_init(uint32_t ticks) { 27 | if ((ticks - 1) > 0xffffff) return; // Systick timer is 24 bit 28 | SYSTICK->LOAD = ticks - 1; 29 | SYSTICK->VAL = 0; 30 | SYSTICK->CTRL = BIT(0) | BIT(1) | BIT(2); // Enable systick 31 | RCC->APB2ENR |= BIT(14); // Enable SYSCFG 32 | } 33 | 34 | struct gpio { 35 | volatile uint32_t MODER, OTYPER, OSPEEDR, PUPDR, IDR, ODR, BSRR, LCKR, AFR[2]; 36 | }; 37 | #define GPIO(bank) ((struct gpio *) (0x40020000 + 0x400 * (bank))) 38 | 39 | // Enum values are per datasheet: 0, 1, 2, 3 40 | enum { GPIO_MODE_INPUT, GPIO_MODE_OUTPUT, GPIO_MODE_AF, GPIO_MODE_ANALOG }; 41 | 42 | static inline void gpio_set_mode(uint16_t pin, uint8_t mode) { 43 | struct gpio *gpio = GPIO(PINBANK(pin)); // GPIO bank 44 | int n = PINNO(pin); // Pin number 45 | gpio->MODER &= ~(3U << (n * 2)); // Clear existing setting 46 | gpio->MODER |= (mode & 3U) << (n * 2); // Set new mode 47 | } 48 | 49 | static inline void gpio_write(uint16_t pin, bool val) { 50 | struct gpio *gpio = GPIO(PINBANK(pin)); 51 | gpio->BSRR = (1U << PINNO(pin)) << (val ? 0 : 16); 52 | } 53 | 54 | static inline void spin(volatile uint32_t count) { 55 | while (count--) asm("nop"); 56 | } 57 | 58 | static volatile uint32_t s_ticks; 59 | void SysTick_Handler(void) { 60 | s_ticks++; 61 | } 62 | 63 | // t: expiration time, prd: period, now: current time. Return true if expired 64 | bool timer_expired(uint32_t *t, uint32_t prd, uint32_t now) { 65 | if (now + prd < *t) *t = 0; // Time wrapped? Reset timer 66 | if (*t == 0) *t = now + prd; // Firt poll? Set expiration 67 | if (*t > now) return false; // Not expired yet, return 68 | *t = (now - *t) > prd ? now + prd : *t + prd; // Next expiration time 69 | return true; // Expired, return true 70 | } 71 | 72 | int main(void) { 73 | uint16_t led = PIN('B', 7); // Blue LED 74 | RCC->AHB1ENR |= BIT(PINBANK(led)); // Enable GPIO clock for LED 75 | systick_init(16000000 / 1000); // Tick every 1 ms 76 | gpio_set_mode(led, GPIO_MODE_OUTPUT); // Set blue LED to output mode 77 | uint32_t timer = 0, period = 500; // Declare timer and 500ms period 78 | for (;;) { 79 | if (timer_expired(&timer, period, s_ticks)) { 80 | static bool on; // This block is executed 81 | gpio_write(led, on); // Every `period` milliseconds 82 | on = !on; // Toggle LED state 83 | } 84 | // Here we could perform other activities! 85 | } 86 | return 0; 87 | } 88 | 89 | // Startup code 90 | __attribute__((naked, noreturn)) void _reset(void) { 91 | // Initialise memory 92 | extern long _sbss, _ebss, _sdata, _edata, _sidata; 93 | for (long *dst = &_sbss; dst < &_ebss; dst++) *dst = 0; 94 | for (long *dst = &_sdata, *src = &_sidata; dst < &_edata;) *dst++ = *src++; 95 | 96 | // Call main() 97 | main(); 98 | for (;;) (void) 0; // Infinite loop 99 | } 100 | 101 | extern void _estack(void); // Defined in link.ld 102 | 103 | // 16 standard and 91 STM32-specific handlers 104 | __attribute__((section(".vectors"))) void (*const tab[16 + 91])(void) = { 105 | _estack, _reset, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, SysTick_Handler}; 106 | -------------------------------------------------------------------------------- /steps/step-3-uart/Makefile: -------------------------------------------------------------------------------- 1 | CFLAGS ?= -W -Wall -Wextra -Werror -Wundef -Wshadow -Wdouble-promotion \ 2 | -Wformat-truncation -fno-common -Wconversion \ 3 | -g3 -Os -ffunction-sections -fdata-sections -I. \ 4 | -mcpu=cortex-m4 -mthumb -mfloat-abi=hard -mfpu=fpv4-sp-d16 $(EXTRA_CFLAGS) 5 | LDFLAGS ?= -Tlink.ld -nostartfiles -nostdlib --specs nano.specs -lc -lgcc -Wl,--gc-sections -Wl,-Map=$@.map 6 | SOURCES = main.c 7 | 8 | ifeq ($(OS),Windows_NT) 9 | RM = cmd /C del /Q /F 10 | else 11 | RM = rm -f 12 | endif 13 | 14 | build: firmware.bin 15 | 16 | firmware.elf: $(SOURCES) 17 | arm-none-eabi-gcc $(SOURCES) $(CFLAGS) $(LDFLAGS) -o $@ 18 | 19 | firmware.bin: firmware.elf 20 | arm-none-eabi-objcopy -O binary $< $@ 21 | 22 | flash: firmware.bin 23 | st-flash --reset write $< 0x8000000 24 | 25 | clean: 26 | $(RM) firmware.* 27 | -------------------------------------------------------------------------------- /steps/step-3-uart/link.ld: -------------------------------------------------------------------------------- 1 | ENTRY(_reset); 2 | MEMORY { 3 | flash(rx) : ORIGIN = 0x08000000, LENGTH = 2048k 4 | sram(rwx) : ORIGIN = 0x20000000, LENGTH = 192k /* remaining 64k in a separate address space */ 5 | } 6 | _estack = ORIGIN(sram) + LENGTH(sram); /* stack points to end of SRAM */ 7 | 8 | SECTIONS { 9 | .vectors : { KEEP(*(.vectors)) } > flash 10 | .text : { *(.text*) } > flash 11 | .rodata : { *(.rodata*) } > flash 12 | 13 | .data : { 14 | _sdata = .; /* .data section start */ 15 | *(.first_data) 16 | *(.data SORT(.data.*)) 17 | _edata = .; /* .data section end */ 18 | } > sram AT > flash 19 | _sidata = LOADADDR(.data); 20 | 21 | .bss : { 22 | _sbss = .; /* .bss section start */ 23 | *(.bss SORT(.bss.*) COMMON) 24 | _ebss = .; /* .bss section end */ 25 | } > sram 26 | 27 | . = ALIGN(8); 28 | _end = .; /* for cmsis_gcc.h */ 29 | } 30 | -------------------------------------------------------------------------------- /steps/step-3-uart/main.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 Cesanta Software Limited 2 | // All rights reserved 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #define FREQ 16000000 // CPU frequency, 16 Mhz 9 | #define BIT(x) (1UL << (x)) 10 | #define PIN(bank, num) ((((bank) - 'A') << 8) | (num)) 11 | #define PINNO(pin) (pin & 255) 12 | #define PINBANK(pin) (pin >> 8) 13 | 14 | static inline void spin(volatile uint32_t count) { 15 | while (count--) asm("nop"); 16 | } 17 | 18 | struct systick { 19 | volatile uint32_t CTRL, LOAD, VAL, CALIB; 20 | }; 21 | #define SYSTICK ((struct systick *) 0xe000e010) // 2.2.2 22 | 23 | struct rcc { 24 | volatile uint32_t CR, PLLCFGR, CFGR, CIR, AHB1RSTR, AHB2RSTR, AHB3RSTR, 25 | RESERVED0, APB1RSTR, APB2RSTR, RESERVED1[2], AHB1ENR, AHB2ENR, AHB3ENR, 26 | RESERVED2, APB1ENR, APB2ENR, RESERVED3[2], AHB1LPENR, AHB2LPENR, 27 | AHB3LPENR, RESERVED4, APB1LPENR, APB2LPENR, RESERVED5[2], BDCR, CSR, 28 | RESERVED6[2], SSCGR, PLLI2SCFGR; 29 | }; 30 | #define RCC ((struct rcc *) 0x40023800) 31 | 32 | static inline void systick_init(uint32_t ticks) { 33 | if ((ticks - 1) > 0xffffff) return; // Systick timer is 24 bit 34 | SYSTICK->LOAD = ticks - 1; 35 | SYSTICK->VAL = 0; 36 | SYSTICK->CTRL = BIT(0) | BIT(1) | BIT(2); // Enable systick 37 | RCC->APB2ENR |= BIT(14); // Enable SYSCFG 38 | } 39 | 40 | struct gpio { 41 | volatile uint32_t MODER, OTYPER, OSPEEDR, PUPDR, IDR, ODR, BSRR, LCKR, AFR[2]; 42 | }; 43 | #define GPIO(bank) ((struct gpio *) (0x40020000 + 0x400 * (bank))) 44 | 45 | // Enum values are per datasheet: 0, 1, 2, 3 46 | enum { GPIO_MODE_INPUT, GPIO_MODE_OUTPUT, GPIO_MODE_AF, GPIO_MODE_ANALOG }; 47 | 48 | static inline void gpio_set_mode(uint16_t pin, uint8_t mode) { 49 | struct gpio *gpio = GPIO(PINBANK(pin)); // GPIO bank 50 | int n = PINNO(pin); // Pin number 51 | RCC->AHB1ENR |= BIT(PINBANK(pin)); // Enable GPIO clock 52 | gpio->MODER &= ~(3U << (n * 2)); // Clear existing setting 53 | gpio->MODER |= (mode & 3U) << (n * 2); // Set new mode 54 | } 55 | 56 | static inline void gpio_set_af(uint16_t pin, uint8_t af_num) { 57 | struct gpio *gpio = GPIO(PINBANK(pin)); // GPIO bank 58 | int n = PINNO(pin); // Pin number 59 | gpio->AFR[n >> 3] &= ~(15UL << ((n & 7) * 4)); 60 | gpio->AFR[n >> 3] |= ((uint32_t) af_num) << ((n & 7) * 4); 61 | } 62 | 63 | static inline void gpio_write(uint16_t pin, bool val) { 64 | struct gpio *gpio = GPIO(PINBANK(pin)); 65 | gpio->BSRR = (1U << PINNO(pin)) << (val ? 0 : 16); 66 | } 67 | 68 | struct uart { 69 | volatile uint32_t SR, DR, BRR, CR1, CR2, CR3, GTPR; 70 | }; 71 | #define UART1 ((struct uart *) 0x40011000) 72 | #define UART2 ((struct uart *) 0x40004400) 73 | #define UART3 ((struct uart *) 0x40004800) 74 | 75 | static inline void uart_init(struct uart *uart, unsigned long baud) { 76 | // https://www.st.com/resource/en/datasheet/stm32f429zi.pdf 77 | uint8_t af = 7; // Alternate function 78 | uint16_t rx = 0, tx = 0; // pins 79 | 80 | if (uart == UART1) RCC->APB2ENR |= BIT(4); 81 | if (uart == UART2) RCC->APB1ENR |= BIT(17); 82 | if (uart == UART3) RCC->APB1ENR |= BIT(18); 83 | 84 | if (uart == UART1) tx = PIN('A', 9), rx = PIN('A', 10); 85 | if (uart == UART2) tx = PIN('A', 2), rx = PIN('A', 3); 86 | if (uart == UART3) tx = PIN('D', 8), rx = PIN('D', 9); 87 | 88 | gpio_set_mode(tx, GPIO_MODE_AF); 89 | gpio_set_af(tx, af); 90 | gpio_set_mode(rx, GPIO_MODE_AF); 91 | gpio_set_af(rx, af); 92 | uart->CR1 = 0; // Disable this UART 93 | uart->BRR = FREQ / baud; // FREQ is a UART bus frequency 94 | uart->CR1 |= BIT(13) | BIT(2) | BIT(3); // Set UE, RE, TE 95 | } 96 | 97 | static inline void uart_write_byte(struct uart *uart, uint8_t byte) { 98 | uart->DR = byte; 99 | while ((uart->SR & BIT(7)) == 0) spin(1); 100 | } 101 | 102 | static inline void uart_write_buf(struct uart *uart, char *buf, size_t len) { 103 | while (len-- > 0) uart_write_byte(uart, *(uint8_t *) buf++); 104 | } 105 | 106 | static inline int uart_read_ready(struct uart *uart) { 107 | return uart->SR & BIT(5); // If RXNE bit is set, data is ready 108 | } 109 | 110 | static inline uint8_t uart_read_byte(struct uart *uart) { 111 | return (uint8_t) (uart->DR & 255); 112 | } 113 | 114 | static volatile uint32_t s_ticks; 115 | void SysTick_Handler(void) { s_ticks++; } 116 | 117 | // t: expiration time, prd: period, now: current time. Return true if expired 118 | bool timer_expired(uint32_t *t, uint32_t prd, uint32_t now) { 119 | if (now + prd < *t) *t = 0; // Time wrapped? Reset timer 120 | if (*t == 0) *t = now + prd; // Firt poll? Set expiration 121 | if (*t > now) return false; // Not expired yet, return 122 | *t = (now - *t) > prd ? now + prd : *t + prd; // Next expiration time 123 | return true; // Expired, return true 124 | } 125 | 126 | int main(void) { 127 | uint16_t led = PIN('B', 7); // Blue LED 128 | systick_init(16000000 / 1000); // Tick every 1 ms 129 | gpio_set_mode(led, GPIO_MODE_OUTPUT); // Set blue LED to output mode 130 | uart_init(UART3, 115200); // Initialise UART 131 | uint32_t timer = 0, period = 500; // Declare timer and 500ms period 132 | for (;;) { 133 | if (timer_expired(&timer, period, s_ticks)) { 134 | static bool on; // This block is executed 135 | gpio_write(led, on); // Every `period` milliseconds 136 | on = !on; // Toggle LED state 137 | uart_write_buf(UART3, "hi\r\n", 4); // Write message 138 | } 139 | // Here we could perform other activities! 140 | } 141 | return 0; 142 | } 143 | 144 | // Startup code 145 | __attribute__((naked, noreturn)) void _reset(void) { 146 | // Initialise memory 147 | extern long _sbss, _ebss, _sdata, _edata, _sidata; 148 | for (long *dst = &_sbss; dst < &_ebss; dst++) *dst = 0; 149 | for (long *dst = &_sdata, *src = &_sidata; dst < &_edata;) *dst++ = *src++; 150 | 151 | // Call main() 152 | main(); 153 | for (;;) (void) 0; // Infinite loop 154 | } 155 | 156 | extern void _estack(void); // Defined in link.ld 157 | 158 | // 16 standard and 91 STM32-specific handlers 159 | __attribute__((section(".vectors"))) void (*const tab[16 + 91])(void) = { 160 | _estack, _reset, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, SysTick_Handler}; 161 | -------------------------------------------------------------------------------- /steps/step-4-printf/Makefile: -------------------------------------------------------------------------------- 1 | CFLAGS ?= -W -Wall -Wextra -Werror -Wundef -Wshadow -Wdouble-promotion \ 2 | -Wformat-truncation -fno-common -Wconversion \ 3 | -g3 -Os -ffunction-sections -fdata-sections -I. \ 4 | -mcpu=cortex-m4 -mthumb -mfloat-abi=hard -mfpu=fpv4-sp-d16 $(EXTRA_CFLAGS) 5 | LDFLAGS ?= -Tlink.ld -nostartfiles -nostdlib --specs nano.specs -lc -lgcc -Wl,--gc-sections -Wl,-Map=$@.map 6 | SOURCES = main.c startup.c syscalls.c 7 | 8 | ifeq ($(OS),Windows_NT) 9 | RM = cmd /C del /Q /F 10 | else 11 | RM = rm -f 12 | endif 13 | 14 | build: firmware.bin 15 | 16 | firmware.elf: $(SOURCES) 17 | arm-none-eabi-gcc $(SOURCES) $(CFLAGS) $(LDFLAGS) -o $@ 18 | 19 | firmware.bin: firmware.elf 20 | arm-none-eabi-objcopy -O binary $< $@ 21 | 22 | flash: firmware.bin 23 | st-flash --reset write $< 0x8000000 24 | 25 | clean: 26 | $(RM) firmware.* 27 | -------------------------------------------------------------------------------- /steps/step-4-printf/hal.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 Cesanta Software Limited 2 | // All rights reserved 3 | 4 | #pragma once 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #define FREQ 16000000 // CPU frequency, 16 Mhz 13 | #define BIT(x) (1UL << (x)) 14 | #define PIN(bank, num) ((((bank) - 'A') << 8) | (num)) 15 | #define PINNO(pin) (pin & 255) 16 | #define PINBANK(pin) (pin >> 8) 17 | 18 | static inline void spin(volatile uint32_t count) { 19 | while (count--) (void) 0; 20 | } 21 | 22 | struct systick { 23 | volatile uint32_t CTRL, LOAD, VAL, CALIB; 24 | }; 25 | #define SYSTICK ((struct systick *) 0xe000e010) // 2.2.2 26 | 27 | struct rcc { 28 | volatile uint32_t CR, PLLCFGR, CFGR, CIR, AHB1RSTR, AHB2RSTR, AHB3RSTR, 29 | RESERVED0, APB1RSTR, APB2RSTR, RESERVED1[2], AHB1ENR, AHB2ENR, AHB3ENR, 30 | RESERVED2, APB1ENR, APB2ENR, RESERVED3[2], AHB1LPENR, AHB2LPENR, 31 | AHB3LPENR, RESERVED4, APB1LPENR, APB2LPENR, RESERVED5[2], BDCR, CSR, 32 | RESERVED6[2], SSCGR, PLLI2SCFGR; 33 | }; 34 | #define RCC ((struct rcc *) 0x40023800) 35 | 36 | static inline void systick_init(uint32_t ticks) { 37 | if ((ticks - 1) > 0xffffff) return; // Systick timer is 24 bit 38 | SYSTICK->LOAD = ticks - 1; 39 | SYSTICK->VAL = 0; 40 | SYSTICK->CTRL = BIT(0) | BIT(1) | BIT(2); // Enable systick 41 | RCC->APB2ENR |= BIT(14); // Enable SYSCFG 42 | } 43 | 44 | struct gpio { 45 | volatile uint32_t MODER, OTYPER, OSPEEDR, PUPDR, IDR, ODR, BSRR, LCKR, AFR[2]; 46 | }; 47 | #define GPIO(bank) ((struct gpio *) (0x40020000 + 0x400 * (bank))) 48 | 49 | // Enum values are per datasheet: 0, 1, 2, 3 50 | enum { GPIO_MODE_INPUT, GPIO_MODE_OUTPUT, GPIO_MODE_AF, GPIO_MODE_ANALOG }; 51 | 52 | static inline void gpio_set_mode(uint16_t pin, uint8_t mode) { 53 | struct gpio *gpio = GPIO(PINBANK(pin)); // GPIO bank 54 | int n = PINNO(pin); // Pin number 55 | RCC->AHB1ENR |= BIT(PINBANK(pin)); // Enable GPIO clock 56 | gpio->MODER &= ~(3U << (n * 2)); // Clear existing setting 57 | gpio->MODER |= (mode & 3U) << (n * 2); // Set new mode 58 | } 59 | 60 | static inline void gpio_set_af(uint16_t pin, uint8_t af_num) { 61 | struct gpio *gpio = GPIO(PINBANK(pin)); // GPIO bank 62 | int n = PINNO(pin); // Pin number 63 | gpio->AFR[n >> 3] &= ~(15UL << ((n & 7) * 4)); 64 | gpio->AFR[n >> 3] |= ((uint32_t) af_num) << ((n & 7) * 4); 65 | } 66 | 67 | static inline void gpio_write(uint16_t pin, bool val) { 68 | struct gpio *gpio = GPIO(PINBANK(pin)); 69 | gpio->BSRR = (1U << PINNO(pin)) << (val ? 0 : 16); 70 | } 71 | 72 | struct uart { 73 | volatile uint32_t SR, DR, BRR, CR1, CR2, CR3, GTPR; 74 | }; 75 | #define UART1 ((struct uart *) 0x40011000) 76 | #define UART2 ((struct uart *) 0x40004400) 77 | #define UART3 ((struct uart *) 0x40004800) 78 | 79 | static inline void uart_init(struct uart *uart, unsigned long baud) { 80 | // https://www.st.com/resource/en/datasheet/stm32f429zi.pdf 81 | uint8_t af = 7; // Alternate function 82 | uint16_t rx = 0, tx = 0; // pins 83 | 84 | if (uart == UART1) RCC->APB2ENR |= BIT(4); 85 | if (uart == UART2) RCC->APB1ENR |= BIT(17); 86 | if (uart == UART3) RCC->APB1ENR |= BIT(18); 87 | 88 | if (uart == UART1) tx = PIN('A', 9), rx = PIN('A', 10); 89 | if (uart == UART2) tx = PIN('A', 2), rx = PIN('A', 3); 90 | if (uart == UART3) tx = PIN('D', 8), rx = PIN('D', 9); 91 | 92 | gpio_set_mode(tx, GPIO_MODE_AF); 93 | gpio_set_af(tx, af); 94 | gpio_set_mode(rx, GPIO_MODE_AF); 95 | gpio_set_af(rx, af); 96 | uart->CR1 = 0; // Disable this UART 97 | uart->BRR = FREQ / baud; // FREQ is a UART bus frequency 98 | uart->CR1 |= BIT(13) | BIT(2) | BIT(3); // Set UE, RE, TE 99 | } 100 | 101 | static inline void uart_write_byte(struct uart *uart, uint8_t byte) { 102 | uart->DR = byte; 103 | while ((uart->SR & BIT(7)) == 0) spin(1); 104 | } 105 | 106 | static inline void uart_write_buf(struct uart *uart, char *buf, size_t len) { 107 | while (len-- > 0) uart_write_byte(uart, *(uint8_t *) buf++); 108 | } 109 | 110 | static inline int uart_read_ready(struct uart *uart) { 111 | return uart->SR & BIT(5); // If RXNE bit is set, data is ready 112 | } 113 | 114 | static inline uint8_t uart_read_byte(struct uart *uart) { 115 | return (uint8_t) (uart->DR & 255); 116 | } 117 | 118 | // t: expiration time, prd: period, now: current time. Return true if expired 119 | static inline bool timer_expired(uint32_t *t, uint32_t prd, uint32_t now) { 120 | if (now + prd < *t) *t = 0; // Time wrapped? Reset timer 121 | if (*t == 0) *t = now + prd; // Firt poll? Set expiration 122 | if (*t > now) return false; // Not expired yet, return 123 | *t = (now - *t) > prd ? now + prd : *t + prd; // Next expiration time 124 | return true; // Expired, return true 125 | } 126 | -------------------------------------------------------------------------------- /steps/step-4-printf/link.ld: -------------------------------------------------------------------------------- 1 | ENTRY(_reset); 2 | MEMORY { 3 | flash(rx) : ORIGIN = 0x08000000, LENGTH = 2048k 4 | sram(rwx) : ORIGIN = 0x20000000, LENGTH = 192k /* remaining 64k in a separate address space */ 5 | } 6 | _estack = ORIGIN(sram) + LENGTH(sram); /* stack points to end of SRAM */ 7 | 8 | SECTIONS { 9 | .vectors : { KEEP(*(.vectors)) } > flash 10 | .text : { *(.text*) } > flash 11 | .rodata : { *(.rodata*) } > flash 12 | 13 | .data : { 14 | _sdata = .; /* .data section start */ 15 | *(.first_data) 16 | *(.data SORT(.data.*)) 17 | _edata = .; /* .data section end */ 18 | } > sram AT > flash 19 | _sidata = LOADADDR(.data); 20 | 21 | .bss : { 22 | _sbss = .; /* .bss section start */ 23 | *(.bss SORT(.bss.*) COMMON) 24 | _ebss = .; /* .bss section end */ 25 | } > sram 26 | 27 | . = ALIGN(8); 28 | _end = .; /* for cmsis_gcc.h */ 29 | } 30 | -------------------------------------------------------------------------------- /steps/step-4-printf/main.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 Cesanta Software Limited 2 | // All rights reserved 3 | 4 | #include "hal.h" 5 | 6 | static volatile uint32_t s_ticks; 7 | void SysTick_Handler(void) { 8 | s_ticks++; 9 | } 10 | 11 | int main(void) { 12 | uint16_t led = PIN('B', 7); // Blue LED 13 | systick_init(FREQ / 1000); // Tick every 1 ms 14 | gpio_set_mode(led, GPIO_MODE_OUTPUT); // Set blue LED to output mode 15 | uart_init(UART3, 115200); // Initialise UART 16 | uint32_t timer = 0, period = 500; // Declare timer and 500ms period 17 | for (;;) { 18 | if (timer_expired(&timer, period, s_ticks)) { 19 | static bool on; // This block is executed 20 | gpio_write(led, on); // Every `period` milliseconds 21 | on = !on; // Toggle LED state 22 | printf("LED: %d, tick: %lu\r\n", on, s_ticks); // Write message 23 | } 24 | // Here we could perform other activities! 25 | } 26 | return 0; 27 | } 28 | -------------------------------------------------------------------------------- /steps/step-4-printf/startup.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 Cesanta Software Limited 2 | // All rights reserved 3 | 4 | // Startup code 5 | __attribute__((naked, noreturn)) void _reset(void) { 6 | // Initialise memory 7 | extern long _sbss, _ebss, _sdata, _edata, _sidata; 8 | for (long *dst = &_sbss; dst < &_ebss; dst++) *dst = 0; 9 | for (long *dst = &_sdata, *src = &_sidata; dst < &_edata;) *dst++ = *src++; 10 | 11 | // Call main() 12 | extern void main(void); 13 | main(); 14 | for (;;) (void) 0; // Infinite loop 15 | } 16 | 17 | extern void SysTick_Handler(void); // Defined in main.c 18 | extern void _estack(void); // Defined in link.ld 19 | 20 | // 16 standard and 91 STM32-specific handlers 21 | __attribute__((section(".vectors"))) void (*const tab[16 + 91])(void) = { 22 | _estack, _reset, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, SysTick_Handler}; 23 | -------------------------------------------------------------------------------- /steps/step-4-printf/syscalls.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 Cesanta Software Limited 2 | // All rights reserved 3 | 4 | #include 5 | 6 | #include "hal.h" 7 | 8 | int _fstat(int fd, struct stat *st) { 9 | if (fd < 0) return -1; 10 | st->st_mode = S_IFCHR; 11 | return 0; 12 | } 13 | 14 | void *_sbrk(int incr) { 15 | extern char _end; 16 | static unsigned char *heap = NULL; 17 | unsigned char *prev_heap; 18 | if (heap == NULL) heap = (unsigned char *) &_end; 19 | prev_heap = heap; 20 | heap += incr; 21 | return prev_heap; 22 | } 23 | 24 | int _open(const char *path) { 25 | (void) path; 26 | return -1; 27 | } 28 | 29 | int _close(int fd) { 30 | (void) fd; 31 | return -1; 32 | } 33 | 34 | int _isatty(int fd) { 35 | (void) fd; 36 | return 1; 37 | } 38 | 39 | int _lseek(int fd, int ptr, int dir) { 40 | (void) fd, (void) ptr, (void) dir; 41 | return 0; 42 | } 43 | 44 | void _exit(int status) { 45 | (void) status; 46 | for (;;) asm volatile("BKPT #0"); 47 | } 48 | 49 | void _kill(int pid, int sig) { 50 | (void) pid, (void) sig; 51 | } 52 | 53 | int _getpid(void) { 54 | return -1; 55 | } 56 | 57 | int _write(int fd, char *ptr, int len) { 58 | (void) fd, (void) ptr, (void) len; 59 | if (fd == 1) uart_write_buf(UART3, ptr, (size_t) len); 60 | return -1; 61 | } 62 | 63 | int _read(int fd, char *ptr, int len) { 64 | (void) fd, (void) ptr, (void) len; 65 | return -1; 66 | } 67 | 68 | int _link(const char *a, const char *b) { 69 | (void) a, (void) b; 70 | return -1; 71 | } 72 | 73 | int _unlink(const char *a) { 74 | (void) a; 75 | return -1; 76 | } 77 | 78 | int _stat(const char *path, struct stat *st) { 79 | (void) path, (void) st; 80 | return -1; 81 | } 82 | 83 | int mkdir(const char *path, mode_t mode) { 84 | (void) path, (void) mode; 85 | return -1; 86 | } 87 | -------------------------------------------------------------------------------- /steps/step-5-cmsis/Makefile: -------------------------------------------------------------------------------- 1 | CFLAGS ?= -W -Wall -Wextra -Werror -Wundef -Wshadow -Wdouble-promotion \ 2 | -Wformat-truncation -fno-common -Wconversion \ 3 | -g3 -Os -ffunction-sections -fdata-sections \ 4 | -I. -Iinclude -Icmsis_core/CMSIS/Core/Include -Icmsis_f4/Include \ 5 | -mcpu=cortex-m4 -mthumb -mfloat-abi=hard -mfpu=fpv4-sp-d16 $(EXTRA_CFLAGS) 6 | LDFLAGS ?= -Tlink.ld -nostartfiles -nostdlib --specs nano.specs -lc -lgcc -Wl,--gc-sections -Wl,-Map=$@.map 7 | SOURCES = main.c syscalls.c 8 | SOURCES += cmsis_f4/Source/Templates/gcc/startup_stm32f429xx.s # ST startup file. Compiler-dependent! 9 | 10 | ifeq ($(OS),Windows_NT) 11 | RM = cmd /C del /Q /F 12 | else 13 | RM = rm -rf 14 | endif 15 | 16 | build: firmware.bin 17 | 18 | firmware.elf: cmsis_core cmsis_f4 hal.h link.ld Makefile $(SOURCES) 19 | arm-none-eabi-gcc $(SOURCES) $(CFLAGS) $(CFLAGS_EXTRA) $(LDFLAGS) -o $@ 20 | 21 | firmware.bin: firmware.elf 22 | arm-none-eabi-objcopy -O binary $< $@ 23 | 24 | flash: firmware.bin 25 | st-flash --reset write $< 0x8000000 26 | 27 | cmsis_core: 28 | git clone -q -c advice.detachedHead=false --depth 1 -b 5.9.0 https://github.com/ARM-software/CMSIS_5 $@ 29 | 30 | cmsis_f4: 31 | git clone -q -c advice.detachedHead=false --depth 1 -b v2.6.8 https://github.com/STMicroelectronics/cmsis_device_f4 $@ 32 | 33 | clean: 34 | $(RM) firmware.* cmsis_* 35 | -------------------------------------------------------------------------------- /steps/step-5-cmsis/hal.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 Cesanta Software Limited 2 | // All rights reserved 3 | 4 | #pragma once 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include "stm32f429xx.h" 13 | 14 | #define FREQ 16000000 // CPU frequency, 16 Mhz 15 | #define BIT(x) (1UL << (x)) 16 | #define PIN(bank, num) ((((bank) - 'A') << 8) | (num)) 17 | #define PINNO(pin) (pin & 255) 18 | #define PINBANK(pin) (pin >> 8) 19 | 20 | static inline void spin(volatile uint32_t count) { 21 | while (count--) asm("nop"); 22 | } 23 | 24 | #define GPIO(bank) ((GPIO_TypeDef *) (GPIOA_BASE + 0x400U * (bank))) 25 | enum { GPIO_MODE_INPUT, GPIO_MODE_OUTPUT, GPIO_MODE_AF, GPIO_MODE_ANALOG }; 26 | 27 | static inline void gpio_set_mode(uint16_t pin, uint8_t mode) { 28 | GPIO_TypeDef *gpio = GPIO(PINBANK(pin)); // GPIO bank 29 | int n = PINNO(pin); // Pin number 30 | RCC->AHB1ENR |= BIT(PINBANK(pin)); // Enable GPIO clock 31 | gpio->MODER &= ~(3U << (n * 2)); // Clear existing setting 32 | gpio->MODER |= (mode & 3U) << (n * 2); // Set new mode 33 | } 34 | 35 | static inline void gpio_set_af(uint16_t pin, uint8_t af_num) { 36 | GPIO_TypeDef *gpio = GPIO(PINBANK(pin)); // GPIO bank 37 | int n = PINNO(pin); // Pin number 38 | gpio->AFR[n >> 3] &= ~(15UL << ((n & 7) * 4)); 39 | gpio->AFR[n >> 3] |= ((uint32_t) af_num) << ((n & 7) * 4); 40 | } 41 | 42 | static inline void gpio_write(uint16_t pin, bool val) { 43 | GPIO_TypeDef *gpio = GPIO(PINBANK(pin)); 44 | gpio->BSRR = (1U << PINNO(pin)) << (val ? 0 : 16); 45 | } 46 | 47 | #define UART1 USART1 48 | #define UART2 USART2 49 | #define UART3 USART3 50 | 51 | #ifndef UART_DEBUG 52 | #define UART_DEBUG USART3 53 | #endif 54 | 55 | static inline void uart_init(USART_TypeDef *uart, unsigned long baud) { 56 | // https://www.st.com/resource/en/datasheet/stm32f429zi.pdf 57 | uint8_t af = 7; // Alternate function 58 | uint16_t rx = 0, tx = 0; // pins 59 | 60 | if (uart == UART1) RCC->APB2ENR |= BIT(4); 61 | if (uart == UART2) RCC->APB1ENR |= BIT(17); 62 | if (uart == UART3) RCC->APB1ENR |= BIT(18); 63 | 64 | if (uart == UART1) tx = PIN('A', 9), rx = PIN('A', 10); 65 | if (uart == UART2) tx = PIN('A', 2), rx = PIN('A', 3); 66 | if (uart == UART3) tx = PIN('D', 8), rx = PIN('D', 9); 67 | 68 | gpio_set_mode(tx, GPIO_MODE_AF); 69 | gpio_set_af(tx, af); 70 | gpio_set_mode(rx, GPIO_MODE_AF); 71 | gpio_set_af(rx, af); 72 | uart->CR1 = 0; // Disable this UART 73 | uart->BRR = FREQ / baud; // FREQ is a UART bus frequency 74 | uart->CR1 |= BIT(13) | BIT(2) | BIT(3); // Set UE, RE, TE 75 | } 76 | 77 | static inline void uart_write_byte(USART_TypeDef *uart, uint8_t byte) { 78 | uart->DR = byte; 79 | while ((uart->SR & BIT(7)) == 0) spin(1); 80 | } 81 | 82 | static inline void uart_write_buf(USART_TypeDef *uart, char *buf, size_t len) { 83 | while (len-- > 0) uart_write_byte(uart, *(uint8_t *) buf++); 84 | } 85 | 86 | static inline int uart_read_ready(USART_TypeDef *uart) { 87 | return uart->SR & BIT(5); // If RXNE bit is set, data is ready 88 | } 89 | 90 | static inline uint8_t uart_read_byte(USART_TypeDef *uart) { 91 | return (uint8_t) (uart->DR & 255); 92 | } 93 | 94 | static inline bool timer_expired(volatile uint32_t *t, uint32_t prd, 95 | uint32_t now) { 96 | if (now + prd < *t) *t = 0; // Time wrapped? Reset timer 97 | if (*t == 0) *t = now + prd; // Firt poll? Set expiration 98 | if (*t > now) return false; // Not expired yet, return 99 | *t = (now - *t) > prd ? now + prd : *t + prd; // Next expiration time 100 | return true; // Expired, return true 101 | } 102 | -------------------------------------------------------------------------------- /steps/step-5-cmsis/link.ld: -------------------------------------------------------------------------------- 1 | ENTRY(Reset_Handler); 2 | MEMORY { 3 | flash(rx) : ORIGIN = 0x08000000, LENGTH = 2048k 4 | sram(rwx) : ORIGIN = 0x20000000, LENGTH = 192k /* remaining 64k in a separate address space */ 5 | } 6 | _estack = ORIGIN(sram) + LENGTH(sram); /* stack points to end of SRAM */ 7 | 8 | SECTIONS { 9 | .vectors : { KEEP(*(.isr_vector)) } > flash 10 | .text : { *(.text*) } > flash 11 | .rodata : { *(.rodata*) } > flash 12 | 13 | .data : { 14 | _sdata = .; 15 | *(.first_data) 16 | *(.data SORT(.data.*)) 17 | _edata = .; 18 | } > sram AT > flash 19 | _sidata = LOADADDR(.data); 20 | 21 | .bss : { _sbss = .; *(.bss SORT(.bss.*) COMMON) _ebss = .; } > sram 22 | 23 | . = ALIGN(8); 24 | _end = .; 25 | } 26 | -------------------------------------------------------------------------------- /steps/step-5-cmsis/main.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 Cesanta Software Limited 2 | // All rights reserved 3 | 4 | #include "hal.h" 5 | 6 | static volatile uint32_t s_ticks; 7 | void SysTick_Handler(void) { 8 | s_ticks++; 9 | } 10 | 11 | uint32_t SystemCoreClock = FREQ; 12 | void SystemInit(void) { 13 | RCC->APB2ENR |= RCC_APB2ENR_SYSCFGEN; // Enable SYSCFG 14 | SysTick_Config(SystemCoreClock / 1000); // Tick every 1 ms 15 | } 16 | 17 | int main(void) { 18 | uint16_t led = PIN('B', 7); // Blue LED 19 | gpio_set_mode(led, GPIO_MODE_OUTPUT); // Set blue LED to output mode 20 | uart_init(UART_DEBUG, 115200); // Initialise UART 21 | volatile uint32_t timer = 0, period = 500; // Declare timers 22 | for (;;) { 23 | if (timer_expired(&timer, period, s_ticks)) { 24 | static bool on; // This block is executed 25 | gpio_write(led, on); // Every `period` milliseconds 26 | on = !on; // Toggle LED state 27 | printf("LED: %d, tick: %lu\r\n", on, s_ticks); // Write message 28 | } 29 | // Here we could perform other activities! 30 | } 31 | return 0; 32 | } 33 | -------------------------------------------------------------------------------- /steps/step-5-cmsis/syscalls.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 Cesanta Software Limited 2 | // All rights reserved 3 | 4 | #include 5 | 6 | #include "hal.h" 7 | 8 | int _fstat(int fd, struct stat *st) { 9 | if (fd < 0) return -1; 10 | st->st_mode = S_IFCHR; 11 | return 0; 12 | } 13 | 14 | void *_sbrk(int incr) { 15 | extern char _end; 16 | static unsigned char *heap = NULL; 17 | unsigned char *prev_heap; 18 | if (heap == NULL) heap = (unsigned char *) &_end; 19 | prev_heap = heap; 20 | heap += incr; 21 | return prev_heap; 22 | } 23 | 24 | int _open(const char *path) { 25 | (void) path; 26 | return -1; 27 | } 28 | 29 | int _close(int fd) { 30 | (void) fd; 31 | return -1; 32 | } 33 | 34 | int _isatty(int fd) { 35 | (void) fd; 36 | return 1; 37 | } 38 | 39 | int _lseek(int fd, int ptr, int dir) { 40 | (void) fd, (void) ptr, (void) dir; 41 | return 0; 42 | } 43 | 44 | void _exit(int status) { 45 | (void) status; 46 | for (;;) asm volatile("BKPT #0"); 47 | } 48 | 49 | void _kill(int pid, int sig) { 50 | (void) pid, (void) sig; 51 | } 52 | 53 | int _getpid(void) { 54 | return -1; 55 | } 56 | 57 | int _write(int fd, char *ptr, int len) { 58 | (void) fd, (void) ptr, (void) len; 59 | if (fd == 1) uart_write_buf(UART_DEBUG, ptr, (size_t) len); 60 | return -1; 61 | } 62 | 63 | int _read(int fd, char *ptr, int len) { 64 | (void) fd, (void) ptr, (void) len; 65 | return -1; 66 | } 67 | 68 | int _link(const char *a, const char *b) { 69 | (void) a, (void) b; 70 | return -1; 71 | } 72 | 73 | int _unlink(const char *a) { 74 | (void) a; 75 | return -1; 76 | } 77 | 78 | int _stat(const char *path, struct stat *st) { 79 | (void) path, (void) st; 80 | return -1; 81 | } 82 | 83 | int mkdir(const char *path, mode_t mode) { 84 | (void) path, (void) mode; 85 | return -1; 86 | } 87 | 88 | void _init(void) { 89 | } 90 | -------------------------------------------------------------------------------- /steps/step-6-clock/Makefile: -------------------------------------------------------------------------------- 1 | CFLAGS ?= -W -Wall -Wextra -Werror -Wundef -Wshadow -Wdouble-promotion \ 2 | -Wformat-truncation -fno-common -Wconversion \ 3 | -g3 -Os -ffunction-sections -fdata-sections \ 4 | -I. -Iinclude -Icmsis_core/CMSIS/Core/Include -Icmsis_f4/Include \ 5 | -mcpu=cortex-m4 -mthumb -mfloat-abi=hard -mfpu=fpv4-sp-d16 6 | LDFLAGS ?= -Tlink.ld -nostartfiles -nostdlib --specs nano.specs -lc -lgcc -Wl,--gc-sections -Wl,-Map=$@.map 7 | SOURCES = main.c syscalls.c sysinit.c 8 | SOURCES += cmsis_f4/Source/Templates/gcc/startup_stm32f429xx.s # ST startup file. Compiler-dependent! 9 | 10 | ifeq ($(OS),Windows_NT) 11 | RM = cmd /C del /Q /F 12 | else 13 | RM = rm -rf 14 | endif 15 | 16 | build: firmware.bin 17 | 18 | firmware.elf: cmsis_core cmsis_f4 hal.h link.ld Makefile $(SOURCES) 19 | arm-none-eabi-gcc $(SOURCES) $(CFLAGS) $(CFLAGS_EXTRA) $(LDFLAGS) -o $@ 20 | 21 | firmware.bin: firmware.elf 22 | arm-none-eabi-objcopy -O binary $< $@ 23 | 24 | flash: firmware.bin 25 | st-flash --reset write $< 0x8000000 26 | 27 | cmsis_core: 28 | git clone -q -c advice.detachedHead=false --depth 1 -b 5.9.0 https://github.com/ARM-software/CMSIS_5 $@ 29 | 30 | cmsis_f4: 31 | git clone -q -c advice.detachedHead=false --depth 1 -b v2.6.8 https://github.com/STMicroelectronics/cmsis_device_f4 $@ 32 | 33 | clean: 34 | $(RM) firmware.* cmsis_* 35 | -------------------------------------------------------------------------------- /steps/step-6-clock/link.ld: -------------------------------------------------------------------------------- 1 | ENTRY(Reset_Handler); 2 | MEMORY { 3 | flash(rx) : ORIGIN = 0x08000000, LENGTH = 2048k 4 | sram(rwx) : ORIGIN = 0x20000000, LENGTH = 192k /* remaining 64k in a separate address space */ 5 | } 6 | _estack = ORIGIN(sram) + LENGTH(sram); /* stack points to end of SRAM */ 7 | 8 | SECTIONS { 9 | .vectors : { KEEP(*(.isr_vector)) } > flash 10 | .text : { *(.text*) } > flash 11 | .rodata : { *(.rodata*) } > flash 12 | 13 | .data : { 14 | _sdata = .; 15 | *(.first_data) 16 | *(.data SORT(.data.*)) 17 | _edata = .; 18 | } > sram AT > flash 19 | _sidata = LOADADDR(.data); 20 | 21 | .bss : { _sbss = .; *(.bss SORT(.bss.*) COMMON) _ebss = .; } > sram 22 | 23 | . = ALIGN(8); 24 | _end = .; 25 | } 26 | -------------------------------------------------------------------------------- /steps/step-6-clock/main.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 Cesanta Software Limited 2 | // All rights reserved 3 | 4 | #include "hal.h" 5 | 6 | static volatile uint32_t s_ticks; 7 | void SysTick_Handler(void) { 8 | s_ticks++; 9 | } 10 | 11 | int main(void) { 12 | uint16_t led = PIN('B', 7); // Blue LED 13 | gpio_output(led); // Set blue LED to output mode 14 | uart_init(UART_DEBUG, 115200); // Initialise UART 15 | 16 | uint32_t timer = 0, period = 500; // Declare timer and 500ms period 17 | for (;;) { 18 | if (timer_expired(&timer, period, s_ticks)) { 19 | static bool on; // This block is executed 20 | gpio_write(led, on); // Every `period` milliseconds 21 | on = !on; // Toggle LED state 22 | printf("LED: %d, tick: %lu\r\n", on, s_ticks); // Write message 23 | } 24 | // Here we could perform other activities! 25 | } 26 | 27 | return 0; 28 | } 29 | -------------------------------------------------------------------------------- /steps/step-6-clock/syscalls.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 Cesanta Software Limited 2 | // All rights reserved 3 | 4 | #include 5 | 6 | #include "hal.h" 7 | 8 | int _fstat(int fd, struct stat *st) { 9 | if (fd < 0) return -1; 10 | st->st_mode = S_IFCHR; 11 | return 0; 12 | } 13 | 14 | void *_sbrk(int incr) { 15 | extern char _end; 16 | static unsigned char *heap = NULL; 17 | unsigned char *prev_heap; 18 | if (heap == NULL) heap = (unsigned char *) &_end; 19 | prev_heap = heap; 20 | heap += incr; 21 | return prev_heap; 22 | } 23 | 24 | int _open(const char *path) { 25 | (void) path; 26 | return -1; 27 | } 28 | 29 | int _close(int fd) { 30 | (void) fd; 31 | return -1; 32 | } 33 | 34 | int _isatty(int fd) { 35 | (void) fd; 36 | return 1; 37 | } 38 | 39 | int _lseek(int fd, int ptr, int dir) { 40 | (void) fd, (void) ptr, (void) dir; 41 | return 0; 42 | } 43 | 44 | void _exit(int status) { 45 | (void) status; 46 | for (;;) asm volatile("BKPT #0"); 47 | } 48 | 49 | void _kill(int pid, int sig) { 50 | (void) pid, (void) sig; 51 | } 52 | 53 | int _getpid(void) { 54 | return -1; 55 | } 56 | 57 | int _write(int fd, char *ptr, int len) { 58 | (void) fd, (void) ptr, (void) len; 59 | if (fd == 1) uart_write_buf(UART_DEBUG, ptr, (size_t) len); 60 | return -1; 61 | } 62 | 63 | int _read(int fd, char *ptr, int len) { 64 | (void) fd, (void) ptr, (void) len; 65 | return -1; 66 | } 67 | 68 | int _link(const char *a, const char *b) { 69 | (void) a, (void) b; 70 | return -1; 71 | } 72 | 73 | int _unlink(const char *a) { 74 | (void) a; 75 | return -1; 76 | } 77 | 78 | int _stat(const char *path, struct stat *st) { 79 | (void) path, (void) st; 80 | return -1; 81 | } 82 | 83 | int mkdir(const char *path, mode_t mode) { 84 | (void) path, (void) mode; 85 | return -1; 86 | } 87 | 88 | void _init(void) { 89 | } 90 | -------------------------------------------------------------------------------- /steps/step-6-clock/sysinit.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023 Cesanta Software Limited 2 | // All rights reserved 3 | // 4 | // This file contains essentials required by the CMSIS: 5 | // uint32_t SystemCoreClock - holds the system core clock value 6 | // SystemInit() - initialises the system, e.g. sets up clocks 7 | 8 | #include "hal.h" 9 | 10 | uint32_t SystemCoreClock = SYS_FREQUENCY; 11 | 12 | void SystemInit(void) { // Called automatically by startup code 13 | SCB->CPACR |= ((3UL << 10 * 2) | (3UL << 11 * 2)); // Enable FPU 14 | FLASH->ACR |= FLASH_LATENCY | BIT(8) | BIT(9); // Flash latency, prefetch 15 | RCC->PLLCFGR &= ~((BIT(17) - 1)); // Clear PLL multipliers 16 | RCC->PLLCFGR |= (((PLL_P - 2) / 2) & 3) << 16; // Set PLL_P 17 | RCC->PLLCFGR |= PLL_M | (PLL_N << 6); // Set PLL_M and PLL_N 18 | RCC->CR |= BIT(24); // Enable PLL 19 | while ((RCC->CR & BIT(25)) == 0) spin(1); // Wait until done 20 | RCC->CFGR = (APB1_PRE << 10) | (APB2_PRE << 13); // Set prescalers 21 | RCC->CFGR |= 2; // Set clock source to PLL 22 | while ((RCC->CFGR & 12) == 0) spin(1); // Wait until done 23 | 24 | RCC->APB2ENR |= RCC_APB2ENR_SYSCFGEN; // Enable SYSCFG 25 | SysTick_Config(SystemCoreClock / 1000); // Sys tick every 1ms 26 | } 27 | -------------------------------------------------------------------------------- /steps/step-7-webserver/ek-tm4c1294xl/Makefile: -------------------------------------------------------------------------------- 1 | CFLAGS ?= -W -Wall -Wextra -Werror -Wundef -Wshadow -Wdouble-promotion \ 2 | -Wformat-truncation -fno-common -Wconversion \ 3 | -g3 -Os -ffunction-sections -fdata-sections -I. -Iinclude \ 4 | -mcpu=cortex-m4 -mthumb -mfloat-abi=hard -mfpu=fpv4-sp-d16 $(EXTRA_CFLAGS) 5 | LDFLAGS ?= -Tlink.ld -nostartfiles -nostdlib --specs nano.specs -lc -lgcc -Wl,--gc-sections -Wl,-Map=$@.map 6 | SOURCES = main.c startup.c syscalls.c mongoose.c net.c packed_fs.c 7 | 8 | ifeq ($(OS),Windows_NT) 9 | RM = cmd /C del /Q /F 10 | else 11 | RM = rm -f 12 | endif 13 | 14 | build: firmware.bin 15 | 16 | firmware.elf: $(SOURCES) 17 | arm-none-eabi-gcc $(SOURCES) $(CFLAGS) $(LDFLAGS) -o $@ 18 | 19 | firmware.bin: firmware.elf 20 | $(DOCKER) arm-none-eabi-objcopy -O binary $< $@ 21 | 22 | flash: firmware.bin 23 | st-flash --reset write firmware.bin 0x8000000 24 | 25 | clean: 26 | $(RM) firmware.* 27 | -------------------------------------------------------------------------------- /steps/step-7-webserver/ek-tm4c1294xl/README.md: -------------------------------------------------------------------------------- 1 | # EK-TM4C1294XL baremetal firmware 2 | 3 | For flashing, download and install [Uniflash](https://mongoose.ws/tutorials/ti/ek-tm4c1294xl-baremetal/#build-and-run) utility. 4 | 5 | -------------------------------------------------------------------------------- /steps/step-7-webserver/ek-tm4c1294xl/include/TM4C129.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************//** 2 | * @file TM4C129.h 3 | * @brief CMSIS Device System Header File for 4 | * Texas Instruments Tiva TM4C129 Device Series 5 | * @version V12591 6 | * @date 12. February 2014 7 | * 8 | * @note 9 | * 10 | ******************************************************************************/ 11 | 12 | #ifndef TM4C129_H 13 | #define TM4C129_H 14 | 15 | #if defined(SC4CE290NCPZ) 16 | #include "SC4CE290NCPZ.h" 17 | #elif defined(TM4C1290NCPDT) 18 | #include "TM4C1290NCPDT.h" 19 | #elif defined(TM4C1290NCZAD) 20 | #include "TM4C1290NCZAD.h" 21 | #elif defined(TM4C1292NCPDT) 22 | #include "TM4C1292NCPDT.h" 23 | #elif defined(TM4C1292NCZAD) 24 | #include "TM4C1292NCZAD.h" 25 | #elif defined(TM4C1294KCPDT) 26 | #include "TM4C1294KCPDT.h" 27 | #elif defined(TM4C1294NCPDT) 28 | #include "TM4C1294NCPDT.h" 29 | #elif defined(TM4C1294NCZAD) 30 | #include "TM4C1294NCZAD.h" 31 | #elif defined(TM4C1297NCZAD) 32 | #include "TM4C1297NCZAD.h" 33 | #elif defined(TM4C1299KCZAD) 34 | #include "TM4C1299KCZAD.h" 35 | #elif defined(TM4C1299NCZAD) 36 | #include "TM4C1299NCZAD.h" 37 | #elif defined(TM4C129CNCPDT) 38 | #include "TM4C129CNCPDT.h" 39 | #elif defined(TM4C129CNCZAD) 40 | #include "TM4C129CNCZAD.h" 41 | #elif defined(TM4C129DNCPDT) 42 | #include "TM4C129DNCPDT.h" 43 | #elif defined(TM4C129DNCZAD) 44 | #include "TM4C129DNCZAD.h" 45 | #elif defined(TM4C129EKCPDT) 46 | #include "TM4C129EKCPDT.h" 47 | #elif defined(TM4C129ENCPDT) 48 | #include "TM4C129ENCPDT.h" 49 | #elif defined(TM4C129ENCZAD) 50 | #include "TM4C129ENCZAD.h" 51 | #elif defined(TM4C129LNCZAD) 52 | #include "TM4C129LNCZAD.h" 53 | #elif defined(TM4C129XKCZAD) 54 | #include "TM4C129XKCZAD.h" 55 | #elif defined(TM4C129XNCZAD) 56 | #include "TM4C129XNCZAD.h" 57 | #else 58 | #error "TM4C129.h: TM4C129 Device NOT specified" 59 | #endif 60 | 61 | #endif /* TM4C129_H */ 62 | -------------------------------------------------------------------------------- /steps/step-7-webserver/ek-tm4c1294xl/include/cmsis_version.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************//** 2 | * @file cmsis_version.h 3 | * @brief CMSIS Core(M) Version definitions 4 | * @version V5.0.2 5 | * @date 19. April 2017 6 | ******************************************************************************/ 7 | /* 8 | * Copyright (c) 2009-2017 ARM Limited. All rights reserved. 9 | * 10 | * SPDX-License-Identifier: Apache-2.0 11 | * 12 | * Licensed under the Apache License, Version 2.0 (the License); you may 13 | * not use this file except in compliance with the License. 14 | * You may obtain a copy of the License at 15 | * 16 | * www.apache.org/licenses/LICENSE-2.0 17 | * 18 | * Unless required by applicable law or agreed to in writing, software 19 | * distributed under the License is distributed on an AS IS BASIS, WITHOUT 20 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 21 | * See the License for the specific language governing permissions and 22 | * limitations under the License. 23 | */ 24 | 25 | #if defined ( __ICCARM__ ) 26 | #pragma system_include /* treat file as system include file for MISRA check */ 27 | #elif defined (__clang__) 28 | #pragma clang system_header /* treat file as system include file */ 29 | #endif 30 | 31 | #ifndef __CMSIS_VERSION_H 32 | #define __CMSIS_VERSION_H 33 | 34 | /* CMSIS Version definitions */ 35 | #define __CM_CMSIS_VERSION_MAIN ( 5U) /*!< [31:16] CMSIS Core(M) main version */ 36 | #define __CM_CMSIS_VERSION_SUB ( 1U) /*!< [15:0] CMSIS Core(M) sub version */ 37 | #define __CM_CMSIS_VERSION ((__CM_CMSIS_VERSION_MAIN << 16U) | \ 38 | __CM_CMSIS_VERSION_SUB ) /*!< CMSIS Core(M) version number */ 39 | #endif 40 | -------------------------------------------------------------------------------- /steps/step-7-webserver/ek-tm4c1294xl/include/system_TM4C129.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************//** 2 | * @file system_TM4C129.h 3 | * @brief CMSIS Cortex-M4 Device Peripheral Access Layer Header File for 4 | * TI Tiva TM4C129 Snowflake Class Devices 5 | * @version V3.1 6 | * @date 15. May 2013 7 | * 8 | * @note 9 | * Copyright (C) 2010-2011 ARM Limited. All rights reserved. 10 | * 11 | * @par 12 | * ARM Limited (ARM) is supplying this software for use with Cortex-M 13 | * processor based microcontrollers. This file can be freely distributed 14 | * within development tools that are supporting such ARM based processors. 15 | * 16 | * @par 17 | * THIS SOFTWARE IS PROVIDED "AS IS". NO WARRANTIES, WHETHER EXPRESS, IMPLIED 18 | * OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF 19 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. 20 | * ARM SHALL NOT, IN ANY CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR 21 | * CONSEQUENTIAL DAMAGES, FOR ANY REASON WHATSOEVER. 22 | * 23 | ******************************************************************************/ 24 | 25 | 26 | #ifndef SYSTEM_TM4C129_H 27 | #define SYSTEM_TM4C129_H 28 | 29 | #ifdef __cplusplus 30 | extern "C" { 31 | #endif 32 | 33 | #include 34 | 35 | extern uint32_t SystemCoreClock; /*!< System Clock Frequency (Core Clock) */ 36 | 37 | 38 | /** 39 | * Initialize the system 40 | * 41 | * @param none 42 | * @return none 43 | * 44 | * @brief Setup the microcontroller system. 45 | * Initialize the System and update the SystemCoreClock variable. 46 | */ 47 | extern void SystemInit (void); 48 | 49 | /** 50 | * Update SystemCoreClock variable 51 | * 52 | * @param none 53 | * @return none 54 | * 55 | * @brief Updates the SystemCoreClock with current core Clock 56 | * retrieved from cpu registers. 57 | */ 58 | extern void SystemCoreClockUpdate (void); 59 | 60 | #ifdef __cplusplus 61 | } 62 | #endif 63 | 64 | #endif /* SYSTEM_TM4C129_H */ 65 | -------------------------------------------------------------------------------- /steps/step-7-webserver/ek-tm4c1294xl/link.ld: -------------------------------------------------------------------------------- 1 | ENTRY(_reset); 2 | MEMORY { 3 | flash(rx) : ORIGIN = 0x00000000, LENGTH = 1024k 4 | sram(rwx) : ORIGIN = 0x20000000, LENGTH = 256k 5 | } 6 | _estack = ORIGIN(sram) + LENGTH(sram); /* stack points to end of SRAM */ 7 | 8 | SECTIONS { 9 | .vectors : { KEEP(*(.vectors)) } > flash 10 | .text : { *(.text*) } > flash 11 | .rodata : { *(.rodata*) } > flash 12 | 13 | .data : { 14 | _sdata = .; /* for init_ram() */ 15 | *(.first_data) 16 | *(.data SORT(.data.*)) 17 | _edata = .; /* for init_ram() */ 18 | } > sram AT > flash 19 | _sidata = LOADADDR(.data); 20 | 21 | .bss : { 22 | _sbss = .; /* for init_ram() */ 23 | *(.bss SORT(.bss.*) COMMON) 24 | _ebss = .; /* for init_ram() */ 25 | } > sram 26 | 27 | . = ALIGN(8); 28 | _end = .; /* for cmsis_gcc.h and init_ram() */ 29 | } 30 | -------------------------------------------------------------------------------- /steps/step-7-webserver/ek-tm4c1294xl/main.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 Cesanta Software Limited 2 | // All rights reserved 3 | 4 | #include "hal.h" 5 | #include "mongoose.h" 6 | 7 | static volatile uint32_t s_ticks; 8 | void SysTick_Handler(void) { 9 | s_ticks++; 10 | } 11 | 12 | uint64_t mg_millis(void) { // Declare our own uptime function 13 | return s_ticks; // Return number of milliseconds since boot 14 | } 15 | 16 | int main(void) { 17 | uint16_t led = PIN('N', 1); // LED1 18 | clock_init(); // Run at 120MHz 19 | systick_init(FREQ / 1000); // Tick every 1 ms 20 | gpio_init(led, GPIO_MODE_OUTPUT, GPIO_OTYPE_PUSH_PULL, GPIO_SPEED_HIGH, 21 | GPIO_PULL_NONE, 0); // Set LED1 to output mode 22 | uart_init(UART0, 115200); // Initialise UART 23 | uint32_t timer = 0, period = 500; // Declare timer and 500ms period 24 | 25 | // Initialise Ethernet, enable LED pins 26 | // See datasheet: https://www.ti.com/lit/pdf/spms433 27 | // Assign LED3 and LED4 to the EPHY, "activity" and "link", respectively. 28 | // (20.4.2.4) 29 | gpio_init(PIN('F', 4), GPIO_MODE_AF, GPIO_OTYPE_PUSH_PULL, GPIO_SPEED_HIGH, 30 | GPIO_PULL_NONE, 5); // EN0LED1 31 | gpio_init(PIN('F', 0), GPIO_MODE_AF, GPIO_OTYPE_PUSH_PULL, GPIO_SPEED_HIGH, 32 | GPIO_PULL_NONE, 5); // EN0LED0 33 | NVIC_EnableIRQ(EMAC0_IRQn); // Setup Ethernet IRQ handler 34 | // Initialize Ethernet clocks, see datasheet section 5 35 | // Turn Flash Prefetch off (silicon errata ETH#02) 36 | uint32_t val = FLASH_CTRL->CONF; 37 | val &= ~BIT(17); 38 | val |= BIT(16); 39 | FLASH_CTRL->CONF = val; 40 | SYSCTL->RCGCEMAC |= BIT(0); // Enable EMAC clock 41 | SYSCTL->SREMAC |= BIT(0); // Reset EMAC 42 | SYSCTL->SREMAC &= ~BIT(0); 43 | SYSCTL->RCGCEPHY |= BIT(0); // Enable EPHY clock 44 | SYSCTL->SREPHY |= BIT(0); // Reset EPHY 45 | SYSCTL->SREPHY &= ~BIT(0); 46 | while (!(SYSCTL->PREMAC & BIT(0)) || !(SYSCTL->PREPHY & BIT(0))) 47 | spin(1); // Wait for reset to complete 48 | 49 | struct mg_mgr mgr; // Initialise Mongoose event manager 50 | mg_mgr_init(&mgr); // and attach it to the MIP interface 51 | mg_log_set(MG_LL_DEBUG); // Set log level 52 | 53 | // Initialise Mongoose network stack 54 | // Specify MAC address, either set use_dhcp or enter a static config. 55 | // For static configuration, specify IP/mask/GW in network byte order 56 | struct mip_driver_tm4c driver_data = {.mdc_cr = 1}; // See driver_tm4c.h 57 | struct mip_if mif = { 58 | .mac = {2, 0, 1, 2, 3, 5}, 59 | .use_dhcp = true, 60 | .driver = &mip_driver_tm4c, 61 | .driver_data = &driver_data, 62 | }; 63 | mip_init(&mgr, &mif); 64 | val = FLASH_CTRL->CONF; // Turn Flash Prefetch on again 65 | val &= ~BIT(16); 66 | val |= BIT(17); 67 | FLASH_CTRL->CONF = val; 68 | extern void device_dashboard_fn(struct mg_connection *, int, void *, void *); 69 | mg_http_listen(&mgr, "http://0.0.0.0", device_dashboard_fn, &mgr); 70 | MG_INFO(("Init done, starting main loop")); 71 | 72 | for (;;) { 73 | if (timer_expired(&timer, period, s_ticks)) { 74 | static bool on; // This block is executed 75 | gpio_write(led, on); // Every `period` milliseconds 76 | on = !on; // Toggle LED state 77 | printf("LED: %d, tick: %lu\r\n", on, s_ticks); // Write message 78 | } 79 | mg_mgr_poll(&mgr, 0); // Handle networking 80 | } 81 | return 0; 82 | } 83 | -------------------------------------------------------------------------------- /steps/step-7-webserver/ek-tm4c1294xl/mongoose_custom.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 Cesanta Software Limited 2 | // All rights reserved 3 | 4 | #pragma once 5 | #define MG_ARCH MG_ARCH_NEWLIB 6 | #define MG_ENABLE_MIP 1 7 | #define MG_ENABLE_DRIVER_TM4C 1 8 | #define MG_ENABLE_PACKED_FS 1 9 | #define MG_IO_SIZE 512 10 | #define MG_ENABLE_CUSTOM_MILLIS 1 11 | -------------------------------------------------------------------------------- /steps/step-7-webserver/ek-tm4c1294xl/syscalls.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 Cesanta Software Limited 2 | // All rights reserved 3 | 4 | #include 5 | 6 | #include "hal.h" 7 | 8 | int _fstat(int fd, struct stat *st) { 9 | if (fd < 0) return -1; 10 | st->st_mode = S_IFCHR; 11 | return 0; 12 | } 13 | 14 | void *_sbrk(int incr) { 15 | extern char _end; 16 | static unsigned char *heap = NULL; 17 | unsigned char *prev_heap; 18 | if (heap == NULL) heap = (unsigned char *) &_end; 19 | prev_heap = heap; 20 | heap += incr; 21 | return prev_heap; 22 | } 23 | 24 | int _open(const char *path) { 25 | (void) path; 26 | return -1; 27 | } 28 | 29 | int _close(int fd) { 30 | (void) fd; 31 | return -1; 32 | } 33 | 34 | int _isatty(int fd) { 35 | (void) fd; 36 | return 1; 37 | } 38 | 39 | int _lseek(int fd, int ptr, int dir) { 40 | (void) fd, (void) ptr, (void) dir; 41 | return 0; 42 | } 43 | 44 | void _exit(int status) { 45 | (void) status; 46 | for (;;) asm volatile("BKPT #0"); 47 | } 48 | 49 | void _kill(int pid, int sig) { 50 | (void) pid, (void) sig; 51 | } 52 | 53 | int _getpid(void) { 54 | return -1; 55 | } 56 | 57 | int _write(int fd, char *ptr, int len) { 58 | (void) fd, (void) ptr, (void) len; 59 | if (fd == 1) uart_write_buf(UART0, ptr, (size_t) len); 60 | return -1; 61 | } 62 | 63 | int _read(int fd, char *ptr, int len) { 64 | (void) fd, (void) ptr, (void) len; 65 | return -1; 66 | } 67 | 68 | int _link(const char *a, const char *b) { 69 | (void) a, (void) b; 70 | return -1; 71 | } 72 | 73 | int _unlink(const char *a) { 74 | (void) a; 75 | return -1; 76 | } 77 | 78 | int _stat(const char *path, struct stat *st) { 79 | (void) path, (void) st; 80 | return -1; 81 | } 82 | 83 | int mkdir(const char *path, mode_t mode) { 84 | (void) path, (void) mode; 85 | return -1; 86 | } 87 | -------------------------------------------------------------------------------- /steps/step-7-webserver/nucleo-f429zi/Makefile: -------------------------------------------------------------------------------- 1 | CFLAGS ?= -W -Wall -Wextra -Werror -Wundef -Wshadow -Wdouble-promotion \ 2 | -Wformat-truncation -fno-common -Wconversion \ 3 | -g3 -Os -ffunction-sections -fdata-sections -I. -Iinclude \ 4 | -mcpu=cortex-m4 -mthumb -mfloat-abi=hard -mfpu=fpv4-sp-d16 $(EXTRA_CFLAGS) 5 | LDFLAGS ?= -Tlink.ld -nostartfiles -nostdlib --specs nano.specs -lc -lgcc -Wl,--gc-sections -Wl,-Map=$@.map 6 | SOURCES = main.c startup.c syscalls.c mongoose.c net.c packed_fs.c 7 | 8 | ifeq ($(OS),Windows_NT) 9 | RM = cmd /C del /Q /F 10 | else 11 | RM = rm -f 12 | endif 13 | 14 | build: firmware.elf 15 | 16 | firmware.elf: $(SOURCES) 17 | arm-none-eabi-gcc $(SOURCES) $(CFLAGS) $(LDFLAGS) -o $@ 18 | 19 | firmware.bin: firmware.elf 20 | arm-none-eabi-objcopy -O binary $< $@ 21 | 22 | flash: firmware.bin 23 | st-flash --reset write firmware.bin 0x8000000 24 | 25 | clean: 26 | $(RM) firmware.* 27 | -------------------------------------------------------------------------------- /steps/step-7-webserver/nucleo-f429zi/hal.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 Cesanta Software Limited 2 | // All rights reserved 3 | 4 | #pragma once 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include "stm32f429xx.h" 13 | 14 | #define BIT(x) (1UL << (x)) 15 | #define PIN(bank, num) ((((bank) - 'A') << 8) | (num)) 16 | #define PINNO(pin) (pin & 255) 17 | #define PINBANK(pin) (pin >> 8) 18 | #define SETBITS(R, CLEARMASK, SETMASK) (R) = ((R) & ~(CLEARMASK)) | (SETMASK) 19 | 20 | // 6.3.3: APB1 clock <= 45MHz; APB2 clock <= 90MHz 21 | // 3.5.1, Table 11: configure flash latency (WS) in accordance to clock freq 22 | // 33.4: The AHB clock must be at least 25 MHz when Ethernet is used 23 | enum { APB1_PRE = 5 /* AHB clock / 4 */, APB2_PRE = 4 /* AHB clock / 2 */ }; 24 | enum { PLL_HSI = 16, PLL_M = 8, PLL_N = 180, PLL_P = 2 }; // Run at 180 Mhz 25 | #define FLASH_LATENCY 5 26 | #define SYS_FREQUENCY ((PLL_HSI * PLL_N / PLL_M / PLL_P) * 1000000) 27 | #define APB2_FREQUENCY (SYS_FREQUENCY / (BIT(APB2_PRE - 3))) 28 | #define APB1_FREQUENCY (SYS_FREQUENCY / (BIT(APB1_PRE - 3))) 29 | 30 | static inline void spin(volatile uint32_t count) { 31 | while (count--) asm("nop"); 32 | } 33 | 34 | static inline void systick_init(uint32_t ticks) { 35 | if ((ticks - 1) > 0xffffff) return; // Systick timer is 24 bit 36 | SysTick->LOAD = ticks - 1; 37 | SysTick->VAL = 0; 38 | SysTick->CTRL = BIT(0) | BIT(1) | BIT(2); // Enable systick 39 | RCC->APB2ENR |= BIT(14); // Enable SYSCFG 40 | } 41 | 42 | #define GPIO(bank) ((GPIO_TypeDef *) (GPIOA_BASE + 0x400U * (bank))) 43 | enum { GPIO_MODE_INPUT, GPIO_MODE_OUTPUT, GPIO_MODE_AF, GPIO_MODE_ANALOG }; 44 | enum { GPIO_OTYPE_PUSH_PULL, GPIO_OTYPE_OPEN_DRAIN }; 45 | enum { GPIO_SPEED_LOW, GPIO_SPEED_MEDIUM, GPIO_SPEED_HIGH, GPIO_SPEED_INSANE }; 46 | enum { GPIO_PULL_NONE, GPIO_PULL_UP, GPIO_PULL_DOWN }; 47 | 48 | static inline void gpio_set_mode(uint16_t pin, uint8_t mode) { 49 | GPIO_TypeDef *gpio = GPIO(PINBANK(pin)); // GPIO bank 50 | int n = PINNO(pin); // Pin number 51 | RCC->AHB1ENR |= BIT(PINBANK(pin)); // Enable GPIO clock 52 | gpio->MODER &= ~(3U << (n * 2)); // Clear existing setting 53 | gpio->MODER |= (mode & 3U) << (n * 2); // Set new mode 54 | } 55 | 56 | static inline void gpio_set_af(uint16_t pin, uint8_t af_num) { 57 | GPIO_TypeDef *gpio = GPIO(PINBANK(pin)); // GPIO bank 58 | int n = PINNO(pin); // Pin number 59 | gpio->AFR[n >> 3] &= ~(15UL << ((n & 7) * 4)); 60 | gpio->AFR[n >> 3] |= ((uint32_t) af_num) << ((n & 7) * 4); 61 | } 62 | 63 | static inline void gpio_write(uint16_t pin, bool val) { 64 | GPIO_TypeDef *gpio = GPIO(PINBANK(pin)); 65 | gpio->BSRR = (1U << PINNO(pin)) << (val ? 0 : 16); 66 | } 67 | 68 | static inline void gpio_init(uint16_t pin, uint8_t mode, uint8_t type, 69 | uint8_t speed, uint8_t pull, uint8_t af) { 70 | GPIO_TypeDef *gpio = GPIO(PINBANK(pin)); // GPIO bank 71 | uint8_t n = (uint8_t) (PINNO(pin)); 72 | RCC->AHB1ENR |= BIT(PINBANK(pin)); // Enable GPIO clock 73 | SETBITS(gpio->OTYPER, 1UL << n, ((uint32_t) type) << n); 74 | SETBITS(gpio->OSPEEDR, 3UL << (n * 2), ((uint32_t) speed) << (n * 2)); 75 | SETBITS(gpio->PUPDR, 3UL << (n * 2), ((uint32_t) pull) << (n * 2)); 76 | SETBITS(gpio->AFR[n >> 3], 15UL << ((n & 7) * 4), 77 | ((uint32_t) af) << ((n & 7) * 4)); 78 | SETBITS(gpio->MODER, 3UL << (n * 2), ((uint32_t) mode) << (n * 2)); 79 | } 80 | 81 | #define UART1 USART1 82 | #define UART2 USART2 83 | #define UART3 USART3 84 | 85 | static inline void uart_init(USART_TypeDef *uart, unsigned long baud) { 86 | // https://www.st.com/resource/en/datasheet/stm32f429zi.pdf 87 | uint8_t af = 7; // Alternate function 88 | uint16_t rx = 0, tx = 0; // pins 89 | uint32_t freq = 0; // Bus frequency. UART1 is on APB2, rest on APB1 90 | 91 | if (uart == UART1) freq = APB2_FREQUENCY, RCC->APB2ENR |= BIT(4); 92 | if (uart == UART2) freq = APB1_FREQUENCY, RCC->APB1ENR |= BIT(17); 93 | if (uart == UART3) freq = APB1_FREQUENCY, RCC->APB1ENR |= BIT(18); 94 | 95 | if (uart == UART1) tx = PIN('A', 9), rx = PIN('A', 10); 96 | if (uart == UART2) tx = PIN('A', 2), rx = PIN('A', 3); 97 | if (uart == UART3) tx = PIN('D', 8), rx = PIN('D', 9); 98 | 99 | gpio_set_mode(tx, GPIO_MODE_AF); 100 | gpio_set_af(tx, af); 101 | gpio_set_mode(rx, GPIO_MODE_AF); 102 | gpio_set_af(rx, af); 103 | uart->CR1 = 0; // Disable this UART 104 | uart->BRR = freq / baud; // Set baud rate 105 | uart->CR1 |= BIT(13) | BIT(2) | BIT(3); // Set UE, RE, TE 106 | } 107 | 108 | static inline void uart_write_byte(USART_TypeDef *uart, uint8_t byte) { 109 | uart->DR = byte; 110 | while ((uart->SR & BIT(7)) == 0) spin(1); 111 | } 112 | 113 | static inline void uart_write_buf(USART_TypeDef *uart, char *buf, size_t len) { 114 | while (len-- > 0) uart_write_byte(uart, *(uint8_t *) buf++); 115 | } 116 | 117 | static inline int uart_read_ready(USART_TypeDef *uart) { 118 | return uart->SR & BIT(5); // If RXNE bit is set, data is ready 119 | } 120 | 121 | static inline uint8_t uart_read_byte(USART_TypeDef *uart) { 122 | return (uint8_t) (uart->DR & 255); 123 | } 124 | 125 | static inline bool timer_expired(uint32_t *t, uint32_t prd, uint32_t now) { 126 | if (now + prd < *t) *t = 0; // Time wrapped? Reset timer 127 | if (*t == 0) *t = now + prd; // Firt poll? Set expiration 128 | if (*t > now) return false; // Not expired yet, return 129 | *t = (now - *t) > prd ? now + prd : *t + prd; // Next expiration time 130 | return true; // Expired, return true 131 | } 132 | 133 | static inline void clock_init(void) { // Set clock frequency 134 | SCB->CPACR |= ((3UL << 10 * 2) | (3UL << 11 * 2)); // Enable FPU 135 | FLASH->ACR |= FLASH_LATENCY | BIT(8) | BIT(9); // Flash latency, caches 136 | RCC->PLLCFGR &= ~((BIT(17) - 1)); // Clear PLL multipliers 137 | RCC->PLLCFGR |= (((PLL_P - 2) / 2) & 3) << 16; // Set PLL_P 138 | RCC->PLLCFGR |= PLL_M | (PLL_N << 6); // Set PLL_M and PLL_N 139 | RCC->CR |= BIT(24); // Enable PLL 140 | while ((RCC->CR & BIT(25)) == 0) spin(1); // Wait until done 141 | RCC->CFGR = (APB1_PRE << 10) | (APB2_PRE << 13); // Set prescalers 142 | RCC->CFGR |= 2; // Set clock source to PLL 143 | while ((RCC->CFGR & 12) == 0) spin(1); // Wait until done 144 | } 145 | -------------------------------------------------------------------------------- /steps/step-7-webserver/nucleo-f429zi/include/cmsis_version.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************//** 2 | * @file cmsis_version.h 3 | * @brief CMSIS Core(M) Version definitions 4 | * @version V5.0.2 5 | * @date 19. April 2017 6 | ******************************************************************************/ 7 | /* 8 | * Copyright (c) 2009-2017 ARM Limited. All rights reserved. 9 | * 10 | * SPDX-License-Identifier: Apache-2.0 11 | * 12 | * Licensed under the Apache License, Version 2.0 (the License); you may 13 | * not use this file except in compliance with the License. 14 | * You may obtain a copy of the License at 15 | * 16 | * www.apache.org/licenses/LICENSE-2.0 17 | * 18 | * Unless required by applicable law or agreed to in writing, software 19 | * distributed under the License is distributed on an AS IS BASIS, WITHOUT 20 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 21 | * See the License for the specific language governing permissions and 22 | * limitations under the License. 23 | */ 24 | 25 | #if defined ( __ICCARM__ ) 26 | #pragma system_include /* treat file as system include file for MISRA check */ 27 | #elif defined (__clang__) 28 | #pragma clang system_header /* treat file as system include file */ 29 | #endif 30 | 31 | #ifndef __CMSIS_VERSION_H 32 | #define __CMSIS_VERSION_H 33 | 34 | /* CMSIS Version definitions */ 35 | #define __CM_CMSIS_VERSION_MAIN ( 5U) /*!< [31:16] CMSIS Core(M) main version */ 36 | #define __CM_CMSIS_VERSION_SUB ( 1U) /*!< [15:0] CMSIS Core(M) sub version */ 37 | #define __CM_CMSIS_VERSION ((__CM_CMSIS_VERSION_MAIN << 16U) | \ 38 | __CM_CMSIS_VERSION_SUB ) /*!< CMSIS Core(M) version number */ 39 | #endif 40 | -------------------------------------------------------------------------------- /steps/step-7-webserver/nucleo-f429zi/include/system_stm32f4xx.h: -------------------------------------------------------------------------------- 1 | /** 2 | ****************************************************************************** 3 | * @file system_stm32f4xx.h 4 | * @author MCD Application Team 5 | * @brief CMSIS Cortex-M4 Device System Source File for STM32F4xx devices. 6 | ****************************************************************************** 7 | * @attention 8 | * 9 | * Copyright (c) 2017 STMicroelectronics. 10 | * All rights reserved. 11 | * 12 | * This software is licensed under terms that can be found in the LICENSE file 13 | * in the root directory of this software component. 14 | * If no LICENSE file comes with this software, it is provided AS-IS. 15 | * 16 | ****************************************************************************** 17 | */ 18 | 19 | /** @addtogroup CMSIS 20 | * @{ 21 | */ 22 | 23 | /** @addtogroup stm32f4xx_system 24 | * @{ 25 | */ 26 | 27 | /** 28 | * @brief Define to prevent recursive inclusion 29 | */ 30 | #ifndef __SYSTEM_STM32F4XX_H 31 | #define __SYSTEM_STM32F4XX_H 32 | 33 | #ifdef __cplusplus 34 | extern "C" { 35 | #endif 36 | 37 | /** @addtogroup STM32F4xx_System_Includes 38 | * @{ 39 | */ 40 | 41 | /** 42 | * @} 43 | */ 44 | 45 | 46 | /** @addtogroup STM32F4xx_System_Exported_types 47 | * @{ 48 | */ 49 | /* This variable is updated in three ways: 50 | 1) by calling CMSIS function SystemCoreClockUpdate() 51 | 2) by calling HAL API function HAL_RCC_GetSysClockFreq() 52 | 3) each time HAL_RCC_ClockConfig() is called to configure the system clock frequency 53 | Note: If you use this function to configure the system clock; then there 54 | is no need to call the 2 first functions listed above, since SystemCoreClock 55 | variable is updated automatically. 56 | */ 57 | extern uint32_t SystemCoreClock; /*!< System Clock Frequency (Core Clock) */ 58 | 59 | extern const uint8_t AHBPrescTable[16]; /*!< AHB prescalers table values */ 60 | extern const uint8_t APBPrescTable[8]; /*!< APB prescalers table values */ 61 | 62 | /** 63 | * @} 64 | */ 65 | 66 | /** @addtogroup STM32F4xx_System_Exported_Constants 67 | * @{ 68 | */ 69 | 70 | /** 71 | * @} 72 | */ 73 | 74 | /** @addtogroup STM32F4xx_System_Exported_Macros 75 | * @{ 76 | */ 77 | 78 | /** 79 | * @} 80 | */ 81 | 82 | /** @addtogroup STM32F4xx_System_Exported_Functions 83 | * @{ 84 | */ 85 | 86 | extern void SystemInit(void); 87 | extern void SystemCoreClockUpdate(void); 88 | /** 89 | * @} 90 | */ 91 | 92 | #ifdef __cplusplus 93 | } 94 | #endif 95 | 96 | #endif /*__SYSTEM_STM32F4XX_H */ 97 | 98 | /** 99 | * @} 100 | */ 101 | 102 | /** 103 | * @} 104 | */ 105 | -------------------------------------------------------------------------------- /steps/step-7-webserver/nucleo-f429zi/link.ld: -------------------------------------------------------------------------------- 1 | ENTRY(_reset); 2 | MEMORY { 3 | flash(rx) : ORIGIN = 0x08000000, LENGTH = 2048k 4 | sram(rwx) : ORIGIN = 0x20000000, LENGTH = 192k /* remaining 64k in a separate address space */ 5 | } 6 | _estack = ORIGIN(sram) + LENGTH(sram); /* stack points to end of SRAM */ 7 | 8 | SECTIONS { 9 | .vectors : { KEEP(*(.vectors)) } > flash 10 | .text : { *(.text*) } > flash 11 | .rodata : { *(.rodata*) } > flash 12 | 13 | .data : { 14 | _sdata = .; /* .data section start */ 15 | *(.first_data) 16 | *(.data SORT(.data.*)) 17 | _edata = .; /* .data section end */ 18 | } > sram AT > flash 19 | _sidata = LOADADDR(.data); 20 | 21 | .bss : { 22 | _sbss = .; /* .bss section start */ 23 | *(.bss SORT(.bss.*) COMMON) 24 | _ebss = .; /* .bss section end */ 25 | } > sram 26 | 27 | . = ALIGN(8); 28 | _end = .; /* for cmsis_gcc.h */ 29 | } 30 | -------------------------------------------------------------------------------- /steps/step-7-webserver/nucleo-f429zi/main.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 Cesanta Software Limited 2 | // All rights reserved 3 | 4 | #include "hal.h" 5 | #include "mongoose.h" 6 | 7 | static volatile uint32_t s_ticks; 8 | void SysTick_Handler(void) { s_ticks++; } // IRQ handler 9 | uint64_t mg_millis(void) { return s_ticks; } // For Mongoose 10 | 11 | int main(void) { 12 | uint16_t led = PIN('B', 7); // Blue LED 13 | clock_init(); // Run at 180Mhz 14 | systick_init(SYS_FREQUENCY / 1000); // Tick every 1 ms 15 | gpio_set_mode(led, GPIO_MODE_OUTPUT); // Set blue LED to output mode 16 | uart_init(UART3, 115200); // Initialise UART 17 | uint32_t timer = 0, period = 500; // Declare timer and 500ms period 18 | 19 | // Initialise Ethernet. Enable MAC GPIO pins, see 20 | // https://www.farnell.com/datasheets/2014265.pdf section 6.10 21 | uint16_t pins[] = {PIN('A', 1), PIN('A', 2), PIN('A', 7), 22 | PIN('B', 13), PIN('C', 1), PIN('C', 4), 23 | PIN('C', 5), PIN('G', 11), PIN('G', 13)}; 24 | for (size_t i = 0; i < sizeof(pins) / sizeof(pins[0]); i++) { 25 | gpio_init(pins[i], GPIO_MODE_AF, 0, GPIO_SPEED_INSANE, GPIO_PULL_NONE, 11); 26 | } 27 | NVIC_EnableIRQ(ETH_IRQn); // Setup Ethernet IRQ handler 28 | RCC->APB2ENR |= BIT(14); // Enable SYSCFG 29 | SYSCFG->PMC |= BIT(23); // Use RMII. Goes first! 30 | RCC->AHB1ENR |= BIT(25) | BIT(26) | BIT(27); // Enable Ethernet clocks 31 | RCC->AHB1RSTR |= BIT(25); // ETHMAC force reset 32 | RCC->AHB1RSTR &= ~BIT(25); // ETHMAC release reset 33 | 34 | struct mg_mgr mgr; // Initialise Mongoose event manager 35 | mg_mgr_init(&mgr); // and attach it to the MIP interface 36 | mg_log_set(MG_LL_DEBUG); // Set log level 37 | 38 | // Initialise Mongoose network stack 39 | // Specify MAC address, and use 0 for IP, mask, GW - i.e. use DHCP 40 | // For static configuration, specify IP/mask/GW in network byte order 41 | struct mip_driver_stm32 driver_data = {.mdc_cr = 4}; // See driver_stm32.h 42 | struct mip_if mif = { 43 | .mac = {2, 0, 1, 2, 3, 5}, 44 | .use_dhcp = true, 45 | .driver = &mip_driver_stm32, 46 | .driver_data = &driver_data, 47 | }; 48 | mip_init(&mgr, &mif); 49 | extern void device_dashboard_fn(struct mg_connection *, int, void *, void *); 50 | mg_http_listen(&mgr, "http://0.0.0.0", device_dashboard_fn, &mgr); 51 | MG_INFO(("Init done, starting main loop")); 52 | 53 | for (;;) { 54 | if (timer_expired(&timer, period, s_ticks)) { 55 | static bool on; // This block is executed 56 | gpio_write(led, on); // Every `period` milliseconds 57 | on = !on; // Toggle LED state 58 | printf("LED: %d, tick: %lu\r\n", on, s_ticks); // Write message 59 | } 60 | mg_mgr_poll(&mgr, 0); // Handle networking 61 | } 62 | return 0; 63 | } 64 | -------------------------------------------------------------------------------- /steps/step-7-webserver/nucleo-f429zi/mongoose_custom.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 Cesanta Software Limited 2 | // All rights reserved 3 | 4 | #pragma once 5 | #define MG_ARCH MG_ARCH_NEWLIB 6 | #define MG_ENABLE_MIP 1 7 | #define MG_ENABLE_PACKED_FS 1 8 | #define MG_IO_SIZE 512 9 | #define MG_ENABLE_CUSTOM_MILLIS 1 10 | -------------------------------------------------------------------------------- /steps/step-7-webserver/nucleo-f429zi/syscalls.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 Cesanta Software Limited 2 | // All rights reserved 3 | 4 | #include 5 | 6 | #include "hal.h" 7 | 8 | int _fstat(int fd, struct stat *st) { 9 | if (fd < 0) return -1; 10 | st->st_mode = S_IFCHR; 11 | return 0; 12 | } 13 | 14 | void *_sbrk(int incr) { 15 | extern char _end; 16 | static unsigned char *heap = NULL; 17 | unsigned char *prev_heap; 18 | if (heap == NULL) heap = (unsigned char *) &_end; 19 | prev_heap = heap; 20 | heap += incr; 21 | return prev_heap; 22 | } 23 | 24 | int _open(const char *path) { 25 | (void) path; 26 | return -1; 27 | } 28 | 29 | int _close(int fd) { 30 | (void) fd; 31 | return -1; 32 | } 33 | 34 | int _isatty(int fd) { 35 | (void) fd; 36 | return 1; 37 | } 38 | 39 | int _lseek(int fd, int ptr, int dir) { 40 | (void) fd, (void) ptr, (void) dir; 41 | return 0; 42 | } 43 | 44 | void _exit(int status) { 45 | (void) status; 46 | for (;;) asm volatile("BKPT #0"); 47 | } 48 | 49 | void _kill(int pid, int sig) { 50 | (void) pid, (void) sig; 51 | } 52 | 53 | int _getpid(void) { 54 | return -1; 55 | } 56 | 57 | int _write(int fd, char *ptr, int len) { 58 | (void) fd, (void) ptr, (void) len; 59 | if (fd == 1) uart_write_buf(UART3, ptr, (size_t) len); 60 | return -1; 61 | } 62 | 63 | int _read(int fd, char *ptr, int len) { 64 | (void) fd, (void) ptr, (void) len; 65 | return -1; 66 | } 67 | 68 | int _link(const char *a, const char *b) { 69 | (void) a, (void) b; 70 | return -1; 71 | } 72 | 73 | int _unlink(const char *a) { 74 | (void) a; 75 | return -1; 76 | } 77 | 78 | int _stat(const char *path, struct stat *st) { 79 | (void) path, (void) st; 80 | return -1; 81 | } 82 | 83 | int mkdir(const char *path, mode_t mode) { 84 | (void) path, (void) mode; 85 | return -1; 86 | } 87 | -------------------------------------------------------------------------------- /steps/step-7-webserver/pico-w5500/Makefile: -------------------------------------------------------------------------------- 1 | CFLAGS ?= -W -Wall -Wextra -Werror -Wundef -Wshadow -Wdouble-promotion \ 2 | -Wformat-truncation -fno-common -Wconversion -ffreestanding \ 3 | -g3 -Os -ffunction-sections -fdata-sections -I. \ 4 | -mcpu=cortex-m0plus -mthumb $(EXTRA_CFLAGS) 5 | LDFLAGS ?= -Tlink.ld -nostartfiles --specs nano.specs -lc -lgcc -Wl,--gc-sections -Wl,-Map=$@.map 6 | SOURCES = main.c startup.c syscalls.c 7 | 8 | SOURCES += mongoose.c 9 | CFLAGS += -DMG_ARCH=MG_ARCH_NEWLIB -DMG_ENABLE_CUSTOM_MILLIS=1 -DMG_ENABLE_MIP=1 10 | 11 | build: firmware.uf2 12 | 13 | ifeq ($(OS),Windows_NT) 14 | RM = cmd /C del /Q /F 15 | BIN2UF2 = bin2uf2.exe 16 | else 17 | BIN2UF2 = ./bin2uf2 18 | RM = rm -f 19 | $(BIN2UF2): tools/bin2uf2.c 20 | $(CC) -W -Wall $< -o $@ 21 | endif 22 | 23 | firmware.elf: $(SOURCES) hal.h 24 | arm-none-eabi-gcc $(SOURCES) $(CFLAGS) $(LDFLAGS) -o $@ 25 | 26 | firmware.bin: firmware.elf 27 | arm-none-eabi-objcopy -O binary $< $@ 28 | 29 | firmware.uf2: firmware.bin $(BIN2UF2) 30 | $(BIN2UF2) $< $@ 31 | 32 | # Requires env variable VCON_API_KEY set 33 | DEVICE_URL ?= https://dash.vcon.io/api/v3/devices/3 34 | test: update 35 | curl --fail -su :$(VCON_API_KEY) $(DEVICE_URL)/tx?t=5 | tee /tmp/output.txt 36 | grep 'READY, IP:' /tmp/output.txt 37 | 38 | update: build 39 | curl --fail -su :$(VCON_API_KEY) $(DEVICE_URL)/ota --data-binary @firmware.bin 40 | 41 | clean: 42 | $(RM) firmware.* bin2uf2 43 | -------------------------------------------------------------------------------- /steps/step-7-webserver/pico-w5500/link.ld: -------------------------------------------------------------------------------- 1 | ENTRY(_reset); 2 | MEMORY { 3 | boot (rx) : ORIGIN = 0x10000000, LENGTH = 256 4 | flash (rx) : ORIGIN = 0x10000100, LENGTH = 2048K - LENGTH(boot) 5 | sram (rwx) : ORIGIN = 0x20000000, LENGTH = 264K 6 | } 7 | 8 | _sflash = ORIGIN(flash); 9 | _estack = ORIGIN(sram) + LENGTH(sram); /* stack points to end of SRAM */ 10 | 11 | SECTIONS { 12 | 13 | .boot : ALIGN(4) { 14 | KEEP(*(.boot)) 15 | . = 256 - 4; 16 | LONG(0xcccccccc) /* RP2040 expects CRC in the last 4 bytes of this block */ 17 | } > boot 18 | 19 | .text : ALIGN(4) { 20 | _stext = .; 21 | KEEP(*(.vectors)) 22 | *(.text*) 23 | *(.rodata) 24 | *(.rodata.*) 25 | . = ALIGN(4); 26 | _etext = .; 27 | } > sram AT > flash 28 | 29 | .data : ALIGN(4) { 30 | _sdata = .; /* .data section start */ 31 | *(.data SORT(.data.*)) 32 | . = ALIGN(4); 33 | _edata = .; /* .data section end */ 34 | } > sram AT > flash 35 | 36 | .bss : ALIGN(4) { 37 | _sbss = .; /* .bss section start */ 38 | *(.bss SORT(.bss.*) COMMON) 39 | . = ALIGN(4); 40 | _ebss = .; /* .bss section end */ 41 | } > sram 42 | 43 | _end = .; /* for cmsis_gcc.h */ 44 | } 45 | -------------------------------------------------------------------------------- /steps/step-7-webserver/pico-w5500/main.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023 Cesanta Software Limited 2 | // All rights reserved 3 | 4 | #include "hal.h" 5 | #include "mongoose.h" 6 | 7 | enum { LED = 25 }; // LED pins 8 | enum { UART_TX = 0, UART_RX = 1 }; // UART pins 9 | enum { SPI_CS = 17, SPI_CLK = 18, SPI_TX = 19, SPI_RX = 16 }; // SPI pins 10 | enum { STATUS_TIMER_MS = 1000, BLINK_TIMER_MS = 500 }; // Timeouts 11 | 12 | void my_spi_begin(void *spi) { spi_begin(spi); } 13 | void my_spi_end(void *spi) { spi_end(spi); } 14 | uint8_t my_spi_txn(void *spi, uint8_t byte) { return spi_txn(spi, byte); } 15 | 16 | void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) { 17 | if (ev == MG_EV_HTTP_MSG) { 18 | mg_http_reply(c, 200, "", "ok\n"); 19 | } 20 | (void) ev_data, (void) fn_data; 21 | } 22 | 23 | static volatile uint64_t s_ticks; 24 | void SysTick_Handler(void) { // SyStick IRQ handler, triggered every 1ms 25 | s_ticks++; 26 | } 27 | 28 | uint64_t mg_millis(void) { return s_ticks; } 29 | 30 | int main(void) { 31 | clock_init(); // Init clocks 32 | uart_init(UART0, 115200, UART_RX, UART_TX); // Init UART 33 | gpio_init(LED, GPIO_MODE_OUTPUT, 0); // Init LED 34 | 35 | MG_INFO(("Starting ...")); 36 | 37 | // Init SPI 38 | struct spi spi0 = { 39 | .miso = SPI_RX, .mosi = SPI_TX, .clk = SPI_CLK, .cs = SPI_CS, .spin = 50}; 40 | bool ok = spi_init(&spi0); 41 | MG_INFO(("SPI init: %d", ok)); 42 | 43 | // Init Mongoose 44 | struct mip_spi spi = {&spi0, my_spi_begin, my_spi_end, my_spi_txn}; 45 | struct mip_if mif = {.mac = {2, 0, 1, 2, 3, 5}, 46 | .driver = &mip_driver_w5500, 47 | .driver_data = &spi}; 48 | struct mg_mgr mgr; // Declare event manager 49 | mg_mgr_init(&mgr); // Init event manager 50 | mg_log_set(MG_LL_DEBUG); // Set DEBUG log level 51 | mip_init(&mgr, &mif); // Init TCP/IP stack 52 | mg_http_listen(&mgr, "http://0.0.0.0", fn, NULL); // HTTP listener 53 | 54 | bool led_on = false; // Initial LED state 55 | uint64_t status_timer = 0, blink_timer = 0; // Initial timer expirations 56 | 57 | // Infinite event manager loop 58 | for (;;) { 59 | if (mg_timer_expired(&blink_timer, BLINK_TIMER_MS, mg_millis())) { 60 | led_on = !led_on; // Flip LED state 61 | if (mip_driver_w5500.up(&mif)) led_on = true; // Always on if Eth up 62 | gpio_write(LED, led_on); // Set LED 63 | } 64 | if (mg_timer_expired(&status_timer, STATUS_TIMER_MS, mg_millis())) { 65 | MG_INFO(("Ethernet: %s", mif.driver->up(&mif) ? "up" : "down")); 66 | } 67 | mg_mgr_poll(&mgr, 1); 68 | } 69 | 70 | return 0; 71 | } 72 | -------------------------------------------------------------------------------- /steps/step-7-webserver/pico-w5500/startup.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 Cesanta Software Limited 2 | // All rights reserved 3 | 4 | #include "hal.h" 5 | 6 | // Startup code 7 | __attribute__((naked, section(".boot"))) void _reset(void) { 8 | // This code is executed by the boot ROM, and it must access flash, where 9 | // this code is located, and "eXecute In Place" (XIP). Flash is external, 10 | // accessed over QSPI, Synchronous Serial Interface (SSI). 11 | XIP_SSI->SSIENR = 0; 12 | XIP_SSI->BAUDR = 2; 13 | XIP_SSI->CTRLR0 = (0U << 21) | (3U << 8) | ((32 - 1) << 16); // Read 14 | XIP_SSI->CTRLR1 = (0 << 0); 15 | XIP_SSI->SPI_CTRLR0 = (3U << 24) | (6U << 2) | (2U << 8) | (0 << 0); 16 | XIP_SSI->SSIENR = BIT(0); // Enable 17 | 18 | // Initialise memory 19 | extern long _sbss, _ebss, _edata, _stext, _sflash; 20 | for (long *src = &_sflash, *dst = &_stext; dst < &_edata;) *dst++ = *src++; 21 | for (long *src = &_sbss; src < &_ebss;) *src++ = 0; 22 | 23 | extern void (*vectors[])(void); 24 | SCB->VTOR = (uint32_t) vectors; // Point SCB to where our IRQ table is 25 | 26 | extern void main(void); 27 | main(); // Call main() 28 | for (;;) (void) 0; // Loop forever if main() returns 29 | } 30 | 31 | void DefaultIRQHandler(void) { 32 | for (;;) (void) 0; 33 | } 34 | #define WEAK_ALIAS __attribute__((weak, alias("DefaultIRQHandler"))) 35 | 36 | WEAK_ALIAS void NMI_Handler(void); 37 | WEAK_ALIAS void HardFault_Handler(void); 38 | WEAK_ALIAS void MemManage_Handler(void); 39 | WEAK_ALIAS void BusFault_Handler(void); 40 | WEAK_ALIAS void UsageFault_Handler(void); 41 | WEAK_ALIAS void SVC_Handler(void); 42 | WEAK_ALIAS void DebugMon_Handler(void); 43 | WEAK_ALIAS void PendSV_Handler(void); 44 | WEAK_ALIAS void SysTick_Handler(void); 45 | 46 | WEAK_ALIAS void TIMER0_IRQHandler(void); 47 | WEAK_ALIAS void TIMER1_IRQHandler(void); 48 | WEAK_ALIAS void TIMER2_IRQHandler(void); 49 | WEAK_ALIAS void TIMER3_IRQHandler(void); 50 | WEAK_ALIAS void PWM_IRQHandler(void); 51 | WEAK_ALIAS void USBCTRL_IRQHandler(void); 52 | WEAK_ALIAS void XIP_IRQHandler(void); 53 | WEAK_ALIAS void PIO00_IRQHandler(void); 54 | WEAK_ALIAS void PIO01_IRQHandler(void); 55 | WEAK_ALIAS void PIO10_IRQHandler(void); 56 | WEAK_ALIAS void PIO11_IRQHandler(void); 57 | WEAK_ALIAS void DMA0_IRQHandler(void); 58 | WEAK_ALIAS void DMA1_IRQHandler(void); 59 | WEAK_ALIAS void IO_BANK0_IRQHandler(void); 60 | WEAK_ALIAS void IO_QSPI_IRQHandler(void); 61 | WEAK_ALIAS void SIO_PROC0_IRQHandler(void); 62 | WEAK_ALIAS void SIO_PROC1_IRQHandler(void); 63 | WEAK_ALIAS void CLOCKS_IRQHandler(void); 64 | WEAK_ALIAS void SPI0_IRQHandler(void); 65 | WEAK_ALIAS void SPI1_IRQHandler(void); 66 | WEAK_ALIAS void UART0_IRQHandler(void); 67 | WEAK_ALIAS void UART1_IRQHandler(void); 68 | WEAK_ALIAS void ADC_FIFO_IRQHandler(void); 69 | WEAK_ALIAS void I2C0_IRQHandler(void); 70 | WEAK_ALIAS void I2C1_IRQHandler(void); 71 | WEAK_ALIAS void RTC_IRQHandler(void); 72 | 73 | extern void _estack(void); 74 | 75 | // IRQ table 76 | __attribute__((section(".vectors"))) void (*vectors[])(void) = { 77 | // Cortex interrupts 78 | _estack, _reset, NMI_Handler, HardFault_Handler, MemManage_Handler, 79 | BusFault_Handler, UsageFault_Handler, 0, 0, 0, 0, SVC_Handler, 80 | DebugMon_Handler, 0, PendSV_Handler, SysTick_Handler, 81 | 82 | // Peripheral interrupts 83 | TIMER0_IRQHandler, TIMER1_IRQHandler, TIMER2_IRQHandler, TIMER3_IRQHandler, 84 | PWM_IRQHandler, USBCTRL_IRQHandler, XIP_IRQHandler, PIO00_IRQHandler, 85 | PIO01_IRQHandler, PIO10_IRQHandler, PIO11_IRQHandler, DMA0_IRQHandler, 86 | DMA1_IRQHandler, IO_BANK0_IRQHandler, IO_QSPI_IRQHandler, 87 | SIO_PROC0_IRQHandler, SIO_PROC1_IRQHandler, CLOCKS_IRQHandler, 88 | SPI0_IRQHandler, SPI1_IRQHandler, UART0_IRQHandler, UART1_IRQHandler, 89 | ADC_FIFO_IRQHandler, I2C0_IRQHandler, I2C1_IRQHandler, RTC_IRQHandler}; 90 | -------------------------------------------------------------------------------- /steps/step-7-webserver/pico-w5500/syscalls.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 Cesanta Software Limited 2 | // All rights reserved 3 | 4 | #include 5 | 6 | #include "hal.h" 7 | 8 | int _fstat(int fd, struct stat *st) { 9 | if (fd < 0) return -1; 10 | st->st_mode = S_IFCHR; 11 | return 0; 12 | } 13 | 14 | void *_sbrk(int incr) { 15 | extern char _end; 16 | static unsigned char *heap = NULL; 17 | unsigned char *prev_heap; 18 | if (heap == NULL) heap = (unsigned char *) &_end; 19 | prev_heap = heap; 20 | heap += incr; 21 | return prev_heap; 22 | } 23 | 24 | int _open(const char *path) { 25 | (void) path; 26 | return -1; 27 | } 28 | 29 | int _close(int fd) { 30 | (void) fd; 31 | return -1; 32 | } 33 | 34 | int _isatty(int fd) { 35 | (void) fd; 36 | return 1; 37 | } 38 | 39 | int _lseek(int fd, int ptr, int dir) { 40 | (void) fd, (void) ptr, (void) dir; 41 | return 0; 42 | } 43 | 44 | void _exit(int status) { 45 | (void) status; 46 | for (;;) asm volatile("BKPT #0"); 47 | } 48 | 49 | void _kill(int pid, int sig) { 50 | (void) pid, (void) sig; 51 | } 52 | 53 | int _getpid(void) { 54 | return -1; 55 | } 56 | 57 | int _write(int fd, char *ptr, int len) { 58 | (void) fd, (void) ptr, (void) len; 59 | if (fd == 1) uart_write_buf(UART0, ptr, (size_t) len); 60 | return -1; 61 | } 62 | 63 | int _read(int fd, char *ptr, int len) { 64 | (void) fd, (void) ptr, (void) len; 65 | return -1; 66 | } 67 | 68 | int _link(const char *a, const char *b) { 69 | (void) a, (void) b; 70 | return -1; 71 | } 72 | 73 | int _unlink(const char *a) { 74 | (void) a; 75 | return -1; 76 | } 77 | 78 | int _stat(const char *path, struct stat *st) { 79 | (void) path, (void) st; 80 | return -1; 81 | } 82 | 83 | int mkdir(const char *path, mode_t mode) { 84 | (void) path, (void) mode; 85 | return -1; 86 | } 87 | -------------------------------------------------------------------------------- /steps/step-7-webserver/pico-w5500/tools/bin2uf2.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 Cesanta Software Limited 2 | // All rights reserved 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | static int fail(const char *fmt, ...) { 11 | va_list ap; 12 | va_start(ap, fmt); 13 | vprintf(fmt, ap); 14 | va_end(ap); 15 | putchar('\n'); 16 | exit(EXIT_FAILURE); 17 | } 18 | 19 | static void wu32(FILE *out, uint32_t value) { 20 | fwrite(&value, 1, sizeof(value), out); 21 | } 22 | 23 | static void addcrc(uint8_t *buf) { 24 | uint32_t crc = ~0U; 25 | for (int j = 0; j < 252; j++) { 26 | uint32_t b = (uint32_t) buf[j] << 24; 27 | for (int i = 0; i < 8; i++) { 28 | crc = (crc << 1) ^ (((crc ^ b) & 0x80000000) ? 0X04c11db7U : 0); 29 | b <<= 1; 30 | } 31 | } 32 | memcpy(buf + 252, &crc, 4); 33 | } 34 | 35 | int main(int argc, char *argv[]) { 36 | FILE *in, *out; 37 | 38 | if (argc != 3) fail("Usage: bin2uf2 FILE.bin FILE.uf2\n"); 39 | if ((in = fopen(argv[1], "rb")) == NULL) fail("open(%s)\n", argv[1]); 40 | if ((out = fopen(argv[2], "w+b")) == NULL) fail("open(%s)\n", argv[2]); 41 | 42 | // Find input file size, and number of blocks 43 | fseek(in, 0, SEEK_END); 44 | size_t size = ftell(in), nblocks = (size + 255) / 256; 45 | fseek(in, 0, SEEK_SET); 46 | 47 | char buf[256]; 48 | size_t n, blockno = 0; 49 | while ((n = fread(buf, 1, sizeof(buf), in)) > 0) { 50 | if (n < sizeof(buf)) memset(buf + n, 0xff, sizeof(buf) - n); 51 | if (blockno == 0) addcrc((uint8_t *) buf); 52 | wu32(out, 0x0a324655); // MAGIC_START_0 53 | wu32(out, 0x9e5d5157); // MAGIC_START_1 54 | wu32(out, 0x00002000); // Flags 55 | wu32(out, 0x10000000 + blockno * sizeof(buf)); // Address 56 | wu32(out, sizeof(buf)); // Size 57 | wu32(out, blockno); // Block number 58 | wu32(out, nblocks); // Total blocks 59 | wu32(out, 0xe48bff56); // Family ID 60 | fwrite(buf, 1, sizeof(buf), out); // Data 61 | for (int i = 0; i < 55; i++) wu32(out, 0); // Remaining 220 bytes 62 | wu32(out, 0x0ab16f30); // MAGIC_END 63 | blockno++; 64 | } 65 | 66 | printf("Written %d bytes into %s\n", (int) ftell(out), argv[2]); 67 | fclose(in); 68 | fclose(out); 69 | 70 | return EXIT_SUCCESS; 71 | } 72 | -------------------------------------------------------------------------------- /templates/blinky/esp32-c3/Makefile: -------------------------------------------------------------------------------- 1 | CFLAGS ?= -W -Wall -Wextra -Werror -Wundef -Wshadow 2 | CFLAGS += -Wdouble-promotion -fno-common -Wconversion 3 | CFLAGS += -march=rv32imc -mabi=ilp32 4 | CFLAGS += -Os -ffunction-sections -fdata-sections -I. 5 | LDFLAGS ?= -Tlink.ld -nostdlib -nostartfiles -Wl,--gc-sections $(EXTRA_LINKFLAGS) 6 | CWD ?= $(realpath $(CURDIR)) 7 | DOCKER ?= docker run -it --rm -v $(CWD):$(CWD) -w $(CWD) mdashnet/riscv 8 | C ?= $(DOCKER) riscv-none-elf-gcc 9 | SOURCES = startup.c main.c 10 | 11 | build: firmware.bin 12 | 13 | firmware.elf: $(SOURCES) hal.h link.ld Makefile 14 | $(C) $(CFLAGS) $(CFLAGS_EXTRA) $(SOURCES) $(LDFLAGS) -o $@ 15 | 16 | firmware.bin: firmware.elf 17 | esputil mkbin firmware.elf $@ 18 | 19 | flash: firmware.bin 20 | esputil flash 0 firmware.bin 21 | 22 | monitor: 23 | esputil monitor 24 | 25 | clean: 26 | @rm -rf *.{bin,elf,map,lst,tgz,zip,hex} firmware* 27 | -------------------------------------------------------------------------------- /templates/blinky/esp32-c3/README.md: -------------------------------------------------------------------------------- 1 | # Blinky 2 | 3 | In order to build this firmware, 4 | 5 | - Install docker 6 | - Install esputil from https://github.com/cpq/esputil 7 | 8 | Use any ESP32-C3 board, for example ESP32-C3-DevKITM-1. 9 | Attach LED to pin 2. Then, 10 | 11 | ```sh 12 | $ export PORT=/dev/SERIAL_PORT 13 | $ make flash monitor 14 | ... 15 | tick: 1001, CPU 160 MHz 16 | tick: 2001, CPU 160 MHz 17 | tick: 3001, CPU 160 MHz 18 | ... 19 | ``` 20 | -------------------------------------------------------------------------------- /templates/blinky/esp32-c3/link.ld: -------------------------------------------------------------------------------- 1 | MEMORY { 2 | iache (rwx) : ORIGIN = 0X4037c000, LENGTH = 16k 3 | iram (rwx) : ORIGIN = 0x40380400, LENGTH = 32k 4 | dram (rw) : ORIGIN = 0x3fc80000 + LENGTH(iram), LENGTH = 128k 5 | } 6 | 7 | _eram = ORIGIN(dram) + LENGTH(dram); 8 | ENTRY(Reset_Handler) 9 | 10 | SECTIONS { 11 | .text : { *(.text) *(.text*) } > iram 12 | 13 | .data : { 14 | . = ALIGN(16); 15 | _sdata = .; 16 | *(.data) 17 | *(.data*) 18 | *(.sdata) 19 | *(.sdata*) 20 | *(.srodata) 21 | *(.srodata*) 22 | *(.rodata) 23 | *(.rodata*) 24 | *(.gnu.linkonce.r.*) 25 | *(.rodata1) 26 | *(.riscv.*) 27 | . = ALIGN(16); 28 | _edata = .; 29 | } > dram 30 | 31 | .bss (NOLOAD): { 32 | . = ALIGN(16); 33 | _sbss = .; 34 | *(.bss) 35 | *(.bss*) 36 | *(.sbss) 37 | *(.sbss*) 38 | *(COMMON) 39 | . = ALIGN(16); 40 | _ebss = .; 41 | } > dram 42 | 43 | . = ALIGN(16); 44 | PROVIDE(end = .); 45 | PROVIDE(_end = .); 46 | } 47 | 48 | PROVIDE(memset = 0x40000354); 49 | PROVIDE(memcpy = 0x40000358); 50 | PROVIDE(memmove = 0x4000035c); 51 | PROVIDE(memcmp = 0x40000360); 52 | PROVIDE(strcpy = 0x40000364); 53 | PROVIDE(strncpy = 0x40000368); 54 | PROVIDE(strcmp = 0x4000036c); 55 | PROVIDE(strncmp = 0x40000370); 56 | PROVIDE(strlen = 0x40000374); 57 | PROVIDE(strstr = 0x40000378); 58 | PROVIDE(bzero = 0x4000037c); 59 | PROVIDE(isalnum = 0x40000388); 60 | PROVIDE(isalpha = 0x4000038c); 61 | PROVIDE(isascii = 0x40000390); 62 | PROVIDE(isblank = 0x40000394); 63 | PROVIDE(iscntrl = 0x40000398); 64 | PROVIDE(isdigit = 0x4000039c); 65 | PROVIDE(islower = 0x400003a0); 66 | PROVIDE(isgraph = 0x400003a4); 67 | PROVIDE(isprint = 0x400003a8); 68 | PROVIDE(ispunct = 0x400003ac); 69 | PROVIDE(isspace = 0x400003b0); 70 | PROVIDE(isupper = 0x400003b4); 71 | PROVIDE(toupper = 0x400003b8); 72 | PROVIDE(tolower = 0x400003bc); 73 | PROVIDE(toascii = 0x400003c0); 74 | PROVIDE(memccpy = 0x400003c4); 75 | PROVIDE(memchr = 0x400003c8); 76 | PROVIDE(memrchr = 0x400003cc); 77 | PROVIDE(strcasecmp = 0x400003d0); 78 | PROVIDE(strcasestr = 0x400003d4); 79 | PROVIDE(strcat = 0x400003d8); 80 | PROVIDE(strdup = 0x400003dc); 81 | PROVIDE(strchr = 0x400003e0); 82 | PROVIDE(strcspn = 0x400003e4); 83 | PROVIDE(strcoll = 0x400003e8); 84 | PROVIDE(strlcat = 0x400003ec); 85 | PROVIDE(strlcpy = 0x400003f0); 86 | PROVIDE(strlwr = 0x400003f4); 87 | PROVIDE(strncasecmp = 0x400003f8); 88 | PROVIDE(strncat = 0x400003fc); 89 | PROVIDE(strndup = 0x40000400); 90 | PROVIDE(strnlen = 0x40000404); 91 | PROVIDE(strrchr = 0x40000408); 92 | PROVIDE(strsep = 0x4000040c); 93 | PROVIDE(strspn = 0x40000410); 94 | PROVIDE(strtok_r = 0x40000414); 95 | PROVIDE(strupr = 0x40000418); 96 | PROVIDE(longjmp = 0x4000041c); 97 | PROVIDE(setjmp = 0x40000420); 98 | PROVIDE(abs = 0x40000424); 99 | PROVIDE(div = 0x40000428); 100 | PROVIDE(labs = 0x4000042c); 101 | PROVIDE(ldiv = 0x40000430); 102 | PROVIDE(qsort = 0x40000434); 103 | PROVIDE(rand_r = 0x40000438); 104 | PROVIDE(rand = 0x4000043c); 105 | PROVIDE(srand = 0x40000440); 106 | PROVIDE(utoa = 0x40000444); 107 | PROVIDE(itoa = 0x40000448); 108 | PROVIDE(atoi = 0x4000044c); 109 | PROVIDE(atol = 0x40000450); 110 | PROVIDE(strtol = 0x40000454); 111 | PROVIDE(strtoul = 0x40000458); 112 | PROVIDE(printf = 0x40000040); 113 | PROVIDE(__udivdi3 = 0x400008ac); 114 | PROVIDE(__umoddi3 = 0x400008bc); 115 | 116 | /* 117 | PROVIDE(__sprint_r = 0x40000480); 118 | PROVIDE(_fiprintf_r = 0x40000484); 119 | PROVIDE(_fprintf_r = 0x40000488); 120 | PROVIDE(_printf_common = 0x4000048c); 121 | PROVIDE(_printf_i = 0x40000490); 122 | PROVIDE(_vfiprintf_r = 0x40000494); 123 | PROVIDE(_vfprintf_r = 0x40000498); 124 | PROVIDE(fiprintf = 0x4000049c); 125 | PROVIDE(fprintf = 0x400004a0); 126 | PROVIDE(XXXprintf = 0x400004a4); 127 | PROVIDE(vfiprintf = 0x400004a8); 128 | PROVIDE(vfprintf = 0x400004ac); 129 | */ 130 | PROVIDE(uart_tx_one_char = 0x40000068); 131 | PROVIDE(uart_tx_one_char2 = 0x4000006c); 132 | PROVIDE(uart_rx_one_char = 0x40000070); 133 | PROVIDE(uart_rx_one_char_block = 0x40000074); 134 | PROVIDE(uart_rx_readbuff = 0x40000078); 135 | 136 | /* 137 | PROVIDE(esprv_intc_int_set_priority = 0x400005e0); 138 | PROVIDE(esprv_intc_int_set_threshold = 0x400005e4); 139 | PROVIDE(esprv_intc_int_enable = 0x400005e8); 140 | PROVIDE(esprv_intc_int_disable = 0x400005ec); 141 | */ 142 | 143 | esprv_intc_int_set_priority = 0x400005e0; 144 | esprv_intc_int_set_threshold = 0x400005e4; 145 | esprv_intc_int_enable = 0x400005e8; 146 | esprv_intc_int_disable = 0x400005ec; 147 | esprv_intc_int_set_type = 0x400005f0; 148 | intr_matrix_set = 0x400005f4; 149 | ets_intr_lock = 0x400005f8; 150 | ets_intr_unlock = 0x400005fc; 151 | 152 | PROVIDE(__divdi3 = 0x400007b4); 153 | -------------------------------------------------------------------------------- /templates/blinky/esp32-c3/main.c: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2022-2023 Cesanta Software Limited 2 | // SPDX-License-Identifier: MIT 3 | 4 | #include "hal.h" 5 | 6 | #define BLINK_PERIOD_MS 500 // LED blinking period in millis 7 | #define LOG_PERIOD_MS 1000 // Info log period in millis 8 | 9 | void SystemInit(void) { // Called automatically by startup code 10 | clock_init(); 11 | } 12 | 13 | static volatile uint64_t s_ticks; // Milliseconds since boot 14 | void SysTick_Handler(void) { // SyStick IRQ handler, triggered every 1ms 15 | s_ticks++; 16 | } 17 | 18 | static void led_task(void) { // Blink LED every BLINK_PERIOD_MS 19 | static uint64_t timer = 0; 20 | if (timer_expired(&timer, BLINK_PERIOD_MS, s_ticks)) { 21 | gpio_toggle(LED_PIN); 22 | } 23 | } 24 | 25 | static void log_task(void) { // Print a log every LOG_PERIOD_MS 26 | static uint64_t timer = 0; 27 | if (timer_expired(&timer, LOG_PERIOD_MS, s_ticks)) { 28 | printf("tick: %5lu, CPU %lu MHz\n", (unsigned long) s_ticks, 29 | clock_sys_freq() / 1000000); 30 | } 31 | } 32 | 33 | static void button_handler(void *param) { 34 | uint16_t pin = (uint16_t) (uintptr_t) param; 35 | printf("Button %u %s\n", pin, gpio_read(pin) ? "pressed" : "released"); 36 | } 37 | 38 | int main(void) { 39 | gpio_input(BUTTON_PIN); 40 | gpio_output(LED_PIN); 41 | uart_init(UART_DEBUG, 115200); 42 | 43 | gpio_set_irq_handler(BUTTON_PIN, button_handler, (void *) BUTTON_PIN); 44 | 45 | for (;;) { 46 | led_task(); 47 | log_task(); 48 | } 49 | 50 | return 0; 51 | } 52 | -------------------------------------------------------------------------------- /templates/blinky/esp32-c3/startup.c: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2021-2023 Cesanta Software Limited 2 | // SPDX-License-Identifier: MIT 3 | 4 | #include "hal.h" 5 | 6 | extern int main(void); 7 | extern void SystemInit(void); 8 | extern char _sbss, _ebss, _end, _eram; 9 | 10 | static char *s_heap_start, *s_heap_end, *s_brk; 11 | 12 | void *sbrk(int diff) { 13 | char *old = s_brk; 14 | if (&s_brk[diff] > s_heap_end) return NULL; 15 | s_brk += diff; 16 | return old; 17 | } 18 | 19 | // Mark it weak - allow user to override it 20 | __attribute__((weak)) void SysTick_Handler(void) { 21 | } 22 | 23 | // C handlers associated with CPU interrupts, with their arguments 24 | struct irq_data g_irq_data[32]; 25 | 26 | // Attribute interrupt makes this function to: 27 | // 1. Return with mret instruction 28 | // 2. Save/restore all used registers 29 | __attribute__((interrupt)) void irq_handler(void) { 30 | unsigned long mcause = CSR_READ(mcause), mepc = CSR_READ(mepc); 31 | //printf("mcause %lx\n", mcause); 32 | if ((mcause & BIT(31))) { // Interrupt 33 | uint32_t no = mcause << 1 >> 1; // Interrupt number 34 | if (no < sizeof(g_irq_data) / sizeof(g_irq_data[0])) { 35 | struct irq_data *d = &g_irq_data[no]; 36 | if (d->clr) d->clr(d->clr_arg); // Clear interrupt 37 | if (d->fn) d->fn(d->arg); // Call user handler 38 | } 39 | } else { // Exception 40 | CSR_WRITE(mepc, mepc + 4); 41 | } 42 | } 43 | 44 | // Vector table. Point all entries to the irq_handler() 45 | __attribute__((aligned(256))) void irqtab(void) { 46 | asm(".rept 32"); // 32 entries 47 | asm("j irq_handler"); // Jump to irq_handler() 48 | asm(".endr"); 49 | } 50 | 51 | // ESP32C3 lets us bind peripheral interrupts to the CPU interrupts, 1..31 52 | int cpu_alloc_interrupt(uint8_t prio /* 1..15 */) { 53 | static uint32_t allocated; 54 | for (uint8_t no = 1; no < 31; no++) { 55 | if (allocated & BIT(no)) continue; // Used, try the next one 56 | allocated |= BIT(no); // Claim this one 57 | REG(C3_INTERRUPT)[0x104 / 4] |= BIT(no); // CPU_INT_ENA 58 | REG(C3_INTERRUPT)[0x118 / 4 + no - 1] = prio; // CPU_INT_PRI_N 59 | // REG(C3_INTERRUPT)[0x108 / 4] |= BIT(no); // Edge 60 | printf("Allocated CPU IRQ %d, prio %u\n", no, prio); 61 | return no; 62 | } 63 | return -1; 64 | } 65 | 66 | static void systimer_clear_interrupt(void *param) { 67 | (void) param; 68 | SYSTIMER->INT_CLR = 7U; // Clear all 3 units 69 | } 70 | 71 | // Systimer is clocked by 16Mhz. Setup alarm and bind it to the CPU IRQ 72 | static void systick_init(void) { 73 | SYSTIMER->TARGET0_CONF = BIT(30) | 16000; // Set period 74 | SYSTIMER->COMP0_LOAD = BIT(0); // Reload period 75 | SYSTIMER->CONF |= BIT(24); // Enable comparator 0 76 | SYSTIMER->INT_ENA |= 7U; // Enable interrupts on all targets 77 | 78 | int no = cpu_alloc_interrupt(1); 79 | g_irq_data[no].fn = (void (*)(void *)) SysTick_Handler; 80 | g_irq_data[no].clr = systimer_clear_interrupt; 81 | REG(C3_INTERRUPT)[0xfc / 4] |= BIT(5) | BIT(6) | BIT(7); // Enable CPU IRQ 82 | //REG(C3_INTERRUPT)[0xfc / 4] |= BIT(5); // Enable CPU IRQ 83 | REG(C3_INTERRUPT)[0x94 / 4] = (uint8_t) no; // LAST: Map systimer IRQ to CPU 84 | } 85 | 86 | void Reset_Handler(void) { 87 | s_heap_start = s_brk = &_end, s_heap_end = &_eram; 88 | for (char *p = &_sbss; p < &_ebss;) *p++ = '\0'; 89 | CSR_WRITE(mtvec, irqtab); // Route all interrupts to the irq_handler() 90 | soc_init(); 91 | systick_init(); 92 | SystemInit(); 93 | main(); 94 | for (;;) (void) 0; 95 | } 96 | -------------------------------------------------------------------------------- /templates/blinky/frdm-mcxw71/Makefile: -------------------------------------------------------------------------------- 1 | CFLAGS ?= -W -Wall -Wextra -Werror -Wundef -Wshadow -Wdouble-promotion -Wformat-truncation -fno-common 2 | CFLAGS += -g3 -Os -ffunction-sections -fdata-sections -Wno-shadow 3 | CFLAGS += -I. -Iinclude -Icmsis_core/CMSIS/Core/Include 4 | CFLAGS += -Icmsis_mcxw71/devices/MCXW716C 5 | CFLAGS += -Icmsis_mcxw71/devices/MCXW716C/periph2 6 | CFLAGS += -DCPU_MCXW716CMFTA -D__ATOLLIC__ 7 | CFLAGS += -mcpu=cortex-m33 -mthumb -mfpu=fpv5-sp-d16 -mfloat-abi=hard $(CFLAGS_EXTRA) 8 | 9 | LDFLAGS ?= -Tlink.ld -nostartfiles -nostdlib --specs nano.specs -lc -lgcc -Wl,--gc-sections -Wl,-Map=$@.map 10 | SOURCES = main.c syscalls.c startup.c 11 | 12 | #LDFLAGS ?= -Tcmsis_mcxw71/devices/MCXW716C/gcc/mcxw716_flash.ld -nostartfiles -nostdlib --specs nano.specs -lc -lgcc -Wl,--gc-sections -Wl,-Map=$@.map 13 | #SOURCES += cmsis_mcxw71/devices/MCXW716C/system_MCXW716C.c 14 | #SOURCES += cmsis_mcxw71/devices/MCXW716C/gcc/startup_MCXW716C.S 15 | 16 | build: firmware.bin 17 | 18 | firmware.elf: cmsis_core cmsis_mcxw71 hal.h link.ld Makefile $(SOURCES) 19 | arm-none-eabi-gcc $(SOURCES) $(CFLAGS) $(CFLAGS_EXTRA) $(LDFLAGS) -o $@ 20 | 21 | firmware.bin: firmware.elf 22 | arm-none-eabi-objcopy -O binary $< $@ 23 | 24 | flash: firmware.bin 25 | (echo LoadFile $< 0; echo exit) | JLinkExe -nogui 1 -device mcxw716 -if swd -AutoConnect 1 -speed 4000 26 | 27 | cmsis_core: 28 | git clone -q -c advice.detachedHead=false --depth 1 -b 5.9.0 https://github.com/ARM-software/CMSIS_5 $@ 29 | 30 | cmsis_mcxw71: 31 | curl -sL https://mcuxpresso.nxp.com/cmsis_pack/repo/NXP.MCXW716C_DFP.25.03.00.pack -o x.zip && mkdir -p $@ && (cd $@ && unzip -q ../x.zip && rm -f ../x.zip) 32 | 33 | clean: 34 | rm -rf firmware.* cmsis_* 35 | -------------------------------------------------------------------------------- /templates/blinky/frdm-mcxw71/hal.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025 Cesanta Software Limited 2 | // SPDX-License-Identifier: MIT 3 | 4 | #pragma once 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include "MCXW716C.h" 12 | #include "MCXW716C_COMMON.h" 13 | 14 | #define BIT(x) (1UL << (x)) 15 | #define CLRSET(R, CLEARMASK, SETMASK) (R) = ((R) & ~(CLEARMASK)) | (SETMASK) 16 | #define PIN(bank, num) ((((bank) - 'A') << 8) | (num)) 17 | #define PINNO(pin) (pin & 255) 18 | #define PINBANK(pin) (pin >> 8) 19 | 20 | #define SYS_FREQUENCY 6000000 21 | 22 | static inline void spin(volatile uint32_t count) { 23 | while (count--) (void) 0; 24 | } 25 | 26 | enum { GPIO_MODE_INPUT, GPIO_MODE_OUTPUT, GPIO_MODE_AF, GPIO_MODE_ANALOG }; 27 | enum { GPIO_OTYPE_PUSH_PULL, GPIO_OTYPE_OPEN_DRAIN }; 28 | enum { GPIO_SPEED_LOW, GPIO_SPEED_MEDIUM, GPIO_SPEED_HIGH, GPIO_SPEED_INSANE }; 29 | enum { GPIO_PULL_NONE, GPIO_PULL_UP, GPIO_PULL_DOWN }; 30 | 31 | static inline GPIO_Type *gpio_bank(uint16_t pin) { 32 | uint8_t bank = PINBANK(pin); 33 | return (GPIO_Type *) (bank == 0 ? GPIOA 34 | : bank == 1 ? GPIOB 35 | : bank == 2 ? GPIOC 36 | : bank == 3 ? GPIOD 37 | : 0); 38 | } 39 | 40 | static inline void gpio_toggle(uint16_t pin) { 41 | GPIO_Type *gpio = gpio_bank(pin); 42 | gpio->PTOR |= BIT(PINNO(pin)); 43 | } 44 | 45 | static inline bool gpio_read(uint16_t pin) { 46 | GPIO_Type *gpio = gpio_bank(pin); 47 | return gpio->PDR[PINNO(pin)]; 48 | } 49 | 50 | static inline void gpio_write(uint16_t pin, bool val) { 51 | GPIO_Type *gpio = gpio_bank(pin); 52 | if (val) { 53 | // gpio->PSOR |= BIT(PINNO(pin)); 54 | gpio->PDR[PINNO(pin)] = 1U; 55 | } else { 56 | // gpio->PCOR |= BIT(PINNO(pin)); 57 | gpio->PDR[PINNO(pin)] = 0; 58 | } 59 | } 60 | 61 | static inline void gpio_init(uint16_t pin, uint8_t mode, uint8_t type, 62 | uint8_t speed, uint8_t pull, uint8_t af) { 63 | GPIO_Type *gpio = gpio_bank(pin); 64 | (void) pin, (void) mode, (void) type, (void) speed, (void) pull, (void) af; 65 | #if 0 66 | if (gpio == GPIOA) { 67 | CLRSET(MRCC->MRCC_GPIOA, MRCC_MRCC_GPIOA_CC_MASK, 1U); 68 | } else if (gpio == GPIOB) { 69 | CLRSET(MRCC->MRCC_GPIOB, MRCC_MRCC_GPIOB_CC_MASK, 1U); 70 | } else if (gpio == GPIOC) { 71 | CLRSET(MRCC->MRCC_GPIOC, MRCC_MRCC_GPIOC_CC_MASK, 1U); 72 | } 73 | __ISB(); 74 | __DSB(); 75 | #endif 76 | if (mode == GPIO_MODE_OUTPUT) { 77 | // gpio->PDOR &= ~BIT(PINNO(pin)); 78 | gpio->PDOR |= BIT(PINNO(pin)); 79 | gpio->PDDR |= BIT(PINNO(pin)); 80 | } else { 81 | gpio->PDDR &= ~BIT(PINNO(pin)); 82 | } 83 | } 84 | static inline void gpio_input(uint16_t pin) { 85 | gpio_init(pin, GPIO_MODE_INPUT, GPIO_OTYPE_PUSH_PULL, GPIO_SPEED_HIGH, 86 | GPIO_PULL_NONE, 0); 87 | } 88 | static inline void gpio_output(uint16_t pin) { 89 | gpio_init(pin, GPIO_MODE_OUTPUT, GPIO_OTYPE_PUSH_PULL, GPIO_SPEED_HIGH, 90 | GPIO_PULL_NONE, 0); 91 | } 92 | 93 | static inline void uart_init(LPUART_Type *uart, unsigned long baud) { 94 | uint32_t val = 0x80000000U; // Module present 95 | val |= 0x40000000U; // Release from reset 96 | val |= 1U; // Enable clock 97 | val |= 2U << 4; // Clock mux: FRO-6M 98 | if (uart == LPUART0) { 99 | MRCC->MRCC_LPUART0 = val; 100 | } else if (uart == LPUART1) { 101 | MRCC->MRCC_LPUART1 = val; 102 | } 103 | (void) baud; 104 | } 105 | 106 | static inline void uart_write_byte(LPUART_Type *uart, uint8_t byte) { 107 | // while ((uart->FIFOSTAT & UART_FIFOSTAT_TXNOTFULL_MASK) == 0) spin(1); 108 | return; 109 | uart->FIFO = byte; 110 | } 111 | static inline void uart_write_buf(LPUART_Type *uart, char *buf, size_t len) { 112 | while (len-- > 0) uart_write_byte(uart, *(uint8_t *) buf++); 113 | } 114 | 115 | // t: expiration time, prd: period, now: current time. Return true if expired 116 | static inline bool timer_expired(volatile uint64_t *t, uint64_t prd, 117 | uint64_t now) { 118 | if (now + prd < *t) *t = 0; // Time wrapped? Reset timer 119 | if (*t == 0) *t = now + prd; // Firt poll? Set expiration 120 | if (*t > now) return false; // Not expired yet, return 121 | *t = (now - *t) > prd ? now + prd : *t + prd; // Next expiration time 122 | return true; // Expired, return true 123 | } 124 | -------------------------------------------------------------------------------- /templates/blinky/frdm-mcxw71/link.ld: -------------------------------------------------------------------------------- 1 | ENTRY(Reset_Handler); 2 | MEMORY { 3 | flash(rx) : ORIGIN = 0x00000000, LENGTH = 1024k 4 | sram(rwx) : ORIGIN = 0x20000000, LENGTH = 112k 5 | } 6 | STACK_SIZE = DEFINED(__stack_size__) ? __stack_size__ : 0x0800; 7 | __StackTop = ORIGIN(sram) + LENGTH(sram); /* stack points to end of SRAM */ 8 | __StackLimit = __StackTop - STACK_SIZE; 9 | 10 | SECTIONS { 11 | .vectors : { KEEP(*(.isr_vector)) } > flash 12 | .text : { *(.text* .text.*) } > flash 13 | .rodata : { *(.rodata*) } > flash 14 | __etext = .; 15 | .data : { __data_start__ = .; *(.first_data) *(.data SORT(.data.*)) __data_end__ = .; } > sram AT > flash 16 | _sidata = LOADADDR(.data); 17 | .bss : { __bss_start__ = .; *(.bss SORT(.bss.*) COMMON) __bss_end__ = .; } > sram 18 | . = ALIGN(8); 19 | __end__ = .; 20 | } 21 | -------------------------------------------------------------------------------- /templates/blinky/frdm-mcxw71/main.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025 Cesanta Software Limited 2 | // SPDX-License-Identifier: MIT 3 | 4 | #include "hal.h" 5 | 6 | #define UART_DEBUG LPUART1 7 | 8 | #define LED1_PIN PIN('A', 19) // Green 9 | //#define LED2_PIN PIN('A', 20) // Blue 10 | #define LED2_PIN PIN('C', 1) // Blue 11 | #define LED3_PIN PIN('A', 21) // Red 12 | 13 | #define BLINK_PERIOD_MS 500 // LED blinking period in millis 14 | #define LOG_PERIOD_MS 1000 // Info log period in millis 15 | 16 | // Retarget standard IO functions ouput (printf, fwrite, etc) to use UART 17 | int _write(int fd, char *ptr, int len) { 18 | if (fd == 1) uart_write_buf(UART_DEBUG, ptr, (size_t) len); 19 | return len; 20 | } 21 | 22 | static volatile uint64_t s_ticks; // Milliseconds since boot 23 | void SysTick_Handler(void) { // SyStick IRQ handler, triggered every 1ms 24 | s_ticks++; 25 | } 26 | 27 | static void led_task(void) { // Blink LED every BLINK_PERIOD_MS 28 | static uint64_t timer = 0; 29 | if (timer_expired(&timer, BLINK_PERIOD_MS, s_ticks)) { 30 | gpio_toggle(LED2_PIN); 31 | } 32 | } 33 | 34 | static void log_task(void) { // Print a log every LOG_PERIOD_MS 35 | static uint64_t timer = 0; 36 | if (timer_expired(&timer, LOG_PERIOD_MS, s_ticks)) { 37 | printf("tick: %5lu, CPU %lu MHz\n", (unsigned long) s_ticks, 38 | SystemCoreClock / 1000000); 39 | } 40 | } 41 | 42 | uint32_t SystemCoreClock; 43 | void SystemInit(void) { 44 | // set CP10, CP11 Full Access in Secure and Non-secure mode 45 | SCB->CPACR |= ((3UL << 10 * 2) | (3UL << 11 * 2)); 46 | SystemCoreClock = SYS_FREQUENCY; // Required by CMSIS 47 | SysTick_Config(SystemCoreClock / 1000); // Sys tick every 1ms 48 | } 49 | 50 | int main(void) { 51 | MRCC_Type *x = MRCC; 52 | gpio_output(LED1_PIN); 53 | gpio_write(LED1_PIN, 1); 54 | 55 | gpio_output(LED2_PIN); 56 | gpio_write(LED2_PIN, 1); 57 | 58 | printf("%p", x); 59 | 60 | uart_init(UART_DEBUG, 115200); 61 | 62 | for (;;) { 63 | led_task(); 64 | log_task(); 65 | } 66 | 67 | return 0; 68 | } 69 | -------------------------------------------------------------------------------- /templates/blinky/frdm-mcxw71/syscalls.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | __attribute__((weak)) int _fstat(int fd, struct stat *st) { 4 | if (fd < 0) return -1; 5 | st->st_mode = S_IFCHR; 6 | return 0; 7 | } 8 | 9 | extern unsigned char __StackTop, __end__; 10 | __attribute__((weak)) void *_sbrk(int incr) { 11 | static unsigned char *heap = &__end__; 12 | unsigned char *prev_heap = heap; 13 | heap += incr; 14 | return prev_heap; 15 | } 16 | 17 | __attribute__((weak)) int _open(const char *path) { 18 | (void) path; 19 | return -1; 20 | } 21 | 22 | __attribute__((weak)) int _close(int fd) { 23 | (void) fd; 24 | return -1; 25 | } 26 | 27 | __attribute__((weak)) int _isatty(int fd) { 28 | (void) fd; 29 | return 1; 30 | } 31 | 32 | __attribute__((weak)) int _lseek(int fd, int ptr, int dir) { 33 | (void) fd, (void) ptr, (void) dir; 34 | return 0; 35 | } 36 | 37 | __attribute__((weak)) void _exit(int status) { 38 | (void) status; 39 | for (;;) asm volatile("BKPT #0"); 40 | } 41 | 42 | __attribute__((weak)) void _kill(int pid, int sig) { 43 | (void) pid, (void) sig; 44 | } 45 | 46 | __attribute__((weak)) int _getpid(void) { 47 | return -1; 48 | } 49 | 50 | __attribute__((weak)) int _write(int fd, char *ptr, int len) { 51 | (void) fd, (void) ptr, (void) len; 52 | return -1; 53 | } 54 | 55 | __attribute__((weak)) int _read(int fd, char *ptr, int len) { 56 | (void) fd, (void) ptr, (void) len; 57 | return -1; 58 | } 59 | 60 | __attribute__((weak)) int _link(const char *a, const char *b) { 61 | (void) a, (void) b; 62 | return -1; 63 | } 64 | 65 | __attribute__((weak)) int _unlink(const char *a) { 66 | (void) a; 67 | return -1; 68 | } 69 | 70 | __attribute__((weak)) int _stat(const char *path, struct stat *st) { 71 | (void) path, (void) st; 72 | return -1; 73 | } 74 | 75 | __attribute__((weak)) int mkdir(const char *path, mode_t mode) { 76 | (void) path, (void) mode; 77 | return -1; 78 | } 79 | 80 | __attribute__((weak)) void _init(void) { 81 | } 82 | -------------------------------------------------------------------------------- /templates/blinky/nRF52840dk/Makefile: -------------------------------------------------------------------------------- 1 | CFLAGS ?= -W -Wall -Wextra -Werror -Wundef -Wshadow -Wdouble-promotion -Wformat-truncation -fno-common 2 | CFLAGS += -g3 -Os -ffunction-sections -fdata-sections -Wno-shadow 3 | CFLAGS += -mcpu=cortex-m4 4 | SOURCES = main.c 5 | 6 | build: 7 | mkdir build 8 | arm-none-eabi-gcc $(SOURCES) -c $(CFLAGS) -o ./build/main.o 9 | arm-none-eabi-gcc -T link.ld -nostdlib ./build/main.o -o ./build/firmware.elf 10 | arm-none-eabi-objcopy -Oihex ./build/firmware.elf ./build/firmware.hex 11 | 12 | flash: 13 | nrfjprog -f NRF52 --program ./build/firmware.hex --recover --verify 14 | nrfjprog -f NRF52 --reset 15 | 16 | clean: 17 | rm -rf ./build 18 | -------------------------------------------------------------------------------- /templates/blinky/nRF52840dk/hal.h: -------------------------------------------------------------------------------- 1 | #define P0 ((struct gpio *) 0x50000000) 2 | #define P1 ((struct gpio *) 0x50000300) 3 | 4 | #define BUILT_IN_BUTTON_4_PIN 25 5 | #define BUILT_IN_BUTTON_3_PIN 24 6 | #define BUILT_IN_BUTTON_2_PIN 12 7 | #define BUILT_IN_BUTTON_1_PIN 11 8 | #define BUILT_IN_LED_1_PIN 13 9 | #define BUILT_IN_LED_2_PIN 14 10 | #define BUILT_IN_LED_3_PIN 15 11 | #define BUILT_IN_LED_4_PIN 16 12 | 13 | enum { GPIO_MODE_INPUT, GPIO_MODE_OUTPUT }; 14 | enum { LOW, HIGH }; 15 | 16 | struct gpio { 17 | volatile uint32_t RESERVED[321], OUT, OUTSET, OUTCLR, IN, DIR, DIRSET, DIRCLR, LATCH, DETECTMODE, RESERVED_SECOND[118], PIN_CNF[32]; 18 | }; 19 | 20 | void set_gpio_mode(struct gpio * port, int pin, int mode, int pull) { 21 | if (pull == 0) { 22 | port->PIN_CNF[pin] = mode << 0; 23 | } 24 | else { 25 | port->PIN_CNF[pin] = mode << 0 | pull << 2; 26 | } 27 | } 28 | 29 | int gpio_read(struct gpio * port, int pin) { 30 | uint32_t button = ~(port->IN); 31 | if (button & (1 << pin)) { 32 | return 1; 33 | } 34 | else { 35 | return 0; 36 | } 37 | } 38 | void gpio_write(struct gpio * port, int pin, int value) { 39 | if (value == 0) { 40 | port->OUTSET = 1 << pin; 41 | } 42 | else { 43 | port->OUTCLR = 1 << pin; 44 | } 45 | } 46 | 47 | static inline void spin(volatile uint32_t count) { 48 | while (count--) (void) 0; 49 | } 50 | -------------------------------------------------------------------------------- /templates/blinky/nRF52840dk/link.ld: -------------------------------------------------------------------------------- 1 | ENTRY(_reset); 2 | 3 | MEMORY { 4 | flash(rx) : ORIGIN = 0x00000000, LENGTH = 1M 5 | sram(rwx) : ORIGIN = 0x20000000, LENGTH = 256k 6 | } 7 | _estack = ORIGIN(sram) + LENGTH(sram); 8 | 9 | SECTIONS { 10 | .vectors : { KEEP(*(.vectors)) } > flash 11 | .text : { *(.text*) } > flash 12 | .rodata : { *(.rodata*) } > flash 13 | 14 | .data : { 15 | _sdata = .; /* .data section start */ 16 | *(.first_data) 17 | *(.data SORT(.data.*)) 18 | _edata = .; /* .data section end */ 19 | } > sram AT > flash 20 | _sidata = LOADADDR(.data); 21 | 22 | .bss : { 23 | _sbss = .; /* .bss section start */ 24 | *(.bss SORT(.bss.*) COMMON) 25 | _ebss = .; /* .bss section end */ 26 | } > sram 27 | 28 | . = ALIGN(8); 29 | _end = .; /* for cmsis_gcc.h */ 30 | } 31 | -------------------------------------------------------------------------------- /templates/blinky/nRF52840dk/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "hal.h" 3 | 4 | int main(void) { 5 | set_gpio_mode(P0, BUILT_IN_LED_1_PIN, GPIO_MODE_OUTPUT, 0); 6 | set_gpio_mode(P0, BUILT_IN_LED_2_PIN, GPIO_MODE_OUTPUT, 0); 7 | set_gpio_mode(P0, BUILT_IN_LED_3_PIN, GPIO_MODE_OUTPUT, 0); 8 | set_gpio_mode(P0, BUILT_IN_LED_4_PIN, GPIO_MODE_OUTPUT, 0); 9 | 10 | while (1) { 11 | gpio_write(P0, BUILT_IN_LED_1_PIN, HIGH); 12 | gpio_write(P0, BUILT_IN_LED_2_PIN, HIGH); 13 | gpio_write(P0, BUILT_IN_LED_3_PIN, HIGH); 14 | gpio_write(P0, BUILT_IN_LED_4_PIN, HIGH); 15 | spin(9999999); 16 | gpio_write(P0, BUILT_IN_LED_4_PIN, LOW); 17 | gpio_write(P0, BUILT_IN_LED_3_PIN, LOW); 18 | gpio_write(P0, BUILT_IN_LED_2_PIN, LOW); 19 | gpio_write(P0, BUILT_IN_LED_1_PIN, LOW); 20 | spin(9999999); 21 | } 22 | } 23 | 24 | // Startup code 25 | __attribute__((naked, noreturn)) void _reset(void) { 26 | // memset .bss to zero, and copy .data section to RAM region 27 | extern long _sbss, _ebss, _sdata, _edata, _sidata; 28 | for (long *dst = &_sbss; dst < &_ebss; dst++) *dst = 0; 29 | for (long *dst = &_sdata, *src = &_sidata; dst < &_edata;) *dst++ = *src++; 30 | 31 | main(); // Call main() 32 | for (;;) (void) 0; // Infinite loop in the case if main() returns 33 | } 34 | 35 | extern void _estack(void); // Defined in link.ld 36 | 37 | // 16 standard and 42 nRF-specific handlers 38 | __attribute__((section(".vectors"))) void (*const tab[16 + 42])(void) = { 39 | _estack, _reset}; 40 | 41 | -------------------------------------------------------------------------------- /templates/blinky/nucleo-f429zi/Makefile: -------------------------------------------------------------------------------- 1 | CFLAGS ?= -W -Wall -Wextra -Werror -Wundef -Wshadow -Wdouble-promotion 2 | CFLAGS += -Wformat-truncation -fno-common -Wconversion 3 | CFLAGS += -g3 -Os -ffunction-sections -fdata-sections 4 | CFLAGS += -I. -Icmsis_core/CMSIS/Core/Include -Icmsis_f4/Include 5 | CFLAGS += -mcpu=cortex-m4 -mthumb -mfloat-abi=hard -mfpu=fpv4-sp-d16 6 | LDFLAGS ?= -Tlink.ld -nostartfiles -nostdlib --specs nano.specs -lc -lgcc -Wl,--gc-sections -Wl,-Map=$@.map 7 | 8 | SOURCES = main.c syscalls.c 9 | SOURCES += cmsis_f4/Source/Templates/gcc/startup_stm32f429xx.s # ST startup file. Compiler-dependent! 10 | 11 | ifeq ($(OS),Windows_NT) 12 | RM = cmd /C del /Q /F 13 | else 14 | RM = rm -rf 15 | endif 16 | 17 | build: firmware.bin 18 | 19 | firmware.elf: cmsis_core cmsis_f4 hal.h link.ld Makefile $(SOURCES) 20 | arm-none-eabi-gcc $(SOURCES) $(CFLAGS) $(CFLAGS_EXTRA) $(LDFLAGS) -o $@ 21 | 22 | firmware.bin: firmware.elf 23 | arm-none-eabi-objcopy -O binary $< $@ 24 | 25 | flash: firmware.bin 26 | st-flash --reset write $< 0x8000000 27 | 28 | cmsis_core: 29 | git clone --depth 1 -b 5.9.0 https://github.com/ARM-software/CMSIS_5 $@ 30 | 31 | cmsis_f4: 32 | git clone --depth 1 -b v2.6.8 https://github.com/STMicroelectronics/cmsis_device_f4 $@ 33 | 34 | clean: 35 | $(RM) firmware.* cmsis_* 36 | 37 | # Automated test via https://vcon.io/automated-firmware-tests/. Set VCON_API_KEY and update DEVICE_URL 38 | DEVICE_URL ?= https://dash.vcon.io/api/v3/devices/2 39 | update: CFLAGS += -DUART_DEBUG=USART1 40 | update: firmware.bin 41 | curl --fail-with-body -su :$(VCON_API_KEY) $(DEVICE_URL)/ota --data-binary @$< 42 | test: update 43 | curl --fail-with-body -su :$(VCON_API_KEY) $(DEVICE_URL)/tx?t=5 | tee /tmp/output.txt 44 | egrep '^tick:.*CPU 180 MHz' /tmp/output.txt 45 | watch: update 46 | curl --fail-with-body -su :$(VCON_API_KEY) $(DEVICE_URL)/tx?t=999 47 | -------------------------------------------------------------------------------- /templates/blinky/nucleo-f429zi/link.ld: -------------------------------------------------------------------------------- 1 | ENTRY(Reset_Handler); 2 | MEMORY { 3 | flash(rx) : ORIGIN = 0x08000000, LENGTH = 2048k 4 | sram(rwx) : ORIGIN = 0x20000000, LENGTH = 192k /* remaining 64k in a separate address space */ 5 | } 6 | _estack = ORIGIN(sram) + LENGTH(sram); /* stack points to end of SRAM */ 7 | 8 | SECTIONS { 9 | .vectors : { KEEP(*(.isr_vector)) } > flash 10 | .text : { *(.text*) } > flash 11 | .rodata : { *(.rodata*) } > flash 12 | 13 | .data : { 14 | _sdata = .; 15 | *(.first_data) 16 | *(.data SORT(.data.*)) 17 | _edata = .; 18 | } > sram AT > flash 19 | _sidata = LOADADDR(.data); 20 | 21 | .bss : { _sbss = .; *(.bss SORT(.bss.*) COMMON) _ebss = .; } > sram 22 | 23 | . = ALIGN(8); 24 | _end = .; 25 | } 26 | -------------------------------------------------------------------------------- /templates/blinky/nucleo-f429zi/main.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022-2023 Cesanta Software Limited 2 | // SPDX-License-Identifier: MIT 3 | 4 | #include "hal.h" 5 | 6 | #define BLINK_PERIOD_MS 500 // LED blinking period in millis 7 | #define LOG_PERIOD_MS 1000 // Info log period in millis 8 | 9 | uint32_t SystemCoreClock; // Required by CMSIS. Holds system core cock value 10 | void SystemInit(void) { // Called automatically by startup code 11 | clock_init(); // Sets SystemCoreClock 12 | } 13 | 14 | static volatile uint64_t s_ticks; // Milliseconds since boot 15 | void SysTick_Handler(void) { // SyStick IRQ handler, triggered every 1ms 16 | s_ticks++; 17 | } 18 | 19 | static void led_task(void) { // Blink LED every BLINK_PERIOD_MS 20 | static uint64_t timer = 0; 21 | if (timer_expired(&timer, BLINK_PERIOD_MS, s_ticks)) { 22 | gpio_toggle(LED_PIN); 23 | } 24 | } 25 | 26 | static void log_task(void) { // Print a log every LOG_PERIOD_MS 27 | static uint64_t timer = 0; 28 | if (timer_expired(&timer, LOG_PERIOD_MS, s_ticks)) { 29 | printf("tick: %5lu, CPU %lu MHz\n", (unsigned long) s_ticks, 30 | SystemCoreClock / 1000000); 31 | } 32 | } 33 | 34 | int main(void) { 35 | gpio_output(LED_PIN); 36 | uart_init(UART_DEBUG, 115200); 37 | 38 | for (;;) { 39 | led_task(); 40 | log_task(); 41 | } 42 | 43 | return 0; 44 | } 45 | -------------------------------------------------------------------------------- /templates/blinky/nucleo-f429zi/syscalls.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 Cesanta Software Limited 2 | // All rights reserved 3 | 4 | #include 5 | 6 | #include "hal.h" 7 | 8 | int _fstat(int fd, struct stat *st) { 9 | if (fd < 0) return -1; 10 | st->st_mode = S_IFCHR; 11 | return 0; 12 | } 13 | 14 | void *_sbrk(int incr) { 15 | extern char _end; 16 | static unsigned char *heap = NULL; 17 | unsigned char *prev_heap; 18 | if (heap == NULL) heap = (unsigned char *) &_end; 19 | prev_heap = heap; 20 | heap += incr; 21 | return prev_heap; 22 | } 23 | 24 | int _open(const char *path) { 25 | (void) path; 26 | return -1; 27 | } 28 | 29 | int _close(int fd) { 30 | (void) fd; 31 | return -1; 32 | } 33 | 34 | int _isatty(int fd) { 35 | (void) fd; 36 | return 1; 37 | } 38 | 39 | int _lseek(int fd, int ptr, int dir) { 40 | (void) fd, (void) ptr, (void) dir; 41 | return 0; 42 | } 43 | 44 | void _exit(int status) { 45 | (void) status; 46 | for (;;) asm volatile("BKPT #0"); 47 | } 48 | 49 | void _kill(int pid, int sig) { 50 | (void) pid, (void) sig; 51 | } 52 | 53 | int _getpid(void) { 54 | return -1; 55 | } 56 | 57 | int _write(int fd, char *ptr, int len) { 58 | (void) fd, (void) ptr, (void) len; 59 | if (fd == 1) uart_write_buf(UART_DEBUG, ptr, (size_t) len); 60 | return -1; 61 | } 62 | 63 | int _read(int fd, char *ptr, int len) { 64 | (void) fd, (void) ptr, (void) len; 65 | return -1; 66 | } 67 | 68 | int _link(const char *a, const char *b) { 69 | (void) a, (void) b; 70 | return -1; 71 | } 72 | 73 | int _unlink(const char *a) { 74 | (void) a; 75 | return -1; 76 | } 77 | 78 | int _stat(const char *path, struct stat *st) { 79 | (void) path, (void) st; 80 | return -1; 81 | } 82 | 83 | int mkdir(const char *path, mode_t mode) { 84 | (void) path, (void) mode; 85 | return -1; 86 | } 87 | 88 | void _init(void) { 89 | } 90 | -------------------------------------------------------------------------------- /templates/blinky/nucleo-l432kc/Makefile: -------------------------------------------------------------------------------- 1 | CFLAGS ?= -W -Wall -Wextra -Werror -Wundef -Wshadow -Wdouble-promotion -Wformat-truncation -fno-common 2 | CFLAGS += -g3 -Os -ffunction-sections -fdata-sections -Wno-shadow 3 | CFLAGS += -I. -Iinclude -Icmsis_core/CMSIS/Core/Include -Icmsis_l4/Include 4 | CFLAGS += -mcpu=cortex-m4 -mthumb -mfloat-abi=hard -mfpu=fpv4-sp-d16 5 | LDFLAGS ?= -Tlink.ld -nostartfiles -nostdlib --specs nano.specs -lc -lgcc -Wl,--gc-sections -Wl,-Map=$@.map 6 | SOURCES = main.c syscalls.c 7 | SOURCES += cmsis_l4/Source/Templates/gcc/startup_stm32l432xx.s # ST startup file. Compiler-dependent! 8 | 9 | ifeq ($(OS),Windows_NT) 10 | RM = cmd /C del /Q /F 11 | else 12 | RM = rm -rf 13 | endif 14 | 15 | build: firmware.bin 16 | 17 | firmware.elf: cmsis_core cmsis_l4 hal.h link.ld Makefile $(SOURCES) 18 | arm-none-eabi-gcc $(SOURCES) $(CFLAGS) $(CFLAGS_EXTRA) $(LDFLAGS) -o $@ 19 | 20 | firmware.bin: firmware.elf 21 | arm-none-eabi-objcopy -O binary $< $@ 22 | 23 | flash: firmware.bin 24 | st-flash --reset write $< 0x8000000 25 | 26 | cmsis_core: 27 | git clone --depth 1 -b 5.9.0 https://github.com/ARM-software/CMSIS_5 $@ 28 | 29 | cmsis_l4: 30 | git clone --depth 1 -b v1.7.2 https://github.com/STMicroelectronics/cmsis_device_l4 $@ 31 | 32 | clean: 33 | $(RM) firmware.* cmsis_* 34 | 35 | # Automated test via https://vcon.io/automated-firmware-tests/. Set VCON_API_KEY and update DEVICE_URL 36 | DEVICE_URL ?= https://dash.vcon.io/api/v3/devices/7 37 | update: CFLAGS += -DUART_DEBUG=USART1 38 | update: firmware.bin 39 | curl --fail-with-body -su :$(VCON_API_KEY) $(DEVICE_URL)/ota --data-binary @$< 40 | test: update 41 | curl --fail-with-body -su :$(VCON_API_KEY) $(DEVICE_URL)/tx?t=5 | tee /tmp/output.txt 42 | egrep '^tick:' /tmp/output.txt 43 | # egrep '^tick:.*CPU 16 MHz' /tmp/output.txt 44 | watch: update 45 | curl --fail-with-body -su :$(VCON_API_KEY) $(DEVICE_URL)/tx?t=999 46 | -------------------------------------------------------------------------------- /templates/blinky/nucleo-l432kc/link.ld: -------------------------------------------------------------------------------- 1 | ENTRY(Reset_Handler); 2 | MEMORY { 3 | flash(rx) : ORIGIN = 0x08000000, LENGTH = 256k 4 | sram(rwx) : ORIGIN = 0x20000000, LENGTH = 64k 5 | } 6 | _estack = ORIGIN(sram) + LENGTH(sram); /* stack points to end of SRAM */ 7 | _eflash = ORIGIN(flash) + LENGTH(flash); /* points to end of flash */ 8 | 9 | SECTIONS { 10 | .vectors : { KEEP(*(.isr_vector)) } > flash 11 | .text : { *(.text* .text.*) } > flash 12 | .rodata : { *(.rodata*) } > flash 13 | .data : { _sdata = .; *(.first_data) *(.data SORT(.data.*)) _edata = .; } > sram AT > flash 14 | _sidata = LOADADDR(.data); 15 | .bss : { _sbss = .; *(.bss SORT(.bss.*) COMMON) _ebss = .; } > sram 16 | . = ALIGN(8); 17 | _end = .; 18 | } 19 | -------------------------------------------------------------------------------- /templates/blinky/nucleo-l432kc/main.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022-2023 Cesanta Software Limited 2 | // SPDX-License-Identifier: MIT 3 | 4 | #include "hal.h" 5 | 6 | #define BLINK_PERIOD_MS 500 // LED blinking period in millis 7 | #define LOG_PERIOD_MS 1000 // Info log period in millis 8 | 9 | uint32_t SystemCoreClock; // Required by CMSIS. Holds system core cock value 10 | void SystemInit(void) { // Called automatically by startup code 11 | clock_init(); // Sets SystemCoreClock 12 | } 13 | 14 | static volatile uint64_t s_ticks; // Milliseconds since boot 15 | void SysTick_Handler(void) { // SyStick IRQ handler, triggered every 1ms 16 | s_ticks++; 17 | } 18 | 19 | static void led_task(void) { // Blink LED every BLINK_PERIOD_MS 20 | static uint64_t timer = 0; 21 | if (timer_expired(&timer, BLINK_PERIOD_MS, s_ticks)) { 22 | gpio_toggle(LED_PIN); 23 | } 24 | } 25 | 26 | static void log_task(void) { // Print a log every LOG_PERIOD_MS 27 | static uint64_t timer = 0; 28 | if (timer_expired(&timer, LOG_PERIOD_MS, s_ticks)) { 29 | printf("tick: %5lu, CPU %lu MHz\n", (unsigned long) s_ticks, 30 | SystemCoreClock / 1000000); 31 | } 32 | } 33 | 34 | int main(void) { 35 | gpio_output(LED_PIN); 36 | uart_init(UART_DEBUG, 115200); 37 | 38 | for (;;) { 39 | led_task(); 40 | log_task(); 41 | } 42 | 43 | return 0; 44 | } 45 | -------------------------------------------------------------------------------- /templates/blinky/nucleo-l432kc/syscalls.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 Cesanta Software Limited 2 | // All rights reserved 3 | 4 | #include 5 | 6 | #include "hal.h" 7 | 8 | int _fstat(int fd, struct stat *st) { 9 | if (fd < 0) return -1; 10 | st->st_mode = S_IFCHR; 11 | return 0; 12 | } 13 | 14 | void *_sbrk(int incr) { 15 | static unsigned char *heap = NULL; 16 | unsigned char *prev_heap; 17 | if (heap == NULL) heap = &_end; 18 | prev_heap = heap; 19 | heap += incr; 20 | return prev_heap; 21 | } 22 | 23 | int _open(const char *path) { 24 | (void) path; 25 | return -1; 26 | } 27 | 28 | int _close(int fd) { 29 | (void) fd; 30 | return -1; 31 | } 32 | 33 | int _isatty(int fd) { 34 | (void) fd; 35 | return 1; 36 | } 37 | 38 | int _lseek(int fd, int ptr, int dir) { 39 | (void) fd, (void) ptr, (void) dir; 40 | return 0; 41 | } 42 | 43 | void _exit(int status) { 44 | (void) status; 45 | for (;;) asm volatile("BKPT #0"); 46 | } 47 | 48 | void _kill(int pid, int sig) { 49 | (void) pid, (void) sig; 50 | } 51 | 52 | int _getpid(void) { 53 | return -1; 54 | } 55 | 56 | int _write(int fd, char *ptr, int len) { 57 | (void) fd, (void) ptr, (void) len; 58 | if (fd == 1) uart_write_buf(UART_DEBUG, ptr, (size_t) len); 59 | return -1; 60 | } 61 | 62 | int _read(int fd, char *ptr, int len) { 63 | (void) fd, (void) ptr, (void) len; 64 | return -1; 65 | } 66 | 67 | int _link(const char *a, const char *b) { 68 | (void) a, (void) b; 69 | return -1; 70 | } 71 | 72 | int _unlink(const char *a) { 73 | (void) a; 74 | return -1; 75 | } 76 | 77 | int _stat(const char *path, struct stat *st) { 78 | (void) path, (void) st; 79 | return -1; 80 | } 81 | 82 | int mkdir(const char *path, mode_t mode) { 83 | (void) path, (void) mode; 84 | return -1; 85 | } 86 | 87 | void _init(void) { 88 | } 89 | -------------------------------------------------------------------------------- /templates/blinky/nucleo-u5a5zj-q/Makefile: -------------------------------------------------------------------------------- 1 | CFLAGS ?= -W -Wall -Wextra -Werror -Wundef -Wshadow -Wdouble-promotion -Wformat-truncation -fno-common 2 | CFLAGS += -g3 -Os -ffunction-sections -fdata-sections -Wno-shadow 3 | CFLAGS += -I. -Iinclude -Icmsis_core/CMSIS/Core/Include -Icmsis_u5/Include 4 | CFLAGS += -mcpu=cortex-m33 -mthumb -mfpu=fpv5-sp-d16 -mfloat-abi=hard $(CFLAGS_EXTRA) 5 | LDFLAGS ?= -Tlink.ld -nostartfiles -nostdlib --specs nano.specs -lc -lgcc -Wl,--gc-sections -Wl,-Map=$@.map 6 | SOURCES = main.c syscalls.c 7 | SOURCES += cmsis_u5/Source/Templates/gcc/startup_stm32u5a5xx.s 8 | 9 | ifeq ($(OS),Windows_NT) 10 | RM = cmd /C del /Q /F 11 | else 12 | RM = rm -rf 13 | endif 14 | 15 | build: firmware.bin 16 | 17 | firmware.elf: cmsis_core cmsis_u5 hal.h link.ld Makefile $(SOURCES) 18 | arm-none-eabi-gcc $(SOURCES) $(CFLAGS) $(CFLAGS_EXTRA) $(LDFLAGS) -o $@ 19 | 20 | firmware.bin: firmware.elf 21 | arm-none-eabi-objcopy -O binary $< $@ 22 | 23 | flash: firmware.bin 24 | STM32_Programmer_CLI -c port=swd -e all -w $< 0x8000000 -hardRst 25 | # st-flash --reset write $< 0x8000000 26 | 27 | cmsis_core: 28 | git clone -q -c advice.detachedHead=false --depth 1 -b 5.9.0 https://github.com/ARM-software/CMSIS_5 $@ 29 | 30 | cmsis_u5: 31 | git clone -q -c advice.detachedHead=false --depth 1 -b v1.4.1 https://github.com/STMicroelectronics/cmsis_device_u5 $@ 32 | 33 | clean: 34 | $(RM) firmware.* cmsis_* 35 | -------------------------------------------------------------------------------- /templates/blinky/nucleo-u5a5zj-q/link.ld: -------------------------------------------------------------------------------- 1 | ENTRY(Reset_Handler); 2 | MEMORY { 3 | flash(rx) : ORIGIN = 0x08000000, LENGTH = 256k 4 | sram(rwx) : ORIGIN = 0x20000000, LENGTH = 64k 5 | } 6 | _estack = ORIGIN(sram) + LENGTH(sram); /* stack points to end of SRAM */ 7 | _eflash = ORIGIN(flash) + LENGTH(flash); /* points to end of flash */ 8 | 9 | SECTIONS { 10 | .vectors : { KEEP(*(.isr_vector)) } > flash 11 | .text : { *(.text* .text.*) } > flash 12 | .rodata : { *(.rodata*) } > flash 13 | .data : { _sdata = .; *(.first_data) *(.data SORT(.data.*)) _edata = .; } > sram AT > flash 14 | _sidata = LOADADDR(.data); 15 | .bss : { _sbss = .; *(.bss SORT(.bss.*) COMMON) _ebss = .; } > sram 16 | . = ALIGN(8); 17 | _end = .; 18 | } 19 | -------------------------------------------------------------------------------- /templates/blinky/nucleo-u5a5zj-q/main.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025 Cesanta Software Limited 2 | // SPDX-License-Identifier: MIT 3 | 4 | #include "hal.h" 5 | 6 | #define UART_DEBUG USART1 7 | 8 | #define LED1_PIN PIN('C', 7) // Green 9 | #define LED2_PIN PIN('B', 7) // Blue 10 | #define LED3_PIN PIN('G', 2) // Red 11 | 12 | #define BLINK_PERIOD_MS 500 // LED blinking period in millis 13 | #define LOG_PERIOD_MS 1000 // Info log period in millis 14 | 15 | static uint16_t s_pins[] = {LED1_PIN, LED2_PIN, LED3_PIN}; 16 | //static uint16_t s_pins[] = {LED1_PIN, PIN('A', 3)}; 17 | #define NUM_LEDS (sizeof(s_pins) / sizeof(s_pins[0])) 18 | 19 | int _write(int fd, char *ptr, int len) { 20 | if (fd == 1) uart_write_buf(UART_DEBUG, ptr, (size_t) len); 21 | return len; 22 | } 23 | 24 | uint32_t SystemCoreClock; // Required by CMSIS. Holds system core cock value 25 | void SystemInit(void) { // Called automatically by startup code 26 | clock_init(); // Sets SystemCoreClock 27 | } 28 | 29 | static volatile uint64_t s_ticks; // Milliseconds since boot 30 | void SysTick_Handler(void) { // SyStick IRQ handler, triggered every 1ms 31 | s_ticks++; 32 | } 33 | 34 | static void led_task(void) { // Blink LED every BLINK_PERIOD_MS 35 | static uint64_t timer = 0; 36 | static size_t idx; 37 | if (timer_expired(&timer, BLINK_PERIOD_MS, s_ticks)) { 38 | for (size_t i = 0; i < NUM_LEDS; i++) gpio_write(s_pins[i], false); 39 | gpio_write(s_pins[idx], true); 40 | if (++idx >= NUM_LEDS) idx = 0; 41 | } 42 | } 43 | 44 | static void log_task(void) { // Print a log every LOG_PERIOD_MS 45 | static uint64_t timer = 0; 46 | if (timer_expired(&timer, LOG_PERIOD_MS, s_ticks)) { 47 | printf("tick: %5lu, CPU %lu MHz\n", (unsigned long) s_ticks, 48 | (unsigned long) (SystemCoreClock / 1000000)); 49 | for (size_t i = 0; i < 8; i++) { 50 | printf("%#lx\n", gpio_bank(i)->LCKR); 51 | } 52 | } 53 | } 54 | 55 | int main(void) { 56 | for (size_t i = 0; i < NUM_LEDS; i++) gpio_output(s_pins[i]); 57 | uart_init(UART_DEBUG, 115200); 58 | 59 | for (;;) { 60 | led_task(); 61 | log_task(); 62 | } 63 | 64 | return 0; 65 | } 66 | -------------------------------------------------------------------------------- /templates/blinky/nucleo-u5a5zj-q/syscalls.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "hal.h" 4 | 5 | __attribute__((weak)) int _fstat(int fd, struct stat *st) { 6 | if (fd < 0) return -1; 7 | st->st_mode = S_IFCHR; 8 | return 0; 9 | } 10 | 11 | __attribute__((weak)) void *_sbrk(int incr) { 12 | static unsigned char *heap = NULL; 13 | unsigned char *prev_heap; 14 | if (heap == NULL) heap = &_end; 15 | prev_heap = heap; 16 | heap += incr; 17 | return prev_heap; 18 | } 19 | 20 | __attribute__((weak)) int _open(const char *path) { 21 | (void) path; 22 | return -1; 23 | } 24 | 25 | __attribute__((weak)) int _close(int fd) { 26 | (void) fd; 27 | return -1; 28 | } 29 | 30 | __attribute__((weak)) int _isatty(int fd) { 31 | (void) fd; 32 | return 1; 33 | } 34 | 35 | __attribute__((weak)) int _lseek(int fd, int ptr, int dir) { 36 | (void) fd, (void) ptr, (void) dir; 37 | return 0; 38 | } 39 | 40 | __attribute__((weak)) void _exit(int status) { 41 | (void) status; 42 | for (;;) asm volatile("BKPT #0"); 43 | } 44 | 45 | __attribute__((weak)) void _kill(int pid, int sig) { 46 | (void) pid, (void) sig; 47 | } 48 | 49 | __attribute__((weak)) int _getpid(void) { 50 | return -1; 51 | } 52 | 53 | __attribute__((weak)) int _write(int fd, char *ptr, int len) { 54 | (void) fd, (void) ptr, (void) len; 55 | return -1; 56 | } 57 | 58 | __attribute__((weak)) int _read(int fd, char *ptr, int len) { 59 | (void) fd, (void) ptr, (void) len; 60 | return -1; 61 | } 62 | 63 | __attribute__((weak)) int _link(const char *a, const char *b) { 64 | (void) a, (void) b; 65 | return -1; 66 | } 67 | 68 | __attribute__((weak)) int _unlink(const char *a) { 69 | (void) a; 70 | return -1; 71 | } 72 | 73 | __attribute__((weak)) int _stat(const char *path, struct stat *st) { 74 | (void) path, (void) st; 75 | return -1; 76 | } 77 | 78 | __attribute__((weak)) int mkdir(const char *path, mode_t mode) { 79 | (void) path, (void) mode; 80 | return -1; 81 | } 82 | 83 | __attribute__((weak)) void _init(void) { 84 | } 85 | -------------------------------------------------------------------------------- /templates/blinky/same54-xplained/Makefile: -------------------------------------------------------------------------------- 1 | CFLAGS ?= -W -Wall -Wextra -Werror -Wundef -Wshadow -Wdouble-promotion 2 | CFLAGS += -Wformat-truncation -fno-common -Wconversion 3 | CFLAGS += -g -O2 -ffunction-sections -fdata-sections 4 | CFLAGS += -I. -Icmsis_core/CMSIS/Core/Include 5 | CFLAGS += -D__SAME54P20A__ -Icmsis_sam/include #-Icmsis_sam/xc32/include 6 | #CFLAGS += -mcpu=cortex-m4 -mthumb -mfloat-abi=hard -mfpu=fpv4-sp-d16 7 | CFLAGS += -mcpu=cortex-m4 -mthumb -mfloat-abi=softfp -mfpu=fpv4-sp-d16 8 | LDFLAGS ?= -Tlink.ld -nostartfiles -nostdlib --specs nano.specs -lc -lgcc -Wl,--gc-sections -Wl,-Map=$@.map 9 | 10 | SOURCES = main.c syscalls.c startup.c 11 | #SOURCES += cmsis_sam/xc32/ATSAME54P20A/startup_atsame54p20a.c 12 | 13 | ifeq ($(OS),Windows_NT) 14 | RM = cmd /C del /Q /F 15 | else 16 | RM = rm -rf 17 | endif 18 | 19 | build: firmware.bin 20 | 21 | firmware.elf: cmsis_core cmsis_sam hal.h link.ld Makefile $(SOURCES) 22 | arm-none-eabi-gcc $(SOURCES) $(CFLAGS) $(CFLAGS_EXTRA) $(LDFLAGS) -o $@ 23 | 24 | firmware.bin: firmware.elf 25 | arm-none-eabi-objcopy -O binary $< $@ 26 | 27 | flash: firmware.bin 28 | bossac -p /dev/cu.usb* -w -v -b $< 29 | 30 | cmsis_core: 31 | git clone --depth 1 -b 5.9.0 https://github.com/ARM-software/CMSIS_5 $@ 32 | 33 | cmsis_sam: 34 | curl -sL https://packs.download.microchip.com/Microchip.SAME54_DFP.3.8.234.pack -o $@.zip 35 | mkdir $@ && cd $@ && unzip ../$@.zip 36 | # git clone --depth 1 -b master https://github.com/modm-io/cmsis-header-sam $@ 37 | 38 | clean: 39 | $(RM) firmware.* cmsis_* *.zip 40 | 41 | # Automated test via https://vcon.io/automated-firmware-tests/. Set VCON_API_KEY and update DEVICE_URL 42 | DEVICE_URL ?= https://dash.vcon.io/api/v3/devices/9 43 | fota: CFLAGS += -DUART_DEBUG=USART1 44 | fota: firmware.bin 45 | curl --fail-with-body -su :$(VCON_API_KEY) $(DEVICE_URL)/ota --data-binary @$< 46 | test: fota 47 | curl --fail-with-body -su :$(VCON_API_KEY) $(DEVICE_URL)/tx?t=5 | tee /tmp/output.txt 48 | egrep '^tick:.*CPU 180 MHz' /tmp/output.txt 49 | watch: fota 50 | curl --fail-with-body -su :$(VCON_API_KEY) $(DEVICE_URL)/tx?t=999 51 | -------------------------------------------------------------------------------- /templates/blinky/same54-xplained/link.ld: -------------------------------------------------------------------------------- 1 | ENTRY(Reset_Handler); 2 | MEMORY { 3 | flash(rx) : ORIGIN = 0x00000000, LENGTH = 1024k 4 | sram(rwx) : ORIGIN = 0x20000000, LENGTH = 256k 5 | } 6 | _estack = ORIGIN(sram) + LENGTH(sram); 7 | 8 | SECTIONS { 9 | .vectors : { FILL(256) KEEP(*(.vectors)) } > flash 10 | .text : { *(.text*) } > flash 11 | .rodata : { *(.rodata*) } > flash 12 | 13 | .data : { 14 | _sdata = .; 15 | *(.first_data) 16 | *(.data SORT(.data.*)) 17 | _edata = .; 18 | } > sram AT > flash 19 | _sidata = LOADADDR(.data); 20 | 21 | .bss : { _sbss = .; *(.bss SORT(.bss.*) COMMON) _ebss = .; } > sram 22 | 23 | . = ALIGN(8); 24 | _end = .; 25 | } 26 | -------------------------------------------------------------------------------- /templates/blinky/same54-xplained/main.c: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2022-2023 Cesanta Software Limited 2 | // SPDX-License-Identifier: MIT 3 | 4 | #include "hal.h" 5 | 6 | #define BLINK_PERIOD_MS 500 // LED blinking period in millis 7 | #define LOG_PERIOD_MS 1000 // Info log period in millis 8 | 9 | void SystemInit(void) { // Called automatically by startup code 10 | clock_init(); 11 | } 12 | 13 | static volatile uint64_t s_ticks; // Milliseconds since boot 14 | void SysTick_Handler(void) { // SyStick IRQ handler, triggered every 1ms 15 | s_ticks++; 16 | } 17 | 18 | static void led_task(void) { // Blink LED every BLINK_PERIOD_MS 19 | static uint64_t timer = 0; 20 | if (timer_expired(&timer, BLINK_PERIOD_MS, s_ticks)) { 21 | gpio_toggle(LED_PIN); 22 | } 23 | } 24 | 25 | static void log_task(void) { // Print a log every LOG_PERIOD_MS 26 | static uint64_t timer = 0; 27 | if (timer_expired(&timer, LOG_PERIOD_MS, s_ticks)) { 28 | printf("tick: %5lu, CPU %lu MHz\n", (unsigned long) s_ticks, 29 | clock_sys_freq() / 1000000); 30 | } 31 | } 32 | 33 | static void button_handler(void *param) { 34 | uint16_t pin = (uint16_t) (uintptr_t) param; 35 | printf("Button %u %s\n", pin, gpio_read(pin) ? "pressed" : "released"); 36 | } 37 | 38 | int main(void) { 39 | gpio_input(BUTTON_PIN); 40 | gpio_output(LED_PIN); 41 | uart_init(UART_DEBUG, 115200); 42 | 43 | gpio_set_irq_handler(BUTTON_PIN, button_handler, (void *) BUTTON_PIN); 44 | 45 | for (;;) { 46 | led_task(); 47 | log_task(); 48 | } 49 | 50 | return 0; 51 | } 52 | -------------------------------------------------------------------------------- /templates/blinky/same54-xplained/startup.c: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2022-2023 Cesanta Software Limited 2 | // SPDX-License-Identifier: MIT 3 | 4 | #include "hal.h" 5 | 6 | void Reset_Handler(void); // Defined below 7 | void Dummy_Handler(void); // Defined below 8 | void SysTick_Handler(void); // Defined in main.c 9 | void SystemInit(void); // Defined in main.c, called by reset handler 10 | void _estack(void); // Defined in link.ld 11 | 12 | #define WEAK_ALIAS __attribute__((weak, alias("Default_Handler"))) 13 | 14 | WEAK_ALIAS void NMI_Handler(void); 15 | WEAK_ALIAS void HardFault_Handler(void); 16 | WEAK_ALIAS void MemoryManagement_Handler(void); 17 | WEAK_ALIAS void BusFault_Handler(void); 18 | WEAK_ALIAS void UsageFault_Handler(void); 19 | WEAK_ALIAS void SVCall_Handler(void); 20 | WEAK_ALIAS void DebugMonitor_Handler(void); 21 | WEAK_ALIAS void PendSV_Handler(void); 22 | WEAK_ALIAS void SysTick_Handler(void); 23 | 24 | __attribute__((section(".vectors"))) void (*const tab[16 + 138])(void) = { 25 | _estack, 26 | Reset_Handler, 27 | NMI_Handler, 28 | HardFault_Handler, 29 | MemoryManagement_Handler, 30 | BusFault_Handler, 31 | UsageFault_Handler, 32 | NULL, 33 | NULL, 34 | NULL, 35 | NULL, 36 | SVCall_Handler, 37 | DebugMonitor_Handler, 38 | NULL, 39 | PendSV_Handler, 40 | SysTick_Handler, 41 | }; 42 | 43 | __attribute__((naked, noreturn)) void Reset_Handler(void) { 44 | // Clear BSS section, and copy data section from flash to RAM 45 | extern long _sbss, _ebss, _sdata, _edata, _sidata; 46 | for (long *dst = &_sbss; dst < &_ebss; dst++) *dst = 0; 47 | for (long *dst = &_sdata, *src = &_sidata; dst < &_edata;) *dst++ = *src++; 48 | 49 | SCB->VTOR = (uint32_t) &tab; 50 | SystemInit(); 51 | 52 | // Call main() 53 | extern void main(void); 54 | main(); 55 | for (;;) (void) 0; // Infinite loop 56 | } 57 | 58 | void Default_Handler(void) { 59 | for (;;) (void) 0; 60 | } 61 | -------------------------------------------------------------------------------- /templates/blinky/same54-xplained/syscalls.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 Cesanta Software Limited 2 | // All rights reserved 3 | 4 | #include 5 | 6 | #include "hal.h" 7 | 8 | int _fstat(int fd, struct stat *st) { 9 | if (fd < 0) return -1; 10 | st->st_mode = S_IFCHR; 11 | return 0; 12 | } 13 | 14 | void *_sbrk(int incr) { 15 | extern char _end; 16 | static unsigned char *heap = NULL; 17 | unsigned char *prev_heap; 18 | if (heap == NULL) heap = (unsigned char *) &_end; 19 | prev_heap = heap; 20 | heap += incr; 21 | return prev_heap; 22 | } 23 | 24 | int _open(const char *path) { 25 | (void) path; 26 | return -1; 27 | } 28 | 29 | int _close(int fd) { 30 | (void) fd; 31 | return -1; 32 | } 33 | 34 | int _isatty(int fd) { 35 | (void) fd; 36 | return 1; 37 | } 38 | 39 | int _lseek(int fd, int ptr, int dir) { 40 | (void) fd, (void) ptr, (void) dir; 41 | return 0; 42 | } 43 | 44 | void _exit(int status) { 45 | (void) status; 46 | for (;;) asm volatile("BKPT #0"); 47 | } 48 | 49 | void _kill(int pid, int sig) { 50 | (void) pid, (void) sig; 51 | } 52 | 53 | int _getpid(void) { 54 | return -1; 55 | } 56 | 57 | int _write(int fd, char *ptr, int len) { 58 | (void) fd, (void) ptr, (void) len; 59 | if (fd == 1) uart_write_buf(UART_DEBUG, ptr, (size_t) len); 60 | return -1; 61 | } 62 | 63 | int _read(int fd, char *ptr, int len) { 64 | (void) fd, (void) ptr, (void) len; 65 | return -1; 66 | } 67 | 68 | int _link(const char *a, const char *b) { 69 | (void) a, (void) b; 70 | return -1; 71 | } 72 | 73 | int _unlink(const char *a) { 74 | (void) a; 75 | return -1; 76 | } 77 | 78 | int _stat(const char *path, struct stat *st) { 79 | (void) path, (void) st; 80 | return -1; 81 | } 82 | 83 | int mkdir(const char *path, mode_t mode) { 84 | (void) path, (void) mode; 85 | return -1; 86 | } 87 | 88 | void _init(void) { 89 | } 90 | -------------------------------------------------------------------------------- /templates/cli/nucleo-f429zi/Makefile: -------------------------------------------------------------------------------- 1 | CFLAGS ?= -W -Wall -Wextra -Werror -Wundef -Wshadow -Wdouble-promotion \ 2 | -Wformat-truncation -fno-common -Wconversion \ 3 | -g3 -Os -ffunction-sections -fdata-sections \ 4 | -I. -Iinclude -Icmsis_core/CMSIS/Core/Include -Icmsis_f4/Include \ 5 | -mcpu=cortex-m4 -mthumb -mfloat-abi=hard -mfpu=fpv4-sp-d16 6 | LDFLAGS ?= -Tlink.ld -nostartfiles -nostdlib --specs nano.specs -lc -lgcc -Wl,--gc-sections -Wl,-Map=$@.map 7 | SOURCES = main.c syscalls.c sysinit.c 8 | SOURCES += cmsis_f4/Source/Templates/gcc/startup_stm32f429xx.s # ST startup file. Compiler-dependent! 9 | 10 | ifeq ($(OS),Windows_NT) 11 | RM = cmd /C del /Q /F 12 | else 13 | RM = rm -rf 14 | endif 15 | 16 | build: firmware.bin 17 | 18 | firmware.elf: cmsis_core cmsis_f4 hal.h link.ld Makefile $(SOURCES) 19 | arm-none-eabi-gcc $(SOURCES) $(CFLAGS) $(CFLAGS_EXTRA) $(LDFLAGS) -o $@ 20 | 21 | firmware.bin: firmware.elf 22 | arm-none-eabi-objcopy -O binary $< $@ 23 | 24 | flash: firmware.bin 25 | st-flash --reset write $< 0x8000000 26 | 27 | cmsis_core: 28 | git clone --depth 1 -b 5.9.0 https://github.com/ARM-software/CMSIS_5 $@ 29 | 30 | cmsis_f4: 31 | git clone --depth 1 -b v2.6.8 https://github.com/STMicroelectronics/cmsis_device_f4 $@ 32 | 33 | clean: 34 | $(RM) firmware.* cmsis_* 35 | 36 | # Automated test via https://vcon.io/automated-firmware-tests/. Set VCON_API_KEY and update DEVICE_URL 37 | DEVICE_URL ?= https://dash.vcon.io/api/v3/devices/2 38 | update: CFLAGS += -DUART_DEBUG=USART1 39 | update: firmware.bin 40 | curl --fail-with-body -su :$(VCON_API_KEY) $(DEVICE_URL)/ota --data-binary @$< 41 | test: update 42 | curl --fail-with-body -su :$(VCON_API_KEY) $(DEVICE_URL)/tx?t=5 | tee /tmp/output.txt 43 | egrep '^COMMANDS' /tmp/output.txt 44 | watch: update 45 | curl --fail-with-body -su :$(VCON_API_KEY) $(DEVICE_URL)/tx?t=999 46 | -------------------------------------------------------------------------------- /templates/cli/nucleo-f429zi/link.ld: -------------------------------------------------------------------------------- 1 | ENTRY(Reset_Handler); 2 | MEMORY { 3 | flash(rx) : ORIGIN = 0x08000000, LENGTH = 2048k 4 | sram(rwx) : ORIGIN = 0x20000000, LENGTH = 192k /* remaining 64k in a separate address space */ 5 | } 6 | _estack = ORIGIN(sram) + LENGTH(sram); /* stack points to end of SRAM */ 7 | 8 | SECTIONS { 9 | .vectors : { KEEP(*(.isr_vector)) } > flash 10 | .text : { *(.text*) } > flash 11 | .rodata : { *(.rodata*) } > flash 12 | 13 | .data : { 14 | _sdata = .; 15 | *(.first_data) 16 | *(.data SORT(.data.*)) 17 | _edata = .; 18 | } > sram AT > flash 19 | _sidata = LOADADDR(.data); 20 | 21 | .bss : { _sbss = .; *(.bss SORT(.bss.*) COMMON) _ebss = .; } > sram 22 | 23 | . = ALIGN(8); 24 | _end = .; 25 | } 26 | -------------------------------------------------------------------------------- /templates/cli/nucleo-f429zi/main.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022-2023 Cesanta Software Limited 2 | // SPDX-License-Identifier: MIT 3 | 4 | #include 5 | #include 6 | #include "hal.h" 7 | 8 | #define LED PIN('B', 7) // On-board LED pin (blue) 9 | 10 | static bool s_led_blink = true; // If false, LED is on or off 11 | static unsigned s_led_blink_period_ms = 300; // LED blinking interval 12 | 13 | static volatile uint64_t s_ticks; // Milliseconds since boot 14 | void SysTick_Handler(void) { // SyStick IRQ handler, triggered every 1ms 15 | s_ticks++; 16 | } 17 | 18 | static void led_task(void) { 19 | static uint64_t timer = 0; 20 | if (s_led_blink && timer_expired(&timer, s_led_blink_period_ms, s_ticks)) { 21 | gpio_toggle(LED); 22 | } 23 | } 24 | 25 | static void cli_prompt(void) { 26 | printf("enter command:\n"); 27 | } 28 | 29 | static void cli_usage(void) { 30 | putchar('\n'); 31 | printf("COMMANDS:\n"); 32 | printf(" hexdump ADDR LENGTH\n"); 33 | printf(" led \n"); 34 | printf(" reboot\n"); 35 | } 36 | 37 | static void cli_led(const char *arg1, const char *arg2) { 38 | if (strcasecmp(arg1, "on") == 0) { 39 | s_led_blink = 0; 40 | gpio_write(LED, true); 41 | } else if (strcasecmp(arg1, "off") == 0) { 42 | s_led_blink = 0; 43 | gpio_write(LED, false); 44 | } else { 45 | s_led_blink = 1; 46 | s_led_blink_period_ms = strtoul(arg2, NULL, 0); 47 | } 48 | printf("LED status: %s, blink: %s, interval: %u ms\n", 49 | gpio_read(LED) ? "on" : "off", s_led_blink ? "yes" : "no", 50 | s_led_blink_period_ms); 51 | } 52 | 53 | static char nibble(char c) { 54 | return c < 10 ? c + '0' : c + 'W'; 55 | } 56 | 57 | static void hexdump(const void *buf, size_t len) { 58 | const uint8_t *p = (const uint8_t *) buf; 59 | char ascii[16]; 60 | size_t i, j, n = 0; 61 | for (i = 0; i < len; i++) { 62 | if ((i % 16) == 0) { 63 | // Print buffered ascii chars 64 | if (i > 0) { 65 | putchar(' '), putchar(' '); 66 | for (j = 0; j < sizeof(ascii); j++) putchar(ascii[j]); 67 | putchar('\n'), n = 0; 68 | } 69 | // Print hex address, then \t 70 | putchar(nibble((i >> 12) & 15)), putchar(nibble((i >> 8) & 15)); 71 | putchar(nibble((i >> 4) & 15)), putchar('0'); 72 | putchar(' '), putchar(' '), putchar(' '); 73 | } 74 | putchar(nibble(p[i] >> 4)), putchar(nibble(p[i] & 15)); 75 | putchar(' '); // Space after hex number 76 | if (p[i] >= ' ' && p[i] <= '~') { 77 | ascii[n++] = (char) p[i]; // Printable 78 | } else { 79 | ascii[n++] = '.'; // Non-printable 80 | } 81 | } 82 | if (n > 0) { 83 | while (n < 16) putchar(' '), putchar(' '), putchar(' '), ascii[n++] = ' '; 84 | putchar(' '), putchar(' '); 85 | for (j = 0; j < sizeof(ascii); j++) putchar(ascii[j]); 86 | } 87 | putchar('\n'); 88 | } 89 | 90 | static void cli_hexdump(const char *addr, const char *len) { 91 | char *buf = (char *) strtoul(addr, NULL, 0); 92 | long n = strtol(len, NULL, 0); 93 | printf("Dumping %ld bytes @ %p\n", n, buf); 94 | hexdump(n < 0 ? buf + n : buf, (size_t) (n < 0 ? -n : n)); 95 | } 96 | 97 | static void cli_exec(const char *command, const char *arg1, const char *arg2) { 98 | if (strcmp(command, "reboot") == 0) { 99 | NVIC_SystemReset(); 100 | } else if (strcmp(command, "led") == 0) { 101 | cli_led(arg1, arg2); 102 | } else if (strcmp(command, "hexdump") == 0) { 103 | cli_hexdump(arg1, arg2); 104 | } else { 105 | printf("Unknown command '%s'\n", command); 106 | cli_usage(); 107 | } 108 | cli_prompt(); 109 | } 110 | static void cli_task(void) { 111 | static char buf[128]; // Input buffer 112 | static size_t len; // Input length 113 | 114 | // Print CLI prompt message 1s after boot 115 | static bool printed = false; 116 | if (s_ticks > 1000 && !printed) cli_usage(), cli_prompt(), printed = true; 117 | 118 | if (uart_read_ready(UART_DEBUG)) { 119 | uint8_t input_byte = uart_read_byte(UART_DEBUG); 120 | if (input_byte == '\n' && len == 0) { 121 | } else if (input_byte == '\n') { 122 | char buf0[10], buf1[50], buf2[100]; // Command, arg1, arg2 123 | buf0[0] = buf1[0] = buf2[0] = '\0'; // NUL-terminate 124 | buf[len] = '\0'; // NUL-terminate input buffer 125 | sscanf(buf, "%9s %49s %99[^\r\n]", buf0, buf1, buf2); // Parse command 126 | cli_exec(buf0, buf1, buf2); // Execute command 127 | len = 0; // Reset input buffer 128 | } else { 129 | if (len >= sizeof(buf)) len = 0; // Reset input buffer on overflow 130 | buf[len++] = input_byte; // Append read byte to the input buffer 131 | } 132 | } 133 | } 134 | 135 | int main(void) { 136 | gpio_output(LED); // Setup green LED 137 | uart_init(UART_DEBUG, 115200); // Initialise UART 138 | 139 | printf("Boot complete. CPU %lu MHz\n", SystemCoreClock / 1000000); 140 | for (;;) { 141 | led_task(); 142 | cli_task(); 143 | } 144 | 145 | return 0; 146 | } 147 | -------------------------------------------------------------------------------- /templates/cli/nucleo-f429zi/syscalls.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 Cesanta Software Limited 2 | // All rights reserved 3 | 4 | #include 5 | 6 | #include "hal.h" 7 | 8 | int _fstat(int fd, struct stat *st) { 9 | if (fd < 0) return -1; 10 | st->st_mode = S_IFCHR; 11 | return 0; 12 | } 13 | 14 | void *_sbrk(int incr) { 15 | extern char _end; 16 | static unsigned char *heap = NULL; 17 | unsigned char *prev_heap; 18 | if (heap == NULL) heap = (unsigned char *) &_end; 19 | prev_heap = heap; 20 | heap += incr; 21 | return prev_heap; 22 | } 23 | 24 | int _open(const char *path) { 25 | (void) path; 26 | return -1; 27 | } 28 | 29 | int _close(int fd) { 30 | (void) fd; 31 | return -1; 32 | } 33 | 34 | int _isatty(int fd) { 35 | (void) fd; 36 | return 1; 37 | } 38 | 39 | int _lseek(int fd, int ptr, int dir) { 40 | (void) fd, (void) ptr, (void) dir; 41 | return 0; 42 | } 43 | 44 | void _exit(int status) { 45 | (void) status; 46 | for (;;) asm volatile("BKPT #0"); 47 | } 48 | 49 | void _kill(int pid, int sig) { 50 | (void) pid, (void) sig; 51 | } 52 | 53 | int _getpid(void) { 54 | return -1; 55 | } 56 | 57 | int _write(int fd, char *ptr, int len) { 58 | (void) fd, (void) ptr, (void) len; 59 | if (fd == 1) uart_write_buf(UART_DEBUG, ptr, (size_t) len); 60 | return -1; 61 | } 62 | 63 | int _read(int fd, char *ptr, int len) { 64 | (void) fd, (void) ptr, (void) len; 65 | return -1; 66 | } 67 | 68 | int _link(const char *a, const char *b) { 69 | (void) a, (void) b; 70 | return -1; 71 | } 72 | 73 | int _unlink(const char *a) { 74 | (void) a; 75 | return -1; 76 | } 77 | 78 | int _stat(const char *path, struct stat *st) { 79 | (void) path, (void) st; 80 | return -1; 81 | } 82 | 83 | int mkdir(const char *path, mode_t mode) { 84 | (void) path, (void) mode; 85 | return -1; 86 | } 87 | 88 | void _init(void) { 89 | } 90 | -------------------------------------------------------------------------------- /templates/cli/nucleo-f429zi/sysinit.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023 Cesanta Software Limited 2 | // All rights reserved 3 | // 4 | // This file contains essentials required by the CMSIS: 5 | // uint32_t SystemCoreClock - holds the system core clock value 6 | // SystemInit() - initialises the system, e.g. sets up clocks 7 | 8 | #include "hal.h" 9 | 10 | uint32_t SystemCoreClock = SYS_FREQUENCY; 11 | 12 | void SystemInit(void) { // Called automatically by startup code 13 | SCB->CPACR |= ((3UL << 10 * 2) | (3UL << 11 * 2)); // Enable FPU 14 | FLASH->ACR |= FLASH_LATENCY | BIT(8) | BIT(9); // Flash latency, prefetch 15 | RCC->PLLCFGR &= ~((BIT(17) - 1)); // Clear PLL multipliers 16 | RCC->PLLCFGR |= (((PLL_P - 2) / 2) & 3) << 16; // Set PLL_P 17 | RCC->PLLCFGR |= PLL_M | (PLL_N << 6); // Set PLL_M and PLL_N 18 | RCC->CR |= BIT(24); // Enable PLL 19 | while ((RCC->CR & BIT(25)) == 0) spin(1); // Wait until done 20 | RCC->CFGR = (APB1_PRE << 10) | (APB2_PRE << 13); // Set prescalers 21 | RCC->CFGR |= 2; // Set clock source to PLL 22 | while ((RCC->CFGR & 12) == 0) spin(1); // Wait until done 23 | 24 | RCC->APB2ENR |= RCC_APB2ENR_SYSCFGEN; // Enable SYSCFG 25 | SysTick_Config(SystemCoreClock / 1000); // Sys tick every 1ms 26 | } 27 | -------------------------------------------------------------------------------- /templates/cli/nucleo-l432kc/Makefile: -------------------------------------------------------------------------------- 1 | CFLAGS ?= -W -Wall -Wextra -Werror -Wundef -Wshadow -Wdouble-promotion -Wformat-truncation -fno-common \ 2 | -g3 -Os -ffunction-sections -fdata-sections -Wno-shadow \ 3 | -I. -Iinclude -Icmsis_core/CMSIS/Core/Include -Icmsis_l4/Include \ 4 | -mcpu=cortex-m4 -mthumb -mfloat-abi=hard -mfpu=fpv4-sp-d16 5 | LDFLAGS ?= -Tlink.ld -nostartfiles -nostdlib --specs nano.specs -lc -lgcc -Wl,--gc-sections -Wl,-Map=$@.map 6 | SOURCES = main.c syscalls.c sysinit.c 7 | SOURCES += cmsis_l4/Source/Templates/gcc/startup_stm32l432xx.s # ST startup file. Compiler-dependent! 8 | 9 | ifeq ($(OS),Windows_NT) 10 | RM = cmd /C del /Q /F 11 | else 12 | RM = rm -rf 13 | endif 14 | 15 | build: firmware.bin 16 | 17 | firmware.elf: cmsis_core cmsis_l4 hal.h link.ld Makefile $(SOURCES) 18 | arm-none-eabi-gcc $(SOURCES) $(CFLAGS) $(CFLAGS_EXTRA) $(LDFLAGS) -o $@ 19 | 20 | firmware.bin: firmware.elf 21 | arm-none-eabi-objcopy -O binary $< $@ 22 | 23 | flash: firmware.bin 24 | st-flash --reset write $< 0x8000000 25 | 26 | cmsis_core: 27 | git clone --depth 1 -b 5.9.0 https://github.com/ARM-software/CMSIS_5 $@ 28 | 29 | cmsis_l4: 30 | git clone --depth 1 -b v1.7.2 https://github.com/STMicroelectronics/cmsis_device_l4 $@ 31 | 32 | clean: 33 | $(RM) firmware.* cmsis_* 34 | 35 | # Automated test via https://vcon.io/automated-firmware-tests/. Set VCON_API_KEY and update DEVICE_URL 36 | DEVICE_URL ?= https://dash.vcon.io/api/v3/devices/7 37 | update: CFLAGS += -DUART_DEBUG=USART1 38 | update: firmware.bin 39 | curl --fail-with-body -su :$(VCON_API_KEY) $(DEVICE_URL)/ota --data-binary @$< 40 | test: update 41 | curl --fail-with-body -su :$(VCON_API_KEY) $(DEVICE_URL)/tx?t=5 | tee /tmp/output.txt 42 | egrep '^COMMANDS' /tmp/output.txt 43 | watch: update 44 | curl --fail-with-body -su :$(VCON_API_KEY) $(DEVICE_URL)/tx?t=999 45 | -------------------------------------------------------------------------------- /templates/cli/nucleo-l432kc/hal.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023 Cesanta Software Limited 2 | // https://www.st.com/resource/en/reference_manual/dm00151940-stm32l41xxx42xxx43xxx44xxx45xxx46xxx-advanced-armbased-32bit-mcus-stmicroelectronics.pdf 3 | // SPDX-License-Identifier: MIT 4 | 5 | #pragma once 6 | 7 | #include 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #define BIT(x) (1UL << (x)) 15 | #define SETBITS(R, CLEARMASK, SETMASK) (R) = ((R) & ~(CLEARMASK)) | (SETMASK) 16 | #define PIN(bank, num) ((((bank) - 'A') << 8) | (num)) 17 | #define PINNO(pin) (pin & 255) 18 | #define PINBANK(pin) (pin >> 8) 19 | 20 | // System clock 21 | enum { AHB_DIV = 1, APB1_DIV = 1, APB2_DIV = 1 }; 22 | enum { PLL_HSI = 16, PLL_M = 1, PLL_N = 10, PLL_R = 2 }; // 80 Mhz 23 | //#define SYS_FREQUENCY ((PLL_HSI * PLL_N / PLL_M / PLL_R) * 1000000) 24 | #define SYS_FREQUENCY 16000000 25 | #define APB2_FREQUENCY (SYS_FREQUENCY / APB2_DIV) 26 | #define APB1_FREQUENCY (SYS_FREQUENCY / APB1_DIV) 27 | 28 | static inline void spin(volatile uint32_t count) { 29 | while (count--) (void) 0; 30 | } 31 | 32 | enum { GPIO_MODE_INPUT, GPIO_MODE_OUTPUT, GPIO_MODE_AF, GPIO_MODE_ANALOG }; 33 | enum { GPIO_OTYPE_PUSH_PULL, GPIO_OTYPE_OPEN_DRAIN }; 34 | enum { GPIO_SPEED_LOW, GPIO_SPEED_MEDIUM, GPIO_SPEED_HIGH, GPIO_SPEED_INSANE }; 35 | enum { GPIO_PULL_NONE, GPIO_PULL_UP, GPIO_PULL_DOWN }; 36 | #define GPIO(N) ((GPIO_TypeDef *) (GPIOA_BASE + 0x400 * (N))) 37 | 38 | static GPIO_TypeDef *gpio_bank(uint16_t pin) { 39 | return GPIO(PINBANK(pin)); 40 | } 41 | static inline void gpio_toggle(uint16_t pin) { 42 | GPIO_TypeDef *gpio = gpio_bank(pin); 43 | uint32_t mask = BIT(PINNO(pin)); 44 | gpio->BSRR = mask << (gpio->ODR & mask ? 16 : 0); 45 | } 46 | static inline int gpio_read(uint16_t pin) { 47 | return gpio_bank(pin)->IDR & BIT(PINNO(pin)) ? 1 : 0; 48 | } 49 | static inline void gpio_write(uint16_t pin, bool val) { 50 | GPIO_TypeDef *gpio = gpio_bank(pin); 51 | gpio->BSRR = BIT(PINNO(pin)) << (val ? 0 : 16); 52 | } 53 | static inline void gpio_init(uint16_t pin, uint8_t mode, uint8_t type, 54 | uint8_t speed, uint8_t pull, uint8_t af) { 55 | GPIO_TypeDef *gpio = gpio_bank(pin); 56 | uint8_t n = (uint8_t) (PINNO(pin)); 57 | RCC->AHB2ENR |= BIT(PINBANK(pin)); // Enable GPIO clock 58 | SETBITS(gpio->OTYPER, 1UL << n, ((uint32_t) type) << n); 59 | SETBITS(gpio->OSPEEDR, 3UL << (n * 2), ((uint32_t) speed) << (n * 2)); 60 | SETBITS(gpio->PUPDR, 3UL << (n * 2), ((uint32_t) pull) << (n * 2)); 61 | SETBITS(gpio->AFR[n >> 3], 15UL << ((n & 7) * 4), 62 | ((uint32_t) af) << ((n & 7) * 4)); 63 | SETBITS(gpio->MODER, 3UL << (n * 2), ((uint32_t) mode) << (n * 2)); 64 | } 65 | static inline void gpio_input(uint16_t pin) { 66 | gpio_init(pin, GPIO_MODE_INPUT, GPIO_OTYPE_PUSH_PULL, GPIO_SPEED_HIGH, 67 | GPIO_PULL_NONE, 0); 68 | } 69 | static inline void gpio_output(uint16_t pin) { 70 | gpio_init(pin, GPIO_MODE_OUTPUT, GPIO_OTYPE_PUSH_PULL, GPIO_SPEED_HIGH, 71 | GPIO_PULL_NONE, 0); 72 | } 73 | 74 | #ifndef UART_DEBUG 75 | #define UART_DEBUG USART2 76 | #endif 77 | 78 | static inline bool uart_init(USART_TypeDef *uart, unsigned long baud) { 79 | // https://www.st.com/resource/en/datasheet/stm32l432kc.pdf 80 | uint8_t aftx = 7, afrx = 7; // Alternate function 81 | uint16_t rx = 0, tx = 0; // pins 82 | uint32_t freq = 0; // Bus frequency. UART1 is on APB2, rest on APB1 83 | 84 | if (uart == USART1) { 85 | freq = APB2_FREQUENCY, RCC->APB2ENR |= RCC_APB2ENR_USART1EN; 86 | tx = PIN('A', 9), rx = PIN('A', 10); 87 | } else if (uart == USART2) { 88 | freq = APB1_FREQUENCY, RCC->APB1ENR1 |= RCC_APB1ENR1_USART2EN; 89 | tx = PIN('A', 2), rx = PIN('A', 15), afrx = 3; 90 | } else { 91 | return false; 92 | } 93 | 94 | gpio_init(tx, GPIO_MODE_AF, GPIO_OTYPE_PUSH_PULL, GPIO_SPEED_HIGH, 0, aftx); 95 | gpio_init(rx, GPIO_MODE_AF, GPIO_OTYPE_PUSH_PULL, GPIO_SPEED_HIGH, 0, afrx); 96 | uart->CR1 = 0; // Disable this UART 97 | uart->BRR = freq / baud; // Set baud rate 98 | uart->CR1 |= BIT(0) | BIT(2) | BIT(3); // Set UE, RE, TE 99 | return true; 100 | } 101 | static inline void uart_write_byte(USART_TypeDef *uart, uint8_t byte) { 102 | uart->TDR = byte; 103 | while ((uart->ISR & BIT(7)) == 0) spin(1); 104 | } 105 | static inline void uart_write_buf(USART_TypeDef *uart, char *buf, size_t len) { 106 | while (len-- > 0) uart_write_byte(uart, *(uint8_t *) buf++); 107 | } 108 | static inline int uart_read_ready(USART_TypeDef *uart) { 109 | return uart->ISR & BIT(5); // If RXNE bit is set, data is ready 110 | } 111 | static inline uint8_t uart_read_byte(USART_TypeDef *uart) { 112 | return (uint8_t) (uart->RDR & 255); 113 | } 114 | 115 | static inline void rng_init(void) { 116 | RCC->AHB2ENR |= RCC_AHB2ENR_RNGEN; 117 | RNG->CR |= RNG_CR_RNGEN; 118 | } 119 | static inline uint32_t rng_read(void) { 120 | while ((RNG->SR & RNG_SR_DRDY) == 0) (void) 0; 121 | return RNG->DR; 122 | } 123 | 124 | #define UUID ((uint8_t *) UID_BASE) // Unique 96-bit chip ID. TRM 41.1 125 | 126 | // Helper macro for MAC generation 127 | #define GENERATE_LOCALLY_ADMINISTERED_MAC() \ 128 | { \ 129 | 2, UUID[0] ^ UUID[1], UUID[2] ^ UUID[3], UUID[4] ^ UUID[5], \ 130 | UUID[6] ^ UUID[7] ^ UUID[8], UUID[9] ^ UUID[10] ^ UUID[11] \ 131 | } 132 | 133 | // t: expiration time, prd: period, now: current time. Return true if expired 134 | static inline bool timer_expired(volatile uint64_t *t, uint64_t prd, 135 | uint64_t now) { 136 | if (now + prd < *t) *t = 0; // Time wrapped? Reset timer 137 | if (*t == 0) *t = now + prd; // Firt poll? Set expiration 138 | if (*t > now) return false; // Not expired yet, return 139 | *t = (now - *t) > prd ? now + prd : *t + prd; // Next expiration time 140 | return true; // Expired, return true 141 | } 142 | // Fill in stack with markers, in order to calculate stack usage later 143 | extern unsigned char _estack, _end; 144 | static inline void stack_fill(void) { 145 | uint32_t dummy, *p = (uint32_t *) &_end; 146 | while (p < &dummy) *p++ = 0xa5a5a5a5; 147 | } 148 | 149 | static inline long stack_usage(void) { 150 | uint32_t *sp = (uint32_t *) &_estack, *end = (uint32_t *) &_end, *p = sp - 1; 151 | while (p > end && *p != 0xa5a5a5a5) p--; 152 | return (sp - p) * sizeof(*p); 153 | } 154 | -------------------------------------------------------------------------------- /templates/cli/nucleo-l432kc/link.ld: -------------------------------------------------------------------------------- 1 | ENTRY(Reset_Handler); 2 | MEMORY { 3 | flash(rx) : ORIGIN = 0x08000000, LENGTH = 256k 4 | sram(rwx) : ORIGIN = 0x20000000, LENGTH = 64k 5 | } 6 | _estack = ORIGIN(sram) + LENGTH(sram); /* stack points to end of SRAM */ 7 | _eflash = ORIGIN(flash) + LENGTH(flash); /* points to end of flash */ 8 | 9 | SECTIONS { 10 | .vectors : { KEEP(*(.isr_vector)) } > flash 11 | .text : { *(.text* .text.*) } > flash 12 | .rodata : { *(.rodata*) } > flash 13 | .data : { _sdata = .; *(.first_data) *(.data SORT(.data.*)) _edata = .; } > sram AT > flash 14 | _sidata = LOADADDR(.data); 15 | .bss : { _sbss = .; *(.bss SORT(.bss.*) COMMON) _ebss = .; } > sram 16 | . = ALIGN(8); 17 | _end = .; 18 | } 19 | -------------------------------------------------------------------------------- /templates/cli/nucleo-l432kc/main.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022-2023 Cesanta Software Limited 2 | // SPDX-License-Identifier: MIT 3 | 4 | #include 5 | #include 6 | #include "hal.h" 7 | 8 | #define LED PIN('B', 3) // On-board LED pin (green) 9 | 10 | static bool s_led_blink = true; // If false, LED is on or off 11 | static unsigned s_led_blink_period_ms = 300; // LED blinking interval 12 | 13 | static volatile uint64_t s_ticks; // Milliseconds since boot 14 | void SysTick_Handler(void) { // SyStick IRQ handler, triggered every 1ms 15 | s_ticks++; 16 | } 17 | 18 | static void led_task(void) { 19 | static uint64_t timer = 0; 20 | if (s_led_blink && timer_expired(&timer, s_led_blink_period_ms, s_ticks)) { 21 | gpio_toggle(LED); 22 | } 23 | } 24 | 25 | static void cli_prompt(void) { 26 | printf("enter command:\n"); 27 | } 28 | 29 | static void cli_usage(void) { 30 | putchar('\n'); 31 | printf("COMMANDS:\n"); 32 | printf(" hexdump ADDR LENGTH\n"); 33 | printf(" led \n"); 34 | printf(" reboot\n"); 35 | } 36 | 37 | static void cli_led(const char *arg1, const char *arg2) { 38 | if (strcasecmp(arg1, "on") == 0) { 39 | s_led_blink = 0; 40 | gpio_write(LED, true); 41 | } else if (strcasecmp(arg1, "off") == 0) { 42 | s_led_blink = 0; 43 | gpio_write(LED, false); 44 | } else { 45 | s_led_blink = 1; 46 | s_led_blink_period_ms = strtoul(arg2, NULL, 0); 47 | } 48 | printf("LED status: %s, blink: %s, interval: %u ms\n", 49 | gpio_read(LED) ? "on" : "off", s_led_blink ? "yes" : "no", 50 | s_led_blink_period_ms); 51 | } 52 | 53 | static char nibble(char c) { 54 | return c < 10 ? c + '0' : c + 'W'; 55 | } 56 | 57 | static void hexdump(const void *buf, size_t len) { 58 | const uint8_t *p = (const uint8_t *) buf; 59 | char ascii[16]; 60 | size_t i, j, n = 0; 61 | for (i = 0; i < len; i++) { 62 | if ((i % 16) == 0) { 63 | // Print buffered ascii chars 64 | if (i > 0) { 65 | putchar(' '), putchar(' '); 66 | for (j = 0; j < sizeof(ascii); j++) putchar(ascii[j]); 67 | putchar('\n'), n = 0; 68 | } 69 | // Print hex address, then \t 70 | putchar(nibble((i >> 12) & 15)), putchar(nibble((i >> 8) & 15)); 71 | putchar(nibble((i >> 4) & 15)), putchar('0'); 72 | putchar(' '), putchar(' '), putchar(' '); 73 | } 74 | putchar(nibble(p[i] >> 4)), putchar(nibble(p[i] & 15)); 75 | putchar(' '); // Space after hex number 76 | if (p[i] >= ' ' && p[i] <= '~') { 77 | ascii[n++] = (char) p[i]; // Printable 78 | } else { 79 | ascii[n++] = '.'; // Non-printable 80 | } 81 | } 82 | if (n > 0) { 83 | while (n < 16) putchar(' '), putchar(' '), putchar(' '), ascii[n++] = ' '; 84 | putchar(' '), putchar(' '); 85 | for (j = 0; j < sizeof(ascii); j++) putchar(ascii[j]); 86 | } 87 | putchar('\n'); 88 | } 89 | 90 | static void cli_hexdump(const char *addr, const char *len) { 91 | char *buf = (char *) strtoul(addr, NULL, 0); 92 | long n = strtol(len, NULL, 0); 93 | printf("Dumping %ld bytes @ %p\n", n, buf); 94 | hexdump(n < 0 ? buf + n : buf, (size_t) (n < 0 ? -n : n)); 95 | } 96 | 97 | static void cli_exec(const char *command, const char *arg1, const char *arg2) { 98 | if (strcmp(command, "reboot") == 0) { 99 | NVIC_SystemReset(); 100 | } else if (strcmp(command, "led") == 0) { 101 | cli_led(arg1, arg2); 102 | } else if (strcmp(command, "hexdump") == 0) { 103 | cli_hexdump(arg1, arg2); 104 | } else { 105 | printf("Unknown command '%s'\n", command); 106 | cli_usage(); 107 | } 108 | cli_prompt(); 109 | } 110 | static void cli_task(void) { 111 | static char buf[128]; // Input buffer 112 | static size_t len; // Input length 113 | 114 | // Print CLI prompt message 1s after boot 115 | static bool printed = false; 116 | if (s_ticks > 1000 && !printed) cli_usage(), cli_prompt(), printed = true; 117 | 118 | if (uart_read_ready(UART_DEBUG)) { 119 | uint8_t input_byte = uart_read_byte(UART_DEBUG); 120 | if (input_byte == '\n' && len == 0) { 121 | } else if (input_byte == '\n') { 122 | char buf0[10], buf1[50], buf2[100]; // Command, arg1, arg2 123 | buf0[0] = buf1[0] = buf2[0] = '\0'; // NUL-terminate 124 | buf[len] = '\0'; // NUL-terminate input buffer 125 | sscanf(buf, "%9s %49s %99[^\r\n]", buf0, buf1, buf2); // Parse command 126 | cli_exec(buf0, buf1, buf2); // Execute command 127 | len = 0; // Reset input buffer 128 | } else { 129 | if (len >= sizeof(buf)) len = 0; // Reset input buffer on overflow 130 | buf[len++] = input_byte; // Append read byte to the input buffer 131 | } 132 | } 133 | } 134 | 135 | int main(void) { 136 | gpio_output(LED); // Setup green LED 137 | uart_init(UART_DEBUG, 115200); // Initialise UART 138 | 139 | printf("Boot complete. CPU %lu MHz\n", SystemCoreClock / 1000000); 140 | for (;;) { 141 | led_task(); 142 | cli_task(); 143 | } 144 | 145 | return 0; 146 | } 147 | -------------------------------------------------------------------------------- /templates/cli/nucleo-l432kc/syscalls.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 Cesanta Software Limited 2 | // All rights reserved 3 | 4 | #include 5 | 6 | #include "hal.h" 7 | 8 | int _fstat(int fd, struct stat *st) { 9 | if (fd < 0) return -1; 10 | st->st_mode = S_IFCHR; 11 | return 0; 12 | } 13 | 14 | void *_sbrk(int incr) { 15 | static unsigned char *heap = NULL; 16 | unsigned char *prev_heap; 17 | if (heap == NULL) heap = &_end; 18 | prev_heap = heap; 19 | heap += incr; 20 | return prev_heap; 21 | } 22 | 23 | int _open(const char *path) { 24 | (void) path; 25 | return -1; 26 | } 27 | 28 | int _close(int fd) { 29 | (void) fd; 30 | return -1; 31 | } 32 | 33 | int _isatty(int fd) { 34 | (void) fd; 35 | return 1; 36 | } 37 | 38 | int _lseek(int fd, int ptr, int dir) { 39 | (void) fd, (void) ptr, (void) dir; 40 | return 0; 41 | } 42 | 43 | void _exit(int status) { 44 | (void) status; 45 | for (;;) asm volatile("BKPT #0"); 46 | } 47 | 48 | void _kill(int pid, int sig) { 49 | (void) pid, (void) sig; 50 | } 51 | 52 | int _getpid(void) { 53 | return -1; 54 | } 55 | 56 | int _write(int fd, char *ptr, int len) { 57 | (void) fd, (void) ptr, (void) len; 58 | if (fd == 1) uart_write_buf(UART_DEBUG, ptr, (size_t) len); 59 | return -1; 60 | } 61 | 62 | int _read(int fd, char *ptr, int len) { 63 | (void) fd, (void) ptr, (void) len; 64 | return -1; 65 | } 66 | 67 | int _link(const char *a, const char *b) { 68 | (void) a, (void) b; 69 | return -1; 70 | } 71 | 72 | int _unlink(const char *a) { 73 | (void) a; 74 | return -1; 75 | } 76 | 77 | int _stat(const char *path, struct stat *st) { 78 | (void) path, (void) st; 79 | return -1; 80 | } 81 | 82 | int mkdir(const char *path, mode_t mode) { 83 | (void) path, (void) mode; 84 | return -1; 85 | } 86 | 87 | void _init(void) { 88 | } 89 | -------------------------------------------------------------------------------- /templates/cli/nucleo-l432kc/sysinit.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023 Cesanta Software Limited 2 | // SPDX-License-Identifier: MIT 3 | // 4 | // This file contains essentials required by the CMSIS: 5 | // uint32_t SystemCoreClock - holds the system core clock value 6 | // SystemInit() - initialises the system, e.g. sets up clocks 7 | 8 | #include "hal.h" 9 | 10 | uint32_t SystemCoreClock = SYS_FREQUENCY; 11 | 12 | void SystemInit(void) { // Called automatically by startup code 13 | SCB->CPACR |= 15 << 20; // Enable FPU 14 | FLASH->ACR |= FLASH_ACR_LATENCY_4WS | FLASH_ACR_ICEN | FLASH_ACR_DCEN; 15 | #if 0 16 | #if 0 17 | SETBITS(RCC->PLLCFGR, RCC_PLLCFGR_PLLM, (PLL_M - 1) << RCC_PLLCFGR_PLLM_Pos); 18 | SETBITS(RCC->PLLCFGR, RCC_PLLCFGR_PLLN, PLL_N << RCC_PLLCFGR_PLLN_Pos); 19 | SETBITS(RCC->PLLCFGR, RCC_PLLCFGR_PLLSRC, RCC_PLLCFGR_PLLSRC_HSI); 20 | RCC->PLLCFGR |= RCC_PLLCFGR_PLLREN; 21 | #else 22 | RCC->PLLCFGR = BIT(24) | BIT(20) | (PLL_N << 8) | RCC_PLLCFGR_PLLSRC_HSI | 23 | (7 << 27) | BIT(16); 24 | RCC->CR |= RCC_CR_PLLON; 25 | #endif 26 | while (!(RCC->CR & RCC_CR_PLLRDY)) spin(1); 27 | #if 0 28 | SETBITS(RCC->CFGR, RCC_CFGR_PPRE1, (3 + APB1_DIV / 2) << RCC_CFGR_PPRE1_Pos); 29 | SETBITS(RCC->CFGR, RCC_CFGR_PPRE2, (3 + APB2_DIV / 2) << RCC_CFGR_PPRE1_Pos); 30 | #endif 31 | RCC->CFGR |= RCC_CFGR_SW_PLL; 32 | while ((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_PLL) spin(1); 33 | #else 34 | RCC->CR |= RCC_CR_HSION; 35 | while (!(RCC->CR & RCC_CR_HSIRDY)) spin(1); 36 | RCC->CFGR &= ~(RCC_CFGR_SW); 37 | RCC->CFGR |= (RCC_CFGR_SW_HSI); 38 | while ((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_HSI) spin(1); 39 | SystemCoreClock = PLL_HSI * 1000000; 40 | #endif 41 | 42 | RCC->APB2ENR |= RCC_APB2ENR_SYSCFGEN; // Enable SYSCFG 43 | rng_init(); // Initialise random number generator 44 | SysTick_Config(SystemCoreClock / 1000); // Sys tick every 1ms 45 | stack_fill(); // Instrument stack for stack usage 46 | } 47 | -------------------------------------------------------------------------------- /templates/lfs/nucleo-f303k8/Makefile: -------------------------------------------------------------------------------- 1 | CFLAGS ?= -W -Wall -Wextra -Werror -Wundef -Wshadow -Wdouble-promotion -Wformat-truncation -fno-common 2 | CFLAGS += -g3 -Os -ffunction-sections -fdata-sections -Wno-shadow 3 | CFLAGS += -I. -Icmsis_core/CMSIS/Core/Include -Icmsis_f3/Include -Ilittlefs 4 | CFLAGS += -mcpu=cortex-m4 -mthumb -mfloat-abi=hard -mfpu=fpv4-sp-d16 5 | LDFLAGS ?= -Tlink.ld -nostartfiles -nostdlib --specs nano.specs -lc -lgcc -Wl,--gc-sections -Wl,-Map=$@.map 6 | SOURCES = main.c syscalls.c littlefs/lfs.c littlefs/lfs_util.c 7 | SOURCES += cmsis_f3/Source/Templates/gcc/startup_stm32f303x8.s # ST startup file. Compiler-dependent! 8 | 9 | ifeq ($(OS),Windows_NT) 10 | RM = cmd /C del /Q /F 11 | else 12 | RM = rm -rf 13 | endif 14 | 15 | build: firmware.bin 16 | 17 | firmware.elf: cmsis_core cmsis_f3 hal.h link.ld Makefile $(SOURCES) 18 | arm-none-eabi-gcc $(SOURCES) $(CFLAGS) $(CFLAGS_EXTRA) $(LDFLAGS) -o $@ 19 | 20 | firmware.bin: firmware.elf 21 | arm-none-eabi-objcopy -O binary $< $@ 22 | 23 | flash: firmware.bin 24 | st-flash --reset write $< 0x8000000 25 | 26 | cmsis_core: 27 | git clone --depth 1 -b 5.9.0 https://github.com/ARM-software/CMSIS_5 $@ 28 | 29 | cmsis_f3: 30 | git clone --depth 1 -b v2.3.7 https://github.com/STMicroelectronics/cmsis_device_f3 $@ 31 | 32 | littlefs/lfs.c: littlefs 33 | littlefs: 34 | git clone --depth 1 -b v2.6.1 https://github.com/littlefs-project/littlefs $@ 35 | 36 | clean: 37 | $(RM) firmware.* cmsis_* littlefs 38 | 39 | # Automated test via https://vcon.io/automated-firmware-tests/. Set VCON_API_KEY and update DEVICE_URL 40 | DEVICE_URL ?= https://dash.vcon.io/api/v3/devices/10 41 | update: CFLAGS += -DUART_DEBUG=USART1 42 | update: firmware.bin 43 | curl --fail-with-body -su :$(VCON_API_KEY) $(DEVICE_URL)/ota --data-binary @$< 44 | test: update 45 | curl --fail-with-body -su :$(VCON_API_KEY) $(DEVICE_URL)/tx?t=5 | tee /tmp/output.txt 46 | egrep '^tick:.*CPU 64 MHz' /tmp/output.txt 47 | watch: update 48 | curl --fail-with-body -su :$(VCON_API_KEY) $(DEVICE_URL)/tx?t=999 49 | -------------------------------------------------------------------------------- /templates/lfs/nucleo-f303k8/link.ld: -------------------------------------------------------------------------------- 1 | ENTRY(Reset_Handler); 2 | MEMORY { 3 | flash(rx) : ORIGIN = 0x08000000, LENGTH = 64k 4 | sram(rwx) : ORIGIN = 0x20000000, LENGTH = 12k 5 | } 6 | _estack = ORIGIN(sram) + LENGTH(sram); /* stack points to end of SRAM */ 7 | _eflash = ORIGIN(flash) + LENGTH(flash); /* points to end of flash */ 8 | 9 | SECTIONS { 10 | .vectors : { KEEP(*(.isr_vector)) } > flash 11 | .text : { *(.text* .text.*) } > flash 12 | .rodata : { *(.rodata*) } > flash 13 | .data : { _sdata = .; *(.first_data) *(.data SORT(.data.*)) _edata = .; } > sram AT > flash 14 | _sidata = LOADADDR(.data); 15 | .bss : { _sbss = .; *(.bss SORT(.bss.*) COMMON) _ebss = .; } > sram 16 | . = ALIGN(8); 17 | _end = .; 18 | } 19 | -------------------------------------------------------------------------------- /templates/lfs/nucleo-f303k8/main.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022-2023 Cesanta Software Limited 2 | // SPDX-License-Identifier: MIT 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "hal.h" 11 | 12 | #define BLINK_PERIOD_MS 500 // LED blinking period in millis 13 | #define LOG_PERIOD_MS 1000 // Info log period in millis 14 | #define DATA_DIR "/data" 15 | #define DATA_FILE DATA_DIR "/boot.txt" 16 | 17 | void SystemInit(void) { // Called automatically by startup code 18 | clock_init(); // Set system clock to maximum 19 | } 20 | 21 | static volatile uint64_t s_ticks; // Milliseconds since boot 22 | void SysTick_Handler(void) { // SyStick IRQ handler, triggered every 1ms 23 | s_ticks++; 24 | } 25 | 26 | static inline void list_files(const char *path) { 27 | if (path == NULL || path[0] == '\0') path = "."; 28 | DIR *dirp = opendir(path); 29 | if (dirp != NULL) { 30 | struct dirent *dp; 31 | while ((dp = readdir(dirp)) != NULL) { 32 | if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")) continue; 33 | char buf[80]; 34 | const char *slash = path[strlen(path) - 1] == '/' ? "" : "/"; 35 | snprintf(buf, sizeof(buf), "%s%s%s", path, slash, dp->d_name); 36 | printf(" %s", buf); 37 | if (dp->d_type & DT_DIR) { 38 | putchar('/'); 39 | list_files(buf); 40 | } 41 | } 42 | closedir(dirp); 43 | } 44 | } 45 | 46 | static inline long read_boot_count(const char *path) { 47 | long count = 0; 48 | FILE *fp = fopen(path, "r"); 49 | if (fp != NULL) { 50 | fscanf(fp, "%ld", &count); 51 | fclose(fp); 52 | } else { 53 | printf("Error opening %s: %d\n", path, errno); 54 | } 55 | return count; 56 | } 57 | 58 | static inline void write_boot_count(const char *path, long count) { 59 | mkdir(DATA_DIR, 0644); 60 | FILE *fp = fopen(path, "w+"); 61 | if (fp != NULL) { 62 | fprintf(fp, "%ld", count); 63 | (void) count; 64 | fclose(fp); 65 | } else { 66 | printf("Error opening %s: %d\n", path, errno); 67 | } 68 | } 69 | 70 | static inline void led_task(void) { // Blink LED every BLINK_PERIOD_MS 71 | static uint64_t timer = 0; 72 | if (timer_expired(&timer, BLINK_PERIOD_MS, s_ticks)) { 73 | gpio_toggle(LED_PIN); 74 | } 75 | } 76 | 77 | static inline void log_task(void) { // Print a log every LOG_PERIOD_MS 78 | static uint64_t timer = ~0 - LOG_PERIOD_MS; 79 | if (timer_expired(&timer, LOG_PERIOD_MS, s_ticks)) { 80 | printf("tick: %5lu, CPU %lu MHz %lx ", (unsigned long) s_ticks, 81 | clock_sys_freq() / 1000000, RCC->CFGR); 82 | // printf("boot count: %ld, files: ", read_boot_count(DATA_FILE)); 83 | list_files("/"); 84 | putchar('\n'); 85 | } 86 | } 87 | 88 | int main(void) { 89 | gpio_output(LED_PIN); 90 | uart_init(UART_DEBUG, 115200); 91 | 92 | // Increment boot count 93 | write_boot_count(DATA_FILE, read_boot_count(DATA_FILE) + 1); 94 | 95 | for (;;) { 96 | led_task(); 97 | log_task(); 98 | } 99 | 100 | return 0; 101 | } 102 | -------------------------------------------------------------------------------- /templates/lfs/nucleo-f303k8/sys/dirent.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | #define DT_DIR 4 5 | #define DT_REG 8 6 | 7 | struct dirent { 8 | char d_name[64]; 9 | unsigned char d_type; 10 | }; 11 | 12 | typedef struct { 13 | struct dirent result; 14 | } DIR; 15 | -------------------------------------------------------------------------------- /templates/lfs/nucleo-h563zi/Makefile: -------------------------------------------------------------------------------- 1 | CFLAGS = -W -Wall -Wextra -Werror -Wundef -Wshadow -Wdouble-promotion 2 | CFLAGS += -Wformat-truncation -fno-common -Wno-sign-conversion 3 | #CFLAGS += -Wconversion # For LFS 4 | CFLAGS += -Wno-error=shadow # For LFS 5 | CFLAGS += -g3 -Os -ffunction-sections -fdata-sections 6 | CFLAGS += -I. -Imongoose -Icmsis_core/CMSIS/Core/Include -Icmsis_h5/Include 7 | CFLAGS += -mcpu=cortex-m33 -mthumb -mfpu=fpv5-sp-d16 -mfloat-abi=hard $(CFLAGS_EXTRA) 8 | 9 | LDFLAGS ?= -Tlink.ld -nostdlib -nostartfiles --specs nano.specs -lc -lgcc -Wl,--gc-sections -Wl,-Map=$@.map 10 | 11 | SOURCES = main.c syscalls.c 12 | SOURCES += retarget_lfs.c littlefs/lfs.c littlefs/lfs_util.c 13 | SOURCES += cmsis_h5/Source/Templates/gcc/startup_stm32h563xx.s 14 | 15 | all build: firmware.bin 16 | 17 | firmware.bin: firmware.elf 18 | arm-none-eabi-objcopy -O binary $< $@ 19 | arm-none-eabi-size --format=berkeley $< 20 | 21 | firmware.elf: cmsis_core cmsis_h5 littlefs $(SOURCES) $(wildcard *.h) link.ld Makefile 22 | arm-none-eabi-gcc $(SOURCES) $(CFLAGS) $(LDFLAGS) -o $@ 23 | 24 | flash: firmware.bin 25 | STM32_Programmer_CLI -c port=swd -e all -w $< 0x8000000 -hardRst 26 | 27 | cmsis_core: 28 | git clone -q -c advice.detachedHead=false --depth 1 -b 5.9.0 https://github.com/ARM-software/CMSIS_5 $@ 29 | 30 | cmsis_h5: 31 | git clone -q -c advice.detachedHead=false --depth 1 -b v1.2.0 https://github.com/STMicroelectronics/cmsis_device_h5 $@ 32 | 33 | littlefs/lfs.c: littlefs 34 | littlefs: 35 | git clone -q -c advice.detachedHead=false --depth 1 -b v2.11.0 https://github.com/littlefs-project/littlefs $@ 36 | 37 | clean: 38 | rm -rf firmware.* cmsis_* littlefs 39 | -------------------------------------------------------------------------------- /templates/lfs/nucleo-h563zi/flash_stm32h5.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022-2025 Cesanta Software Limited 2 | // SPDX-License-Identifier: GPL-2.0-only or commercial 3 | #pragma once 4 | 5 | #define FLASH_START_ADDR 0x8000000 6 | #define FLASH_TOTAL_SIZE (2 * 1024 * 1024) // Defined in the CMSIS header 7 | #define FLASH_BLOCK_SIZE 8192 // FLash block size in bytes 8 | #define FLASH_ALIGN_SIZE 16 9 | 10 | #define MG_REG(x) ((volatile uint32_t *) (x))[0] 11 | #define MG_BIT(x) (((uint32_t) 1U) << (x)) 12 | #define MG_SET_BITS(R, CLRMASK, SETMASK) (R) = ((R) & ~(CLRMASK)) | (SETMASK) 13 | 14 | #ifndef MG_DEBUG 15 | #define MG_DEBUG(x) // printf x; putchar('\n') 16 | #define MG_ERROR(x) // printf x; putchar('\n') 17 | #endif 18 | 19 | #if defined(__GNUC__) 20 | #define MG_ARM_DISABLE_IRQ() asm volatile("cpsid i" : : : "memory") 21 | #define MG_ARM_ENABLE_IRQ() asm volatile("cpsie i" : : : "memory") 22 | #elif defined(__CCRH__) 23 | #define MG_RH850_DISABLE_IRQ() __DI() 24 | #define MG_RH850_ENABLE_IRQ() __EI() 25 | #else 26 | #define MG_ARM_DISABLE_IRQ() 27 | #define MG_ARM_ENABLE_IRQ() 28 | #endif 29 | 30 | #define MG_FLASH_CONTROLLER_ADDR 0x40022000 // Flash controller base address 31 | #define FLASH_KEYR (MG_FLASH_CONTROLLER_ADDR + 0x4) // See RM0481 7.11 32 | #define FLASH_OPTKEYR (MG_FLASH_CONTROLLER_ADDR + 0xc) 33 | #define FLASH_OPTCR (MG_FLASH_CONTROLLER_ADDR + 0x1c) 34 | #define FLASH_NSSR (MG_FLASH_CONTROLLER_ADDR + 0x20) 35 | #define FLASH_NSCR (MG_FLASH_CONTROLLER_ADDR + 0x28) 36 | #define FLASH_NSCCR (MG_FLASH_CONTROLLER_ADDR + 0x30) 37 | #define FLASH_OPTSR_CUR (MG_FLASH_CONTROLLER_ADDR + 0x50) 38 | #define FLASH_OPTSR_PRG (MG_FLASH_CONTROLLER_ADDR + 0x54) 39 | 40 | static inline void flash_unlock(void) { 41 | if (MG_REG(FLASH_NSCR) & MG_BIT(0)) { 42 | MG_REG(FLASH_KEYR) = 0x45670123; 43 | MG_REG(FLASH_KEYR) = 0xcdef89ab; 44 | MG_REG(FLASH_OPTKEYR) = 0x08192a3b; 45 | MG_REG(FLASH_OPTKEYR) = 0x4c5d6e7f; 46 | } 47 | } 48 | 49 | static inline int flash_page_start(volatile uint32_t *dst) { 50 | char *base = (char *) FLASH_START_ADDR, *end = base + FLASH_TOTAL_SIZE; 51 | volatile char *p = (char *) dst; 52 | return p >= base && p < end && ((p - base) % FLASH_BLOCK_SIZE) == 0; 53 | } 54 | 55 | static inline bool flash_is_err(void) { 56 | return MG_REG(FLASH_NSSR) & ((MG_BIT(8) - 1) << 17); // RM0481 7.11.9 57 | } 58 | 59 | static inline void flash_wait(void) { 60 | while ((MG_REG(FLASH_NSSR) & MG_BIT(0)) && 61 | (MG_REG(FLASH_NSSR) & MG_BIT(16)) == 0) { 62 | (void) 0; 63 | } 64 | } 65 | 66 | static inline void flash_clear_err(void) { 67 | flash_wait(); // Wait until ready 68 | MG_REG(FLASH_NSCCR) = ((MG_BIT(9) - 1) << 16U); // Clear all errors 69 | } 70 | 71 | static inline bool flash_bank_is_swapped(void) { 72 | return MG_REG(FLASH_OPTCR) & MG_BIT(31); // RM0481 7.11.8 73 | } 74 | 75 | static inline bool flash_erase_block(void *location) { 76 | bool ok = false; 77 | if (flash_page_start(location) == false) { 78 | MG_ERROR(("%p is not on a sector boundary", location)); 79 | } else { 80 | uintptr_t diff = (char *) location - (char *) FLASH_START_ADDR; 81 | uint32_t sector = diff / FLASH_BLOCK_SIZE; 82 | flash_unlock(); 83 | flash_clear_err(); 84 | MG_REG(FLASH_NSCR) = 0; 85 | if ((sector < 128 && flash_bank_is_swapped()) || 86 | (sector > 127 && !flash_bank_is_swapped())) { 87 | MG_REG(FLASH_NSCR) |= MG_BIT(31); // Set FLASH_CR_BKSEL 88 | } 89 | if (sector > 127) sector -= 128; 90 | MG_REG(FLASH_NSCR) |= MG_BIT(2) | (sector << 6); // Erase | sector_num 91 | MG_REG(FLASH_NSCR) |= MG_BIT(5); // Start erasing 92 | flash_wait(); 93 | ok = !flash_is_err(); 94 | MG_DEBUG(("Erase sector %lu @ %p: %s. CR %#lx SR %#lx", sector, location, 95 | ok ? "ok" : "fail", MG_REG(FLASH_NSCR), MG_REG(FLASH_NSSR))); 96 | // mg_hexdump(location, 32); 97 | MG_REG(FLASH_NSCR) = 0; // Restore saved CR 98 | } 99 | return ok; 100 | } 101 | 102 | static inline bool flash_swap_bank(void) { 103 | uint32_t desired = flash_bank_is_swapped() ? 0 : MG_BIT(31); 104 | flash_unlock(); 105 | flash_clear_err(); 106 | // printf("OPTSR_PRG 1 %#lx\n", FLASH->OPTSR_PRG); 107 | MG_SET_BITS(MG_REG(FLASH_OPTSR_PRG), MG_BIT(31), desired); 108 | // printf("OPTSR_PRG 2 %#lx\n", FLASH->OPTSR_PRG); 109 | MG_REG(FLASH_OPTCR) |= MG_BIT(1); // OPTSTART 110 | while ((MG_REG(FLASH_OPTSR_CUR) & MG_BIT(31)) != desired) (void) 0; 111 | return true; 112 | } 113 | 114 | static inline size_t flash_write_buf(void *addr, const void *buf, size_t len) { 115 | if ((len % FLASH_ALIGN_SIZE) != 0) { 116 | MG_ERROR(("%u is not aligned to %u", len, FLASH_ALIGN_SIZE)); 117 | return 0; 118 | } 119 | uint32_t *dst = (uint32_t *) addr; 120 | uint32_t *src = (uint32_t *) buf; 121 | uint32_t *end = (uint32_t *) ((char *) buf + len); 122 | bool ok = true; 123 | MG_ARM_DISABLE_IRQ(); 124 | flash_unlock(); 125 | flash_clear_err(); 126 | MG_REG(FLASH_NSCR) = MG_BIT(1); // Set programming flag 127 | while (ok && src < end) { 128 | #if 0 129 | if (flash_page_start(dst) && flash_erase_block(dst) == false) { 130 | ok = false; 131 | break; 132 | } 133 | #endif 134 | *(volatile uint32_t *) dst++ = *src++; 135 | flash_wait(); 136 | if (flash_is_err()) { 137 | ok = false; 138 | break; 139 | } 140 | } 141 | MG_ARM_ENABLE_IRQ(); 142 | MG_DEBUG(("Flash write %zu bytes @ %p: %s. CR %#lx SR %#lx", len, dst, 143 | flash_is_err() ? "fail" : "ok", MG_REG(FLASH_NSCR), 144 | MG_REG(FLASH_NSSR))); 145 | MG_REG(FLASH_NSCR) = 0; // Restore CR 146 | return (size_t) ((char *) src - (char *) buf); 147 | } 148 | -------------------------------------------------------------------------------- /templates/lfs/nucleo-h563zi/link.ld: -------------------------------------------------------------------------------- 1 | ENTRY(Reset_Handler); 2 | MEMORY { 3 | flash(rx) : ORIGIN = 0x08000000, LENGTH = 2048k 4 | sram(rwx) : ORIGIN = 0x20000000, LENGTH = 640k 5 | } 6 | _estack = ORIGIN(sram) + LENGTH(sram); /* End of RAM. stack points here */ 7 | 8 | SECTIONS { 9 | .vectors : { KEEP(*(.isr_vector)) } > flash 10 | .text : { *(.text* .text.*) } > flash 11 | .rodata : { *(.rodata*) } > flash 12 | 13 | .data : { 14 | _sdata = .; 15 | *(.first_data) 16 | *(.ram) 17 | *(.data SORT(.data.*)) 18 | _edata = .; 19 | } > sram AT > flash 20 | _sidata = LOADADDR(.data); 21 | 22 | .bss : { 23 | _sbss = .; 24 | *(.bss SORT(.bss.*) COMMON) 25 | _ebss = .; 26 | } > sram 27 | 28 | . = ALIGN(8); 29 | _end = .; 30 | } 31 | -------------------------------------------------------------------------------- /templates/lfs/nucleo-h563zi/main.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022-2023 Cesanta Software Limited 2 | // SPDX-License-Identifier: MIT 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "hal.h" 10 | 11 | #define LED1 PIN('B', 0) // On-board LED pin (green) 12 | #define LED2 PIN('F', 4) // On-board LED pin (yellow) 13 | #define LED3 PIN('G', 4) // On-board LED pin (red) 14 | 15 | #define BLINK_PERIOD_MS 500 // LED blinking period in millis 16 | #define LOG_PERIOD_MS 5000 // Info log period in millis 17 | #define DATA_DIR "/data" 18 | #define DATA_FILE DATA_DIR "/boot.txt" 19 | 20 | uint32_t SystemCoreClock; // Required by CMSIS. Holds system core cock value 21 | void SystemInit(void) { // Called automatically by startup code 22 | } 23 | 24 | static volatile uint64_t s_ticks; // Milliseconds since boot 25 | void SysTick_Handler(void) { // SyStick IRQ handler, triggered every 1ms 26 | s_ticks++; 27 | } 28 | 29 | bool timer_expired(volatile uint64_t *t, uint64_t prd, uint64_t now) { 30 | if (now + prd < *t) *t = 0; // Time wrapped? Reset timer 31 | if (*t == 0) *t = now + prd; // Firt poll? Set expiration 32 | if (*t > now) return false; // Not expired yet, return 33 | *t = (now - *t) > prd ? now + prd : *t + prd; // Next expiration time 34 | return true; // Expired, return true 35 | } 36 | 37 | static void list_files(const char *path) { 38 | if (path == NULL || path[0] == '\0') path = "."; 39 | DIR *dirp = opendir(path); 40 | if (dirp != NULL) { 41 | struct dirent *dp; 42 | while ((dp = readdir(dirp)) != NULL) { 43 | if (strcmp(dp->d_name, ".") == 0 || strcmp(dp->d_name, "..") == 0) continue; 44 | printf(" %s/%s%s\n", path, dp->d_name, (dp->d_type & DT_DIR) ? "/" : ""); 45 | if (dp->d_type & DT_DIR) { 46 | char dir[strlen(path) + strlen(dp->d_name) + 2]; 47 | snprintf(dir, sizeof(dir), "%s/%s", path, dp->d_name); 48 | list_files(dir); 49 | } 50 | } 51 | closedir(dirp); 52 | } 53 | } 54 | 55 | static long read_boot_count(const char *path) { 56 | long count = 0; 57 | FILE *fp = fopen(path, "r"); 58 | if (fp != NULL) { 59 | fscanf(fp, "%ld", &count); 60 | fclose(fp); 61 | } else { 62 | printf("Error opening %s: %d\n", path, errno); 63 | } 64 | return count; 65 | } 66 | 67 | static void write_boot_count(const char *path, long count) { 68 | mkdir(DATA_DIR, 0644); 69 | FILE *fp = fopen(path, "w+"); 70 | if (fp != NULL) { 71 | fprintf(fp, "%ld", count); 72 | (void) count; 73 | fclose(fp); 74 | } else { 75 | printf("Error opening %s: %d\n", path, errno); 76 | } 77 | } 78 | 79 | static void led_task(void) { // Blink LED every BLINK_PERIOD_MS 80 | static uint64_t timer = 0; 81 | if (timer_expired(&timer, BLINK_PERIOD_MS, s_ticks)) { 82 | gpio_toggle(LED1); 83 | } 84 | } 85 | 86 | static void log_task(void) { // Print a log every LOG_PERIOD_MS 87 | static uint64_t timer = 0; 88 | if (timer_expired(&timer, LOG_PERIOD_MS, s_ticks)) { 89 | printf("tick: %5lu, CPU %lu MHz, boot count: %ld", 90 | (unsigned long) s_ticks, SystemCoreClock / 1000000, 91 | read_boot_count(DATA_FILE)); 92 | putchar('\n'); 93 | } 94 | } 95 | 96 | int main(void) { 97 | system_init(); 98 | clock_init(); 99 | 100 | SystemCoreClock = SYS_FREQUENCY; 101 | SysTick_Config(SystemCoreClock / 1000); // Sys tick every 1ms 102 | 103 | rng_init(); 104 | ethernet_init(); 105 | 106 | gpio_output(LED1); 107 | uart_init(UART_DEBUG, 115200); 108 | 109 | printf("Starting, CPU clock is %lu MHz\n", SystemCoreClock / 1000000); 110 | 111 | // Increment boot count 112 | write_boot_count(DATA_FILE, read_boot_count(DATA_FILE) + 1); 113 | list_files("/"); 114 | 115 | for (;;) { 116 | led_task(); 117 | log_task(); 118 | } 119 | 120 | return 0; 121 | } 122 | -------------------------------------------------------------------------------- /templates/lfs/nucleo-h563zi/sys/dirent.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | #define DT_DIR 4 5 | #define DT_REG 8 6 | 7 | struct dirent { 8 | char d_name[FILENAME_MAX]; 9 | unsigned char d_type; 10 | }; 11 | 12 | typedef struct { 13 | struct dirent result; 14 | } DIR; 15 | -------------------------------------------------------------------------------- /templates/lfs/nucleo-h563zi/syscalls.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2024 Cesanta Software Limited 2 | // All rights reserved 3 | 4 | #include "hal.h" 5 | 6 | #include // For _fstat() 7 | 8 | __attribute__((weak)) int _fstat(int fd, struct stat *st) { 9 | (void) fd, (void) st; 10 | return -1; 11 | } 12 | 13 | __attribute__((weak)) void *_sbrk(int incr) { 14 | unsigned char *prev_heap; 15 | unsigned char *heap_end = (unsigned char *) ((size_t) &heap_end - 256); 16 | prev_heap = s_current_heap_end; 17 | // Check how much space we got from the heap end to the stack end 18 | if (s_current_heap_end + incr > heap_end) return (void *) -1; 19 | s_current_heap_end += incr; 20 | return prev_heap; 21 | } 22 | 23 | __attribute__((weak)) int _open(const char *path) { 24 | (void) path; 25 | return -1; 26 | } 27 | 28 | __attribute__((weak)) int _close(int fd) { 29 | (void) fd; 30 | return -1; 31 | } 32 | 33 | __attribute__((weak)) int _isatty(int fd) { 34 | (void) fd; 35 | return 1; 36 | } 37 | 38 | __attribute__((weak)) int _lseek(int fd, int ptr, int dir) { 39 | (void) fd, (void) ptr, (void) dir; 40 | return 0; 41 | } 42 | 43 | __attribute__((weak)) void _exit(int status) { 44 | (void) status; 45 | for (;;) asm volatile("BKPT #0"); 46 | } 47 | 48 | __attribute__((weak)) void _kill(int pid, int sig) { 49 | (void) pid, (void) sig; 50 | } 51 | 52 | __attribute__((weak)) int _getpid(void) { 53 | return -1; 54 | } 55 | 56 | __attribute__((weak)) int _write(int fd, char *ptr, int len) { 57 | (void) fd, (void) ptr, (void) len; 58 | return -1; 59 | } 60 | 61 | __attribute__((weak)) int _read(int fd, char *ptr, int len) { 62 | (void) fd, (void) ptr, (void) len; 63 | return -1; 64 | } 65 | 66 | __attribute__((weak)) int _link(const char *a, const char *b) { 67 | (void) a, (void) b; 68 | return -1; 69 | } 70 | 71 | __attribute__((weak)) int _unlink(const char *a) { 72 | (void) a; 73 | return -1; 74 | } 75 | 76 | __attribute__((weak)) int _stat(const char *path, struct stat *st) { 77 | (void) path, (void) st; 78 | return -1; 79 | } 80 | 81 | __attribute__((weak)) void _init(void) { 82 | } 83 | -------------------------------------------------------------------------------- /templates/lfs/nucleo-l432kc/Makefile: -------------------------------------------------------------------------------- 1 | CFLAGS ?= -W -Wall -Wextra -Werror -Wundef -Wshadow -Wdouble-promotion -Wformat-truncation -fno-common 2 | CFLAGS += -g3 -Os -ffunction-sections -fdata-sections -Wno-shadow 3 | CFLAGS += -I. -Icmsis_core/CMSIS/Core/Include -Icmsis_l4/Include -Ilittlefs 4 | CFLAGS += -mcpu=cortex-m4 -mthumb -mfloat-abi=hard -mfpu=fpv4-sp-d16 5 | LDFLAGS ?= -Tlink.ld -nostartfiles -nostdlib --specs nano.specs -lc -lgcc -Wl,--gc-sections -Wl,-Map=$@.map 6 | SOURCES = main.c syscalls.c littlefs/lfs.c littlefs/lfs_util.c 7 | SOURCES += cmsis_l4/Source/Templates/gcc/startup_stm32l432xx.s # ST startup file. Compiler-dependent! 8 | 9 | ifeq ($(OS),Windows_NT) 10 | RM = cmd /C del /Q /F 11 | else 12 | RM = rm -rf 13 | endif 14 | 15 | build: firmware.bin 16 | 17 | firmware.elf: cmsis_core cmsis_l4 hal.h link.ld Makefile $(SOURCES) 18 | arm-none-eabi-gcc $(SOURCES) $(CFLAGS) $(CFLAGS_EXTRA) $(LDFLAGS) -o $@ 19 | 20 | firmware.bin: firmware.elf 21 | arm-none-eabi-objcopy -O binary $< $@ 22 | 23 | flash: firmware.bin 24 | st-flash --reset write $< 0x8000000 25 | 26 | cmsis_core: 27 | git clone -q -c advice.detachedHead=false --depth 1 -b 5.9.0 https://github.com/ARM-software/CMSIS_5 $@ 28 | 29 | cmsis_l4: 30 | git clone -q -c advice.detachedHead=false --depth 1 -b v1.7.2 https://github.com/STMicroelectronics/cmsis_device_l4 $@ 31 | 32 | littlefs/lfs.c: littlefs 33 | littlefs: 34 | git clone -q -c advice.detachedHead=false --depth 1 -b v2.6.1 https://github.com/littlefs-project/littlefs $@ 35 | 36 | clean: 37 | $(RM) firmware.* cmsis_* littlefs 38 | 39 | # Automated test via https://vcon.io/automated-firmware-tests/. Set VCON_API_KEY and update DEVICE_URL 40 | DEVICE_URL ?= https://dash.vcon.io/api/v3/devices/7 41 | update: CFLAGS += -DUART_DEBUG=USART1 42 | update: firmware.bin 43 | curl --fail-with-body -su :$(VCON_API_KEY) $(DEVICE_URL)/ota --data-binary @$< 44 | test: update 45 | curl --fail-with-body -su :$(VCON_API_KEY) $(DEVICE_URL)/tx?t=5 | tee /tmp/output.txt 46 | egrep '^tick:.*CPU 16 MHz' /tmp/output.txt 47 | watch: update 48 | curl --fail-with-body -su :$(VCON_API_KEY) $(DEVICE_URL)/tx?t=999 49 | -------------------------------------------------------------------------------- /templates/lfs/nucleo-l432kc/link.ld: -------------------------------------------------------------------------------- 1 | ENTRY(Reset_Handler); 2 | MEMORY { 3 | flash(rx) : ORIGIN = 0x08000000, LENGTH = 256k 4 | sram(rwx) : ORIGIN = 0x20000000, LENGTH = 64k 5 | } 6 | _estack = ORIGIN(sram) + LENGTH(sram); /* stack points to end of SRAM */ 7 | _eflash = ORIGIN(flash) + LENGTH(flash); /* points to end of flash */ 8 | 9 | SECTIONS { 10 | .vectors : { KEEP(*(.isr_vector)) } > flash 11 | .text : { *(.text* .text.*) } > flash 12 | .rodata : { *(.rodata*) } > flash 13 | .data : { _sdata = .; *(.first_data) *(.data SORT(.data.*)) _edata = .; } > sram AT > flash 14 | _sidata = LOADADDR(.data); 15 | .bss : { _sbss = .; *(.bss SORT(.bss.*) COMMON) _ebss = .; } > sram 16 | . = ALIGN(8); 17 | _end = .; 18 | } 19 | -------------------------------------------------------------------------------- /templates/lfs/nucleo-l432kc/main.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022-2023 Cesanta Software Limited 2 | // SPDX-License-Identifier: MIT 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "hal.h" 10 | 11 | #define BLINK_PERIOD_MS 500 // LED blinking period in millis 12 | #define LOG_PERIOD_MS 5000 // Info log period in millis 13 | #define DATA_DIR "/data" 14 | #define DATA_FILE DATA_DIR "/boot.txt" 15 | 16 | uint32_t SystemCoreClock; // Required by CMSIS. Holds system core cock value 17 | void SystemInit(void) { // Called automatically by startup code 18 | clock_init(); // Sets SystemCoreClock 19 | } 20 | 21 | static volatile uint64_t s_ticks; // Milliseconds since boot 22 | void SysTick_Handler(void) { // SyStick IRQ handler, triggered every 1ms 23 | s_ticks++; 24 | } 25 | 26 | static void list_files(const char *path) { 27 | if (path == NULL || path[0] == '\0') path = "."; 28 | DIR *dirp = opendir(path); 29 | if (dirp != NULL) { 30 | struct dirent *dp; 31 | while ((dp = readdir(dirp)) != NULL) { 32 | printf(" %s%s", dp->d_name, (dp->d_type & DT_DIR) ? "/" : ""); 33 | } 34 | closedir(dirp); 35 | } 36 | } 37 | 38 | static long read_boot_count(const char *path) { 39 | long count = 0; 40 | FILE *fp = fopen(path, "r"); 41 | if (fp != NULL) { 42 | fscanf(fp, "%ld", &count); 43 | fclose(fp); 44 | } else { 45 | printf("Error opening %s: %d\n", path, errno); 46 | } 47 | return count; 48 | } 49 | 50 | static void write_boot_count(const char *path, long count) { 51 | mkdir(DATA_DIR, 0644); 52 | FILE *fp = fopen(path, "w+"); 53 | if (fp != NULL) { 54 | fprintf(fp, "%ld", count); 55 | (void) count; 56 | fclose(fp); 57 | } else { 58 | printf("Error opening %s: %d\n", path, errno); 59 | } 60 | } 61 | 62 | static void led_task(void) { // Blink LED every BLINK_PERIOD_MS 63 | static uint64_t timer = 0; 64 | if (timer_expired(&timer, BLINK_PERIOD_MS, s_ticks)) { 65 | gpio_toggle(LED_PIN); 66 | } 67 | } 68 | 69 | static void log_task(void) { // Print a log every LOG_PERIOD_MS 70 | static uint64_t timer = 0; 71 | if (timer_expired(&timer, LOG_PERIOD_MS, s_ticks)) { 72 | printf("tick: %5lu, CPU %lu MHz, boot count: %ld, files: ", 73 | (unsigned long) s_ticks, SystemCoreClock / 1000000, 74 | read_boot_count(DATA_FILE)); 75 | list_files("/"); 76 | list_files("/data"); 77 | putchar('\n'); 78 | } 79 | } 80 | 81 | int main(void) { 82 | gpio_output(LED_PIN); 83 | uart_init(UART_DEBUG, 115200); 84 | 85 | // Increment boot count 86 | write_boot_count(DATA_FILE, read_boot_count(DATA_FILE) + 1); 87 | 88 | for (;;) { 89 | led_task(); 90 | log_task(); 91 | } 92 | 93 | return 0; 94 | } 95 | -------------------------------------------------------------------------------- /templates/lfs/nucleo-l432kc/sys/dirent.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | #define DT_DIR 4 5 | #define DT_REG 8 6 | 7 | struct dirent { 8 | char d_name[FILENAME_MAX]; 9 | unsigned char d_type; 10 | }; 11 | 12 | typedef struct { 13 | struct dirent result; 14 | } DIR; 15 | --------------------------------------------------------------------------------