Skip to content

Commit 32af21e

Browse files
committed
Implement SUP-based AVR bootloader and Python loader script
1 parent 652bf62 commit 32af21e

File tree

21 files changed

+1446
-68
lines changed

21 files changed

+1446
-68
lines changed

.gitignore

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,7 @@ build/
66

77
# ...but do NOT ignore the cmake-kits.json file and the parent folder
88
!.vscode/
9-
!/.vscode/cmake-kits.json
9+
!/.vscode/cmake-kits.json
10+
11+
# Ignore Python cache files
12+
__pycache__/

CMakeLists.txt

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ set(MCU atmega328p)
1818
set(F_CPU 16000000UL)
1919

2020
# UART Baud rate settings
21-
set(BAUD 9600)
21+
set(BAUD 57600)
2222

2323
# Programmer for avrdude
2424
set(AVRDUDE_PROGRAMMER usbasp)
@@ -28,24 +28,47 @@ set(CMAKE_C_STANDARD 11)
2828
set(CMAKE_C_STANDARD_REQUIRED ON)
2929
set(CMAKE_C_EXTENSIONS OFF)
3030

31+
# Set the start address for the bootloader (Boot Reset Vector) for ATmega328P
32+
# Fuse bits must be set accordingly to enable the boot reset vector (BOOTRST = 0)
33+
# and set bootloader section size.
34+
# BOOTSZ0 = 0, BOOTSZ1 = 1 for 1024 words (0x3C00 * 2 = 0x7800 for ATmega328P)
35+
set(BOOTLOADER_START_ADDRESS 0x7800)
36+
37+
# Fuse bits for ATmega328P
38+
# Default values are set for a 16MHz external crystal with 1024 words bootloader size
39+
# CAUTION: Setting incorrect fuse bits can brick your microcontroller.
40+
# https://www.engbedded.com/fusecalc/
41+
set(LFUSE 0xFF)
42+
set(HFUSE 0xDA)
43+
set(EFUSE 0xFD)
44+
3145
# Common compiler flags for AVR targets
32-
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Os -mmcu=${MCU} -DF_CPU=${F_CPU} -DBAUD=${BAUD}")
46+
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Os -mmcu=${MCU} -DF_CPU=${F_CPU} -DBAUD=${BAUD} -DMAX_USER_APP_SIZE=${BOOTLOADER_START_ADDRESS}")
3347

3448
# Add subprojects
3549
add_subdirectory(blinky)
3650
add_subdirectory(bootloader_hardcoded)
3751
add_subdirectory(simple_uart_protocol)
52+
add_subdirectory(final)
3853

3954
# Clean all build files and directories
4055
add_custom_target(
41-
clean_build
56+
all_clean_build
4257
COMMAND ${CMAKE_COMMAND} -E remove_directory "${CMAKE_BINARY_DIR}"
4358
COMMENT "[[${PROJECT_NAME}]] Cleaning the entire build directory..."
4459
)
4560

4661
# Fully erase the flash and EEPROM (if present) of the microcontroller
4762
add_custom_target(
48-
erase_flash
63+
all_erase_flash
4964
COMMAND avrdude -c ${AVRDUDE_PROGRAMMER} -p ${MCU} -e
5065
COMMENT "[[${PROJECT_NAME}]] Erasing the flash and EEPROM of \"${MCU}\" using \"${AVRDUDE_PROGRAMMER}\""
51-
)
66+
)
67+
68+
# Write fuse bits
69+
add_custom_target(
70+
all_write_fusebits
71+
COMMAND avrdude -c ${AVRDUDE_PROGRAMMER} -p ${MCU} -U lfuse:w:${LFUSE}:m -U hfuse:w:${HFUSE}:m -U efuse:w:${EFUSE}:m
72+
COMMENT "[[${PROJECT_NAME}]] Writing fuse bits to \"${MCU}\" using \"${AVRDUDE_PROGRAMMER}\""
73+
COMMENT "[[${PROJECT_NAME}]] Fuse bits set to: L=${LFUSE}, H=${HFUSE}, E=${EFUSE}"
74+
)

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ int main(void)
5959
```
6060
cd blinky
6161
mkdir build
62-
avr-gcc -Wall -Os -mmcu=atmega328p -std=c11 -o build/main.o -c src/main.c
62+
avr-gcc -Wall -Os -mmcu=atmega328p -std=c11 -o build/main.o -c main.c
6363
avr-gcc -Wall -Os -mmcu=atmega328p -std=c11 -o build/program.elf build/main.o
6464
```
6565
@@ -323,7 +323,7 @@ avrdude -c usbasp -p m328p -U lfuse:w:0xFF:m -U hfuse:w:0xDA:m -U efuse:w:0xFD:m
323323
Adding `-Wl,-section-start=.text=0x7800` flags to linker options of AVR-GCC makes start address of the bootloader program to be set on the start address of boot section.
324324

