IOT FIRMWARE DESIGN: BEST PRACTICES (for embedded development that sucks less) @farmckon @BuLogics
Jul 16, 2015
IOT FIRMWARE DESIGN:
BEST PRACTICES
(for embedded development that sucks less)
@farmckon
@BuLogics
EMBEDDED
DEVELOPMENT IS
PAINFULIt doesn’t have to be.
Common Problems:
• “Code, flash, bang-on-it” cycle
• Development environment setup sucks
• Debugging with little/no IO
• Complex bug scenario recreation
• “Hurry up and wait” on hardware spins
Design To Avoid ‘em:
• Program in C
• State Machines Everywhere
• Proper Modular Design
• Hardware Abstraction Layer,
• Hardware Abstraction Layer,
• Hardware Abstraction Layer!
• On-Device debugging is a last resort
Program Drivers/Firmware in C++?
Program Drivers/Firmware in C!
Just do it
• Platform portable
• Fast
• Clear behavior (mostly)
• Simple
State Machines Everywhere
Business Logic: State Machine
Hardware Abstraction: State Machine
Grocery List: State Machine
• Manages complexity.
• Testable.
• Automatically avoids most ‘'Once a year on a full moon”.
• Avoids accidental algrorithmic runtime complexity.
• Easier to share/outsource/reuse.
Modular Design
• Manages complexity
• Testable, unit and integration
• By side effect avoids “Once a year on a full moon”.
• Avoids accidental algorithmic runtime complexity.
• Allows desktop (a.k.a., much faster, better, stronger) debugging and testing by divorcing behavior from hardware.
AlarmBehavior.c, AlarmIO.c, AlertSend.c
vs.
Main.c, IO.c, Wireless.c
HARDWARE
ABSTRACTION LAYER
(HAL)
Separate hardware interface from behavior
Keep It Simple Stupid.
Do the minimum to create a clear abstraction.
(do not teach it to read lips)
Hardware Abstraction Layer (HAL)
A basic re-usable HAL design:
• HW_Init
• SW_Init
• Tick_1ms (10, 100)
• $OtherInterrupts
• ProcessData
• StateUpdate (Optional)
• SW_Deinit, HW_Deinit (Optional)
HW_Init, SW_Init
• HW_Init call to setup hardware (IO, memory, DMA)
• SW_Init to setup software (state machines, load NVM
values, etc)
• These happen on every awake, restore, or power on.
• Called by start-up or shutdown interrupts.
• (Optional mirror ‘HW_/SW_Deinit’ for sleep/shutdown).
• These do nothing but setup the landscape for the
program
Tick_?ms
• Maintenance function to tick clocks, watchdogs, timers
• Set time-based flags (timeouts, timers, error-flags)
• 1, 10, 100ms based on how “real time” your needs are
• Called by timer interrupt(s)
$OtherInterrupts
• All of your other interrupts in this category:
• DMA
• SPI
• FPU/GPU
• Radio
• Always: Clear or checks interrupt status, sets flags.
• Sometimes: Ticks state, sets timers, etc.
• Never: Processes data, does anything time-intensive.
ProcessData
• Checks flags. Does logic and intelligence. Shuffles
data/settings to modules.
• Called over and over again by main loop. Put time-
intensive things here.
• Checks flags, does business logic, and pulls/processes
data.
• For most projects StateUpdate behavior can be part of
this.
StateUpdate
• Updates state of HAL or driver
• Every HAL is a StateMachine
• With documentation
• And exceptions noted in cod
• Can be wrapped into ProcessData in most cases
SW_Deinit, HW_Deinit (Optional)
• Called on sleep/shutdown/timeout/fail
• First stop HAL software (SW_Deinit) then Hardware
• Set lines high
• Unregister interrupts
• Panic and/or cry
• In a lot of designs, this is avoidable.
• May never be called in a crash or a power-loss, so keep it
to “nice-to-have” things (sleep, pin safety, etc.)
Example Main.c
#include "spi.h"
#include "dma.h"
#include <interrupts.h>
// wake callback
int wakeFunction() I01_INT
{
int reason = IO1_INT_CAUSE & 0x0F;// Wake, POR,
SPI_HW_Init(reason);
DMA_HW_INIT(reason);
SPI_SW_INIT();
DMA_HW_INIT();
}
int main()
{
SPI_ProcessData();
DMA_ProcessData();
// Business Logic of how to handle, etc here.
If (DMA_running())
ThinkAboutDma();
else if (SPI_Complete())SpiToDmaCollection();
}
int Tick_10ms() TIMER_0_INT
{
tick++;
DMA_Tick_10ms();
if(tick %10)
SPI_Tick_100ms()
watchdogKick();
}
void watchdogTimeout() WD_DOWN_INT
{
DMA_Deinit();
// no SPI_Deinit. Too simple
}
// #endif _EXAMPLE_MAIN_
Example SPI.c#include "spi.h”
#include <interrupts.h>
Void SPI_HW_Init(WAKE_REASON r)
{
reason = r;
SET_INPUT(MISO);
SET_OUTPUT(MOSI,0);
SET_OUTPUT(SEL,0);
SET_OUTPUT(CLK,0);
w = CreateWatchdog();
Timer(&tick_10ms, 1);
}
void SPI_SW_Init()
{
queuedSz = 0; //bytes in quueed
memset(queued,0x5A, sizeof(queued)
clkLevel = 0;
byteJustCompleted = FALSE:
}
void SPI_Tick_10ms()
{
if (byteJustCompleted ) {
queueNextByte(); }
ColckBit() ..setsMOSI for next byte to send, reads MISO
PIN_SET(CLK, clkLevel)
clkLevel = (clkLevel == 1) ? 0 : 1 ; //toggle clk
// could do timeout checking here.
}
// We are master. No interrupts for us.
uint8_t SPI_QueueData( uint8_t data, uint8_t sz) {
//in reality, check size, buffer overflow, etc
memcpy( (&queued) + queueSz, data, sz)
queueSz += sz;
}
void SPI_ProcessData() {
tickWatchdog(w) ;
}
void SPI_SW_Deinit () { /*nothing to do here */ } ,
boolean SPI_Complete(){
return queueSz == 0;
}
void SPI_HW_Deinit ()
{
SET_OUTPUT(MISO,1);
SET_OUTPUT(MOSI,1);
SET_OUTPUT(SEL,1);
SET_OUTPUT(CLK,1);
}
ON DEVICE DEBUGGING
IS YOUR LAST RESORT
On device debugging is Last Resort
• If you are debugging on a device and it turns out it’s not a
hardware issue, you lost.
• Almost all testing should be on a desktop, using modern
desktop tools:
• Faster
• Better tools
• Less Flash/Test/WTF?/Tinker -> Flash/Test cycle
• Test -> WTF -> Test/ WTF cycle
• Using HAL and Modules = easy desktop debugging
• Using desktop debugging = easy HAL and modules
Bonus Best Pratices
20% Free Content, Yours For Only $10.99!!!
• Build and implement OTA/programming first(ish)
• Virtual Machines (for standard compilers at least)
• Have a canonical build server (even if just a senior
developer)
• Unit Test (almost) All The Things.
• Open Source (almost) All The Things.
• Decide on debugging streams at hardware design time
Questions? Comments?
Now Go Eat Cookies.
@farmckon
Bulogics.com
FarMcKon.net
Credits for Media
Pictures
• P1 - by Moyan Brenn on Flickr
• P2 – by Dannobytes on Flickr
• P4 - http://pixabay.com/en/lego-build-building-blocks-toys-708088/
• P5 - learnyousomeerlang.com (dog diagram)