Skip to content

Commit 1e3b960

Browse files
committed
Add a demo for using LVGL, DRAM, LLVM Toolchain, ELF on an STM32F746G discovery board
1 parent 3cd0f63 commit 1e3b960

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

63 files changed

+16029
-20
lines changed

.github/workflows/build-stm.yml

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,16 @@ jobs:
1515
strategy:
1616
fail-fast: false
1717
matrix:
18-
example: [stm32-blink]
18+
example: [stm32-blink, stm32-lvgl]
1919

2020
steps:
2121
- name: Checkout repo
2222
uses: actions/checkout@v4
2323

24+
- name: Fixup for running locally in act
25+
if: ${{ env.ACT }}
26+
run: echo /opt/acttoolcache/node/18.20.8/x64/bin >> $GITHUB_PATH
27+
2428
- name: Set up Python
2529
uses: actions/setup-python@v5
2630
with:
@@ -35,5 +39,12 @@ jobs:
3539
- name: Build ${{ matrix.example }}
3640
working-directory: ${{ matrix.example }}
3741
run: |
38-
export STM_BOARD=STM32F746G_DISCOVERY
39-
./build-elf.sh
42+
if [[ "${{ matrix.example }}" == "stm32-blink" ]]; then
43+
export STM_BOARD=STM32F746G_DISCOVERY
44+
./build-elf.sh
45+
elif [[ "${{ matrix.example }}" == "stm32-lvgl" ]]; then
46+
./fetch-dependencies.sh
47+
make
48+
else
49+
exit 1
50+
fi

