├── .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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------