325325
```
326-
avr-gcc -Wall -Os -mmcu=atmega328p -std=c11 -o build/main.o -c src/main.c
326+
avr-gcc -Wall -Os -mmcu=atmega328p -std=c11 -o build/main.o -c main.c
327327
avr-gcc -Wall -Os -mmcu=atmega328p -std=c11 -Wl,-section-start=.text=0x7800 -o build/program.elf build/main.o
328328
```
329329

blinky/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ cmake_minimum_required(VERSION 3.13)
44
get_filename_component(PROJECT_NAME ${CMAKE_CURRENT_SOURCE_DIR} NAME)
55
project(${PROJECT_NAME} C)
66

7-
add_executable(${PROJECT_NAME}.elf src/main.c)
7+
add_executable(${PROJECT_NAME}.elf main.c)
88

99
add_custom_target(
1010
${PROJECT_NAME}_build ALL
File renamed without changes.

bootloader_hardcoded/CMakeLists.txt

Lines changed: 2 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -5,23 +5,11 @@ cmake_minimum_required(VERSION 3.13)
55
get_filename_component(PROJECT_NAME ${CMAKE_CURRENT_SOURCE_DIR} NAME)
66
project(${PROJECT_NAME} C)
77

8-
# Set the start address for the bootloader (Boot Reset Vector) for ATmega328P
9-
# Fuse bits must be set accordingly to enable the bootloader section
10-
# BOOTSZ0 = 0, BOOTSZ1 = 1 for 1024 words (0x3C00 * 2 = 0x7800 for ATmega328P)
11-
set(BOOTLOADER_START_ADDRESS 0x7800)
12-
13-
# Fuse bits for ATmega328P
14-
# Default values are set for a 16MHz external crystal with 1024 words bootloader size
15-
# CAUTION: Setting incorrect fuse bits can brick your microcontroller.
16-
# https://www.engbedded.com/fusecalc/
17-
set(LFUSE 0xFF)
18-
set(HFUSE 0xDA)
19-
set(EFUSE 0xFD)
20-
8+
# Linker flags to set the bootloader start address
219
set(CMAKE_EXE_LINKER_FLAGS
2210
"${CMAKE_EXE_LINKER_FLAGS} -Wl,-section-start=.text=${BOOTLOADER_START_ADDRESS}")
2311

24-
add_executable(${PROJECT_NAME}.elf src/main.c)
12+
add_executable(${PROJECT_NAME}.elf main.c)
2513

2614
# Build the hex and bin files
2715
# which are needed for flashing the bootloader
@@ -34,14 +22,6 @@ add_custom_target(
3422
COMMENT "[[${PROJECT_NAME}]] Building .hex and .bin files for \"${MCU}\""
3523
)
3624

37-
# Write fuse bits
38-
add_custom_target(
39-
${PROJECT_NAME}_write_fusebits
40-
COMMAND avrdude -c ${AVRDUDE_PROGRAMMER} -p ${MCU} -U lfuse:w:${LFUSE}:m -U hfuse:w:${HFUSE}:m -U efuse:w:${EFUSE}:m
41-
COMMENT "[[${PROJECT_NAME}]] Writing fuse bits to \"${MCU}\" using \"${AVRDUDE_PROGRAMMER}\""
42-
COMMENT "[[${PROJECT_NAME}]] Fuse bits set to: L=${LFUSE}, H=${HFUSE}, E=${EFUSE}"
43-
)
44-
4525
# Flash the bootloader to the microcontroller
4626
add_custom_target(
4727
${PROJECT_NAME}_flash

bootloader_hardcoded/src/main.c renamed to bootloader_hardcoded/main.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,7 @@ static bool write_to_flash(const uint32_t destination_address, const uint8_t* so
172172
// Fill word into page buffer
173173
boot_page_fill(page_addr + offset, word_data);
174174
}
175-
175+
176176
// Write page to flash
177177
boot_page_write(page_addr);
178178
// Wait for the write operation to complete
@@ -262,8 +262,8 @@ int main(void)
262262
// Loading failed - infinite error blink
263263
while (true)
264264
{
265-
//led_blink(FAILURE_BLINKS_COUNT, FAILURE_BLINK_DELAY_MS);
266-
//_delay_ms(2000);
265+
led_blink(FAILURE_BLINKS_COUNT, FAILURE_BLINK_DELAY_MS);
266+
_delay_ms(2000);
267267
}
268268
}
269269
}