.swiftformatignore

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
./harmony/*
2-
./stm32-lcd-logo/Sources/STM32F7X6/*
3-
./stm32-neopixel/Sources/STM32F7X6/*
4-
./stm32-uart-echo/Sources/STM32F7X6/*
2+
./stm32-lcd-logo/Sources/Application/Registers/*
3+
./stm32-lvgl/Sources/Registers/*
4+
./stm32-neopixel/Sources/Application/Registers/*
5+
./stm32-uart-echo/Sources/Application/Registers/*

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ Each example in this repository contains build and deployment instructions, howe
3333
| [rpi-picow-blink-sdk](./rpi-picow-blink-sdk) | Raspberry Pi Pico W | Pico SDK | Blink an LED to signal 'SOS' in Morse code repeatedly with Swift & the Pico SDK. | <img width="300" src="https://github.com/apple/swift-embedded-examples/assets/26223064/a4949a2e-1887-4325-8f5f-a681963c93d7"> |
3434
| [stm32-blink](./stm32-blink) | STM32F746G-DISCO | None | Blink an LED repeatedly. | <img width="300" src="https://github.com/apple/swift-embedded-examples/assets/1186214/739e98fd-a438-4a64-a7aa-9dddee25034b"> |
3535
| [stm32-lcd-logo](./stm32-lcd-logo) | STM32F746G-DISCO | None | Animate the Swift Logo on the built-in LCD. | <img width="300" src="https://github.com/apple/swift-embedded-examples/assets/1186214/9e117d81-e808-493e-a20c-7284ea630f37"> |
36+
| [stm32-lvgl](./stm32-lvgl) | STM32F746G-DISCO || Baremetal setup of LCD, touch panel, DRAM, using the LLVM Embedded toolchain for ARM. Renders graphics, animations, and reacts to user input via LVGL. Includes a macOS/Linux SDL based host simulation app. | <img width="300" src="stm32-lvgl/assets/thumbnail.gif"> |
3637
| [stm32-neopixel](./stm32-neopixel) | STM32F746G-DISCO | None | Control NeoPixel LEDs using SPI. | <img width="300" src="https://github.com/apple/swift-embedded-examples/assets/1186214/9c5d8f74-f8aa-4632-831e-212a3e35e75a"> |
3738
| [stm32-uart-echo](./stm32-uart-echo) | STM32F746G-DISCO | None | Echo user input using UART. | <img width="300" src="https://github.com/apple/swift-embedded-examples/assets/1186214/97d3c465-9a07-4b86-9654-0c2aaaa43b3d">|
3839

Tools/elf2hex.py

Lines changed: 49 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,23 @@
1818
# file format suitable for flashing onto some embedded devices.
1919
#
2020
# Usage:
21-
# $ elf2hex.py <input> <output> [--symbol-map <output>]
21+
# $ elf2hex.py <input> <output> [--symbol-map <output>] [--relocate-data-segment]
2222
#
2323
# Example:
2424
# $ elf2hex.py ./blink ./blink.hex --symbol-map ./blink.symbols
2525
#
26+
# The --relocate-data-segment option expects to be able to locate symbols with names
27+
# - __data_start
28+
# - __flash_data_start
29+
# - __flash_data_len
30+
# and then it physically relocates a segment located at __data_start to
31+
# __flash_data_start, without changing virtual/physical addresses of any ELF
32+
# headers. This means that the .hex file is not validly mapped until a boot-time
33+
# reverse relocation step.
34+
#
35+
# See the linker script used in a particular demo folder for a detailed
36+
# explanation of the linking, packing, and runtime relocation scheme.
37+
#
2638

2739
import argparse
2840
import json
@@ -36,6 +48,7 @@ def main():
3648
parser.add_argument('input')
3749
parser.add_argument('output')
3850
parser.add_argument('--symbol-map')
51+
parser.add_argument('--relocate-data-segment', action='store_true')
3952
args = parser.parse_args()
4053

4154
inf = open(args.input, "rb")
@@ -70,32 +83,54 @@ def emit(vmaddr, data):
7083
vmaddr += chunklen
7184

7285
elffile = elftools.elf.elffile.ELFFile(inf)
73-
for segment in elffile.iter_segments():
74-
if segment.header.p_type != "PT_LOAD":
75-
continue
76-
vmaddr = segment.header.p_paddr
77-
data = segment.data()
78-
emit(segment.header.p_paddr, data)
79-
80-
chunklen = 0
81-
vmaddr = 0
82-
recordtype = "01" # EOF
83-
emitrecord(f"{chunklen:02X}{vmaddr:04X}{recordtype}")
8486

8587
symbol_map = {}
8688
symtab_section = elffile.get_section_by_name(".symtab")
8789
for s in symtab_section.iter_symbols():
8890
if s.entry.st_info.type not in ["STT_FUNC", "STT_NOTYPE"]:
8991
continue
90-
if s.entry.st_shndx == "SHN_ABS":
91-
continue
9292
if s.name == "":
9393
continue
9494
symbol_map[s.name] = s.entry.st_value
9595

9696
if args.symbol_map is not None:
9797
pathlib.Path(args.symbol_map).write_text(json.dumps(symbol_map))
9898

99+
relocations = {}
100+
if args.relocate_data_segment:
101+
__flash_data_start = symbol_map["__flash_data_start"]
102+
__data_start = symbol_map["__data_start"]
103+
__flash_data_len = symbol_map["__flash_data_len"]
104+
print("Relocation info:")
105+
print(f" __flash_data_start = 0x{__flash_data_start:08x}")
106+
print(f" __data_start = 0x{__data_start:08x}")
107+
print(f" __flash_data_len = 0x{__flash_data_len:08x}")
108+
relocations = {__data_start: __flash_data_start}
109+
110+
for segment in elffile.iter_segments():
111+
if segment.header.p_type != "PT_LOAD":
112+
continue
113+
vmaddr = segment.header.p_paddr
114+
data = segment.data()
115+
flags = ""
116+
flags += "r" if segment.header.p_flags & 0x4 else "-"
117+
flags += "w" if segment.header.p_flags & 0x2 else "-"
118+
flags += "x" if segment.header.p_flags & 0x1 else "-"
119+
print(f"PT_LOAD {flags} at 0x{segment.header.p_paddr:08x} - "
120+
f"0x{segment.header.p_paddr + len(data):08x}, "
121+
f"size {len(data)} "
122+
f"(0x{len(data):04x})")
123+
placement_addr = segment.header.p_paddr
124+
if segment.header.p_paddr in relocations:
125+
placement_addr = relocations[segment.header.p_paddr]
126+
print(f" ... relocating to 0x{placement_addr:08x}")
127+
emit(placement_addr, data)
128+
129+
chunklen = 0
130+
vmaddr = 0
131+
recordtype = "01" # EOF
132+
emitrecord(f"{chunklen:02X}{vmaddr:04X}{recordtype}")
133+
99134
inf.close()
100135
outf.close()
101136

stm32-lvgl/.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
lvgl
2+
llvm-toolchain

stm32-lvgl/.sourcekit-lsp/config.json

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{
2+
"swiftPM": {
3+
"configuration": "release",
4+
"triple": "armv7em-none-none-eabi",
5+
6+
"__comment": "XXX SourceKit-LSP seems to ignore the toolset (relative or absolute path)...",
7+
"toolset": ".../toolset.json",
8+
9+
"swiftCompilerFlags": [
10+
"-enable-experimental-feature", "Embedded",
11+
"-enable-experimental-feature", "Extern",
12+
]
13+
}
14+
}

stm32-lvgl/.swift-format

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
{
2+
"fileScopedDeclarationPrivacy" : {
3+
"accessLevel" : "private"
4+
},
5+
"indentation" : {
6+
"spaces" : 2
7+
},
8+
"indentConditionalCompilationBlocks" : false,
9+
"indentSwitchCaseLabels" : false,
10+
"lineBreakAroundMultilineExpressionChainComponents" : false,
11+
"lineBreakBeforeControlFlowKeywords" : false,
12+
"lineBreakBeforeEachArgument" : false,
13+
"lineBreakBeforeEachGenericRequirement" : false,
14+
"lineLength" : 120,
15+
"maximumBlankLines" : 1,
16+
"multiElementCollectionTrailingCommas" : true,
17+
"noAssignmentInExpressions" : {
18+
"allowedFunctions" : [
19+
"XCTAssertNoThrow"
20+
]
21+
},
22+
"prioritizeKeepingFunctionOutputTogether" : false,
23+
"respectsExistingLineBreaks" : true,
24+
"rules" : {
25+
"AllPublicDeclarationsHaveDocumentation" : false,
26+
"AlwaysUseLiteralForEmptyCollectionInit" : true,
27+
"AlwaysUseLowerCamelCase" : false,
28+
"AmbiguousTrailingClosureOverload" : false,
29+
"BeginDocumentationCommentWithOneLineSummary" : false,
30+
"DoNotUseSemicolons" : true,
31+
"DontRepeatTypeInStaticProperties" : true,
32+
"FileScopedDeclarationPrivacy" : true,
33+
"FullyIndirectEnum" : true,
34+
"GroupNumericLiterals" : true,
35+
"IdentifiersMustBeASCII" : true,
36+
"NeverForceUnwrap" : false,
37+
"NeverUseForceTry" : true,
38+
"NeverUseImplicitlyUnwrappedOptionals" : false,
39+
"NoAccessLevelOnExtensionDeclaration" : true,
40+
"NoAssignmentInExpressions" : true,
41+
"NoBlockComments" : false,
42+
"NoCasesWithOnlyFallthrough" : true,
43+
"NoEmptyTrailingClosureParentheses" : true,
44+
"NoLabelsInCasePatterns" : true,
45+
"NoLeadingUnderscores" : false,
46+
"NoParensAroundConditions" : true,
47+
"NoPlaygroundLiterals" : true,
48+
"NoVoidReturnOnFunctionSignature" : true,
49+
"OmitExplicitReturns" : false,
50+
"OneCasePerLine" : true,
51+
"OneVariableDeclarationPerLine" : true,
52+
"OnlyOneTrailingClosureArgument" : true,
53+
"OrderedImports" : true,
54+
"ReplaceForEachWithForLoop" : true,
55+
"ReturnVoidInsteadOfEmptyTuple" : true,
56+
"TypeNamesShouldBeCapitalized" : true,
57+
"UseEarlyExits" : false,
58+
"UseExplicitNilCheckInConditions" : true,
59+
"UseLetInEveryBoundCaseVariable" : true,
60+
"UseShorthandTypeNames" : true,
61+
"UseSingleLinePropertyGetter" : true,
62+
"UseSynthesizedInitializer" : true,
63+
"UseTripleSlashForDocumentationComments" : true,
64+
"UseWhereClausesInForLoops" : false,
65+
"ValidateDocumentationComments" : true
66+
},
67+
"spacesBeforeEndOfLineComments": 2,
68+
"spacesAroundRangeFormationOperators" : false,
69+
"tabWidth" : 2,
70+
"version" : 1
71+
}

stm32-lvgl/.swift-version

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
main-snapshot-2025-03-28

stm32-lvgl/Makefile

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
##===----------------------------------------------------------------------===##
2+
##
3+
## This source file is part of the Swift open source project
4+
##
5+
## Copyright (c) 2023 Apple Inc. and the Swift project authors.
6+
## Licensed under Apache License v2.0 with Runtime Library Exception
7+
##
8+
## See https://swift.org/LICENSE.txt for license information
9+
##
10+
##===----------------------------------------------------------------------===##
11+
12+
# Paths
13+
REPOROOT := $(shell git rev-parse --show-toplevel)
14+
TOOLSROOT := $(REPOROOT)/Tools
15+
TOOLSET := $(PWD)/toolset.json
16+
ELF2HEX := $(TOOLSROOT)/elf2hex.py
17+
SWIFT_BUILD := swift build
18+
NM := nm
19+
LLVM_TOOLCHAIN := $(PWD)/llvm-toolchain
20+
21+
# Flags
22+
ARCH := armv7em
23+
TARGET := $(ARCH)-none-none-eabi
24+
SWIFT_BUILD_ARGS := \
25+
--configuration release \
26+
--triple $(TARGET) \
27+
--toolset $(TOOLSET) \
28+
--product Application
29+
BUILDROOT := $(shell $(SWIFT_BUILD) $(SWIFT_BUILD_ARGS) --show-bin-path)
30+
31+
.PHONY: build
32+
build:
33+
@echo "checking dependencies..."
34+
35+
# TODO: Check that we have swiftly and recent Swift main toolchain
36+
37+
if [[ ! -d $(PWD)/lvgl ]]; then echo "\n *** LVGL checkout not found, please run ./fetch-dependencies.sh\n" ; exit 1 ; fi
38+
if [[ ! -d $(PWD)/llvm-toolchain ]]; then echo "\n *** LLVM toolchain checkout not found, please run ./fetch-dependencies.sh\n" ; exit 1 ; fi
39+
40+
mkdir -p .build
41+
42+
@echo "configuring LVGL..."
43+
cmake -B .build/lvgl -G Ninja ./lvgl \
44+
-DCMAKE_EXPORT_COMPILE_COMMANDS=On \
45+
-DTOOLCHAIN_PATH=$(LLVM_TOOLCHAIN) \
46+
-DCMAKE_TOOLCHAIN_FILE=../clang-arm-toolchain.cmake \
47+
-DLV_CONF_PATH=../Sources/CLVGL/include/lv_conf.h
48+
49+
@echo "building LVGL..."
50+
cmake --build .build/lvgl
51+
52+
@echo "building..."
53+
$(SWIFT_BUILD) \
54+
$(SWIFT_BUILD_ARGS) \
55+
--verbose
56+
57+
@echo "disassembling..."
58+
$(LLVM_TOOLCHAIN)/bin/llvm-objdump --all-headers --disassemble --mcpu=cortex-m7 \
59+
$(BUILDROOT)/Application \
60+
| c++filt | swift demangle > $(BUILDROOT)/Application.disassembly
61+
62+
@echo "extracting binary..."
63+
$(ELF2HEX) \
64+
$(BUILDROOT)/Application $(BUILDROOT)/Application.hex --relocate
65+
ls -al $(BUILDROOT)/Application.hex
66+
@echo "\n *** All done, build succeeded!\n"
67+
68+
flash:
69+
@echo "flashing..."
70+
st-flash --reset --format ihex write $(BUILDROOT)/Application.hex
71+
72+
simulator:
73+
mkdir -p .build
74+
75+
@echo "configuring LVGL..."
76+
cmake -B .build/lvgl-host -G Ninja ./lvgl \
77+
-DCMAKE_EXPORT_COMPILE_COMMANDS=On \
78+
-DLV_CONF_PATH=../Sources/CLVGL/include/lv_conf.h
79+
80+
@echo "building LVGL..."
81+
cmake --build .build/lvgl-host
82+
83+
@echo "building..."
84+
$(SWIFT_BUILD) \
85+
--configuration release \
86+
--product HostSDLApp \
87+
--verbose
88+
89+
@echo "running..."
90+
$(PWD)/.build/release/HostSDLApp
91+
92+
.PHONY: clean
93+
clean:
94+
@echo "cleaning..."
95+
@swift package clean
96+
@rm -rf .build

stm32-lvgl/Package.resolved

Lines changed: 42 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)