You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: README.md
+22-23Lines changed: 22 additions & 23 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -1,24 +1,31 @@
1
1
## Overview
2
2
3
-
This repo demonstrates how to incorporate some best practices into STM32CubeIDE-based projects. It preserves ST's .ioc workflow with autogeneration and easy pin reconfiguration in a git-friendly way, and also showcases:
3
+
This repo demonstrates a strategy for working with STM32CubeIDE-based projects. It preserves ST's .ioc workflow with autogeneration and easy pin reconfiguration in a git-friendly way, and also showcases:
4
4
- C++ used appropriately in a constrained embedded environment (no dynamic allocations - see [no_new.cpp](common/src/no_new.cpp)).
5
5
- Convenience wrappers for static allocation of FreeRTOS components (see [static_rtos.h](common/inc/static_rtos.h))
6
6
- Code deduplication:
7
7
- Linking to versioned vendor firmware
8
8
- Common code shared across projects
9
-
- Unit testing with [CppUTest](https://cpputest.github.io/) (not used extensively yet, [example](common/tests/src/test_basic.cpp))
9
+
- Unit testing with [CppUTest](https://cpputest.github.io/) ([example](common/tests/src/test_software_crc.cpp))
10
10
- Code coverage with lcov
11
11
- Autoformatting with [clang-format](https://clang.llvm.org/docs/ClangFormat.html)
12
12
- ITM debug logging
13
13
- FreeRTOS task profiling
14
-
- High-performance UART and USB communication interface abstractions (see the [loopback](loopback) project)
14
+
- High-performance UART and USB peripheral abstractions (see the [loopback](loopback) project)
15
15
16
16
CI checks on each pull request ensure that all projects compile, pass unit tests, and are formatted correctly. Here are example PRs demonstrating:
-[Passing all checks](https://github.com/milesfrain/stm32template/pull/5)
21
21
22
+
## Projects
23
+
24
+
-[`common`](common) - Common code shared among all projects.
25
+
-[`loopback`](loopback) - Demonstrates sending binary packets across USB and UART peripherals.
26
+
-[`vfd_bench`](vfd_bench) - A more real-world project demonstrating control of VFDs (variable-frequency drives) via modbus commands.
27
+
-[`host_apps`](host_apps) - Applications which run on the host PC for communicating with the target microcontroller.
28
+
22
29
## Linux setup instructions
23
30
24
31
These instructions were verified on a fresh install of Ubuntu 20.04.
@@ -34,7 +41,7 @@ sh st-stm32cubeide_1.4.0_7511_20200720_0928_amd64.sh
34
41
```
35
42
Note that `ctrl-C` will let you jump to the bottom of the licenses.
36
43
37
-
When updating the IDE, it will continue to use the original install path. So rather than requiring constant path editing or full IDE reinstalls for CI script compatibility, we're using a versionless symlinked path that's also compatible with the docker image directory structure. Set up that symlink by running (substitue`1.5.1` for your particular IDE version):
44
+
When updating the IDE, it will continue to use the original install path. So rather than requiring constant path editing or full IDE reinstalls for CI script compatibility, we're using a versionless symlinked path that's also compatible with the docker image directory structure. Set up that symlink by running (substitute`1.5.1` for your particular IDE version):
There was a bug in FreeRTOS message buffers that has recently been [fixed](https://github.com/FreeRTOS/FreeRTOS-Kernel/pull/264); however, this fix has not yet been incorporated into [ST's F4 firmware package](https://github.com/STMicroelectronics/STM32CubeF4). As of writing, ST's F4 firmware V1.26.1 uses FreeRTOS V10.3.1. The FreeRTOS bug affects at least V10.4.1 and below.
114
+
115
+
In the meantime, the simplest solution is to overwrite `Middlewares/Third_Party/FreeRTOS/Source` with a clone of this [patched repo](https://github.com/milesfrain/stm32FreeRTOS).
102
116
103
117
#### Project import
118
+
104
119
Add each folder to this workspace.
105
120
```
106
121
File > Open Projects from Files System
@@ -111,22 +126,6 @@ loopback
111
126
```
112
127
Now try building and flashing each project. Sometimes you'll need to expand the project folder in order for the build hammer to work.
113
128
114
-
### Debugging info
115
-
116
-
When you first debug, you'll see a popup. This default configuration works fine, but if you want to see ITM traces you'll need to enable SWV:
117
-
`Debugger > Serial Wire Viewer`, check `Enable`, set `Core Clock` to `96.0`. This needs to match `SYSCLK` in the .ioc clock configuration.
118
-
119
-
If you skip this step for the first pop-up, you can find the setting later under `Run > Debug Configurations`.
120
-
121
-
To view ITM traces while debugging, open `Window > Show View > SWV > SWV ITM Data Console`. Click on the wrench icon in this new terminal and check `ITM Stimulus Ports``2` and `0`. Logs are written to port `0`, but we're using port `2` as a workaround for this issue:
Here are some notes on the steps to create a new C++ project. You can either start a fresh project, or import an existing .ioc. This guide shows the "from .ioc" method.
@@ -157,7 +156,7 @@ In the `Project Explorer`, right click on the active project and select `Convert
157
156
Copy the `custom` directory over from the `loopback` example. Note that you'll likely need to right-click on your project in the `Project Explorer` and select `Refresh` for this new folder to appear.
158
157
159
158
#### Link to common code
160
-
In the `Project Explorer`, right click on the active project, `New > Folder > Advaced > Link to ...`, paste:
159
+
In the `Project Explorer`, right click on the active project, `New > Folder > Advanced > Link to ...`, paste:
This directory contains common code shared across applications.
4
+
5
+
## Interfaces
6
+
7
+
Common [interfaces](inc/interfaces.h) (specifically `read()` and `write()`) enable composability among hardware abstractions and some FreeRTOS wrappers.
8
+
9
+
See the [loopback](../loopback) and [vfd_bench](../vfd_bench) projects for more usage examples of these interfaces.
10
+
11
+
## Static FreeRTOS Wrappers
12
+
13
+
The STM32 applications in this repo use FreeRTOS. Most FreeRTOS components allow either dynamic or static allocation, with static allocation offering some additional runtime predictability. Unfortunately, static allocation can be more tedious and error-prone with the existing C-API. C++ wrappers simplify the creation of static components, and offer some additional conveniences.
14
+
15
+
See [static_rtos.h](inc/static_rtos.h) for what wrappers are currently available. More wrappers may be added as required.
16
+
17
+
## Hardware Abstractions
18
+
19
+
High-performance hardware abstractions are available for UART and USB peripherals. These abstractions are DMA-based and built on top of FreeRTOS.
20
+
21
+
Common interfaces make it easy to substitute peripherals. For example, it is trivial to swap a UART communications link for USB.
22
+
23
+
See the [loopback](../loopback) project readme for more detailed examples of these abstractions in-use.
24
+
25
+
## Binary Packets
26
+
27
+
Data is sent between devices (e.g. microcontrollers and host PC) and between tasks running on each device as binary c-struct packets.
-`magicStart` - A hardcoded value which improves efficiency of resynchronizing after data corruption.
33
+
-`CRC` - The CRC value of all following bytes in the packet.
34
+
-`length` - The number of bytes in the packet. This excludes the size of the first two `magicStart` and `CRC` fields. Those two fields are considered part of the "Packet Wrapper" and are only applied to data "on the wire". These fields are not passed around internally.
35
+
-`sequenceNum` - An incrementing number assigned to each packet, which is used to check if any packets were dropped.
36
+
-`origin` - Describes where the packet was created.
37
+
-`id` - Describes which packet to use from the following `body` union field.
38
+
-`body` - A union of all packet types. For efficiency, only the bytes used by the particular sub-type are transmitted, rather than always sending bytes for the full union.
39
+
40
+
The packet definitions ([`packets.h`](inc/packets.h)) and parsing, printing, and packing code ([`packet_utils.cpp`](src/packet_utils.cpp)) are shared across all applications in this repo. This avoids packet definition mismatches between applications running on the host PC and target devices.
41
+
42
+
To add new a binary packet, edit the following:
43
+
-[`PacketID`](inc/packets.h)
44
+
-[`Packet`](inc/packets.h)
45
+
-[`packetBodySizeFromID`](inc/packets.h)
46
+
-[`packetIdToString`](inc/packets.h)
47
+
-[`snprintPacket`](src/packet_utils.cpp)
48
+
49
+
50
+
## ITM Logging
51
+
52
+
Tasks may send printf-style log messages over the ITM interface via the `ItmLogger`. Since this is commonly used by all tasks, it is included as part of [TaskUtilities](#Task-Utilities) for convenience.
To see ITM traces in the IDE you'll need to enable SWV: `Run > Debug Configurations > Debugger > Serial Wire Viewer`, check `Enable`, set `Core Clock` to `96.0`. This needs to match `SYSCLK` in the .ioc clock configuration.
57
+
58
+
To view ITM traces while debugging, open `Window > Show View > SWV > SWV ITM Data Console`. Click on the wrench icon in this new terminal and check `ITM Stimulus Ports``31` and `0`. Logs are written to port `0`, but we're using port `31` as a workaround for [this issue](https://community.st.com/s/question/0D53W00000Hx6dxSAB/bug-itm-active-port-ter-defaults-to-port-0-enabled-when-tracing-is-disabled).
59
+
60
+
Then click the red circle to "Start Trace". Note that this can only be toggled when execution is paused. When tracing is disabled, then the expensive printf formatting operation is skipped.
61
+
62
+
<imgsrc="../docs/images/itm-ide.png"width="600">
63
+
64
+
Counters tracking incoming and outgoing byte and packet totals are also logged over ITM. These can be a helpful sanity check for troubleshooting suspected data loss. See the [`ItmPort` enum](inc/itm_logging.h) for port mapping. Unfortunately, the IDE interface is [not as user-friendly as it could be](https://community.st.com/s/question/0D53W00000Y4DuCSAV/log-numeric-data-in-swv-itm-data-console).
65
+
66
+
An area of future work is to completely eliminate formatting operations from the target microcontroller. One possibility is to send an ID representing the particular format string, along with the raw arguments to the host for formatting.
67
+
68
+
Note that [binary packets](#binary-packets) are another high-performance logging option that is currently available, but they're a bit more tedious to introduce than printf log statements for debugging purposes.
69
+
70
+
## Watchdog
71
+
72
+
A watchdog task monitors other tasks for unexpected stalls. A maximum of 24 tasks may be monitored, due to the use of FreeRTOS event group for efficiency.
73
+
74
+
To use this feature, pass the watchdog object to each task you wish to monitor, then call `registerTask()` in each task's entry function, followed by `kick()` for each loop iteration.
75
+
76
+
If any task is not kicked for 2000 ticks (2 seconds with current project config), then a stall is detected and a watchdog timeout is reported. This timeout report is sent over ITM logging and/or Binary Packet logging, depending on which output options are provided to the watchdog. This watchdog task may be extended to allow a hardware watchdog to reset the device; however, reporting without resetting seems like the most practical way to handle stalls during product prototyping.
77
+
78
+
See [TaskUtilities](#Task-Utilities) for a more convenient way to use the watchdog.
79
+
80
+
<imgsrc="../docs/images/watchdog.png"width="300">
81
+
82
+
## Error capture
83
+
84
+
There are a limited number of hardware breakpoints available on Cortex-M devices. The STM32F4 has 6. In order to conserve the number of breakpoints that are necessary to detect a variety of errors, common error-detection functions in [`catch_errors.cpp`](src/catch_errors.cpp) are aggregated. For example, setting a single breakpoint at `catchDefault()` will break at all critical errors, nonCritical warnings, and non-ideal timeouts.
Almost all tasks make use of ITM Logging and Watchdog. Task Utilities wraps this common functionality and adds some additional conveniences:
93
+
94
+
### ITM Logging
95
+
96
+
Manages a local `LogMsg` struct per task, which is necessary for high-performance logging.
97
+
98
+
### Watchdog
99
+
100
+
Manages a per-task ID necessary for using the watchdog functions. It also provides wrappers for `read()` and `write()` interfaces to allow infinite blocking without triggering watchdog timeouts, while still allowing inspecting stack frames of blocked tasks during debugging. Setting a breakpoint at `allTimeouts()` steps through all blocking tasks.
101
+
102
+
### Initialization
103
+
104
+
Rather than passing both a `Watchdog` and `ItmLogger` instance to each task, pass a single `TaskUtilitiesArg` to the task's constructor.
0 commit comments