final/CMakeLists.txt

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
2+
cmake_minimum_required(VERSION 3.13)
3+
4+
# Dynamically set project name from directory name
5+
get_filename_component(PROJECT_NAME ${CMAKE_CURRENT_SOURCE_DIR} NAME)
6+
project(${PROJECT_NAME} C)
7+
8+
add_executable(${PROJECT_NAME}_bootloader.elf bootloader/main.c ../simple_uart_protocol/sup.c)
9+
add_executable(${PROJECT_NAME}_user_app.elf user_app/main.c ../simple_uart_protocol/sup.c)
10+
11+
# Use target_link_options to apply linker flags ONLY to the bootloader.
12+
# This is the correct, modern CMake way to handle this.
13+
target_link_options(${PROJECT_NAME}_bootloader.elf PRIVATE
14+
"-Wl,-section-start=.text=${BOOTLOADER_START_ADDRESS}"
15+
)
16+
17+
# Place a small dedicated section ".bootflag" at a fixed SRAM address so that
18+
# both bootloader and user application can reliably share a RAM-resident flag
19+
# across software resets without using EEPROM. Address chosen near top of SRAM
20+
# for ATmega328P; adjust if you use a different MCU.
21+
target_link_options(${PROJECT_NAME}_bootloader.elf PRIVATE
22+
"-Wl,--section-start=.bootflag=0x08F8"
23+
)
24+
25+
target_link_options(${PROJECT_NAME}_user_app.elf PRIVATE
26+
"-Wl,--section-start=.bootflag=0x08F8"
27+
)
28+
29+
# Generate a linker map file next to each ELF. Use the output file name derived from
30+
# the target name so the map is easy to find (e.g. final_bootloader.elf -> final_bootloader.map)
31+
target_link_options(${PROJECT_NAME}_bootloader.elf PRIVATE
32+
"-Wl,-Map=${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}_bootloader.map"
33+
)
34+
35+
target_link_options(${PROJECT_NAME}_user_app.elf PRIVATE
36+
"-Wl,-Map=${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}_user_app.map"
37+
)
38+
39+
# Build the hex and bin files
40+
# which are needed for flashing the bootloader
41+
add_custom_target(
42+
${PROJECT_NAME}_build_bootloader ALL
43+
COMMAND avr-objcopy -j .text -j .data -O ihex ${PROJECT_NAME}_bootloader.elf ${PROJECT_NAME}_bootloader.hex
44+
COMMAND avr-objcopy -j .text -j .data -O binary ${PROJECT_NAME}_bootloader.elf ${PROJECT_NAME}_bootloader.bin
45+
COMMAND avr-size --format=avr --mcu=${MCU} ${PROJECT_NAME}_bootloader.elf
46+
DEPENDS ${PROJECT_NAME}_bootloader.elf
47+
COMMENT "[[${PROJECT_NAME}]] Building bootloader .hex and .bin files for \"${MCU}\""
48+
)
49+
50+
add_custom_target(
51+
${PROJECT_NAME}_build_user_app ALL
52+
COMMAND avr-objcopy -j .text -j .data -O ihex ${PROJECT_NAME}_user_app.elf ${PROJECT_NAME}_user_app.hex
53+
COMMAND avr-objcopy -j .text -j .data -O binary ${PROJECT_NAME}_user_app.elf ${PROJECT_NAME}_user_app.bin
54+
COMMAND avr-size --format=avr --mcu=${MCU} ${PROJECT_NAME}_user_app.elf
55+
DEPENDS ${PROJECT_NAME}_user_app.elf
56+
COMMENT "[[${PROJECT_NAME}]] Building user application .hex and .bin files for \"${MCU}\""
57+
)
58+
59+
add_custom_target(
60+
${PROJECT_NAME}_build_all ALL
61+
DEPENDS ${PROJECT_NAME}_build_bootloader ${PROJECT_NAME}_build_user_app
62+
COMMENT "[[${PROJECT_NAME}]] Building all .hex and .bin files for \"${MCU}\""
63+
)
64+
65+
# Flash the bootloader to the microcontroller
66+
add_custom_target(
67+
${PROJECT_NAME}_flash_bootloader
68+
COMMAND avrdude -c ${AVRDUDE_PROGRAMMER} -p ${MCU} -U flash:w:${PROJECT_NAME}_bootloader.hex:i
69+
DEPENDS ${PROJECT_NAME}_build_bootloader
70+
COMMENT "[[${PROJECT_NAME}]] Flashing the bootloader to \"${MCU}\" using \"${AVRDUDE_PROGRAMMER}\""
71+
COMMENT "[[${PROJECT_NAME}]] Bootloader start address: 0x${BOOTLOADER_START_ADDRESS}"
72+
)
73+
74+
# Flash the user application to the microcontroller
75+
add_custom_target(
76+
${PROJECT_NAME}_flash_user_app
77+
COMMAND avrdude -c ${AVRDUDE_PROGRAMMER} -p ${MCU} -U flash:w:${PROJECT_NAME}_user_app.hex:i
78+
DEPENDS ${PROJECT_NAME}_build_user_app
79+
COMMENT "[[${PROJECT_NAME}]] Flashing user application to \"${MCU}\" using \"${AVRDUDE_PROGRAMMER}\""
80+
)
81+
82+
# Flash both the bootloader and user application to the microcontroller
83+
add_custom_target(
84+
${PROJECT_NAME}_flash_all
85+
COMMAND avrdude -c ${AVRDUDE_PROGRAMMER} -p ${MCU} -U flash:w:${PROJECT_NAME}_user_app.hex:i -U flash:w:${PROJECT_NAME}_bootloader.hex:i
86+
DEPENDS ${PROJECT_NAME}_build_all
87+
COMMENT "[[${PROJECT_NAME}]] Flashing both bootloader and user application to \"${MCU}\" using \"${AVRDUDE_PROGRAMMER}\""
88+
)

final/boot_sync.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#ifndef BOOT_SYNC_H
2+
#define BOOT_SYNC_H
3+
4+
#include <stdint.h>
5+
6+
/**
7+
* @brief A magic value to indicate a firmware update is requested.
8+
*/
9+
#define FW_UPDATE_INDICATOR_MAGIC 0xDEADBEEFU
10+
11+
extern volatile uint32_t boot_sync_flag;
12+
13+
#endif // BOOT_SYNC_H

0 commit comments

Comments
 (0)