Firmware Integration Recipes
Contents
These recipes extend the UM firmware walkthrough with transport-specific settings. Install the host tools as described there. All three recipes use Bind; keep each projectβs TIL/LI and run Bind before compilation whenever its log sources change.
For a 1.x project, apply the migration notes before copying target files or rebuilding. For example, change "msg:literal={x}" to "msg:literal={{x}}" to keep displaying literal={x}. Replay old firmware captures with their matching old tool and dictionaries.
1. Quickstart: Existing non-blocking byte writer, deferred auxiliary 8-bit
Use this path when your project already has a tested output function, for example:
- a non-blocking UART TX queue,
- a USB CDC/VCOM TX queue,
- a DMA-backed byte stream,
- a socket or pipe in a host-native test program,
- a file writer in a PC-side demo.
This is often the most universal first integration because Trice does not need to know your peripheral driver. It only needs a function that accepts a byte buffer and length.
1.1. Add Trice target sources
Add the complete src folder to your target project unchanged, including its default_conf subdirectory.
Search project and generated header directories first, followed by src and finally src/default_conf, for example -Iproject -Ibuild/sidecars -Isrc -Isrc/default_conf.
Create a project-specific triceConfig.h in your application include path.
1.2. Configure deferred auxiliary 8-bit output
Minimal triceConfig.h starting point:
#ifndef TRICE_CONFIG_H_
#define TRICE_CONFIG_H_
#define TRICE_DEFERRED_OUTPUT 1
#define TRICE_BUFFER TRICE_RING_BUFFER
#define TRICE_DEFERRED_AUXILIARY8 1
/* Adapt these to your target if Trice can run from interrupts or multiple contexts. */
#define TRICE_ENTER_CRITICAL_SECTION {
#define TRICE_LEAVE_CRITICAL_SECTION }
#endif /* TRICE_CONFIG_H_ */
Notes:
TRICE_RING_BUFFERis a balanced default.TRICE_DOUBLE_BUFFERcan be faster for the Trice call itself, at the cost of more RAM and different buffer behavior.- Keep the writer non-blocking or at least tightly bounded. A blocking writer moves the latency problem into
TriceTransfer().
1.3. Assign your writer function
Example:
#include "trice.h"
static void MyNonBlockingByteWrite(const uint8_t* data, size_t len) {
/* Replace this with your project's existing writer. */
ExistingTxQueueWrite(data, len);
}
void AppInit(void) {
BoardInit();
TriceInit(); // Initialize the configured Trice backends.
UserNonBlockingDeferredWrite8AuxiliaryFn = MyNonBlockingByteWrite;
trice("boot\n");
}
void AppMainLoop(void) {
for (;;) {
AppRun();
TriceTransfer();
}
}
TriceTransfer() moves accumulated Trice records from the deferred buffer to your writer. Call it cyclically from the main loop, a low-priority task, or another context that is safe for your output driver.
The writer must accept the complete chunk or implement an explicit overflow policy. It must not retain the supplied pointer after returning. A DMA or transmit queue needs its own copy or another correctly managed transport adapter. Empty critical-section macros above are only a single-context starting point; replace them before logging from interrupts or multiple tasks.
1.4. Bind before compiling
From your project root:
touch til.json li.json
trice bind -src project -genDir build/sidecars -i til.json -li li.json
Here project is your application source/configuration directory. Bind creates build/sidecars and its parents. Add that directory to the compiler include path shown above, and make Bind a dependency of compilation. Then build and flash. See build integration for build-system rules; do not commit generated sidecars.
1.5. Decode on the PC
For a serial or USB virtual COM port:
trice log -p COM15 -baud 921600 -i ./til.json -li ./li.json
On Linux/macOS, adapt the port:
trice log -p /dev/ttyACM0 -baud 921600 -i ./til.json -li ./li.json
If your writer produces a file, pipe, TCP stream, or another source, use the matching trice log -p ... input port.
1.6. Common first checks
- If you see no output, first confirm that your writer is called from
TriceTransfer(). - If raw data appears but does not decode, check framing settings on target and host.
- If output stops under burst load, increase
TRICE_DEFERRED_BUFFER_SIZE, callTriceTransfer()more often, or improve the non-blocking writer queue. - If interrupts or multiple tasks can call Trice, provide real critical-section macros.
1.7. Why this quickstart matters
The auxiliary writer reuses an existing byte stream and needs no probe-specific transport. For RTT-specific setup use the next recipe.
2. Quickstart: SEGGER RTT direct mode with J-Link
Use this path when you have a SEGGER J-Link and want the smallest amount of target-specific transport code. See Convert Evaluation Board onboard ST-Link to J-Link for a cheap option.
2.1. Install tools
- Follow the UM prerequisites for the host tool.
- Install the SEGGER J-Link software package so that
JLinkRTTLoggeror the relevant J-Link tools are inPATH.
2.2. Add target sources
Use the source layout and include order from the first recipe, with src/default_conf last.
2.3. Configure direct RTT
Minimal triceConfig.h:
#ifndef TRICE_CONFIG_H_
#define TRICE_CONFIG_H_
#define TRICE_DIRECT_OUTPUT 1
#define TRICE_BUFFER TRICE_STACK_BUFFER
#define TRICE_DIRECT_SEGGER_RTT_32BIT_WRITE 1
#endif /* TRICE_CONFIG_H_ */
2.4. Add a first Trice call
#include "trice.h"
int main(void) {
BoardInit();
TriceInit(); // Initialize the configured Trice backends.
trice("Hello RTT\n");
for (;;) {
AppRun();
}
}
Direct RTT does not require TriceTransfer() for the normal direct output path.
2.5. Bind, build, flash
touch til.json li.json
trice bind -src project -genDir build/sidecars -i til.json -li li.json
Use your application source directory instead of project. Apply the same Bind build dependency and generated include path as above, then build and flash the target. If calls run in interrupts or multiple tasks, configure critical sections for the target.
2.6. Log through J-Link RTT
Example command; adapt the device name and speed:
trice log -p JLINK \
-args "-Device STM32G0B1RE -if SWD -Speed 4000 -RTTChannel 0" \
-pf none -prefix off -hs off -d16 \
-i ./til.json -li ./li.json
Alternative file-based workflow:
rm -f ./temp/trice.bin
mkdir -p ./temp
touch ./temp/trice.bin
JLinkRTTLogger -Device STM32G0B1RE -If SWD -Speed 4000 -RTTChannel 0 ./temp/trice.bin
In a second terminal:
trice log -p FILE -args ./temp/trice.bin \
-pf none -prefix off -hs off -d16 \
-i ./til.json -li ./li.json
2.7. When this path is ideal
Direct RTT is excellent for lab development because the target writes to RTT memory and the probe drains it. It avoids UART setup and usually feels close to printf debugging.
2.8. When this path is not ideal
It depends on J-Link/RTT infrastructure. If that hardware or closed host tooling is a blocker, start with the Quickstart: Existing non-blocking byte writer, deferred auxiliary 8-bit or a UART/VCOM deferred path.
3. Quickstart: UART or USB-VCOM deferred output
See also Communication Ports, the example projects, and the UART-related configuration examples. This section is intentionally short because UART setup is MCU/vendor-specific.
Use this path with the built-in UART backend and the required MCU UART hooks. A USB CDC/VCOM writer is normally an auxiliary byte writer; use the first recipe unless your application supplies a compatible UART adapter. See F030 UART instrumentation for concrete peripheral and interrupt integration.
Minimal shape of triceConfig.h:
#ifndef TRICE_CONFIG_H_
#define TRICE_CONFIG_H_
#include "main.h" /* or your MCU/vendor header */
#define TRICE_DEFERRED_OUTPUT 1
#define TRICE_BUFFER TRICE_RING_BUFFER
#define TRICE_DEFERRED_UARTA 1
#define TRICE_UARTA USART2 /* adapt to your project */
#endif /* TRICE_CONFIG_H_ */
Application shape:
#include "trice.h"
int main(void) {
BoardInit();
UartInit();
TriceInit(); // Initialize the configured Trice backends.
trice("boot\n");
for (;;) {
AppRun();
TriceTransfer();
}
}
Prepare this project with the same Bind step and include order before compilation. Adapt the UART, its transmit interrupt/hardware hooks and critical sections to your board; the outline alone is not a complete driver.
Host side (baud rate must match the UART configuration):
trice log -p COM15 -baud 921600 -i ./til.json -li ./li.json
On Linux/macOS:
trice log -p /dev/ttyACM0 -baud 921600 -i ./til.json -li ./li.json
For a quick first success, the Quickstart: Existing non-blocking byte writer, deferred auxiliary 8-bit may be easier if your project already has a working serial or USB write function.