Kbox-UniStack Docs

Porting to a New Platform

How to bring the MCU-agnostic KNX core to a new microcontroller by implementing the peripheral interfaces and rebuilding the static library for your target.

What porting means

The core of kbox-stack is MCU-agnostic: it depends only on the I<X> peripheral interfaces, never on any register, HAL, or vendor SDK. Porting means implementing those interfaces for your MCU and HAL — and nothing more. You do not modify, fork, or even read core source; you supply the hardware layer the core plugs into.

The prebuilt archive shipped today targets Cortex-M3 / STM32F103xB (libkbox-stack---cortexm3---STM32F103xB.a). The design is portable, but a different CPU or ABI needs the core rebuilt for that target — the shipped .a will not link against, say, a Cortex-M0+ or Cortex-M4F ABI. A rebuilt binary for your target is required in addition to your peripheral implementations.

Interfaces to implement

Implement one class per peripheral. Where a Base<X> exists, subclass it and fill only the pure hooks (see Reusing Base<X> below).

IBusDriver — the KNX TP1 bus. Subclass BaseBusDriver (it gives you TX/RX queues, the Work() pump and inter-byte timing) and implement Init(), AvailableForTx() and Transmit() against your UART and transceiver.

IClock — a monotonic millisecond time base. Subclass BaseClock and call Tick() from a 1 ms timer interrupt; the counter logic is already done.

IWatchDog — the independent hardware watchdog. Subclass BaseWatchDog and implement Reset() to kick your IWDG; Enable/Disable/IsEnabled are provided.

IFlash — non-volatile storage for the tables and stack state. Subclass BaseFlash (address bookkeeping and page size are provided) and implement the Write / Put / ErasePages / ReadUintN / ReadBit / Lock / Unlock methods against your flash controller.

ILed — the programming LED. Subclass BaseLed (the blink state machine runs off IClock) and implement the two GPIO hooks writePin(bool) and togglePin().

IButton — the programming button. Subclass BaseButton (callback storage is done) and wire your GPIO interrupt to call OnInterupt().

ITransmitter — sets the device's individual address on the transceiver. Subclass BaseTransmitter (it holds the IBusDriver&) and implement Init() and SetIndividualAddress().

All method signatures are documented in Peripheral Interfaces.

Reusing Base<X>

Every peripheral ships a Base<X> companion that implements the platform-independent logic once, so your port only writes the hardware-touching parts:

InterfaceBase companionWhat Base gives youWhat you still implement
IBusDriverBaseBusDriverTX/RX ring queues, Work() pump, inter-byte timingInit, AvailableForTx, Transmit
IClockBaseClockfull ms/second countercall Tick() from a 1 ms ISR
IWatchDogBaseWatchDogenabled flag + Enable/Disable/IsEnabledReset() (the kick)
IFlashBaseFlashaddress bookkeeping + 1024-byte page size, Get*read/write/erase/lock
ILedBaseLedblink state machine off IClockwritePin, togglePin
IButtonBaseButtoncallback storage + dispatchwire GPIO IRQ to OnInterupt()
ITransmitterBaseTransmitterholds the IBusDriver&Init, SetIndividualAddress

Subclassing Base<X> is the recommended path — it keeps your port small and inherits the behavior the reference platform is validated with.

Timing & flash constraints

  • UART line timing. BaseBusDriver enforces an inter-byte wait after the last data byte (a waitAfterLastDataByte gap) and a post-transmit delay before the next frame. Your Transmit() must honor the physical TP1 line timing; drive Tick()/Work() at a 1 ms cadence so these gaps are measured correctly.
  • Flash page / erase semantics. BaseFlash assumes a fixed page size (default 1024 bytes) and word-addressed writes. Erase is page-granular via ErasePages, and writes are in 32-bit words (Write / Put take uint32_t* and a word count). Map these onto your controller's real page size and program width; if your flash differs, override the page-size accessor and honor your erase granularity.

Validating your port

Because the shipped .a is ARM-only, validate the portable application logic with host (PC) tests rather than on-target only. The examples follow this pattern:

  • Each example ships an <example>/tests/ folder with host tests built as a separate native CMake project using GoogleTest.
  • Prefer testing your logic by injecting mocks / fakes for the I<X> interfaces — a fake IClock and IFlash let you exercise timing and persistence deterministically on the host.
  • Host tests compile the small set of portable stack source files they need; they are a developer tool and are not part of the customer's dist-only package.

Study the examples under examples/<PLATFORM-HAL>/<example-name>/ (for example examples/STM32F103-HAL/led-indicator/). Each is a complete, buildable, flashable app that uses only the public <Stack/...> API — copy one as your port's skeleton.

Reference implementation

The STM32F103 + NCN5130 platform is the fully worked port. Read it as your template, then document your own platform section alongside it.