State machines bouwen met Test-Driven Development in Embedded C
State machines zijn fundamenteel voor veel embedded-systemen, maar hun ontwikkeling kan complex zijn. Dit artikel schetst bewezen strategieën voor het bouwen van state-machine (SM) software met Test-Driven Development (TDD), met focus op C-implementaties geschikt voor resource-beperkte omgevingen.
In de kern bestaat een SM-model uit states, transities en acties. States vertegenwoordigen condities, transities definiëren het pad van de ene state naar de andere - meestal getriggerd door een event - en acties vangen het gedrag op dat tijdens entry, exit of tijdens het overgaan uitgevoerd wordt.
In UML-state-machines kunnen acties gekoppeld worden aan state-entry/exit, de transitie zelf, of interne reacties. Alle state-machine-formalismen veronderstellen een Run-To-Completion (RTC) uitvoeringsmodel: elk event wordt volledig verwerkt - inclusief exit-acties, transitie-acties en entry-acties - voordat het volgende event afgehandeld wordt.
Voordat we in TDD-strategieën duiken, is het essentieel om te begrijpen waarom TDD ertoe doet. TDD is een incrementele ontwikkelbenadering die het schrijven van een faalende unit-test voordat enige productiecode wordt geschreven, mandeteert. Tests zijn klein, geautomatiseerd en dienen als de specificatie voor het gewenste gedrag. De TDD-cyclus - afgeleid van James Grenning's "Test-Driven Development for Embedded C" - omvat:
- Schrijf een kleine, faalende test.
- Voer alle tests uit; de nieuwe test zou moeten falen.
- Schrijf de minimale code om de test te laten slagen.
- Voer alle tests opnieuw uit om te bevestigen dat de nieuwe test slaagt.
- Refactor om duplicatie te elimineren en duidelijkheid te verbeteren.
Overweeg het SM-diagram in Figuur 1. Het begint in StateA en, bij ontvangst van het Alpha-event, gaat over naar StateB, waarbij xStateA(), effect() en nStateB() in sequentie uitgevoerd worden. Hoe valideren we dat dit gedrag correct is?
Figuur 1. Basis state machine (Bron: VortexMakes)
De meest voor de hand liggende testbenadering omvat het verifiëren van de SM's transitietabel. Dit creëert één testcase per state, stimuleert de SM met relevante events en bevestigt de resulterende state en uitgevoerde acties. Complexe acties rechtvaardigen dedicated tests. (Zie het artikel "Testing state machines using unit test" voor een diepgaande discussie.)
Elke test volgt het vier-fasen xUnit-patroon:
- Setup - precondities vastleggen (bijv. huidige state, event, verwachte target-state en actie-sequentie).
- Exercise - de SM triggeren met het event.
- Verify - beweren dat de uitkomsten overeenstemmen met verwachtingen.
- Cleanup - de SM resetten naar de initiële state (optioneel).
Terwijl deze strategie goed werkt voor eenvoudige SMs, vereist complexer gedrag - zoals een keten van transities waar een effect pas na meerdere stappen observeerbaar is - functioneel-scenario-testen. In plaats van geïsoleerde state-checks testen we een complete sequentie die de benodigde states, events en acties oefent.
Figuur 2 illustreert dit met een DoWhile SM die een traditionele do-while-lus nabootst. De machine initialiseert met standaardwaarden via x = 0 en output = 0, stelt de lus-teller in met x++ of x = (x > 0) ? x-: x, en itereert met i++ tot de guard i == x faalt, waarna het output = i uitgeeft.
Figuur 2. De DoWhile state machine (Bron: VortexMakes)
Voorafgaand aan coderen, creëren we een testlijst afgeleid van de specificatie. Voor DoWhile omvat de initiële lijst:
- Alle data ingesteld op standaarden bij initialisatie.
- Het X-attribuut incrementeren.
- Het X-attribuut decrementeren.
- Een single-iteration lus uitvoeren.
- Meerdere iteraties uitvoeren.
- Een non-itererend scenario hanteren.
- Out-of-bounds condities controleren.
We gebruiken Ceedling als build-systeem en Unity als lichtgewicht test-harness voor de C-code. De SM is gedefinieerd in DoWhile.h en DoWhile.c, en de test-suite resideert in test_DoWhile.c. Hieronder is een beknopt fragment die de "single-iteration" testcase illustreert.
Code Listing 1: Single iteration test (Bron: VortexMakes)
De test initialiseert de SM, stelt de iteratie-telling op nul, verstuurt het Up-event om het te incrementeren, trigger het Start-event, en stuurt tenslotte Alpha om één lus-iteratie uit te voeren. Asserties verifiëren dat het out-attribuut gelijk is aan het aantal iteraties en dat de SM in StateC blijft.
Het uitbreiden van dezelfde test demonstreert meerdere iteraties (zie Code Listing 2). Een afzonderlijke test (Listing 3) bevestigt dat de SM geen iteraties uitvoert wanneer Alpha ontvangen wordt zonder een voorgaand Up-event.
Code Listing 2: Multiple iteration test (Bron: VortexMakes)
Code Listing 3: Non-iteration test (Bron: VortexMakes)
De implementatiedetails zijn niet de focus van dit artikel, maar fragmenten uit DoWhile.c en DoWhile.h illustreren een schone switch-case-benadering voor state-handling.
Code Listing 4: Fragment van DoWhile-implementatie (Bron: VortexMakes)
Code Listing 5: Fragment van DoWhile-specificatie (Bron: VortexMakes)
Samenvattend, biedt TDD twee complementaire strategieën voor state-machine-ontwikkeling: (1) state-voor-state transitietesten, en (2) functioneel-scenario-testen die meerdere states en acties dekken. Deze methoden zijn taal-onafhankelijk, tool-onafhankelijk en bijzonder goed geschikt voor embedded-systemen waar state-gebaseerde logica domineert.
Omdat C de lingua franca blijft van embedded-software, levert het koppelen ervan aan Ceedling en Unity een robuuste TDD-workflow. Het adopteren van deze praktijken leidt tot software die flexibeler, onderhoudbaarder en herbruikbaarder is dan conventionele benaderingen.
Alle code en modellen zijn beschikbaar in de GitHub-repository https://github.com/leanfrancucci/sm-tdd.git.
Ingebed
- Verbeterde CNC-betrouwbaarheid: Moderne freesgereedschappen leveren snelheid, nauwkeurigheid en lagere kosten
- Productietrends 2021: Deel 2 – Inzichten van Make UK
- Boy Machines: Kleinschalige Productie Vooruitbrengen Met Cutting‑Edge Technologie
- AI-gedreven testautomatisering: QA voor moderne ERP-systemen revolutioneren
- Accelerate Project gelanceerd met strategisch outsourcing
- Veelvoorkomende CNC-Machine Problemen en Expert Oplossingen
- Verticale Freesmachines Meesteren: Voordelen, Operaties en Waarom Ze Horizontale Modellen Overtreffen
- Valkuilen Vermijden bij Aankoop Gebruikte CNC-Machines
- Ontdek Oneindige Mogelijkheden: Wat een CNC-machine Kan Maken
- Perfecte afwerkingen behalen met Okamoto's precisieslijpmachines
- Upgrade Okuma CNC's: USB & Ethernet Vervanging voor Floppy Stations