12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775 |
- <!DOCTYPE html>
- <html xmlns="http://www.w3.org/1999/xhtml" lang="" xml:lang="">
- <head>
- <meta charset="utf-8" />
- <meta name="generator" content="pandoc" />
- <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
- <meta name="author" content="Pat Beirne" />
- <title>Cooperative Multitasking</title>
- <style>
- code{white-space: pre-wrap;}
- span.smallcaps{font-variant: small-caps;}
- div.columns{display: flex; gap: min(4vw, 1.5em);}
- div.column{flex: auto; overflow-x: auto;}
- div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;}
- /* The extra [class] is a hack that increases specificity enough to
- override a similar rule in reveal.js */
- ul.task-list[class]{list-style: none;}
- ul.task-list li input[type="checkbox"] {
- font-size: inherit;
- width: 0.8em;
- margin: 0 0.8em 0.2em -1.6em;
- vertical-align: middle;
- }
- .display.math{display: block; text-align: center; margin: 0.5rem auto;}
- /* CSS for syntax highlighting */
- pre > code.sourceCode { white-space: pre; position: relative; }
- pre > code.sourceCode > span { line-height: 1.25; }
- pre > code.sourceCode > span:empty { height: 1.2em; }
- .sourceCode { overflow: visible; }
- code.sourceCode > span { color: inherit; text-decoration: inherit; }
- div.sourceCode { margin: 1em 0; }
- pre.sourceCode { margin: 0; }
- @media screen {
- div.sourceCode { overflow: auto; }
- }
- @media print {
- pre > code.sourceCode { white-space: pre-wrap; }
- pre > code.sourceCode > span { text-indent: -5em; padding-left: 5em; }
- }
- pre.numberSource code
- { counter-reset: source-line 0; }
- pre.numberSource code > span
- { position: relative; left: -4em; counter-increment: source-line; }
- pre.numberSource code > span > a:first-child::before
- { content: counter(source-line);
- position: relative; left: -1em; text-align: right; vertical-align: baseline;
- border: none; display: inline-block;
- -webkit-touch-callout: none; -webkit-user-select: none;
- -khtml-user-select: none; -moz-user-select: none;
- -ms-user-select: none; user-select: none;
- padding: 0 4px; width: 4em;
- color: #aaaaaa;
- }
- pre.numberSource { margin-left: 3em; border-left: 1px solid #aaaaaa; padding-left: 4px; }
- div.sourceCode
- { }
- @media screen {
- pre > code.sourceCode > span > a:first-child::before { text-decoration: underline; }
- }
- code span.al { color: #ff0000; font-weight: bold; } /* Alert */
- code span.an { color: #60a0b0; font-weight: bold; font-style: italic; } /* Annotation */
- code span.at { color: #7d9029; } /* Attribute */
- code span.bn { color: #40a070; } /* BaseN */
- code span.bu { color: #008000; } /* BuiltIn */
- code span.cf { color: #007020; font-weight: bold; } /* ControlFlow */
- code span.ch { color: #4070a0; } /* Char */
- code span.cn { color: #880000; } /* Constant */
- code span.co { color: #60a0b0; font-style: italic; } /* Comment */
- code span.cv { color: #60a0b0; font-weight: bold; font-style: italic; } /* CommentVar */
- code span.do { color: #ba2121; font-style: italic; } /* Documentation */
- code span.dt { color: #902000; } /* DataType */
- code span.dv { color: #40a070; } /* DecVal */
- code span.er { color: #ff0000; font-weight: bold; } /* Error */
- code span.ex { } /* Extension */
- code span.fl { color: #40a070; } /* Float */
- code span.fu { color: #06287e; } /* Function */
- code span.im { color: #008000; font-weight: bold; } /* Import */
- code span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Information */
- code span.kw { color: #007020; font-weight: bold; } /* Keyword */
- code span.op { color: #666666; } /* Operator */
- code span.ot { color: #007020; } /* Other */
- code span.pp { color: #bc7a00; } /* Preprocessor */
- code span.sc { color: #4070a0; } /* SpecialChar */
- code span.ss { color: #bb6688; } /* SpecialString */
- code span.st { color: #4070a0; } /* String */
- code span.va { color: #19177c; } /* Variable */
- code span.vs { color: #4070a0; } /* VerbatimString */
- code span.wa { color: #60a0b0; font-weight: bold; font-style: italic; } /* Warning */
- </style>
- <link rel="stylesheet" href="pandoc.css" />
- </head>
- <body>
- <header id="title-block-header">
- <h1 class="title">Cooperative Multitasking</h1>
- <p class="author">Pat Beirne</p>
- <p class="date">2025/01/15</p>
- </header>
- <nav id="TOC" role="doc-toc">
- <ul>
- <li><a href="#part-1-cooperative-multitasking"
- id="toc-part-1-cooperative-multitasking">Part 1: Cooperative
- Multitasking</a>
- <ul>
- <li><a href="#intro" id="toc-intro">Intro</a>
- <ul>
- <li><a href="#abstract" id="toc-abstract">Abstract</a></li>
- <li><a href="#audience" id="toc-audience">Audience</a></li>
- </ul></li>
- <li><a href="#the-problem" id="toc-the-problem">The Problem</a></li>
- <li><a href="#first-page" id="toc-first-page">First Page</a></li>
- <li><a href="#interrupts" id="toc-interrupts">Interrupts</a></li>
- <li><a href="#events" id="toc-events">Events</a></li>
- <li><a href="#newevent" id="toc-newevent">newEvent()</a></li>
- <li><a href="#dispatch" id="toc-dispatch">Dispatch</a></li>
- <li><a href="#tasks" id="toc-tasks">Tasks</a></li>
- <li><a href="#state-machine" id="toc-state-machine">State
- Machine</a></li>
- <li><a href="#another-state-machine"
- id="toc-another-state-machine">Another State Machine</a></li>
- <li><a href="#dispatcher-details" id="toc-dispatcher-details">Dispatcher
- Details</a></li>
- <li><a href="#final-implementation" id="toc-final-implementation">Final
- Implementation</a></li>
- </ul></li>
- <li><a href="#part-2" id="toc-part-2">Part 2</a>
- <ul>
- <li><a href="#variations" id="toc-variations">Variations</a></li>
- <li><a href="#working-with-arduino"
- id="toc-working-with-arduino">Working with Arduino</a></li>
- <li><a href="#tasks-1" id="toc-tasks-1">Tasks</a></li>
- <li><a href="#state-machine-examples"
- id="toc-state-machine-examples">State Machine Examples</a>
- <ul>
- <li><a href="#car-window" id="toc-car-window">Car Window</a></li>
- <li><a href="#fridge-door" id="toc-fridge-door">Fridge Door</a></li>
- <li><a href="#door-opener" id="toc-door-opener">Door Opener</a></li>
- <li><a href="#beer-vat" id="toc-beer-vat">Beer Vat</a></li>
- <li><a href="#vending-machine" id="toc-vending-machine">Vending
- Machine</a></li>
- </ul></li>
- <li><a href="#events-1" id="toc-events-1">Events</a></li>
- <li><a href="#timers" id="toc-timers">Timers</a></li>
- <li><a href="#real-code" id="toc-real-code">Real Code</a></li>
- <li><a href="#substates" id="toc-substates">Substates</a></li>
- <li><a href="#state-machine-initialization"
- id="toc-state-machine-initialization">State Machine
- Initialization</a></li>
- <li><a href="#messages" id="toc-messages">Messages</a>
- <ul>
- <li><a href="#postmessage" id="toc-postmessage">PostMessage</a></li>
- <li><a href="#sendmessage" id="toc-sendmessage">SendMessage</a></li>
- <li><a href="#usage" id="toc-usage">Usage</a></li>
- </ul></li>
- <li><a href="#ideas-for-tasks" id="toc-ideas-for-tasks">Ideas for
- Tasks</a></li>
- <li><a href="#more-state-machines" id="toc-more-state-machines">More
- State Machines</a>
- <ul>
- <li><a href="#radio-tuner" id="toc-radio-tuner">Radio Tuner</a></li>
- </ul></li>
- </ul></li>
- </ul>
- </nav>
- <h1 id="part-1-cooperative-multitasking">Part 1: Cooperative
- Multitasking</h1>
- <!-- ## Introduction -->
- <p>Small microcontrollers are often required to handle several tasks,
- sometimes with overlapping phases. This paper will lead you through the
- creation of a small <strong><em>operating system</em></strong> which can
- be implemented on a tiny microcontroller, without the fancy time-slicing
- that is offered by sophisticated operating systems like <a
- href="https://linux.org">Linux</a> and <a
- href="https://freertos.org">FreeRTos</a>.</p>
- <p>The core of <em>this</em> technique is covered in <a
- href="#part-1-cooperative-multitasking">Part 1</a> of this paper. If you
- just want to see how it all comes together, jump to <a
- href="#final-implementation">Final Implementaion</a>.</p>
- <p><a href="#part-2">Part 2</a> contains enhancements and variations,
- probably useful reading if you decide to adopt this programming
- technique in your projects.</p>
- <h2 id="intro">Intro</h2>
- <h3 id="abstract">Abstract</h3>
- <p>This paper will take you through the creation of a tiny <em>operating
- system</em> that can be implemented on a small microcontroller.</p>
- <h3 id="audience">Audience</h3>
- <p>These techniques can be applied by anyone with experience in
- <strong><em>C</em></strong>, <strong><em>Python</em></strong> or any
- modern computer language. It helps to have a passing knowledge of how to
- connect a transducer (button, LED, buzzer, etc) to a
- microcontroller.</p>
- <blockquote>
- <p>The <strong><em>Reality Check</em></strong> dropdowns in this article
- provide extra, often practical supplementary reading.</p>
- </blockquote>
- <details>
- <summary>
- Reality Check
- </summary>
- <p>The technique described here is also called <strong><em>event driven
- programming</em></strong>.</p>
- <p>This technique was used in the original Window (1995), including some
- of the system calls:
- <code>sendMessage(), postMessage() and setTimer()</code>.</p>
- <p>This event-driven technique is applicable to a whole host of small
- microcontrollers, including</p>
- <ul>
- <li>MSP430</li>
- <li>Cortex M0, M0+ (SAM, STM32, PY32, Cypress, Kinetis, HT32, XMC,
- LPC81x)</li>
- <li>AtMega, AtTiny</li>
- <li>8051 (SiliconLabs, Nuvoton, HT85)</li>
- <li>RL78 (Renesas)</li>
- <li>Pic 12/14/16</li>
- <li>Risc (ch32v)</li>
- <li>STM8</li>
- </ul>
- <p><em>Some of the really tiny ones don’t have enough stack space to
- implement these techniques (Puolop PB150, Padauk PxS15x, Bojuxing BJ8P,
- Yspring MDT1x, EastSoft HR7P, Holtek Ht68.)</em></p>
- <img src="blb.jpg" alt="Here" /> is a typical board hosting 12 buttons,
- 12 RGB LEDs, all hosted by an STM32. Every light and button can be
- controlled separately and simultaneously, using the technique described
- in this paper.
- </details>
- <h2 id="the-problem">The Problem</h2>
- <p>Let’s suppose we want to flash an LED at 1 flash/2 sec, and
- independently, respond to a push button by operating a different LED for
- 1.5 seconds. Both operations must operate separately. How do we
- structure the code for the microcontroller to make this happen?</p>
- <h2 id="first-page">First Page</h2>
- <p>Let’s look an a pseudocode overview of what we want to do:</p>
- <div class="sourceCode" id="cb1"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="dt">void</span> initialize<span class="op">(</span><span class="dt">void</span><span class="op">)</span> <span class="op">{</span></span>
- <span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a> setup_hardware<span class="op">();</span></span>
- <span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a> setup_interrupts<span class="op">();</span></span>
- <span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span>
- <span id="cb1-5"><a href="#cb1-5" aria-hidden="true" tabindex="-1"></a></span>
- <span id="cb1-6"><a href="#cb1-6" aria-hidden="true" tabindex="-1"></a>INTERRUPT <span class="dt">void</span> irq<span class="op">(</span><span class="dt">void</span><span class="op">)</span> <span class="op">{</span></span>
- <span id="cb1-7"><a href="#cb1-7" aria-hidden="true" tabindex="-1"></a> create_event<span class="op">();</span></span>
- <span id="cb1-8"><a href="#cb1-8" aria-hidden="true" tabindex="-1"></a> acknowledge_interrupt<span class="op">();</span></span>
- <span id="cb1-9"><a href="#cb1-9" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span>
- <span id="cb1-10"><a href="#cb1-10" aria-hidden="true" tabindex="-1"></a></span>
- <span id="cb1-11"><a href="#cb1-11" aria-hidden="true" tabindex="-1"></a><span class="dt">void</span> main<span class="op">(</span><span class="dt">void</span><span class="op">)</span> <span class="op">{</span></span>
- <span id="cb1-12"><a href="#cb1-12" aria-hidden="true" tabindex="-1"></a> initialize<span class="op">();</span></span>
- <span id="cb1-13"><a href="#cb1-13" aria-hidden="true" tabindex="-1"></a></span>
- <span id="cb1-14"><a href="#cb1-14" aria-hidden="true" tabindex="-1"></a> <span class="cf">while</span> <span class="op">(</span><span class="dv">1</span><span class="op">)</span> <span class="op">{</span></span>
- <span id="cb1-15"><a href="#cb1-15" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> <span class="op">(</span>event<span class="op">)</span> <span class="op">{</span></span>
- <span id="cb1-16"><a href="#cb1-16" aria-hidden="true" tabindex="-1"></a> flashLedTask<span class="op">(</span>event<span class="op">);</span></span>
- <span id="cb1-17"><a href="#cb1-17" aria-hidden="true" tabindex="-1"></a> respondToButtonTask<span class="op">(</span>event<span class="op">);</span></span>
- <span id="cb1-18"><a href="#cb1-18" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
- <span id="cb1-19"><a href="#cb1-19" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
- <span id="cb1-20"><a href="#cb1-20" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
- <p>That’s about it.</p>
- <p>Of course, we will need to write the code for
- <code>setup_hardware()</code> and <code>setup_interrupts()</code>. And
- <code>flashLedTask()</code> and <code>respondToButtonTask()</code>. And
- create the <em>magic</em> that allows <code>event</code> information to
- flow. Don’t worry, it will all be laid out in the following pages.</p>
- <p>If you’re concerned with complexity, feel free to jump ahead to the
- <a href="#final-implementation">final implementation</a> page to see
- real, tested, code.</p>
- <p>Between here and there, I’ll walk you step-by-step through building
- the structure and implementation.</p>
- <p>This paper continues after that though, to show you how to expand
- upon a build as the project-definition changes. There are also several
- examples of state machines, and some discussion of practical matters,
- refactoring and project structure.</p>
- <h2 id="interrupts">Interrupts</h2>
- <p>In order to have a responsive system, it would make sense to use the
- <em>interrupt</em> capabilities of these small micrcontrollers.</p>
- <p>In the task described, we need to respond to a button press. So let’s
- connect the button to an input pin and enable it to repsond to a button
- press with interrupt code.</p>
- <p>We also need to keep track of time…..so let’s hook up a system timer
- to another interrupt.</p>
- <p>Each interrupt causes the execution of <em>interrupt handler</em>
- code. For this project, it might look somthing like this:</p>
- <div class="sourceCode" id="cb2"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="kw">enum</span> <span class="op">{</span>EVT_NONE<span class="op">,</span> EVT_TICK<span class="op">,</span> EVT_BUTTON<span class="op">};</span></span>
- <span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a></span>
- <span id="cb2-3"><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a>INTERRUPT timer_isr<span class="op">(</span><span class="dt">void</span><span class="op">)</span> <span class="op">{</span></span>
- <span id="cb2-4"><a href="#cb2-4" aria-hidden="true" tabindex="-1"></a> newEvent<span class="op">(</span>EVT_TICK<span class="op">);</span></span>
- <span id="cb2-5"><a href="#cb2-5" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span>
- <span id="cb2-6"><a href="#cb2-6" aria-hidden="true" tabindex="-1"></a></span>
- <span id="cb2-7"><a href="#cb2-7" aria-hidden="true" tabindex="-1"></a>INTERRUPT button_isr<span class="op">(</span><span class="dt">void</span><span class="op">)</span> <span class="op">{</span></span>
- <span id="cb2-8"><a href="#cb2-8" aria-hidden="true" tabindex="-1"></a> newEvent<span class="op">(</span>EVT_BUTTON<span class="op">);</span> <span class="co">// see notes about button bounce and acknowledgement</span></span>
- <span id="cb2-9"><a href="#cb2-9" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
- <p>The interrupt handlers are both very simple. They just create a
- unique <em>event</em> and let the system know about it. Next, let’s talk
- about <em>events</em>.</p>
- <details>
- <summary>
- Reality check
- </summary>
- <p>In some microcontrollers, interrupts must be <em>acknowledged</em>.
- Sometimes that means setting a hardware flag to indicate to the device
- that you’re ready for another interrupt of the same type. And in some
- cases, there’s no need to ack. Specifically, the SYS_TICK interrupt in
- the ARM Cortex processors does <em>not</em> need an ack, so the above
- code example for <code>timer_isr()</code> is complete.</p>
- <p>When a pushbutton or switch is connected to a microcontroller, the
- first bit of activity will cause the interrupt and execute the
- <code>button_isr()</code> code. However, real buttons produce about
- 5msec of <em>bounce</em> and this will cause subsequent interrupts
- unless they are somehow filtered out. There are lots of ways to handle
- <em>bounce</em>, and I’ll let you read about that
- [elsewhere]](#debouncing). Most techniques boil down to either ignoring
- subsequent interrupts (from the same button) for about 5 msec, or
- disabling that specific interrupt until the 5msec has passed.</p>
- <p>As a general rule, <em>input</em> pins should be observed either by
- interrupt service routine (ISR), or scanned periodically by the timer
- ISR. <em>Outputs</em> should be controlled in the task code, which we’ll
- see below.</p>
- </details>
- <h2 id="events">Events</h2>
- <p>An <em>event</em> in this context is a small bit of information that
- appears asynchronously in the system. Implemented, it can be a
- <em>byte</em>, or an <em>int</em> or an even larger structure. But in
- these small microcontrollers, let’s use a <em>byte</em>.</p>
- <div class="sourceCode" id="cb3"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="dt">volatile</span> uint8 event<span class="op">;</span></span></code></pre></div>
- <p>These <em>events</em> will be created in interrupt level of the code,
- and processed at the main level. We use the <code>event</code> object to
- send information between these levels, so we have to mark it <a
- href="https://en.wikipedia.org/wiki/Volatile_(computer_programming)"><code>volatile</code></a>.</p>
- <p>In this paper, the word <em>message</em> and <em>event</em> are
- equivalent. <em>Message</em> has the sense of “a bit of communication
- from another part of the program” and <em>event</em> has the sense of
- “something external just happened”. At this point in the design, they
- both mean the same thing.</p>
- <p>By convention, let’s use <em>zero</em> to indicate the absence of an
- event/message, and <em>non-zero</em> to represent an event/message.</p>
- <div class="sourceCode" id="cb4"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a><span class="dt">volatile</span> uint8 event<span class="op">;</span></span>
- <span id="cb4-2"><a href="#cb4-2" aria-hidden="true" tabindex="-1"></a><span class="kw">enum</span> <span class="op">{</span>EVT_NONE<span class="op">,</span> EVT_TICK<span class="op">,</span> EVT_BUTTON<span class="op">};</span> <span class="co">// 0,1,2</span></span></code></pre></div>
- <details>
- <summary>
- Reality check
- </summary>
- <hr>
- For this project, let’s suppose the timer ticks happen every 10ms (100
- per second).
- <hr>
- </details>
- <p>So now we start to get an idea of what kind of information the
- <em>interrupt handler</em> code generates. Now let’s look at how that
- information gets sent to the rest of the code:
- <code>newEvent()</code>.</p>
- <h2 id="newevent">newEvent()</h2>
- <p>Here’s a block diagram of the message flow <img
- src="dispatch1.jpg" /></p>
- <p>How do we send the information (events/messages) from the interrupt
- service routine to the <code>main()</code> code? We used shared
- memory.</p>
- <p>One way would be to have a global <code>volatile uint8</code>
- location into which we drop the <em>event</em> information. But having
- only one socket for that would be a bit naive; what happens if a timer
- tick and a button press happen very close in time? What happens if the
- timer tick events start to stack up?</p>
- <p>It makes more sense to have an array:
- <code>volatile uint8 events[NUM_EVENTS]</code> where NUM_EVENTS is on
- the order of 5..10. That would give us 50-100msec to catch up in case
- there’s a pileup of events/messages.</p>
- <p>At the beginning, before anything happens, we need to make sure the
- <code>events[]</code> is full of zeros (EVT_NONE), indicating that it’s
- empty.</p>
- <p>The <code>newEvent(evt)</code> routine simply adds the
- <code>evt</code> to the array <code>events[]</code>.</p>
- <p>Something like this might work:</p>
- <div class="sourceCode" id="cb5"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true" tabindex="-1"></a><span class="dt">void</span> newEvent<span class="op">(</span>uint8r evt<span class="op">)</span> <span class="op">{</span></span>
- <span id="cb5-2"><a href="#cb5-2" aria-hidden="true" tabindex="-1"></a> <span class="dt">static</span> <span class="dt">unsigned</span> uint8 nextEvent<span class="op">;</span></span>
- <span id="cb5-3"><a href="#cb5-3" aria-hidden="true" tabindex="-1"></a> events<span class="op">[</span>nextEvent<span class="op">++]</span> <span class="op">=</span> evt<span class="op">;</span></span>
- <span id="cb5-4"><a href="#cb5-4" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> <span class="op">(</span>nextEvent <span class="op">==</span> NUM_EVENTS<span class="op">)</span></span>
- <span id="cb5-5"><a href="#cb5-5" aria-hidden="true" tabindex="-1"></a> nextEvent <span class="op">=</span> <span class="dv">0</span><span class="op">;</span></span>
- <span id="cb5-6"><a href="#cb5-6" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
- <details>
- <summary>
- Reality check
- </summary>
- <p>There is a problem with the above code. What happens if a
- <code>key_isr()</code> is running, and is halfway through its call to
- <code>newEvent()</code> when a <code>timer_isr()</code> happens, and
- reenters the <code>newEvent()</code> routine. This will get really
- messed up. So we need to wrap <em>this particular</em> code in a
- critical section.</p>
- <p>Here’s a more realistic version:</p>
- <div class="sourceCode" id="cb6"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb6-1"><a href="#cb6-1" aria-hidden="true" tabindex="-1"></a><span class="dt">void</span> newEvent<span class="op">(</span><span class="dt">char</span> evt<span class="op">)</span> <span class="op">{</span></span>
- <span id="cb6-2"><a href="#cb6-2" aria-hidden="true" tabindex="-1"></a> <span class="dt">static</span> <span class="dt">unsigned</span> <span class="dt">char</span> nextEvent<span class="op">;</span> <span class="co">// keep track of where we are in queue</span></span>
- <span id="cb6-3"><a href="#cb6-3" aria-hidden="true" tabindex="-1"></a> disable_irq<span class="op">();</span> <span class="co">// critical section</span></span>
- <span id="cb6-4"><a href="#cb6-4" aria-hidden="true" tabindex="-1"></a> events<span class="op">[</span>nextEvent<span class="op">++]</span> <span class="op">=</span> evt<span class="op">;</span> <span class="co">// insert event into queue</span></span>
- <span id="cb6-5"><a href="#cb6-5" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> <span class="op">(</span>nextEvent <span class="op">==</span> NUM_EVENTS<span class="op">)</span> <span class="co">// loop back to the start of queue</span></span>
- <span id="cb6-6"><a href="#cb6-6" aria-hidden="true" tabindex="-1"></a> nextEvent <span class="op">=</span> <span class="dv">0</span><span class="op">;</span></span>
- <span id="cb6-7"><a href="#cb6-7" aria-hidden="true" tabindex="-1"></a> enable_irq<span class="op">();</span> <span class="co">// end critical section, probably <100us of blockage</span></span>
- <span id="cb6-8"><a href="#cb6-8" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
- </details>
- <p>In the next section, we’ll show how the <code>main()</code> code
- pulls the events out of the array, and leaves an EVT_NONE in its
- place.</p>
- <h2 id="dispatch">Dispatch</h2>
- <p>The <code>main()</code> code can simply watch the
- <code>events[]</code> to see when an entry goes non-zero (!=EVT_NONE).
- When that happens, <code>main()</code> will pull out the
- <em>event/message</em> from the array, and call the task subroutines. In
- this case, <code>flashLedTask()</code> and
- <code>respondToButtonTask()</code>.</p>
- <p>To see the real code for the dispatcher, <a
- href="#dispatcher-details">jump ahead</a>.</p>
- <details>
- <summary>
- Terminology
- </summary>
- <p>The <code>main()</code> code illustrated here calls the tasks as
- subroutines, sending each one a copy of the event number.</p>
- <p>As I mentioned earlier, <em>events</em> can also be refered to as
- <em>messages</em>. We can also refer to this process “sending a message
- to the task”</p>
- <p>So, for this paper, these are equivalent</p>
- <ul>
- <li>calling a task subroutine with the event information</li>
- <li>sending a message to a task</li>
- </ul>
- </details>
- <figure>
- <img src="dispatch2.jpg" alt="Call task routines with event" />
- <figcaption aria-hidden="true">Call task routines with
- event</figcaption>
- </figure>
- <details>
- <summary>
- Reality check
- </summary>
- <p>It may seem wasteful to send <em>all</em> events to <em>all</em>
- tasks. Probably some tasks don’t care about certain classes of events.
- But classifying, sorting and filtering the events takes real time, and
- code space, and probably isn’t worth it for these small
- microcontrollers.</p>
- <p>More sophisticated event systems do, in fact, filter and sort events.
- For example, Windows only sends <code>MOUSE_MOVE</code> events to the
- code belonging to the window over which the mouse is travelling. All the
- other windows don’t get the event, speeding up the whole system.</p>
- </details>
- <h2 id="tasks">Tasks</h2>
- <p>In this environment, the code that impliments a <em>task</em> is
- simply a subroutine that accepts an <em>event/message</em>.</p>
- <div class="sourceCode" id="cb7"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb7-1"><a href="#cb7-1" aria-hidden="true" tabindex="-1"></a><span class="dt">void</span> taskCode<span class="op">(</span>uint8 event<span class="op">)</span> <span class="op">{</span></span>
- <span id="cb7-2"><a href="#cb7-2" aria-hidden="true" tabindex="-1"></a> <span class="op">...</span> process the event information <span class="op">...</span></span>
- <span id="cb7-3"><a href="#cb7-3" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
- <p>The subroutine should be designed to flow-through as quickly as
- possible, without any pauses/waits/delays.</p>
- <p>If the problem that you’re trying to solve involves the passage of
- time, or any delay, then you must break down the actions into individual
- items, and build a <a href="#state-machine">state machine</a>.</p>
- <h2 id="state-machine">State Machine</h2>
- <p>A <em>state machine</em> in this environment is a subroutine which
- can be called many times, and it remembers in which <em>state</em> it
- was left from the previous call. Some invocations of this subroutine may
- cause it to change state, which can be represented in a net diagram.</p>
- <p>Here’s a state diagram for the task which reacts to a button press by
- flashing an LED.</p>
- <figure>
- <img src="rtb_state.png" alt="simple state diagram" />
- <figcaption aria-hidden="true">simple state diagram</figcaption>
- </figure>
- <p>How does the <em>state code</em> remember what state it’s in between
- invocations? We can use a <code>static</code> variable. A
- <code>static</code> is stored in main memory (<em>not on the
- stack</em>), persists between calls and is initialized to zero.</p>
- <p>The above diagram can be implemented in this code:</p>
- <div class="sourceCode" id="cb8"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb8-1"><a href="#cb8-1" aria-hidden="true" tabindex="-1"></a><span class="kw">enum</span> <span class="op">{</span>RTB_IDLE<span class="op">,</span> RTB_ON<span class="op">};</span> <span class="co">// RTB_IDLE=0, RTB_ON=1</span></span>
- <span id="cb8-2"><a href="#cb8-2" aria-hidden="true" tabindex="-1"></a><span class="dt">static</span> uint8 rtbState <span class="op">=</span> RTB_IDLE<span class="op">;</span></span>
- <span id="cb8-3"><a href="#cb8-3" aria-hidden="true" tabindex="-1"></a><span class="dt">static</span> uint16 rtbTimerCount <span class="op">=</span> <span class="dv">0</span><span class="op">;</span></span>
- <span id="cb8-4"><a href="#cb8-4" aria-hidden="true" tabindex="-1"></a><span class="dt">const</span> uint16 TIMER_LIMIT <span class="op">=</span> <span class="dv">150</span><span class="op">;</span></span>
- <span id="cb8-5"><a href="#cb8-5" aria-hidden="true" tabindex="-1"></a></span>
- <span id="cb8-6"><a href="#cb8-6" aria-hidden="true" tabindex="-1"></a><span class="dt">void</span> respondToButtonTask<span class="op">(</span>uint8 evt<span class="op">)</span> <span class="op">{</span></span>
- <span id="cb8-7"><a href="#cb8-7" aria-hidden="true" tabindex="-1"></a> <span class="cf">switch</span><span class="op">(</span>rtbState<span class="op">)</span> <span class="op">{</span></span>
- <span id="cb8-8"><a href="#cb8-8" aria-hidden="true" tabindex="-1"></a> <span class="cf">case</span> RTB_IDLE<span class="op">:</span></span>
- <span id="cb8-9"><a href="#cb8-9" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> <span class="op">(</span>evt <span class="op">==</span> EVT_BUTTON<span class="op">)</span> <span class="op">{</span></span>
- <span id="cb8-10"><a href="#cb8-10" aria-hidden="true" tabindex="-1"></a> rtbState <span class="op">=</span> RTB_ON<span class="op">;</span></span>
- <span id="cb8-11"><a href="#cb8-11" aria-hidden="true" tabindex="-1"></a> rtbTimerCount <span class="op">=</span> TIMER_LIMIT<span class="op">;</span></span>
- <span id="cb8-12"><a href="#cb8-12" aria-hidden="true" tabindex="-1"></a> gpio_set<span class="op">(</span>LED<span class="op">,</span> ON<span class="op">);</span></span>
- <span id="cb8-13"><a href="#cb8-13" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
- <span id="cb8-14"><a href="#cb8-14" aria-hidden="true" tabindex="-1"></a> <span class="cf">break</span><span class="op">;</span></span>
- <span id="cb8-15"><a href="#cb8-15" aria-hidden="true" tabindex="-1"></a> <span class="cf">case</span> RTB_ON<span class="op">:</span></span>
- <span id="cb8-16"><a href="#cb8-16" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> <span class="op">(</span>evt <span class="op">==</span> EVT_TICK<span class="op">)</span> <span class="op">{</span></span>
- <span id="cb8-17"><a href="#cb8-17" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> <span class="op">(--</span>rtbTimerCount <span class="op">==</span> <span class="dv">0</span><span class="op">)</span> <span class="op">{</span></span>
- <span id="cb8-18"><a href="#cb8-18" aria-hidden="true" tabindex="-1"></a> gpio_set<span class="op">(</span>LED<span class="op">,</span> OFF<span class="op">);</span></span>
- <span id="cb8-19"><a href="#cb8-19" aria-hidden="true" tabindex="-1"></a> rtbState <span class="op">=</span> RTB_IDLE<span class="op">;</span></span>
- <span id="cb8-20"><a href="#cb8-20" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
- <span id="cb8-21"><a href="#cb8-21" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
- <span id="cb8-22"><a href="#cb8-22" aria-hidden="true" tabindex="-1"></a> <span class="cf">break</span><span class="op">;</span></span>
- <span id="cb8-23"><a href="#cb8-23" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
- <span id="cb8-24"><a href="#cb8-24" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
- <p>Each time this routine is called, it checks the <em>event</em> that
- it was given, and sometimes processes it. And sometimes it changes
- state.</p>
- <p>Here’s a few things to notice:</p>
- <ul>
- <li>The code <em>flows through</em>. It does not stop or wait for
- anything.</li>
- <li>Each arrow in the net diagram corresponds to a phrase in the code.
- The <em>tail</em> of the arrow corresponds to an <code>if</code>
- statement</li>
- <li>The code ignores <em>events</em> that are not relevant.</li>
- </ul>
- <details>
- <summary>
- Reality check
- </summary>
- <p>The <em>state</em> variable must be <code>static</code> or in the
- global memory space. <em>NEVER put a state variable on the stack!</em>
- i.e. as a local variable.</p>
- The tick counter could equally well count up from zero to threshold. See
- the discussion about <a href="#timers-as-a-resource">shared timers</a>
- </details>
- <h2 id="another-state-machine">Another State Machine</h2>
- <p>The other task of this project simply flashes the LED on and off.</p>
- <p><img src="led_state_simple.png" /></p>
- <p>The code for this might be:</p>
- <div class="sourceCode" id="cb9"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb9-1"><a href="#cb9-1" aria-hidden="true" tabindex="-1"></a><span class="kw">enum</span> <span class="op">{</span>LED_ON<span class="op">,</span> LED_OFF<span class="op">};</span></span>
- <span id="cb9-2"><a href="#cb9-2" aria-hidden="true" tabindex="-1"></a><span class="dt">static</span> uint8 ledState <span class="op">=</span> LED_OFF<span class="op">;</span></span>
- <span id="cb9-3"><a href="#cb9-3" aria-hidden="true" tabindex="-1"></a><span class="dt">static</span> uint16 ledTimerCount <span class="op">=</span> <span class="dv">0</span><span class="op">;</span></span>
- <span id="cb9-4"><a href="#cb9-4" aria-hidden="true" tabindex="-1"></a><span class="dt">const</span> uint16 LED_ON_TIME <span class="op">=</span> <span class="dv">100</span><span class="op">;</span></span>
- <span id="cb9-5"><a href="#cb9-5" aria-hidden="true" tabindex="-1"></a><span class="dt">const</span> uint16 LED_OFF_TIME <span class="op">=</span> <span class="dv">100</span><span class="op">;</span></span>
- <span id="cb9-6"><a href="#cb9-6" aria-hidden="true" tabindex="-1"></a></span>
- <span id="cb9-7"><a href="#cb9-7" aria-hidden="true" tabindex="-1"></a><span class="dt">void</span> ledTask<span class="op">(</span>uint8 evt<span class="op">)</span> <span class="op">{</span></span>
- <span id="cb9-8"><a href="#cb9-8" aria-hidden="true" tabindex="-1"></a> <span class="cf">switch</span><span class="op">(</span>ledState<span class="op">)</span> <span class="op">{</span></span>
- <span id="cb9-9"><a href="#cb9-9" aria-hidden="true" tabindex="-1"></a> <span class="cf">case</span> LED_OFF<span class="op">:</span></span>
- <span id="cb9-10"><a href="#cb9-10" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> <span class="op">(</span>evt <span class="op">==</span> EVT_TICK<span class="op">)</span> <span class="op">{</span></span>
- <span id="cb9-11"><a href="#cb9-11" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> <span class="op">(--</span>ledTimerCount <span class="op">==</span> <span class="dv">0</span><span class="op">)</span> <span class="op">{</span></span>
- <span id="cb9-12"><a href="#cb9-12" aria-hidden="true" tabindex="-1"></a> gpio_set<span class="op">(</span>LED<span class="op">,</span> ON<span class="op">);</span></span>
- <span id="cb9-13"><a href="#cb9-13" aria-hidden="true" tabindex="-1"></a> ledTimerCount <span class="op">=</span> LED_ON_TIME<span class="op">;</span></span>
- <span id="cb9-14"><a href="#cb9-14" aria-hidden="true" tabindex="-1"></a> ledState <span class="op">=</span> LED_ON<span class="op">;</span></span>
- <span id="cb9-15"><a href="#cb9-15" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
- <span id="cb9-16"><a href="#cb9-16" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
- <span id="cb9-17"><a href="#cb9-17" aria-hidden="true" tabindex="-1"></a> <span class="cf">break</span><span class="op">;</span></span>
- <span id="cb9-18"><a href="#cb9-18" aria-hidden="true" tabindex="-1"></a> <span class="cf">case</span> LED_ON<span class="op">:</span></span>
- <span id="cb9-19"><a href="#cb9-19" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> <span class="op">(</span>evt <span class="op">==</span> EVT_TICK<span class="op">)</span> <span class="op">{</span></span>
- <span id="cb9-20"><a href="#cb9-20" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> <span class="op">(--</span>ledTimerCount <span class="op">==</span> <span class="dv">0</span><span class="op">)</span> <span class="op">{</span></span>
- <span id="cb9-21"><a href="#cb9-21" aria-hidden="true" tabindex="-1"></a> gpio_set<span class="op">(</span>LED<span class="op">,</span> OFF<span class="op">);</span></span>
- <span id="cb9-22"><a href="#cb9-22" aria-hidden="true" tabindex="-1"></a> ledTimerCount <span class="op">=</span> LED_OFF_TIME<span class="op">;</span></span>
- <span id="cb9-23"><a href="#cb9-23" aria-hidden="true" tabindex="-1"></a> ledState <span class="op">=</span> LED_OFF<span class="op">;</span></span>
- <span id="cb9-24"><a href="#cb9-24" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
- <span id="cb9-25"><a href="#cb9-25" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
- <span id="cb9-26"><a href="#cb9-26" aria-hidden="true" tabindex="-1"></a> <span class="cf">break</span><span class="op">;</span></span>
- <span id="cb9-27"><a href="#cb9-27" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
- <span id="cb9-28"><a href="#cb9-28" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
- <details>
- <summary>
- Discussion
- </summary>
- <p>Perhaps a more accurate state diagram might be:</p>
- <p><img src="led_state.png" /></p>
- <p>As you build the code from the diagram, I <em>strongly</em> suggest
- that you use a <code>switch(currentState) ....case STATE_1</code>
- approach to the task routine. If you try and code it with multiple
- <code>if(currentState==STATE_1)</code>, you will find yourself in a
- tangle of spaghetti; and if you forget a critical <code>else</code>,
- nothing will work as you expect.</p>
- <div class="sourceCode" id="cb10"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb10-1"><a href="#cb10-1" aria-hidden="true" tabindex="-1"></a><span class="cf">if</span> <span class="op">(</span>state<span class="op">==</span><span class="dv">1</span><span class="op">)</span> <span class="op">{</span></span>
- <span id="cb10-2"><a href="#cb10-2" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> <span class="op">(</span>event <span class="op">=</span> EVT_1<span class="op">)</span> <span class="op">{</span></span>
- <span id="cb10-3"><a href="#cb10-3" aria-hidden="true" tabindex="-1"></a> P3OUT <span class="op">=</span> <span class="dv">27</span><span class="op">;</span> <span class="co">// turn on some hardware</span></span>
- <span id="cb10-4"><a href="#cb10-4" aria-hidden="true" tabindex="-1"></a> state <span class="op">=</span> <span class="dv">2</span><span class="op">;</span> <span class="co">// and change state</span></span>
- <span id="cb10-5"><a href="#cb10-5" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
- <span id="cb10-6"><a href="#cb10-6" aria-hidden="true" tabindex="-1"></a><span class="op">}</span> </span>
- <span id="cb10-7"><a href="#cb10-7" aria-hidden="true" tabindex="-1"></a><span class="cf">if</span> <span class="op">(</span>state<span class="op">==</span><span class="dv">2</span><span class="op">)</span> <span class="op">{</span> <span class="co">// <<<----- BAD BAD</span></span>
- <span id="cb10-8"><a href="#cb10-8" aria-hidden="true" tabindex="-1"></a> P3OUT <span class="op">=</span> <span class="dv">14</span><span class="op">;</span></span>
- <span id="cb10-9"><a href="#cb10-9" aria-hidden="true" tabindex="-1"></a> state <span class="op">=</span> <span class="dv">3</span><span class="op">;</span> <span class="co">// <<<--- dont' change state twice in the same event</span></span>
- <span id="cb10-10"><a href="#cb10-10" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span>
- <span id="cb10-11"><a href="#cb10-11" aria-hidden="true" tabindex="-1"></a><span class="op">...</span></span></code></pre></div>
- <p>rather</p>
- <div class="sourceCode" id="cb11"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb11-1"><a href="#cb11-1" aria-hidden="true" tabindex="-1"></a><span class="cf">switch</span><span class="op">(</span>state<span class="op">)</span> <span class="op">{</span></span>
- <span id="cb11-2"><a href="#cb11-2" aria-hidden="true" tabindex="-1"></a> <span class="cf">case</span> <span class="dv">1</span><span class="op">:</span></span>
- <span id="cb11-3"><a href="#cb11-3" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> <span class="op">(</span>event <span class="op">=</span> EVT_1<span class="op">)</span> <span class="op">{</span></span>
- <span id="cb11-4"><a href="#cb11-4" aria-hidden="true" tabindex="-1"></a> P3OUT <span class="op">=</span> <span class="dv">27</span><span class="op">;</span> <span class="co">// turn on some hardware</span></span>
- <span id="cb11-5"><a href="#cb11-5" aria-hidden="true" tabindex="-1"></a> state <span class="op">=</span> <span class="dv">2</span><span class="op">;</span></span>
- <span id="cb11-6"><a href="#cb11-6" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
- <span id="cb11-7"><a href="#cb11-7" aria-hidden="true" tabindex="-1"></a> <span class="cf">break</span><span class="op">;</span></span>
- <span id="cb11-8"><a href="#cb11-8" aria-hidden="true" tabindex="-1"></a> <span class="cf">case</span> <span class="dv">2</span><span class="op">:</span></span>
- <span id="cb11-9"><a href="#cb11-9" aria-hidden="true" tabindex="-1"></a> P3OUT <span class="op">=</span> <span class="dv">14</span><span class="op">;</span></span>
- <span id="cb11-10"><a href="#cb11-10" aria-hidden="true" tabindex="-1"></a> state <span class="op">=</span> <span class="dv">3</span><span class="op">;</span></span>
- <span id="cb11-11"><a href="#cb11-11" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
- <span id="cb11-12"><a href="#cb11-12" aria-hidden="true" tabindex="-1"></a> <span class="op">...</span></span>
- <span id="cb11-13"><a href="#cb11-13" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
- <p>As a general rule, try to avoid changing states more than once per
- event; this kind of discipline will help with debugging a complex
- project.</p>
- </details>
- <h2 id="dispatcher-details">Dispatcher Details</h2>
- <p>The last piece of the puzzle is the <code>main()</code> code which
- observes the <code>events[]</code> array and calls the tasks.</p>
- <p>The <code>events[]</code> array is designed so that a 0 means ‘no
- event’, and the non-zero events are dropped into the array in order…so
- pulling them out is pretty straight forward.</p>
- <div class="sourceCode" id="cb12"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb12-1"><a href="#cb12-1" aria-hidden="true" tabindex="-1"></a><span class="dt">void</span> main<span class="op">()</span> <span class="op">{</span></span>
- <span id="cb12-2"><a href="#cb12-2" aria-hidden="true" tabindex="-1"></a> initialize<span class="op">();</span></span>
- <span id="cb12-3"><a href="#cb12-3" aria-hidden="true" tabindex="-1"></a></span>
- <span id="cb12-4"><a href="#cb12-4" aria-hidden="true" tabindex="-1"></a> <span class="cf">while</span><span class="op">(</span><span class="dv">1</span><span class="op">)</span> <span class="op">{</span></span>
- <span id="cb12-5"><a href="#cb12-5" aria-hidden="true" tabindex="-1"></a> <span class="dt">int</span> i<span class="op">;</span></span>
- <span id="cb12-6"><a href="#cb12-6" aria-hidden="true" tabindex="-1"></a> <span class="cf">for</span> <span class="op">(</span>i<span class="op">=</span><span class="dv">0</span><span class="op">;</span> i<span class="op"><</span>NUM_EVENTS<span class="op">;</span> i<span class="op">++)</span> <span class="op">{</span></span>
- <span id="cb12-7"><a href="#cb12-7" aria-hidden="true" tabindex="-1"></a> <span class="cf">while</span> <span class="op">(</span>events<span class="op">[</span>i<span class="op">]==</span>EVT_NONE<span class="op">)</span></span>
- <span id="cb12-8"><a href="#cb12-8" aria-hidden="true" tabindex="-1"></a> <span class="op">{}</span></span>
- <span id="cb12-9"><a href="#cb12-9" aria-hidden="true" tabindex="-1"></a> ledTask<span class="op">(</span>events<span class="op">[</span>i<span class="op">]);</span></span>
- <span id="cb12-10"><a href="#cb12-10" aria-hidden="true" tabindex="-1"></a> respondToButtonTask<span class="op">(</span>events<span class="op">[</span>i<span class="op">]);</span></span>
- <span id="cb12-11"><a href="#cb12-11" aria-hidden="true" tabindex="-1"></a> events<span class="op">[</span>i<span class="op">]</span> <span class="op">=</span> EVT_NONE<span class="op">;</span></span>
- <span id="cb12-12"><a href="#cb12-12" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
- <span id="cb12-13"><a href="#cb12-13" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
- <span id="cb12-14"><a href="#cb12-14" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
- <p>Each non-zero event is “sent” to each task…..by means of a subroutine
- call. Once all the tasks have been invoked, the <code>event</code> is
- thrown away and its slot in the array is set to zero.</p>
- <details>
- <summary>
- Reality check
- </summary>
- <p>The <code>main()</code> code above needs to run with interrupts
- enabled.</p>
- <p>There’s lots of ways to structure the ‘wait for event’ loop. For
- example, when you detect that the <code>events[]</code> array is empty,
- you could power-down the microcontroller. In most microprocessors, an
- interrupt will wake the CPU again, and deliver a non-zero event into the
- array, so you might as well power-down while you’re waiting.</p>
- </details>
- <h2 id="final-implementation">Final Implementation</h2>
- <p>Let’s put all of the above together, for an SMT32F Cortex M0, in
- <em>C</em> code.</p>
- <div class="sourceCode" id="cb13"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb13-1"><a href="#cb13-1" aria-hidden="true" tabindex="-1"></a><span class="co">/***** declarations ****/</span></span>
- <span id="cb13-2"><a href="#cb13-2" aria-hidden="true" tabindex="-1"></a><span class="pp">#define NUM_EVENTS </span><span class="dv">10</span></span>
- <span id="cb13-3"><a href="#cb13-3" aria-hidden="true" tabindex="-1"></a><span class="dt">volatile</span> uint8 events<span class="op">[</span>NUM_EVENTS<span class="op">];</span></span>
- <span id="cb13-4"><a href="#cb13-4" aria-hidden="true" tabindex="-1"></a></span>
- <span id="cb13-5"><a href="#cb13-5" aria-hidden="true" tabindex="-1"></a><span class="dt">void</span> newEvent<span class="op">(</span>uint8 e<span class="op">);</span></span>
- <span id="cb13-6"><a href="#cb13-6" aria-hidden="true" tabindex="-1"></a><span class="dt">void</span> ledTask<span class="op">(</span>uint8 evt<span class="op">);</span></span>
- <span id="cb13-7"><a href="#cb13-7" aria-hidden="true" tabindex="-1"></a><span class="dt">void</span> respondToButtonTask<span class="op">(</span>uint8 evt<span class="op">);</span></span>
- <span id="cb13-8"><a href="#cb13-8" aria-hidden="true" tabindex="-1"></a></span>
- <span id="cb13-9"><a href="#cb13-9" aria-hidden="true" tabindex="-1"></a><span class="co">/********** interrupts **************/</span></span>
- <span id="cb13-10"><a href="#cb13-10" aria-hidden="true" tabindex="-1"></a></span>
- <span id="cb13-11"><a href="#cb13-11" aria-hidden="true" tabindex="-1"></a><span class="dt">void</span> timer_isr<span class="op">(</span><span class="dt">void</span><span class="op">)</span> <span class="op">{</span></span>
- <span id="cb13-12"><a href="#cb13-12" aria-hidden="true" tabindex="-1"></a> newEvent<span class="op">(</span>EVT_TICK<span class="op">);</span></span>
- <span id="cb13-13"><a href="#cb13-13" aria-hidden="true" tabindex="-1"></a> <span class="co">// this interrupt is auto-ack'd</span></span>
- <span id="cb13-14"><a href="#cb13-14" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span>
- <span id="cb13-15"><a href="#cb13-15" aria-hidden="true" tabindex="-1"></a></span>
- <span id="cb13-16"><a href="#cb13-16" aria-hidden="true" tabindex="-1"></a><span class="dt">void</span> button_isr<span class="op">(</span><span class="dt">void</span><span class="op">)</span> <span class="op">{</span></span>
- <span id="cb13-17"><a href="#cb13-17" aria-hidden="true" tabindex="-1"></a> newEvent<span class="op">(</span>EVT_BUTTON<span class="op">);</span></span>
- <span id="cb13-18"><a href="#cb13-18" aria-hidden="true" tabindex="-1"></a> EXTI<span class="op">-></span>PR <span class="op">|=</span> KEY_IRQ_ACK_MASK<span class="op">;</span> <span class="co">// the hardware requires that we acknowledge </span></span>
- <span id="cb13-19"><a href="#cb13-19" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span>
- <span id="cb13-20"><a href="#cb13-20" aria-hidden="true" tabindex="-1"></a></span>
- <span id="cb13-21"><a href="#cb13-21" aria-hidden="true" tabindex="-1"></a><span class="co">/** newEvent</span></span>
- <span id="cb13-22"><a href="#cb13-22" aria-hidden="true" tabindex="-1"></a><span class="co"> * add the event to the event queue</span></span>
- <span id="cb13-23"><a href="#cb13-23" aria-hidden="true" tabindex="-1"></a><span class="co"> * wrapped in critical section</span></span>
- <span id="cb13-24"><a href="#cb13-24" aria-hidden="true" tabindex="-1"></a><span class="co"> * </span></span>
- <span id="cb13-25"><a href="#cb13-25" aria-hidden="true" tabindex="-1"></a><span class="co"> * </span><span class="an">@param</span><span class="co"> </span><span class="cv">the</span><span class="co"> event</span></span>
- <span id="cb13-26"><a href="#cb13-26" aria-hidden="true" tabindex="-1"></a><span class="co"> */</span></span>
- <span id="cb13-27"><a href="#cb13-27" aria-hidden="true" tabindex="-1"></a><span class="dt">void</span> newEvent<span class="op">(</span>uint8 e<span class="op">)</span> <span class="op">{</span></span>
- <span id="cb13-28"><a href="#cb13-28" aria-hidden="true" tabindex="-1"></a> <span class="dt">static</span> uint8 nextEvent<span class="op">;</span></span>
- <span id="cb13-29"><a href="#cb13-29" aria-hidden="true" tabindex="-1"></a> dint<span class="op">();</span> <span class="co">// critical section</span></span>
- <span id="cb13-30"><a href="#cb13-30" aria-hidden="true" tabindex="-1"></a> events<span class="op">[</span>nextEvent<span class="op">++]</span> <span class="op">=</span> e<span class="op">;</span></span>
- <span id="cb13-31"><a href="#cb13-31" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> <span class="op">(</span>nextEvent<span class="op">==</span>NUM_EVENTS<span class="op">)</span></span>
- <span id="cb13-32"><a href="#cb13-32" aria-hidden="true" tabindex="-1"></a> nextEvent <span class="op">=</span> <span class="dv">0</span><span class="op">;</span></span>
- <span id="cb13-33"><a href="#cb13-33" aria-hidden="true" tabindex="-1"></a> eint<span class="op">();</span></span>
- <span id="cb13-34"><a href="#cb13-34" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span>
- <span id="cb13-35"><a href="#cb13-35" aria-hidden="true" tabindex="-1"></a></span>
- <span id="cb13-36"><a href="#cb13-36" aria-hidden="true" tabindex="-1"></a><span class="co">/****** main() and dispatcher *********/</span></span>
- <span id="cb13-37"><a href="#cb13-37" aria-hidden="true" tabindex="-1"></a><span class="dt">void</span> main<span class="op">(</span><span class="dt">void</span><span class="op">)</span> <span class="op">{</span></span>
- <span id="cb13-38"><a href="#cb13-38" aria-hidden="true" tabindex="-1"></a></span>
- <span id="cb13-39"><a href="#cb13-39" aria-hidden="true" tabindex="-1"></a> eint<span class="op">();</span></span>
- <span id="cb13-40"><a href="#cb13-40" aria-hidden="true" tabindex="-1"></a></span>
- <span id="cb13-41"><a href="#cb13-41" aria-hidden="true" tabindex="-1"></a> <span class="co">// dispatcher loop</span></span>
- <span id="cb13-42"><a href="#cb13-42" aria-hidden="true" tabindex="-1"></a> <span class="cf">while</span><span class="op">(</span><span class="dv">1</span><span class="op">)</span> <span class="op">{</span></span>
- <span id="cb13-43"><a href="#cb13-43" aria-hidden="true" tabindex="-1"></a> <span class="dt">int</span> j<span class="op">;</span></span>
- <span id="cb13-44"><a href="#cb13-44" aria-hidden="true" tabindex="-1"></a> <span class="cf">for</span> <span class="op">(</span>j<span class="op">=</span><span class="dv">0</span><span class="op">;</span> j<span class="op"><</span>NUM_EVENTS<span class="op">;</span> j<span class="op">++)</span> <span class="op">{</span></span>
- <span id="cb13-45"><a href="#cb13-45" aria-hidden="true" tabindex="-1"></a> <span class="cf">while</span> <span class="op">(</span>events<span class="op">[</span>j<span class="op">]==</span>EVT_NONE<span class="op">)</span></span>
- <span id="cb13-46"><a href="#cb13-46" aria-hidden="true" tabindex="-1"></a> <span class="op">{}</span></span>
- <span id="cb13-47"><a href="#cb13-47" aria-hidden="true" tabindex="-1"></a> ledTask<span class="op">(</span>events<span class="op">[</span>j<span class="op">]);</span></span>
- <span id="cb13-48"><a href="#cb13-48" aria-hidden="true" tabindex="-1"></a> respondToButtonTask<span class="op">(</span>events<span class="op">[</span>j<span class="op">]);</span></span>
- <span id="cb13-49"><a href="#cb13-49" aria-hidden="true" tabindex="-1"></a> events<span class="op">[</span>j<span class="op">]</span> <span class="op">=</span> EVT_NONE<span class="op">;</span></span>
- <span id="cb13-50"><a href="#cb13-50" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
- <span id="cb13-51"><a href="#cb13-51" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
- <span id="cb13-52"><a href="#cb13-52" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span>
- <span id="cb13-53"><a href="#cb13-53" aria-hidden="true" tabindex="-1"></a></span>
- <span id="cb13-54"><a href="#cb13-54" aria-hidden="true" tabindex="-1"></a><span class="co">/*********** task code, with states ************/</span></span>
- <span id="cb13-55"><a href="#cb13-55" aria-hidden="true" tabindex="-1"></a><span class="kw">enum</span> <span class="op">{</span>LED_ON<span class="op">,</span> LED_OFF<span class="op">};</span></span>
- <span id="cb13-56"><a href="#cb13-56" aria-hidden="true" tabindex="-1"></a><span class="kw">enum</span> <span class="op">{</span>RTB_IDLE<span class="op">,</span> RTB_ON<span class="op">};</span> <span class="co">// states</span></span>
- <span id="cb13-57"><a href="#cb13-57" aria-hidden="true" tabindex="-1"></a><span class="dt">static</span> uint8 rtbState <span class="op">=</span> RTB_IDLE<span class="op">;</span></span>
- <span id="cb13-58"><a href="#cb13-58" aria-hidden="true" tabindex="-1"></a><span class="dt">static</span> uint16 rtbTimerCount <span class="op">=</span> <span class="dv">0</span><span class="op">;</span></span>
- <span id="cb13-59"><a href="#cb13-59" aria-hidden="true" tabindex="-1"></a><span class="dt">const</span> uint16 LED_ON_TIME <span class="op">=</span> <span class="dv">150</span><span class="op">;</span></span>
- <span id="cb13-60"><a href="#cb13-60" aria-hidden="true" tabindex="-1"></a></span>
- <span id="cb13-61"><a href="#cb13-61" aria-hidden="true" tabindex="-1"></a><span class="dt">void</span> respondToButtonTask<span class="op">(</span>uint8 evt<span class="op">)</span> <span class="op">{</span></span>
- <span id="cb13-62"><a href="#cb13-62" aria-hidden="true" tabindex="-1"></a> <span class="cf">switch</span><span class="op">(</span>rtbState<span class="op">)</span> <span class="op">{</span></span>
- <span id="cb13-63"><a href="#cb13-63" aria-hidden="true" tabindex="-1"></a> <span class="cf">case</span> RTB_IDLE<span class="op">:</span></span>
- <span id="cb13-64"><a href="#cb13-64" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> <span class="op">(</span>evt <span class="op">==</span> EVT_BUTTON<span class="op">)</span> <span class="op">{</span></span>
- <span id="cb13-65"><a href="#cb13-65" aria-hidden="true" tabindex="-1"></a> rtbTimerCount <span class="op">=</span> <span class="dv">0</span><span class="op">;</span></span>
- <span id="cb13-66"><a href="#cb13-66" aria-hidden="true" tabindex="-1"></a> rtbState <span class="op">=</span> RTB_ON<span class="op">;</span></span>
- <span id="cb13-67"><a href="#cb13-67" aria-hidden="true" tabindex="-1"></a> gpio_set<span class="op">(</span>LED<span class="op">,</span> LED_ON<span class="op">);</span></span>
- <span id="cb13-68"><a href="#cb13-68" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
- <span id="cb13-69"><a href="#cb13-69" aria-hidden="true" tabindex="-1"></a> <span class="cf">break</span><span class="op">;</span></span>
- <span id="cb13-70"><a href="#cb13-70" aria-hidden="true" tabindex="-1"></a> <span class="cf">case</span> RTB_ON<span class="op">:</span></span>
- <span id="cb13-71"><a href="#cb13-71" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> <span class="op">(</span>evt <span class="op">==</span> EVT_TICK<span class="op">)</span> <span class="op">{</span></span>
- <span id="cb13-72"><a href="#cb13-72" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> <span class="op">(++</span>rtbTimerCount <span class="op">></span> LED_ON_TIME<span class="op">)</span> <span class="op">{</span></span>
- <span id="cb13-73"><a href="#cb13-73" aria-hidden="true" tabindex="-1"></a> gpio_set<span class="op">(</span>LED<span class="op">,</span> LED_OFF<span class="op">);</span></span>
- <span id="cb13-74"><a href="#cb13-74" aria-hidden="true" tabindex="-1"></a> rtbState <span class="op">=</span> RTB_IDLE<span class="op">;</span></span>
- <span id="cb13-75"><a href="#cb13-75" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
- <span id="cb13-76"><a href="#cb13-76" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
- <span id="cb13-77"><a href="#cb13-77" aria-hidden="true" tabindex="-1"></a> <span class="cf">break</span><span class="op">;</span></span>
- <span id="cb13-78"><a href="#cb13-78" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
- <span id="cb13-79"><a href="#cb13-79" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span>
- <span id="cb13-80"><a href="#cb13-80" aria-hidden="true" tabindex="-1"></a></span>
- <span id="cb13-81"><a href="#cb13-81" aria-hidden="true" tabindex="-1"></a><span class="dt">const</span> uint16 LED_ON_TIME <span class="op">=</span> <span class="dv">150</span><span class="op">;</span></span>
- <span id="cb13-82"><a href="#cb13-82" aria-hidden="true" tabindex="-1"></a><span class="dt">const</span> uint16 LED_OFF_TIME <span class="op">=</span> <span class="dv">50</span><span class="op">;</span></span>
- <span id="cb13-83"><a href="#cb13-83" aria-hidden="true" tabindex="-1"></a><span class="dt">static</span> uint8 ledState <span class="op">=</span> LED_OFF<span class="op">;</span></span>
- <span id="cb13-84"><a href="#cb13-84" aria-hidden="true" tabindex="-1"></a><span class="dt">static</span> uint16 ledTimerCount <span class="op">=</span> <span class="dv">0</span><span class="op">;</span></span>
- <span id="cb13-85"><a href="#cb13-85" aria-hidden="true" tabindex="-1"></a></span>
- <span id="cb13-86"><a href="#cb13-86" aria-hidden="true" tabindex="-1"></a><span class="dt">void</span> ledTask<span class="op">(</span>uint8 evt<span class="op">)</span> <span class="op">{</span></span>
- <span id="cb13-87"><a href="#cb13-87" aria-hidden="true" tabindex="-1"></a> <span class="cf">switch</span><span class="op">(</span>ledState<span class="op">)</span> <span class="op">{</span></span>
- <span id="cb13-88"><a href="#cb13-88" aria-hidden="true" tabindex="-1"></a> <span class="cf">case</span> LED_OFF<span class="op">:</span></span>
- <span id="cb13-89"><a href="#cb13-89" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> <span class="op">(</span>evt <span class="op">==</span> EVT_TICK<span class="op">)</span> <span class="op">{</span></span>
- <span id="cb13-90"><a href="#cb13-90" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> <span class="op">(++</span>ledTimerCount <span class="op">></span> LED_OFF_TIME<span class="op">)</span> <span class="op">{</span></span>
- <span id="cb13-91"><a href="#cb13-91" aria-hidden="true" tabindex="-1"></a> gpio_set<span class="op">(</span>LED2<span class="op">,</span> LED_ON<span class="op">);</span></span>
- <span id="cb13-92"><a href="#cb13-92" aria-hidden="true" tabindex="-1"></a> ledTimerCount <span class="op">=</span> <span class="dv">0</span><span class="op">;</span></span>
- <span id="cb13-93"><a href="#cb13-93" aria-hidden="true" tabindex="-1"></a> ledState <span class="op">=</span> LED_ON<span class="op">;</span></span>
- <span id="cb13-94"><a href="#cb13-94" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
- <span id="cb13-95"><a href="#cb13-95" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
- <span id="cb13-96"><a href="#cb13-96" aria-hidden="true" tabindex="-1"></a> <span class="cf">break</span><span class="op">;</span></span>
- <span id="cb13-97"><a href="#cb13-97" aria-hidden="true" tabindex="-1"></a> <span class="cf">case</span> LED_ON<span class="op">:</span></span>
- <span id="cb13-98"><a href="#cb13-98" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> <span class="op">(</span>evt <span class="op">==</span> EVT_TICK<span class="op">)</span> <span class="op">{</span></span>
- <span id="cb13-99"><a href="#cb13-99" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> <span class="op">(++</span>ledTimerCount <span class="op">></span> LED_ON_TIME<span class="op">)</span> <span class="op">{</span></span>
- <span id="cb13-100"><a href="#cb13-100" aria-hidden="true" tabindex="-1"></a> gpio_set<span class="op">(</span>LED2<span class="op">,</span> LED_OFF<span class="op">);</span></span>
- <span id="cb13-101"><a href="#cb13-101" aria-hidden="true" tabindex="-1"></a> ledTimerCount <span class="op">=</span> <span class="dv">0</span><span class="op">;</span></span>
- <span id="cb13-102"><a href="#cb13-102" aria-hidden="true" tabindex="-1"></a> ledState <span class="op">=</span> LED_OFF<span class="op">;</span></span>
- <span id="cb13-103"><a href="#cb13-103" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
- <span id="cb13-104"><a href="#cb13-104" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
- <span id="cb13-105"><a href="#cb13-105" aria-hidden="true" tabindex="-1"></a> <span class="cf">break</span><span class="op">;</span></span>
- <span id="cb13-106"><a href="#cb13-106" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
- <span id="cb13-107"><a href="#cb13-107" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
- <p>This is the end of the main presentation. With the above techniques,
- you can make a tiny microprocessor appear to multitask.</p>
- <p>For further tips, read the next section. The interesting topics
- are:</p>
- <ul>
- <li><a href="#state-machine-examples">more state code examples</a>
- <ul>
- <li>with implementation</li>
- </ul></li>
- <li><a href="#state-machine-initialization">how to initialize a state
- machine</a></li>
- <li><a href="#messages">inter-task communication</a></li>
- <li><a href="#timers">unified timers</a></li>
- </ul>
- <details>
- <summary>
- Reality Check
- </summary>
- <p>What’s missing from this code sample is the initialization of the
- hardware……..some kind of <code>setup()</code> routine. I left it out
- because it’s going to vary so much between processors, and only
- distracts from the point of this paper. If you want a detailed, tested
- copy of this code, see <a href="build/demo.c">here</a></p>
- The above example only shows 2 tasks, so all the formal structure may
- seem a bit silly. But once you have the infrastructure in place, you can
- easily handle <em>dozens</em> of tasks, even on sub-$1 processors.
- </details>
- <h1 id="part-2">Part 2</h1>
- <h2 id="variations">Variations</h2>
- <style>
- .imageleft {float: left;}
- .imageright {float: right;}
- </style>
- <p>Now that the infrastructure is in place, it’s easy to expand or
- modify the code for changes in the project definition.</p>
- <p>For example, suppose we want the <code>respondToButtonTask()</code>
- to restart the LED timer on each key press:</p>
- <p><img src="rtb_state_2.png" class="imageleft" /></p>
- <div class="sourceCode" id="cb14"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb14-1"><a href="#cb14-1" aria-hidden="true" tabindex="-1"></a><span class="dt">void</span> rtbTask<span class="op">(</span>uint8 event<span class="op">)</span> <span class="op">{</span></span>
- <span id="cb14-2"><a href="#cb14-2" aria-hidden="true" tabindex="-1"></a> <span class="cf">switch</span><span class="op">(</span>rtbState<span class="op">)</span> <span class="op">{</span></span>
- <span id="cb14-3"><a href="#cb14-3" aria-hidden="true" tabindex="-1"></a> <span class="cf">case</span> RTB_IDLE<span class="op">:</span></span>
- <span id="cb14-4"><a href="#cb14-4" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> <span class="op">(</span>event <span class="op">==</span> EVT_BUTTON<span class="op">)</span> <span class="op">{</span></span>
- <span id="cb14-5"><a href="#cb14-5" aria-hidden="true" tabindex="-1"></a> gpio_set<span class="op">(</span>LED<span class="op">,</span> LED_ON<span class="op">);</span></span>
- <span id="cb14-6"><a href="#cb14-6" aria-hidden="true" tabindex="-1"></a> rtbState <span class="op">=</span> RTB_ON<span class="op">;</span></span>
- <span id="cb14-7"><a href="#cb14-7" aria-hidden="true" tabindex="-1"></a> rtbTimer <span class="op">=</span> RTB_TIMEOUT<span class="op">;</span></span>
- <span id="cb14-8"><a href="#cb14-8" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
- <span id="cb14-9"><a href="#cb14-9" aria-hidden="true" tabindex="-1"></a> <span class="cf">break</span><span class="op">;</span></span>
- <span id="cb14-10"><a href="#cb14-10" aria-hidden="true" tabindex="-1"></a> <span class="cf">case</span> RTB_ON<span class="op">:</span></span>
- <span id="cb14-11"><a href="#cb14-11" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> <span class="op">(</span>event <span class="op">==</span> EVT_BUTTON<span class="op">)</span> <span class="op">{</span> </span>
- <span id="cb14-12"><a href="#cb14-12" aria-hidden="true" tabindex="-1"></a> rtbTimer <span class="op">=</span> RTB_TIMEOUT<span class="op">;</span></span>
- <span id="cb14-13"><a href="#cb14-13" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
- <span id="cb14-14"><a href="#cb14-14" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> <span class="op">(</span>event <span class="op">==</span> EVT_TICK<span class="op">)</span> <span class="op">{</span></span>
- <span id="cb14-15"><a href="#cb14-15" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> <span class="op">(--</span>rtbTimer <span class="op">==</span> <span class="dv">0</span><span class="op">)</span> <span class="op">{</span></span>
- <span id="cb14-16"><a href="#cb14-16" aria-hidden="true" tabindex="-1"></a> gpio_set<span class="op">(</span>LED<span class="op">,</span> LED_OFF<span class="op">);</span></span>
- <span id="cb14-17"><a href="#cb14-17" aria-hidden="true" tabindex="-1"></a> rtbState <span class="op">=</span> RTB_IDLE<span class="op">;</span></span>
- <span id="cb14-18"><a href="#cb14-18" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
- <span id="cb14-19"><a href="#cb14-19" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
- <span id="cb14-20"><a href="#cb14-20" aria-hidden="true" tabindex="-1"></a> <span class="cf">break</span><span class="op">;</span></span>
- <span id="cb14-21"><a href="#cb14-21" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
- <span id="cb14-22"><a href="#cb14-22" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
- <hr>
- <p>Or have a 2nd press of the button cause the LED to extinguish
- early:</p>
- <p><img src="rtb_state_3.png" class="imageleft" /></p>
- <div class="sourceCode" id="cb15"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb15-1"><a href="#cb15-1" aria-hidden="true" tabindex="-1"></a><span class="dt">void</span> rtbTask<span class="op">(</span>uint8 event<span class="op">)</span> <span class="op">{</span></span>
- <span id="cb15-2"><a href="#cb15-2" aria-hidden="true" tabindex="-1"></a> <span class="cf">switch</span><span class="op">(</span>rtbState<span class="op">)</span> <span class="op">{</span></span>
- <span id="cb15-3"><a href="#cb15-3" aria-hidden="true" tabindex="-1"></a> <span class="cf">case</span> RTB_IDLE<span class="op">:</span></span>
- <span id="cb15-4"><a href="#cb15-4" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> <span class="op">(</span>event <span class="op">==</span> EVT_BUTTON<span class="op">)</span> <span class="op">{</span></span>
- <span id="cb15-5"><a href="#cb15-5" aria-hidden="true" tabindex="-1"></a> gpio_set<span class="op">(</span>LED<span class="op">,</span> LED_ON<span class="op">);</span></span>
- <span id="cb15-6"><a href="#cb15-6" aria-hidden="true" tabindex="-1"></a> rtbState <span class="op">=</span> RTB_ON<span class="op">;</span></span>
- <span id="cb15-7"><a href="#cb15-7" aria-hidden="true" tabindex="-1"></a> rtbTimer <span class="op">=</span> RTB_TIMEOUT<span class="op">;</span></span>
- <span id="cb15-8"><a href="#cb15-8" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
- <span id="cb15-9"><a href="#cb15-9" aria-hidden="true" tabindex="-1"></a> <span class="cf">break</span><span class="op">;</span></span>
- <span id="cb15-10"><a href="#cb15-10" aria-hidden="true" tabindex="-1"></a> <span class="cf">case</span> RTB_ON<span class="op">:</span></span>
- <span id="cb15-11"><a href="#cb15-11" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> <span class="op">(</span>event <span class="op">==</span> EVT_BUTTON<span class="op">)</span> <span class="op">{</span> </span>
- <span id="cb15-12"><a href="#cb15-12" aria-hidden="true" tabindex="-1"></a> gpio_set<span class="op">(</span>LED<span class="op">,</span> LED_OFF<span class="op">);</span></span>
- <span id="cb15-13"><a href="#cb15-13" aria-hidden="true" tabindex="-1"></a> rtbState <span class="op">=</span> RTB_IDLE<span class="op">;</span></span>
- <span id="cb15-14"><a href="#cb15-14" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
- <span id="cb15-15"><a href="#cb15-15" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> <span class="op">(</span>event <span class="op">==</span> EVT_TICK<span class="op">)</span> <span class="op">{</span></span>
- <span id="cb15-16"><a href="#cb15-16" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> <span class="op">(--</span>rtbTimer <span class="op">==</span> <span class="dv">0</span><span class="op">)</span> <span class="op">{</span></span>
- <span id="cb15-17"><a href="#cb15-17" aria-hidden="true" tabindex="-1"></a> gpio_set<span class="op">(</span>LED<span class="op">,</span> LED_OFF<span class="op">);</span></span>
- <span id="cb15-18"><a href="#cb15-18" aria-hidden="true" tabindex="-1"></a> rtbState <span class="op">=</span> RTB_IDLE<span class="op">;</span></span>
- <span id="cb15-19"><a href="#cb15-19" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
- <span id="cb15-20"><a href="#cb15-20" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
- <span id="cb15-21"><a href="#cb15-21" aria-hidden="true" tabindex="-1"></a> <span class="cf">break</span><span class="op">;</span></span>
- <span id="cb15-22"><a href="#cb15-22" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
- <span id="cb15-23"><a href="#cb15-23" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
- <hr>
- <p>How about have the button start a flash sequence, and a 2nd press
- stops it: (see also <a href="#substates">substates</a>)</p>
- <p><img src="rtb_state_4.png" class="imageright" /></p>
- <div class="sourceCode" id="cb16"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb16-1"><a href="#cb16-1" aria-hidden="true" tabindex="-1"></a><span class="dt">void</span> rtbTask<span class="op">(</span>uint8 event<span class="op">)</span> <span class="op">{</span></span>
- <span id="cb16-2"><a href="#cb16-2" aria-hidden="true" tabindex="-1"></a> <span class="cf">switch</span><span class="op">(</span>rtbState<span class="op">)</span> <span class="op">{</span></span>
- <span id="cb16-3"><a href="#cb16-3" aria-hidden="true" tabindex="-1"></a> <span class="cf">case</span> RTB_IDLE<span class="op">:</span></span>
- <span id="cb16-4"><a href="#cb16-4" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> <span class="op">(</span>event <span class="op">==</span> EVT_BUTTON<span class="op">)</span> <span class="op">{</span></span>
- <span id="cb16-5"><a href="#cb16-5" aria-hidden="true" tabindex="-1"></a> gpio_set<span class="op">(</span>LED<span class="op">,</span> LED_ON<span class="op">);</span></span>
- <span id="cb16-6"><a href="#cb16-6" aria-hidden="true" tabindex="-1"></a> rtbState <span class="op">=</span> RTB_ON<span class="op">;</span></span>
- <span id="cb16-7"><a href="#cb16-7" aria-hidden="true" tabindex="-1"></a> rtbTimer <span class="op">=</span> RTB_TIMEOUT<span class="op">;</span></span>
- <span id="cb16-8"><a href="#cb16-8" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
- <span id="cb16-9"><a href="#cb16-9" aria-hidden="true" tabindex="-1"></a> <span class="cf">break</span><span class="op">;</span></span>
- <span id="cb16-10"><a href="#cb16-10" aria-hidden="true" tabindex="-1"></a> <span class="cf">case</span> RTB_ON<span class="op">:</span></span>
- <span id="cb16-11"><a href="#cb16-11" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> <span class="op">(</span>event <span class="op">==</span> EVT_BUTTON<span class="op">)</span> <span class="op">{</span> </span>
- <span id="cb16-12"><a href="#cb16-12" aria-hidden="true" tabindex="-1"></a> gpio_set<span class="op">(</span>LED<span class="op">,</span> LED_OFF<span class="op">);</span></span>
- <span id="cb16-13"><a href="#cb16-13" aria-hidden="true" tabindex="-1"></a> rtbState <span class="op">=</span> RTB_IDLE<span class="op">;</span></span>
- <span id="cb16-14"><a href="#cb16-14" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
- <span id="cb16-15"><a href="#cb16-15" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> <span class="op">(</span>event <span class="op">==</span> EVT_TICK<span class="op">)</span> <span class="op">{</span></span>
- <span id="cb16-16"><a href="#cb16-16" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> <span class="op">(--</span>rtbTimer <span class="op">==</span> <span class="dv">0</span><span class="op">)</span> <span class="op">{</span></span>
- <span id="cb16-17"><a href="#cb16-17" aria-hidden="true" tabindex="-1"></a> gpio_set<span class="op">(</span>LED<span class="op">,</span> LED_OFF<span class="op">);</span></span>
- <span id="cb16-18"><a href="#cb16-18" aria-hidden="true" tabindex="-1"></a> rtbTimer <span class="op">=</span> RTB_FLASH_TIME<span class="op">;</span></span>
- <span id="cb16-19"><a href="#cb16-19" aria-hidden="true" tabindex="-1"></a> rtbState <span class="op">=</span> RTB_OFF<span class="op">;</span></span>
- <span id="cb16-20"><a href="#cb16-20" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
- <span id="cb16-21"><a href="#cb16-21" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
- <span id="cb16-22"><a href="#cb16-22" aria-hidden="true" tabindex="-1"></a> <span class="cf">break</span><span class="op">;</span></span>
- <span id="cb16-23"><a href="#cb16-23" aria-hidden="true" tabindex="-1"></a> <span class="cf">case</span> RTB_OFF<span class="op">:</span></span>
- <span id="cb16-24"><a href="#cb16-24" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> <span class="op">(</span>event <span class="op">==</span> EVT_BUTTON<span class="op">)</span> <span class="op">{</span> </span>
- <span id="cb16-25"><a href="#cb16-25" aria-hidden="true" tabindex="-1"></a> gpio_set<span class="op">(</span>LED<span class="op">,</span> LED_OFF<span class="op">);</span></span>
- <span id="cb16-26"><a href="#cb16-26" aria-hidden="true" tabindex="-1"></a> rtbState <span class="op">=</span> RTB_IDLE<span class="op">;</span></span>
- <span id="cb16-27"><a href="#cb16-27" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
- <span id="cb16-28"><a href="#cb16-28" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> <span class="op">(</span>event <span class="op">==</span> EVT_TICK<span class="op">)</span> <span class="op">{</span></span>
- <span id="cb16-29"><a href="#cb16-29" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> <span class="op">(--</span>rtbTimer <span class="op">==</span> <span class="dv">0</span><span class="op">)</span> <span class="op">{</span></span>
- <span id="cb16-30"><a href="#cb16-30" aria-hidden="true" tabindex="-1"></a> gpio_set<span class="op">(</span>LED<span class="op">,</span> LED_ON<span class="op">);</span></span>
- <span id="cb16-31"><a href="#cb16-31" aria-hidden="true" tabindex="-1"></a> rtbTimer <span class="op">=</span> RTB_FLASH_TIME<span class="op">;</span></span>
- <span id="cb16-32"><a href="#cb16-32" aria-hidden="true" tabindex="-1"></a> rtbState <span class="op">=</span> RTB_ON<span class="op">;</span></span>
- <span id="cb16-33"><a href="#cb16-33" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
- <span id="cb16-34"><a href="#cb16-34" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
- <span id="cb16-35"><a href="#cb16-35" aria-hidden="true" tabindex="-1"></a> <span class="cf">break</span><span class="op">;</span></span>
- <span id="cb16-36"><a href="#cb16-36" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
- <span id="cb16-37"><a href="#cb16-37" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
- <hr>
- <p>Each of these diagrams corresponds to trivial changes in the state
- code.</p>
- <h2 id="working-with-arduino">Working with Arduino</h2>
- <p>If you’re from the Arduino world, you have no doubt seen the
- similarity between this OS plan and the Arduino infrastructure.</p>
- <div class="sourceCode" id="cb17"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb17-1"><a href="#cb17-1" aria-hidden="true" tabindex="-1"></a>setup<span class="op">()</span> </span>
- <span id="cb17-2"><a href="#cb17-2" aria-hidden="true" tabindex="-1"></a> <span class="op">{}</span> <span class="co">// initialize and start up the devices and services</span></span>
- <span id="cb17-3"><a href="#cb17-3" aria-hidden="true" tabindex="-1"></a></span>
- <span id="cb17-4"><a href="#cb17-4" aria-hidden="true" tabindex="-1"></a>loop<span class="op">()</span></span>
- <span id="cb17-5"><a href="#cb17-5" aria-hidden="true" tabindex="-1"></a> <span class="op">{}</span> <span class="co">// code for continuous operation</span></span></code></pre></div>
- <p>You can certainly merge this paper’s operating system into the
- Arduino architecture:</p>
- <div class="sourceCode" id="cb18"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb18-1"><a href="#cb18-1" aria-hidden="true" tabindex="-1"></a><span class="dt">char</span> events<span class="op">[];</span></span>
- <span id="cb18-2"><a href="#cb18-2" aria-hidden="true" tabindex="-1"></a></span>
- <span id="cb18-3"><a href="#cb18-3" aria-hidden="true" tabindex="-1"></a>interrupt_service_routines<span class="op">()</span> <span class="op">{</span></span>
- <span id="cb18-4"><a href="#cb18-4" aria-hidden="true" tabindex="-1"></a> newEvent<span class="op">(</span>evt<span class="op">);</span> <span class="co">// insert events/messages into events[]</span></span>
- <span id="cb18-5"><a href="#cb18-5" aria-hidden="true" tabindex="-1"></a><span class="op">}</span> </span>
- <span id="cb18-6"><a href="#cb18-6" aria-hidden="true" tabindex="-1"></a></span>
- <span id="cb18-7"><a href="#cb18-7" aria-hidden="true" tabindex="-1"></a><span class="dt">static</span> <span class="dt">int</span> nextEvent<span class="op">;</span></span>
- <span id="cb18-8"><a href="#cb18-8" aria-hidden="true" tabindex="-1"></a><span class="dt">void</span> newEvent<span class="op">(</span><span class="dt">char</span> evt<span class="op">)</span> </span>
- <span id="cb18-9"><a href="#cb18-9" aria-hidden="true" tabindex="-1"></a> <span class="op">{}</span> <span class="co">// same as above; put evt into the events[]</span></span>
- <span id="cb18-10"><a href="#cb18-10" aria-hidden="true" tabindex="-1"></a></span>
- <span id="cb18-11"><a href="#cb18-11" aria-hidden="true" tabindex="-1"></a>setup<span class="op">()</span></span>
- <span id="cb18-12"><a href="#cb18-12" aria-hidden="true" tabindex="-1"></a> <span class="op">{}</span></span>
- <span id="cb18-13"><a href="#cb18-13" aria-hidden="true" tabindex="-1"></a></span>
- <span id="cb18-14"><a href="#cb18-14" aria-hidden="true" tabindex="-1"></a>loop<span class="op">()</span> <span class="op">{</span></span>
- <span id="cb18-15"><a href="#cb18-15" aria-hidden="true" tabindex="-1"></a> <span class="dt">static</span> <span class="dt">int</span> nextTaskEvent<span class="op">;</span></span>
- <span id="cb18-16"><a href="#cb18-16" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> <span class="op">(</span>events<span class="op">[</span>nextTaskEvent<span class="op">]!=</span>EVT_NONE<span class="op">)</span> <span class="op">{</span> <span class="co">// check for non-zero events[],</span></span>
- <span id="cb18-17"><a href="#cb18-17" aria-hidden="true" tabindex="-1"></a> task1<span class="op">(</span>events<span class="op">[</span>nextTaskEvent<span class="op">]);</span> <span class="co">// and call the task routines</span></span>
- <span id="cb18-18"><a href="#cb18-18" aria-hidden="true" tabindex="-1"></a> task2<span class="op">(</span>events<span class="op">[</span>nextTaskEvent<span class="op">]);</span></span>
- <span id="cb18-19"><a href="#cb18-19" aria-hidden="true" tabindex="-1"></a> events<span class="op">[</span>nextTaskEvent<span class="op">)</span> <span class="op">=</span> EVT_NONE<span class="op">;</span> <span class="co">// free the slot, fill with 0</span></span>
- <span id="cb18-20"><a href="#cb18-20" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> <span class="op">(++</span>nextTaskEvent <span class="op">></span> NUM_EVENTS<span class="op">)</span></span>
- <span id="cb18-21"><a href="#cb18-21" aria-hidden="true" tabindex="-1"></a> nextTaskEvent <span class="op">=</span> <span class="dv">0</span><span class="op">;</span> <span class="co">// and loop through the array</span></span>
- <span id="cb18-22"><a href="#cb18-22" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
- <span id="cb18-23"><a href="#cb18-23" aria-hidden="true" tabindex="-1"></a><span class="op">}</span> </span>
- <span id="cb18-24"><a href="#cb18-24" aria-hidden="true" tabindex="-1"></a></span>
- <span id="cb18-25"><a href="#cb18-25" aria-hidden="true" tabindex="-1"></a><span class="dt">void</span> task1<span class="op">(</span><span class="dt">char</span> evt<span class="op">)</span> <span class="op">{}</span></span>
- <span id="cb18-26"><a href="#cb18-26" aria-hidden="true" tabindex="-1"></a><span class="dt">void</span> task2<span class="op">(</span><span class="dt">char</span> evt<span class="op">)</span> <span class="op">{}</span></span>
- <span id="cb18-27"><a href="#cb18-27" aria-hidden="true" tabindex="-1"></a> </span></code></pre></div>
- <h2 id="tasks-1">Tasks</h2>
- <p>The fundamental guideline for tasks is that <em>they do not
- stop</em>. Control flows through and out the bottom, returning to the
- dispatcher quickly.</p>
- <details>
- <summary>
- Reality Check
- </summary>
- <p>In practical terms, if you have a timer tick that runs at every 10ms,
- and about 5 tasks, then if you can keep each task under 2ms, you won’t
- lose any events.</p>
- <p>If a task occasionally runs into the 10’s of msec, the event queue
- will handle buffering the events until they can be processed.</p>
- Under no circumstances should a task take more than 100msec. Use a new
- state, and return from the tast. Process the new state later.
- <hr>
- </details>
- <h2 id="state-machine-examples">State Machine Examples</h2>
- <h3 id="car-window">Car Window</h3>
- <p>Suppose we want to control the power window on a car? For this
- problem, we have an up/down button, a motor to drive the window up or
- down, and a motor-overload sensor to detect when the motor is straining.
- So the buttons & overload sensor are inputs, and the motor drive is
- outputs.</p>
- <p>When the user presses the “up” button, we should start the motor
- moving upward. When the overload sensor detects a strain, then either
- the window is all the way up….or it has encountered a finger or hand; in
- either case, we need to turn the motor drive off.</p>
- <p>Here’s a possible state diagram. <img src="window_state.png"
- alt="window motor control" /></p>
- <p>And here’s the matching code. Note the correspondence between the
- diagram and the code: arrows leaving a state correspond to an
- <code>if()</code> phrase.</p>
- <div class="sourceCode" id="cb19"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb19-1"><a href="#cb19-1" aria-hidden="true" tabindex="-1"></a><span class="kw">enum</span> <span class="op">{</span>WINDOW_IDLE<span class="op">,</span> WINDOW_UP<span class="op">,</span> WINDOW_DOWN<span class="op">};</span></span>
- <span id="cb19-2"><a href="#cb19-2" aria-hidden="true" tabindex="-1"></a><span class="dt">static</span> uint8 windowState <span class="op">=</span> WINDOW_IDLE<span class="op">;</span></span>
- <span id="cb19-3"><a href="#cb19-3" aria-hidden="true" tabindex="-1"></a></span>
- <span id="cb19-4"><a href="#cb19-4" aria-hidden="true" tabindex="-1"></a><span class="dt">void</span> windowTask<span class="op">(</span>uint8 evt<span class="op">)</span> <span class="op">{</span></span>
- <span id="cb19-5"><a href="#cb19-5" aria-hidden="true" tabindex="-1"></a> <span class="cf">switch</span><span class="op">(</span>windowState<span class="op">)</span> <span class="op">{</span></span>
- <span id="cb19-6"><a href="#cb19-6" aria-hidden="true" tabindex="-1"></a> <span class="cf">case</span> WINDOW_IDLE<span class="op">:</span></span>
- <span id="cb19-7"><a href="#cb19-7" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> <span class="op">(</span>evt <span class="op">==</span> EVT_BUTTON_UP<span class="op">)</span> <span class="op">{</span></span>
- <span id="cb19-8"><a href="#cb19-8" aria-hidden="true" tabindex="-1"></a> gpio_set<span class="op">(</span>MOTOR<span class="op">,</span> UP<span class="op">);</span></span>
- <span id="cb19-9"><a href="#cb19-9" aria-hidden="true" tabindex="-1"></a> windowState <span class="op">=</span> WINDOW_UP<span class="op">;</span></span>
- <span id="cb19-10"><a href="#cb19-10" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
- <span id="cb19-11"><a href="#cb19-11" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> <span class="op">(</span>evt <span class="op">==</span> EVT_BUTTON_DOWN<span class="op">)</span> <span class="op">{</span></span>
- <span id="cb19-12"><a href="#cb19-12" aria-hidden="true" tabindex="-1"></a> gpio_set<span class="op">(</span>MOTOR<span class="op">,</span> DOWN<span class="op">);</span></span>
- <span id="cb19-13"><a href="#cb19-13" aria-hidden="true" tabindex="-1"></a> windowState <span class="op">=</span> WINDOW_DOWN<span class="op">;</span></span>
- <span id="cb19-14"><a href="#cb19-14" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
- <span id="cb19-15"><a href="#cb19-15" aria-hidden="true" tabindex="-1"></a> <span class="cf">break</span><span class="op">;</span></span>
- <span id="cb19-16"><a href="#cb19-16" aria-hidden="true" tabindex="-1"></a> <span class="cf">case</span> WINDOW_UP<span class="op">:</span></span>
- <span id="cb19-17"><a href="#cb19-17" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> <span class="op">(</span>evt <span class="op">==</span> EVT_MOTOR_SENSE <span class="op">||</span> </span>
- <span id="cb19-18"><a href="#cb19-18" aria-hidden="true" tabindex="-1"></a> evt <span class="op">==</span> EVT_BUTTON_RELEASE<span class="op">)</span> <span class="op">{</span></span>
- <span id="cb19-19"><a href="#cb19-19" aria-hidden="true" tabindex="-1"></a> gpio_set<span class="op">(</span>MOTOR<span class="op">,</span> OFF<span class="op">);</span></span>
- <span id="cb19-20"><a href="#cb19-20" aria-hidden="true" tabindex="-1"></a> windowState <span class="op">=</span> WINDOW_IDLE<span class="op">;</span></span>
- <span id="cb19-21"><a href="#cb19-21" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
- <span id="cb19-22"><a href="#cb19-22" aria-hidden="true" tabindex="-1"></a> <span class="cf">break</span><span class="op">;</span></span>
- <span id="cb19-23"><a href="#cb19-23" aria-hidden="true" tabindex="-1"></a> <span class="cf">case</span> WINDOW_DOWN<span class="op">:</span></span>
- <span id="cb19-24"><a href="#cb19-24" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> <span class="op">(</span>evt <span class="op">==</span> EVT_MOTOR_SENSE <span class="op">||</span> </span>
- <span id="cb19-25"><a href="#cb19-25" aria-hidden="true" tabindex="-1"></a> evt <span class="op">==</span> EVT_BUTTON_RELEASE<span class="op">)</span> <span class="op">{</span></span>
- <span id="cb19-26"><a href="#cb19-26" aria-hidden="true" tabindex="-1"></a> gpio_set<span class="op">(</span>MOTOR<span class="op">,</span> OFF<span class="op">);</span></span>
- <span id="cb19-27"><a href="#cb19-27" aria-hidden="true" tabindex="-1"></a> windowState <span class="op">=</span> WINDOW_IDLE<span class="op">;</span></span>
- <span id="cb19-28"><a href="#cb19-28" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
- <span id="cb19-29"><a href="#cb19-29" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
- <span id="cb19-30"><a href="#cb19-30" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
- <p>Now, suppose the problem definition is changed: if the user presses
- the “up” button, the motor should only operate while the button is
- pressed; stop on release. But if the user presses the “up” button a
- second time within 1 second of the first release, the motor should drive
- the window all the way up (auto-close).</p>
- <p>Here is a possible state diagram. Notice the significant re-use of
- code from the previous version.</p>
- <figure>
- <img src="window_complex.png"
- alt="window control with auto open/close, (_ma) motor activated (_ms) motor stopped" />
- <figcaption aria-hidden="true">window control with auto open/close,
- (_ma) motor activated (_ms) motor stopped</figcaption>
- </figure>
- <details>
- <summary>
- Reality Check
- </summary>
- <p>This is such a simple task, with only a few I/O pins involved. In
- theory, a cheap microcontroller could control a dozen windows, each
- appearing to operate independantly.</p>
- <p>In the code, one wouldn’t need to create a dozen tasks……just create
- an index into the same code and invoke it in a way that makes it appear
- as an independent task:</p>
- <pre><code>void main(void) {
- eint();
- while(1) {
- for (i=0; i<NUM_EVENTS; i++) {
- while (events[i]==EVT_NONE)
- {}
- taskWindow(events[i],0);
- taskWindow(events[i],1);
- // ...
- taskWindow(events[i],NUM_WINDOWS-1);
- events[i] = EVT_NONE;
- }
- }
- }
- // window management task
- int windowState[NUM_WINDOWS];
- void taskWindow(char event, int windowIndex) {
- // find the state from the windowState[windowIndex]
- // run through the state machine
- // any I/O will require an array of I/O addresses, use 'windowIndex'
- switch(windowState[windowIndex]) {
- case WS_IDLE:
- ...
- ...
- }
- }</code></pre>
- </details>
- <h3 id="fridge-door">Fridge Door</h3>
- <p>A simple state machine can control the interior light of a fridge.
- Here’s the use-case:</p>
- <p>The problem has one input (door open) and two outputs (light and
- audible alarm). If the door is open, turn on the light and start a timer
- for 90 seconds. If the door is still open at the end of the 90 seconds,
- start an audible alarm. If the door closes, stop the timer and turn off
- the light and alarm. And if the door closes during the 90 seconds, turn
- off the light.</p>
- <p>Here is the state diagram.</p>
- <p><img src="fridge_state.png" /></p>
- <p>And here is the corresponding code.</p>
- <div class="sourceCode" id="cb21"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb21-1"><a href="#cb21-1" aria-hidden="true" tabindex="-1"></a><span class="kw">enum</span> <span class="op">{</span>FRIDGE_CLOSED<span class="op">,</span> FRIDGE_OPEN<span class="op">,</span> FRIDGE_BEEPING<span class="op">};</span></span>
- <span id="cb21-2"><a href="#cb21-2" aria-hidden="true" tabindex="-1"></a>uint8 fridgeState<span class="op">;</span></span>
- <span id="cb21-3"><a href="#cb21-3" aria-hidden="true" tabindex="-1"></a>uint16 fridgeTimer<span class="op">;</span></span>
- <span id="cb21-4"><a href="#cb21-4" aria-hidden="true" tabindex="-1"></a><span class="dt">const</span> uint16 FRIDGE_OPEN_LIMIT <span class="op">=</span> <span class="dv">9000</span><span class="op">;</span> <span class="co">// 90 seconds at 10msec tick</span></span>
- <span id="cb21-5"><a href="#cb21-5" aria-hidden="true" tabindex="-1"></a><span class="dt">void</span> fridgeTask<span class="op">(</span><span class="dt">char</span> event<span class="op">)</span> <span class="op">{</span></span>
- <span id="cb21-6"><a href="#cb21-6" aria-hidden="true" tabindex="-1"></a> <span class="cf">switch</span><span class="op">(</span>fridgeState<span class="op">)</span> <span class="op">{</span></span>
- <span id="cb21-7"><a href="#cb21-7" aria-hidden="true" tabindex="-1"></a> <span class="cf">case</span> FRIDGE_CLOSED<span class="op">:</span></span>
- <span id="cb21-8"><a href="#cb21-8" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> <span class="op">(</span>event <span class="op">==</span> EVT_OPEN<span class="op">)</span> <span class="op">{</span></span>
- <span id="cb21-9"><a href="#cb21-9" aria-hidden="true" tabindex="-1"></a> set_io<span class="op">(</span>LIGHT<span class="op">,</span> ON<span class="op">);</span></span>
- <span id="cb21-10"><a href="#cb21-10" aria-hidden="true" tabindex="-1"></a> fridgeTimer <span class="op">=</span> FRIDGE_OPEN_LIMIT<span class="op">;</span></span>
- <span id="cb21-11"><a href="#cb21-11" aria-hidden="true" tabindex="-1"></a> fridgeState <span class="op">=</span> FRIDGE_OPEN<span class="op">;</span></span>
- <span id="cb21-12"><a href="#cb21-12" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
- <span id="cb21-13"><a href="#cb21-13" aria-hidden="true" tabindex="-1"></a> <span class="cf">break</span><span class="op">;</span></span>
- <span id="cb21-14"><a href="#cb21-14" aria-hidden="true" tabindex="-1"></a> <span class="cf">case</span> FRIDGE_OPEN<span class="op">:</span></span>
- <span id="cb21-15"><a href="#cb21-15" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> <span class="op">(</span>event <span class="op">==</span> EVT_CLOSE<span class="op">)</span> <span class="op">{</span></span>
- <span id="cb21-16"><a href="#cb21-16" aria-hidden="true" tabindex="-1"></a> set_io<span class="op">(</span>LIGHT<span class="op">,</span> OFF<span class="op">);</span></span>
- <span id="cb21-17"><a href="#cb21-17" aria-hidden="true" tabindex="-1"></a> fridgeState <span class="op">=</span> FRIDGE_CLOSED<span class="op">;</span></span>
- <span id="cb21-18"><a href="#cb21-18" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
- <span id="cb21-19"><a href="#cb21-19" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> <span class="op">(</span>evt <span class="op">==</span> EVT_TICK<span class="op">)</span> <span class="op">{</span></span>
- <span id="cb21-20"><a href="#cb21-20" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> <span class="op">(--</span>fridgeTimer <span class="op">==</span> <span class="dv">0</span><span class="op">)</span> <span class="op">{</span></span>
- <span id="cb21-21"><a href="#cb21-21" aria-hidden="true" tabindex="-1"></a> set_io<span class="op">(</span>ALARM<span class="op">,</span> ON<span class="op">);</span></span>
- <span id="cb21-22"><a href="#cb21-22" aria-hidden="true" tabindex="-1"></a> fridgeState <span class="op">=</span> FRIDGE_BEEPING<span class="op">;</span></span>
- <span id="cb21-23"><a href="#cb21-23" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
- <span id="cb21-24"><a href="#cb21-24" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
- <span id="cb21-25"><a href="#cb21-25" aria-hidden="true" tabindex="-1"></a> <span class="cf">break</span><span class="op">;</span></span>
- <span id="cb21-26"><a href="#cb21-26" aria-hidden="true" tabindex="-1"></a> <span class="cf">case</span> FRIDGE_BEEPING<span class="op">:</span></span>
- <span id="cb21-27"><a href="#cb21-27" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> <span class="op">(</span>event <span class="op">==</span> EVT_CLOSE<span class="op">)</span> <span class="op">{</span></span>
- <span id="cb21-28"><a href="#cb21-28" aria-hidden="true" tabindex="-1"></a> set_io<span class="op">(</span>ALARM<span class="op">,</span> OFF<span class="op">);</span></span>
- <span id="cb21-29"><a href="#cb21-29" aria-hidden="true" tabindex="-1"></a> set_io<span class="op">(</span>LIGHT<span class="op">,</span> OFF<span class="op">);</span></span>
- <span id="cb21-30"><a href="#cb21-30" aria-hidden="true" tabindex="-1"></a> fridgeState <span class="op">=</span> FRIDGE_CLOSED<span class="op">;</span></span>
- <span id="cb21-31"><a href="#cb21-31" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
- <span id="cb21-32"><a href="#cb21-32" aria-hidden="true" tabindex="-1"></a> <span class="cf">break</span><span class="op">;</span></span>
- <span id="cb21-33"><a href="#cb21-33" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
- <span id="cb21-34"><a href="#cb21-34" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
- <h4 id="refactor-for-changestate">Refactor for
- <code>changeState()</code></h4>
- <p>Notice on the state diagram, the arrows heads and tails cluster, and
- similar actions happen for multiple arrows. Perhaps we should write a
- function that just deals with all the actions required when leaving or
- entering a state. Then the task code would only have to manage the
- <em>arrows</em> of the state diagram. Like this:</p>
- <div class="sourceCode" id="cb22"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb22-1"><a href="#cb22-1" aria-hidden="true" tabindex="-1"></a><span class="co">// this outer code deals with the arrows on the state diagram</span></span>
- <span id="cb22-2"><a href="#cb22-2" aria-hidden="true" tabindex="-1"></a><span class="dt">void</span> fridgeTask<span class="op">(</span><span class="dt">char</span> event<span class="op">)</span> <span class="op">{</span></span>
- <span id="cb22-3"><a href="#cb22-3" aria-hidden="true" tabindex="-1"></a> <span class="cf">switch</span> <span class="op">(</span>fridgeState<span class="op">)</span> <span class="op">{</span></span>
- <span id="cb22-4"><a href="#cb22-4" aria-hidden="true" tabindex="-1"></a> <span class="cf">case</span> FRIDGE_CLOSED<span class="op">:</span></span>
- <span id="cb22-5"><a href="#cb22-5" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> <span class="op">(</span>event <span class="op">==</span> EVT_OPEN<span class="op">)</span> changeFridgeState<span class="op">(</span>FRIDGE_OPEN<span class="op">);</span></span>
- <span id="cb22-6"><a href="#cb22-6" aria-hidden="true" tabindex="-1"></a> <span class="cf">break</span><span class="op">;</span></span>
- <span id="cb22-7"><a href="#cb22-7" aria-hidden="true" tabindex="-1"></a> <span class="cf">case</span> FRIDGE_OPEN<span class="op">:</span></span>
- <span id="cb22-8"><a href="#cb22-8" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> <span class="op">(</span>event <span class="op">==</span> EVT_CLOSE<span class="op">)</span> changeFridgeState<span class="op">(</span>FRIDGE_CLOSED<span class="op">);</span></span>
- <span id="cb22-9"><a href="#cb22-9" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> <span class="op">(</span>event <span class="op">==</span> EVT_FRIDGE_TIMEOUT<span class="op">)</span> changeFridgeState<span class="op">(</span>FRIDGE_BEEPING<span class="op">);</span></span>
- <span id="cb22-10"><a href="#cb22-10" aria-hidden="true" tabindex="-1"></a> <span class="cf">break</span><span class="op">;</span></span>
- <span id="cb22-11"><a href="#cb22-11" aria-hidden="true" tabindex="-1"></a> <span class="cf">case</span> FRIDGE_BEEPING<span class="op">:</span></span>
- <span id="cb22-12"><a href="#cb22-12" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> <span class="op">(</span>event <span class="op">==</span> EVT_CLOSE<span class="op">)</span> changeFridgeState<span class="op">(</span>FRIDGE_CLOSED<span class="op">);</span></span>
- <span id="cb22-13"><a href="#cb22-13" aria-hidden="true" tabindex="-1"></a> <span class="cf">break</span><span class="op">;</span></span>
- <span id="cb22-14"><a href="#cb22-14" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
- <span id="cb22-15"><a href="#cb22-15" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
- <p>while the inner code deals with the actions required for
- <em>entry</em> and <em>exit</em> from each state</p>
- <div class="sourceCode" id="cb23"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb23-1"><a href="#cb23-1" aria-hidden="true" tabindex="-1"></a><span class="dt">void</span> changeFridgeState<span class="op">(</span><span class="dt">char</span> newState<span class="op">)</span> <span class="op">{</span></span>
- <span id="cb23-2"><a href="#cb23-2" aria-hidden="true" tabindex="-1"></a> <span class="dt">static</span> <span class="dt">char</span> oldState <span class="op">=</span> FRIDGE_CLOSED<span class="op">;</span></span>
- <span id="cb23-3"><a href="#cb23-3" aria-hidden="true" tabindex="-1"></a> <span class="co">// do all the state-leaving actions</span></span>
- <span id="cb23-4"><a href="#cb23-4" aria-hidden="true" tabindex="-1"></a> <span class="cf">switch</span><span class="op">(</span>oldState<span class="op">)</span> <span class="op">{</span></span>
- <span id="cb23-5"><a href="#cb23-5" aria-hidden="true" tabindex="-1"></a> <span class="cf">case</span> FRIDGE_CLOSED<span class="op">:</span></span>
- <span id="cb23-6"><a href="#cb23-6" aria-hidden="true" tabindex="-1"></a> set_io<span class="op">(</span>LIGHT<span class="op">,</span> ON<span class="op">);</span></span>
- <span id="cb23-7"><a href="#cb23-7" aria-hidden="true" tabindex="-1"></a> setTimer<span class="op">(</span>FRIDGE_TIMER<span class="op">,</span> FRIDGE_OPEN_LIMIT<span class="op">);</span></span>
- <span id="cb23-8"><a href="#cb23-8" aria-hidden="true" tabindex="-1"></a> <span class="cf">break</span><span class="op">;</span></span>
- <span id="cb23-9"><a href="#cb23-9" aria-hidden="true" tabindex="-1"></a> <span class="cf">case</span> FRIDGE_OPEN<span class="op">:</span></span>
- <span id="cb23-10"><a href="#cb23-10" aria-hidden="true" tabindex="-1"></a> <span class="cf">break</span><span class="op">;</span></span>
- <span id="cb23-11"><a href="#cb23-11" aria-hidden="true" tabindex="-1"></a> <span class="cf">case</span> FRIDGE_BEEPING<span class="op">:</span></span>
- <span id="cb23-12"><a href="#cb23-12" aria-hidden="true" tabindex="-1"></a> set_io<span class="op">(</span>ALARM<span class="op">,</span> OFF<span class="op">);</span></span>
- <span id="cb23-13"><a href="#cb23-13" aria-hidden="true" tabindex="-1"></a> <span class="cf">break</span><span class="op">;</span></span>
- <span id="cb23-14"><a href="#cb23-14" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
- <span id="cb23-15"><a href="#cb23-15" aria-hidden="true" tabindex="-1"></a> <span class="co">// change state</span></span>
- <span id="cb23-16"><a href="#cb23-16" aria-hidden="true" tabindex="-1"></a> fridgeState <span class="op">=</span> oldState <span class="op">=</span> newState<span class="op">;</span></span>
- <span id="cb23-17"><a href="#cb23-17" aria-hidden="true" tabindex="-1"></a> <span class="co">// and do the state-entry code</span></span>
- <span id="cb23-18"><a href="#cb23-18" aria-hidden="true" tabindex="-1"></a> <span class="cf">switch</span><span class="op">(</span>newState<span class="op">)</span> <span class="op">{</span></span>
- <span id="cb23-19"><a href="#cb23-19" aria-hidden="true" tabindex="-1"></a> <span class="cf">case</span> FRIDGE_CLOSED<span class="op">:</span></span>
- <span id="cb23-20"><a href="#cb23-20" aria-hidden="true" tabindex="-1"></a> set_io<span class="op">(</span>LIGHT<span class="op">,</span> OFF<span class="op">);</span></span>
- <span id="cb23-21"><a href="#cb23-21" aria-hidden="true" tabindex="-1"></a> setTimer<span class="op">(</span>FRIDGE_TIMER<span class="op">,</span> <span class="dv">0</span><span class="op">);</span></span>
- <span id="cb23-22"><a href="#cb23-22" aria-hidden="true" tabindex="-1"></a> <span class="cf">break</span><span class="op">;</span></span>
- <span id="cb23-23"><a href="#cb23-23" aria-hidden="true" tabindex="-1"></a> <span class="cf">case</span> FRIDGE_OPEN<span class="op">:</span></span>
- <span id="cb23-24"><a href="#cb23-24" aria-hidden="true" tabindex="-1"></a> <span class="cf">break</span><span class="op">;</span></span>
- <span id="cb23-25"><a href="#cb23-25" aria-hidden="true" tabindex="-1"></a> <span class="cf">case</span> FRIDGE_BEEPING<span class="op">:</span></span>
- <span id="cb23-26"><a href="#cb23-26" aria-hidden="true" tabindex="-1"></a> set_io<span class="op">(</span>ALARM<span class="op">,</span> ON<span class="op">);</span></span>
- <span id="cb23-27"><a href="#cb23-27" aria-hidden="true" tabindex="-1"></a> <span class="cf">break</span><span class="op">;</span></span>
- <span id="cb23-28"><a href="#cb23-28" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
- <span id="cb23-29"><a href="#cb23-29" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
- <h3 id="door-opener">Door Opener</h3>
- <p>Suppose we have a power lock on a door, using a solenoid, and an RFID
- tag detector on the “outside” and a push button on the “inside”. There
- is also a WiFi connection to a server, by which we report door openings.
- When the RFID tag sends us a message, it will contain a serial number.
- If the number matches a known record, then operate the door-opener
- solenoid for 4 seconds. If the “inside” button is pushed, operate the
- door-opener for 4 seconds; if the “inside” button is pressed during the
- 4 seconds, restart the 4 second timer.</p>
- <p>After the door is locked, send a report to the master control via the
- WiFi.</p>
- <p>Here’s the state diagram:</p>
- <figure>
- <img src="door.png" alt="RFID operated door lock" />
- <figcaption aria-hidden="true">RFID operated door lock</figcaption>
- </figure>
- <p>I use <code>EVT_</code> type events to indicate that they originate
- in hardware, probably at the interrupt level; and <code>MSG_</code> type
- events to indicate they come from a software source, perhaps a sibling
- task.</p>
- <p>Suppose now that the serial number needs to be verified by a central
- service. So when an RFID tag is detected, send a request to the master
- control and wait for an ACK or NAK response. In the case of an ACK, open
- the door solenoid for 4 seconds. The rest of the problem is as stated
- above.</p>
- <p>Here’s the modified state diagram:</p>
- <figure>
- <img src="door2.png" alt="RFID operated door, with remove key verify" />
- <figcaption aria-hidden="true">RFID operated door, with remove key
- verify</figcaption>
- </figure>
- <h3 id="beer-vat">Beer Vat</h3>
- <p>Suppose we have to move a servo motor to lift the lid from a brewing
- vat, to release excess pressure.</p>
- <p>Inputs: pressure valve, manual operation button Outputs: servo, LED,
- beeper</p>
- <p>If high pressure is detected, flash the LED for 10 seconds, then
- operate the beeper for 5 seconds, then operate servo; hold it open for 5
- seconds and return the servo, LED and beeper to idle.</p>
- <p>If the manual operation button is pressed, go directly to “operate
- servo” as above. <img src="beer_vat.png"
- alt="state machine for beer vat pressure release valve" /></p>
- <p>If the manual button is pressed while the lid is open, close
- immediately.</p>
- <h3 id="vending-machine">Vending Machine</h3>
- <p>When idle, wait for payment tap. If selection button is pressed
- before payment, display cost to inform the user for 3 seconds.</p>
- <p>After payment tap, request the user select item.</p>
- <p>Operate dispense motor.</p>
- <p>Wait for object to be removed from output bin.</p>
- <figure>
- <img src="vending.png" alt="states for vending machine" />
- <figcaption aria-hidden="true">states for vending machine</figcaption>
- </figure>
- <h2 id="events-1">Events</h2>
- <p>In all the above, events were implemented as a simple
- <code>unsigned char</code>, allowing 255 different event types.</p>
- <p>There’s no reason we couldn’t use an <code>unsigned short</code> or
- even an <code>int</code>. Further, a 16 bit event number could be
- designed to be 8 bits of event type, and 8 bits of supplementary event
- information:</p>
- <div class="sourceCode" id="cb24"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb24-1"><a href="#cb24-1" aria-hidden="true" tabindex="-1"></a><span class="kw">typedef</span> <span class="kw">struct</span> Event <span class="op">{</span></span>
- <span id="cb24-2"><a href="#cb24-2" aria-hidden="true" tabindex="-1"></a> <span class="dt">unsigned</span> <span class="dt">char</span> type<span class="op">;</span></span>
- <span id="cb24-3"><a href="#cb24-3" aria-hidden="true" tabindex="-1"></a> <span class="dt">unsigned</span> <span class="dt">char</span> info<span class="op">;</span></span>
- <span id="cb24-4"><a href="#cb24-4" aria-hidden="true" tabindex="-1"></a><span class="op">}</span> Event<span class="op">;</span></span>
- <span id="cb24-5"><a href="#cb24-5" aria-hidden="true" tabindex="-1"></a></span>
- <span id="cb24-6"><a href="#cb24-6" aria-hidden="true" tabindex="-1"></a>Event e <span class="op">=</span> <span class="op">{</span>MSG_KEYPRESS<span class="op">,</span> KEY_A<span class="op">};</span></span></code></pre></div>
- <p>In the old Windows system, <em>events/messages</em> were realized as
- a 16 bit number, with an extra 32 bit number glued to it for extra
- information.</p>
- <div class="sourceCode" id="cb25"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb25-1"><a href="#cb25-1" aria-hidden="true" tabindex="-1"></a><span class="kw">typedef</span> <span class="kw">struct</span> MSG <span class="op">{</span></span>
- <span id="cb25-2"><a href="#cb25-2" aria-hidden="true" tabindex="-1"></a> UINT message<span class="op">;</span></span>
- <span id="cb25-3"><a href="#cb25-3" aria-hidden="true" tabindex="-1"></a> LPARAM lParam<span class="op">;</span></span>
- <span id="cb25-4"><a href="#cb25-4" aria-hidden="true" tabindex="-1"></a><span class="op">};</span> <span class="co">// some extra detail removed</span></span></code></pre></div>
- For example, <code>WM_CHAR=0x0102</code> indicates that a key was
- pressed, with the extra 32bit <code>lParam</code> carrying the
- information about <em>which</em> key.
- <hr>
- <h2 id="timers">Timers</h2>
- <p>The simplest state timer is made with a static variable associated
- with the state code.</p>
- <p>To start the timer, simply initialize the static variable. On timer
- ticks, decrement (or increment if you prefer) this variable until it
- hits a limit, and then make a state change.</p>
- <p>For instance, to create a timer that waits 100 timer ticks, you could
- use:</p>
- <div class="sourceCode" id="cb26"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb26-1"><a href="#cb26-1" aria-hidden="true" tabindex="-1"></a><span class="dt">static</span> <span class="dt">int</span> state<span class="op">;</span></span>
- <span id="cb26-2"><a href="#cb26-2" aria-hidden="true" tabindex="-1"></a><span class="dt">static</span> <span class="dt">int</span> stateTimer<span class="op">;</span></span>
- <span id="cb26-3"><a href="#cb26-3" aria-hidden="true" tabindex="-1"></a><span class="dt">void</span> stateCode<span class="op">(</span><span class="dt">char</span> event<span class="op">)</span> <span class="op">{</span></span>
- <span id="cb26-4"><a href="#cb26-4" aria-hidden="true" tabindex="-1"></a> <span class="cf">switch</span><span class="op">(</span>state<span class="op">)</span> <span class="op">{</span></span>
- <span id="cb26-5"><a href="#cb26-5" aria-hidden="true" tabindex="-1"></a> <span class="cf">case</span> STATE_IDLE<span class="op">:</span></span>
- <span id="cb26-6"><a href="#cb26-6" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> <span class="op">(</span>event <span class="op">=</span> EVENT_TRIGGER<span class="op">)</span> <span class="op">{</span></span>
- <span id="cb26-7"><a href="#cb26-7" aria-hidden="true" tabindex="-1"></a> stateTimer <span class="op">=</span> <span class="dv">100</span><span class="op">;</span></span>
- <span id="cb26-8"><a href="#cb26-8" aria-hidden="true" tabindex="-1"></a> state <span class="op">=</span> STATE_DELAY<span class="op">;</span></span>
- <span id="cb26-9"><a href="#cb26-9" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
- <span id="cb26-10"><a href="#cb26-10" aria-hidden="true" tabindex="-1"></a> <span class="cf">break</span><span class="op">;</span></span>
- <span id="cb26-11"><a href="#cb26-11" aria-hidden="true" tabindex="-1"></a> <span class="cf">case</span> STATE_DELAY<span class="op">:</span></span>
- <span id="cb26-12"><a href="#cb26-12" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> <span class="op">(</span>event <span class="op">=</span> EVENT_TICK<span class="op">)</span> <span class="op">{</span></span>
- <span id="cb26-13"><a href="#cb26-13" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> <span class="op">(--</span>stateTimer <span class="op">==</span> <span class="dv">0</span><span class="op">)</span> <span class="op">{</span></span>
- <span id="cb26-14"><a href="#cb26-14" aria-hidden="true" tabindex="-1"></a> <span class="co">// the timer is finished</span></span>
- <span id="cb26-15"><a href="#cb26-15" aria-hidden="true" tabindex="-1"></a> state <span class="op">=</span> STATE_NEXT<span class="op">;</span></span>
- <span id="cb26-16"><a href="#cb26-16" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
- <span id="cb26-17"><a href="#cb26-17" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
- <span id="cb26-18"><a href="#cb26-18" aria-hidden="true" tabindex="-1"></a> <span class="cf">break</span><span class="op">;</span></span>
- <span id="cb26-19"><a href="#cb26-19" aria-hidden="true" tabindex="-1"></a> <span class="co">// ....</span></span>
- <span id="cb26-20"><a href="#cb26-20" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
- <span id="cb26-21"><a href="#cb26-21" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span>
- <span id="cb26-22"><a href="#cb26-22" aria-hidden="true" tabindex="-1"></a></span>
- <span id="cb26-23"><a href="#cb26-23" aria-hidden="true" tabindex="-1"></a></span>
- <span id="cb26-24"><a href="#cb26-24" aria-hidden="true" tabindex="-1"></a>In the above example<span class="op">,</span> you could equally well have set the timer to start at zero</span>
- <span id="cb26-25"><a href="#cb26-25" aria-hidden="true" tabindex="-1"></a>and increment until it hits the desired limit <span class="op">(</span><span class="dv">100</span> in this <span class="cf">case</span><span class="op">).</span></span>
- <span id="cb26-26"><a href="#cb26-26" aria-hidden="true" tabindex="-1"></a></span>
- <span id="cb26-27"><a href="#cb26-27" aria-hidden="true" tabindex="-1"></a>In a moderate sized project<span class="op">,</span> timers like this will proliferate throughout the code<span class="op">,</span></span>
- <span id="cb26-28"><a href="#cb26-28" aria-hidden="true" tabindex="-1"></a>making it awkward to read<span class="op">.</span> One solution to this is to centralize the timers<span class="op">.</span></span>
- <span id="cb26-29"><a href="#cb26-29" aria-hidden="true" tabindex="-1"></a></span>
- <span id="cb26-30"><a href="#cb26-30" aria-hidden="true" tabindex="-1"></a>In all the above examples<span class="op">,</span> the `timer_irq<span class="op">()</span>` code is trivial<span class="op">,</span> just `newEvent<span class="op">(</span>EVT_TICK<span class="op">)</span>`<span class="op">.</span></span>
- <span id="cb26-31"><a href="#cb26-31" aria-hidden="true" tabindex="-1"></a>Suppose we add code to the `timer_irq<span class="op">()</span>` so that it can process timer counting on</span>
- <span id="cb26-32"><a href="#cb26-32" aria-hidden="true" tabindex="-1"></a>behalf of the tasks<span class="op">......</span></span>
- <span id="cb26-33"><a href="#cb26-33" aria-hidden="true" tabindex="-1"></a></span>
- <span id="cb26-34"><a href="#cb26-34" aria-hidden="true" tabindex="-1"></a><span class="pp">#</span><span class="er">## Timers as a Resource</span></span>
- <span id="cb26-35"><a href="#cb26-35" aria-hidden="true" tabindex="-1"></a></span>
- <span id="cb26-36"><a href="#cb26-36" aria-hidden="true" tabindex="-1"></a>Let<span class="ch">'s</span><span class="er"> create a centralized service called `setTimer(timer_index, timer_count)`. </span></span>
- <span id="cb26-37"><a href="#cb26-37" aria-hidden="true" tabindex="-1"></a></span>
- <span id="cb26-38"><a href="#cb26-38" aria-hidden="true" tabindex="-1"></a>A task can call this service with a unique `timer_index` and a requested count<span class="op">.</span> The</span>
- <span id="cb26-39"><a href="#cb26-39" aria-hidden="true" tabindex="-1"></a>`timer_irq<span class="op">()</span>` will then count out the ticks on behalf of the task<span class="op">,</span> and when the tick</span>
- <span id="cb26-40"><a href="#cb26-40" aria-hidden="true" tabindex="-1"></a>count is finished<span class="op">,</span> the `timer_irq<span class="op">()</span>` code can generate a unique event<span class="op">,</span> perhaps</span>
- <span id="cb26-41"><a href="#cb26-41" aria-hidden="true" tabindex="-1"></a>`EVT_TIMER_n`<span class="op">.</span> </span>
- <span id="cb26-42"><a href="#cb26-42" aria-hidden="true" tabindex="-1"></a></span>
- <span id="cb26-43"><a href="#cb26-43" aria-hidden="true" tabindex="-1"></a>So the state code can then look something like this<span class="op">:</span></span>
- <span id="cb26-44"><a href="#cb26-44" aria-hidden="true" tabindex="-1"></a></span>
- <span id="cb26-45"><a href="#cb26-45" aria-hidden="true" tabindex="-1"></a>```C</span>
- <span id="cb26-46"><a href="#cb26-46" aria-hidden="true" tabindex="-1"></a><span class="dt">static</span> <span class="dt">int</span> state<span class="op">;</span></span>
- <span id="cb26-47"><a href="#cb26-47" aria-hidden="true" tabindex="-1"></a><span class="dt">void</span> stateCode<span class="op">(</span><span class="dt">char</span> event<span class="op">)</span> <span class="op">{</span></span>
- <span id="cb26-48"><a href="#cb26-48" aria-hidden="true" tabindex="-1"></a> <span class="cf">switch</span><span class="op">(</span>state<span class="op">)</span> <span class="op">{</span></span>
- <span id="cb26-49"><a href="#cb26-49" aria-hidden="true" tabindex="-1"></a> <span class="cf">case</span> STATE_IDLE<span class="op">:</span></span>
- <span id="cb26-50"><a href="#cb26-50" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> <span class="op">(</span>event <span class="op">=</span> EVENT_TRIGGER<span class="op">)</span> <span class="op">{</span></span>
- <span id="cb26-51"><a href="#cb26-51" aria-hidden="true" tabindex="-1"></a> setTimer<span class="op">(</span>TIMER_1<span class="op">,</span><span class="dv">100</span><span class="op">);</span></span>
- <span id="cb26-52"><a href="#cb26-52" aria-hidden="true" tabindex="-1"></a> state <span class="op">=</span> STATE_DELAY<span class="op">;</span></span>
- <span id="cb26-53"><a href="#cb26-53" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
- <span id="cb26-54"><a href="#cb26-54" aria-hidden="true" tabindex="-1"></a> <span class="cf">break</span><span class="op">;</span></span>
- <span id="cb26-55"><a href="#cb26-55" aria-hidden="true" tabindex="-1"></a> <span class="cf">case</span> STATE_DELAY<span class="op">:</span></span>
- <span id="cb26-56"><a href="#cb26-56" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> <span class="op">(</span>event <span class="op">=</span> EVENT_TIMER_1<span class="op">)</span> <span class="op">{</span></span>
- <span id="cb26-57"><a href="#cb26-57" aria-hidden="true" tabindex="-1"></a> <span class="co">// the timer is finished</span></span>
- <span id="cb26-58"><a href="#cb26-58" aria-hidden="true" tabindex="-1"></a> state <span class="op">=</span> STATE_NEXT<span class="op">;</span></span>
- <span id="cb26-59"><a href="#cb26-59" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
- <span id="cb26-60"><a href="#cb26-60" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
- <span id="cb26-61"><a href="#cb26-61" aria-hidden="true" tabindex="-1"></a> <span class="cf">break</span><span class="op">;</span></span>
- <span id="cb26-62"><a href="#cb26-62" aria-hidden="true" tabindex="-1"></a> <span class="co">// ....</span></span>
- <span id="cb26-63"><a href="#cb26-63" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
- <span id="cb26-64"><a href="#cb26-64" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
- <p>This makes the state code much simpler to read, hiding all the
- increments/decrements and limit testing.</p>
- <p>The overhead for this ends up in the <code>timer_irq()</code> code,
- and might look something like this:</p>
- <div class="sourceCode" id="cb27"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb27-1"><a href="#cb27-1" aria-hidden="true" tabindex="-1"></a><span class="dt">static</span> <span class="dt">unsigned</span> <span class="dt">int</span> timers<span class="op">[</span>NUM_TIMERS<span class="op">];</span></span>
- <span id="cb27-2"><a href="#cb27-2" aria-hidden="true" tabindex="-1"></a><span class="pp">#define EVT_TIMER_OFFSET </span><span class="dv">100</span></span>
- <span id="cb27-3"><a href="#cb27-3" aria-hidden="true" tabindex="-1"></a><span class="kw">enum</span> <span class="op">{</span>EVENT_TIMER_1<span class="op">=</span>EVT_TIMER_OFFSET<span class="op">,</span> EVENT_TIMER_2<span class="op">,</span> EVENT_TIMER_3<span class="op">};</span> <span class="co">// 100, 101...</span></span>
- <span id="cb27-4"><a href="#cb27-4" aria-hidden="true" tabindex="-1"></a><span class="kw">enum</span> <span class="op">{</span>TIMER_1<span class="op">,</span> TIMER_2<span class="op">,</span> TIMER_3<span class="op">};</span> <span class="co">// 0,1,2,....</span></span>
- <span id="cb27-5"><a href="#cb27-5" aria-hidden="true" tabindex="-1"></a></span>
- <span id="cb27-6"><a href="#cb27-6" aria-hidden="true" tabindex="-1"></a><span class="dt">void</span> timer_irq<span class="op">()</span> <span class="op">{</span></span>
- <span id="cb27-7"><a href="#cb27-7" aria-hidden="true" tabindex="-1"></a> newEvent<span class="op">(</span>EVT_TICK<span class="op">);</span> <span class="co">// the main tick, fires every time</span></span>
- <span id="cb27-8"><a href="#cb27-8" aria-hidden="true" tabindex="-1"></a> <span class="cf">for</span> <span class="op">(</span>i<span class="op">=</span><span class="dv">0</span><span class="op">;</span> i<span class="op"><</span>NUM_TIMERS<span class="op">)</span> <span class="op">{</span> <span class="co">// the system timers, fires on completion</span></span>
- <span id="cb27-9"><a href="#cb27-9" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> <span class="op">(</span>timers<span class="op">[</span>i<span class="op">]></span><span class="dv">0</span><span class="op">)</span> <span class="op">{</span></span>
- <span id="cb27-10"><a href="#cb27-10" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> <span class="op">(--</span>timers<span class="op">[</span>i<span class="op">]==</span><span class="dv">0</span><span class="op">)</span></span>
- <span id="cb27-11"><a href="#cb27-11" aria-hidden="true" tabindex="-1"></a> newEvent<span class="op">(</span>i <span class="op">+</span> EVT_TIMER_OFFSET<span class="op">);</span></span>
- <span id="cb27-12"><a href="#cb27-12" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
- <span id="cb27-13"><a href="#cb27-13" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
- <span id="cb27-14"><a href="#cb27-14" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span>
- <span id="cb27-15"><a href="#cb27-15" aria-hidden="true" tabindex="-1"></a></span>
- <span id="cb27-16"><a href="#cb27-16" aria-hidden="true" tabindex="-1"></a><span class="co">/* common service, available to all tasks */</span></span>
- <span id="cb27-17"><a href="#cb27-17" aria-hidden="true" tabindex="-1"></a><span class="dt">void</span> setTimer<span class="op">(</span><span class="dt">int</span> timerIndex<span class="op">,</span> <span class="dt">unsigned</span> <span class="dt">int</span> timerCount<span class="op">)</span> <span class="op">{</span></span>
- <span id="cb27-18"><a href="#cb27-18" aria-hidden="true" tabindex="-1"></a> timers<span class="op">[</span>timerIndex<span class="op">]</span> <span class="op">=</span> timerCount<span class="op">;</span></span>
- <span id="cb27-19"><a href="#cb27-19" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
- <details>
- <summary>
- Reality Check
- </summary>
- <p>On a typical microcontroller running at 24MHz, with 5 timers, this
- adds about 2 microseconds of extra time to the <code>timer_irq()</code>
- code, which typically runs every 10 or 100msec. It considerably
- simplifies the task code, makes it more legible and probably reduces
- bugs that may appear by duplication of code.</p>
- <p>Another possible design for timers is to have the main
- <code>timer_isr()</code> increment a global atomic
- <code>voloatile int timer</code> variable, and the tasks can observe
- this timer and set a target based on that.</p>
- <div class="sourceCode" id="cb28"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb28-1"><a href="#cb28-1" aria-hidden="true" tabindex="-1"></a><span class="dt">volatile</span> <span class="dt">int</span> timer<span class="op">;</span> <span class="co">// on most microcontrollers, access to an int</span></span>
- <span id="cb28-2"><a href="#cb28-2" aria-hidden="true" tabindex="-1"></a> <span class="co">// will be atomic</span></span>
- <span id="cb28-3"><a href="#cb28-3" aria-hidden="true" tabindex="-1"></a>INTERRUPT timer_isr<span class="op">(</span><span class="dt">void</span><span class="op">)</span> <span class="op">{</span></span>
- <span id="cb28-4"><a href="#cb28-4" aria-hidden="true" tabindex="-1"></a> timer<span class="op">++;</span></span>
- <span id="cb28-5"><a href="#cb28-5" aria-hidden="true" tabindex="-1"></a> newEvent<span class="op">(</span>EVT_TICK<span class="op">);</span></span>
- <span id="cb28-6"><a href="#cb28-6" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span>
- <span id="cb28-7"><a href="#cb28-7" aria-hidden="true" tabindex="-1"></a></span>
- <span id="cb28-8"><a href="#cb28-8" aria-hidden="true" tabindex="-1"></a><span class="co">// .... state code ...</span></span>
- <span id="cb28-9"><a href="#cb28-9" aria-hidden="true" tabindex="-1"></a><span class="dt">static</span> <span class="dt">int</span> targetTime<span class="op">;</span></span>
- <span id="cb28-10"><a href="#cb28-10" aria-hidden="true" tabindex="-1"></a><span class="dt">void</span> stateCode<span class="op">(</span>uint8 event<span class="op">)</span> <span class="op">{</span></span>
- <span id="cb28-11"><a href="#cb28-11" aria-hidden="true" tabindex="-1"></a> <span class="cf">switch</span><span class="op">(</span>state<span class="op">)</span> <span class="op">{</span></span>
- <span id="cb28-12"><a href="#cb28-12" aria-hidden="true" tabindex="-1"></a> <span class="cf">case</span> STATE1<span class="op">:</span></span>
- <span id="cb28-13"><a href="#cb28-13" aria-hidden="true" tabindex="-1"></a> <span class="co">//..... set a target</span></span>
- <span id="cb28-14"><a href="#cb28-14" aria-hidden="true" tabindex="-1"></a> targetTimer <span class="op">=</span> time <span class="op">+</span> DELAY_TIME<span class="op">;</span></span>
- <span id="cb28-15"><a href="#cb28-15" aria-hidden="true" tabindex="-1"></a> state <span class="op">=</span> state2<span class="op">;</span></span>
- <span id="cb28-16"><a href="#cb28-16" aria-hidden="true" tabindex="-1"></a> <span class="co">//.....</span></span>
- <span id="cb28-17"><a href="#cb28-17" aria-hidden="true" tabindex="-1"></a> <span class="cf">case</span> STATE2<span class="op">:</span></span>
- <span id="cb28-18"><a href="#cb28-18" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> <span class="op">(</span>event <span class="op">==</span> EVT_TICK<span class="op">)</span> <span class="op">{</span></span>
- <span id="cb28-19"><a href="#cb28-19" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> <span class="op">(</span>time <span class="op">==</span> targetTimer<span class="op">)</span> <span class="op">{</span></span>
- <span id="cb28-20"><a href="#cb28-20" aria-hidden="true" tabindex="-1"></a> <span class="co">//.... we've waited long enough</span></span>
- <span id="cb28-21"><a href="#cb28-21" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
- <span id="cb28-22"><a href="#cb28-22" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
- <span id="cb28-23"><a href="#cb28-23" aria-hidden="true" tabindex="-1"></a> <span class="co">//...</span></span>
- <span id="cb28-24"><a href="#cb28-24" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
- <span id="cb28-25"><a href="#cb28-25" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
- </details>
- <h2 id="real-code">Real Code</h2>
- <p>Here is an example of a complete and tested project on a small
- STM32F030 board. Open all the little arrows to see the complete
- code.</p>
- <details>
- <summary>
- board specific defines
- </summary>
- <div class="sourceCode" id="cb29"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb29-1"><a href="#cb29-1" aria-hidden="true" tabindex="-1"></a><span class="pp">#include </span><span class="im">"stm32f030.h"</span></span>
- <span id="cb29-2"><a href="#cb29-2" aria-hidden="true" tabindex="-1"></a></span>
- <span id="cb29-3"><a href="#cb29-3" aria-hidden="true" tabindex="-1"></a><span class="co">/****** project hardware ******/</span></span>
- <span id="cb29-4"><a href="#cb29-4" aria-hidden="true" tabindex="-1"></a><span class="co">// on this demo board, there is a push button on PB13 and</span></span>
- <span id="cb29-5"><a href="#cb29-5" aria-hidden="true" tabindex="-1"></a><span class="co">// an LED on PA4 and PF5</span></span>
- <span id="cb29-6"><a href="#cb29-6" aria-hidden="true" tabindex="-1"></a></span>
- <span id="cb29-7"><a href="#cb29-7" aria-hidden="true" tabindex="-1"></a><span class="pp">#define LED </span><span class="bn">0x10</span><span class="pp"> </span><span class="co">// port A bit 4 these LED are LOW=lit-up</span></span>
- <span id="cb29-8"><a href="#cb29-8" aria-hidden="true" tabindex="-1"></a><span class="pp">#define LED2 </span><span class="bn">0x50020</span><span class="pp"> </span><span class="co">// port F bit 5</span></span>
- <span id="cb29-9"><a href="#cb29-9" aria-hidden="true" tabindex="-1"></a>GPIO_TypeDef <span class="op">*</span> GROUP<span class="op">[]</span> <span class="op">=</span> <span class="op">{</span>GPIOA<span class="op">,</span> GPIOB<span class="op">,</span> GPIOC<span class="op">,</span> GPIOD<span class="op">,</span> <span class="dv">0</span><span class="op">,</span> GPIOF<span class="op">};</span> <span class="co">// 0,0x10000,0x20000, etc</span></span>
- <span id="cb29-10"><a href="#cb29-10" aria-hidden="true" tabindex="-1"></a><span class="dt">void</span> gpio_set<span class="op">(</span>uint32 bitPosition<span class="op">,</span> <span class="dt">bool</span> value<span class="op">)</span> <span class="op">{</span></span>
- <span id="cb29-11"><a href="#cb29-11" aria-hidden="true" tabindex="-1"></a> vu32<span class="op">*</span> group <span class="op">=</span> <span class="op">&((</span>GROUP<span class="op">[</span>bitPosition <span class="op">>></span> <span class="dv">16</span><span class="op">])-></span>ODR<span class="op">);</span></span>
- <span id="cb29-12"><a href="#cb29-12" aria-hidden="true" tabindex="-1"></a> bitPosition <span class="op">&=</span> <span class="bn">0xFFFF</span><span class="op">;</span></span>
- <span id="cb29-13"><a href="#cb29-13" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> <span class="op">(</span>value<span class="op">)</span></span>
- <span id="cb29-14"><a href="#cb29-14" aria-hidden="true" tabindex="-1"></a> <span class="op">*</span>group <span class="op">|=</span> bitPosition<span class="op">;</span></span>
- <span id="cb29-15"><a href="#cb29-15" aria-hidden="true" tabindex="-1"></a> <span class="cf">else</span></span>
- <span id="cb29-16"><a href="#cb29-16" aria-hidden="true" tabindex="-1"></a> <span class="op">*</span>group <span class="op">&=</span> <span class="op">~</span>bitPosition<span class="op">;</span></span>
- <span id="cb29-17"><a href="#cb29-17" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
- </details>
- <div class="sourceCode" id="cb30"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb30-1"><a href="#cb30-1" aria-hidden="true" tabindex="-1"></a><span class="co">/***** events ****/</span></span>
- <span id="cb30-2"><a href="#cb30-2" aria-hidden="true" tabindex="-1"></a><span class="pp">#define NUM_EVENTS </span><span class="dv">10</span></span>
- <span id="cb30-3"><a href="#cb30-3" aria-hidden="true" tabindex="-1"></a><span class="dt">volatile</span> uint8 events<span class="op">[</span>NUM_EVENTS<span class="op">];</span></span>
- <span id="cb30-4"><a href="#cb30-4" aria-hidden="true" tabindex="-1"></a><span class="kw">enum</span> <span class="op">{</span> EVT_NONE<span class="op">,</span></span>
- <span id="cb30-5"><a href="#cb30-5" aria-hidden="true" tabindex="-1"></a> EVT_TICK<span class="op">,</span></span>
- <span id="cb30-6"><a href="#cb30-6" aria-hidden="true" tabindex="-1"></a> EVT_BUTTON<span class="op">};</span></span>
- <span id="cb30-7"><a href="#cb30-7" aria-hidden="true" tabindex="-1"></a><span class="dt">void</span> newEvent<span class="op">(</span>uint8 e<span class="op">);</span></span>
- <span id="cb30-8"><a href="#cb30-8" aria-hidden="true" tabindex="-1"></a></span>
- <span id="cb30-9"><a href="#cb30-9" aria-hidden="true" tabindex="-1"></a><span class="co">/********** tasks ***********/</span></span>
- <span id="cb30-10"><a href="#cb30-10" aria-hidden="true" tabindex="-1"></a><span class="dt">void</span> ledTask<span class="op">(</span>uint8 evt<span class="op">);</span></span>
- <span id="cb30-11"><a href="#cb30-11" aria-hidden="true" tabindex="-1"></a><span class="dt">void</span> respondToButtonTask<span class="op">(</span>uint8 evt<span class="op">);</span></span>
- <span id="cb30-12"><a href="#cb30-12" aria-hidden="true" tabindex="-1"></a></span>
- <span id="cb30-13"><a href="#cb30-13" aria-hidden="true" tabindex="-1"></a><span class="co">/********** interrupts **************/</span></span>
- <span id="cb30-14"><a href="#cb30-14" aria-hidden="true" tabindex="-1"></a><span class="dt">volatile</span> uint32 tick<span class="op">;</span> <span class="co">// increasing at 100 ticks/sec</span></span>
- <span id="cb30-15"><a href="#cb30-15" aria-hidden="true" tabindex="-1"></a> <span class="co">// takes a year to roll-over</span></span>
- <span id="cb30-16"><a href="#cb30-16" aria-hidden="true" tabindex="-1"></a></span>
- <span id="cb30-17"><a href="#cb30-17" aria-hidden="true" tabindex="-1"></a><span class="dt">void</span> timer_isr<span class="op">(</span><span class="dt">void</span><span class="op">)</span> <span class="op">{</span></span>
- <span id="cb30-18"><a href="#cb30-18" aria-hidden="true" tabindex="-1"></a> tick<span class="op">++;</span></span>
- <span id="cb30-19"><a href="#cb30-19" aria-hidden="true" tabindex="-1"></a> newEvent<span class="op">(</span>EVT_TICK<span class="op">);</span></span>
- <span id="cb30-20"><a href="#cb30-20" aria-hidden="true" tabindex="-1"></a> <span class="co">// this interrupt is auto-ack'd</span></span>
- <span id="cb30-21"><a href="#cb30-21" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span>
- <span id="cb30-22"><a href="#cb30-22" aria-hidden="true" tabindex="-1"></a></span>
- <span id="cb30-23"><a href="#cb30-23" aria-hidden="true" tabindex="-1"></a><span class="dt">void</span> button_isr<span class="op">(</span><span class="dt">void</span><span class="op">)</span> <span class="op">{</span></span>
- <span id="cb30-24"><a href="#cb30-24" aria-hidden="true" tabindex="-1"></a> newEvent<span class="op">(</span>EVT_BUTTON<span class="op">);</span></span>
- <span id="cb30-25"><a href="#cb30-25" aria-hidden="true" tabindex="-1"></a> EXTI<span class="op">-></span>PR <span class="op">|=</span> <span class="bn">0x3001</span><span class="op">;</span> <span class="co">// ack it </span></span>
- <span id="cb30-26"><a href="#cb30-26" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span>
- <span id="cb30-27"><a href="#cb30-27" aria-hidden="true" tabindex="-1"></a></span>
- <span id="cb30-28"><a href="#cb30-28" aria-hidden="true" tabindex="-1"></a><span class="co">/** newEvent</span></span>
- <span id="cb30-29"><a href="#cb30-29" aria-hidden="true" tabindex="-1"></a><span class="co"> * add the event to the event queue</span></span>
- <span id="cb30-30"><a href="#cb30-30" aria-hidden="true" tabindex="-1"></a><span class="co"> * wrapped in critical section</span></span>
- <span id="cb30-31"><a href="#cb30-31" aria-hidden="true" tabindex="-1"></a><span class="co"> * </span></span>
- <span id="cb30-32"><a href="#cb30-32" aria-hidden="true" tabindex="-1"></a><span class="co"> * </span><span class="an">@param</span><span class="co"> </span><span class="cv">the</span><span class="co"> event</span></span>
- <span id="cb30-33"><a href="#cb30-33" aria-hidden="true" tabindex="-1"></a><span class="co"> */</span></span>
- <span id="cb30-34"><a href="#cb30-34" aria-hidden="true" tabindex="-1"></a><span class="dt">void</span> newEvent<span class="op">(</span><span class="dt">char</span> e<span class="op">)</span> <span class="op">{</span></span>
- <span id="cb30-35"><a href="#cb30-35" aria-hidden="true" tabindex="-1"></a> <span class="dt">static</span> uint nextEvent<span class="op">;</span></span>
- <span id="cb30-36"><a href="#cb30-36" aria-hidden="true" tabindex="-1"></a> dint<span class="op">();</span> <span class="co">// critical section</span></span>
- <span id="cb30-37"><a href="#cb30-37" aria-hidden="true" tabindex="-1"></a> events<span class="op">[</span>nextEvent<span class="op">++]</span> <span class="op">=</span> e<span class="op">;</span></span>
- <span id="cb30-38"><a href="#cb30-38" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> <span class="op">(</span>nextEvent<span class="op">==</span>NUM_EVENTS<span class="op">)</span></span>
- <span id="cb30-39"><a href="#cb30-39" aria-hidden="true" tabindex="-1"></a> nextEvent <span class="op">=</span> <span class="dv">0</span><span class="op">;</span></span>
- <span id="cb30-40"><a href="#cb30-40" aria-hidden="true" tabindex="-1"></a> eint<span class="op">();</span></span>
- <span id="cb30-41"><a href="#cb30-41" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
- </details>
- <details>
- <summary>
- hardware initialization
- </summary>
- <div class="sourceCode" id="cb31"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb31-1"><a href="#cb31-1" aria-hidden="true" tabindex="-1"></a><span class="co">/***** init ******/</span></span>
- <span id="cb31-2"><a href="#cb31-2" aria-hidden="true" tabindex="-1"></a><span class="co">/* called by newlib startup */</span></span>
- <span id="cb31-3"><a href="#cb31-3" aria-hidden="true" tabindex="-1"></a><span class="dt">void</span> _init<span class="op">(</span><span class="dt">void</span><span class="op">)</span> <span class="op">{</span></span>
- <span id="cb31-4"><a href="#cb31-4" aria-hidden="true" tabindex="-1"></a> <span class="co">// startup code</span></span>
- <span id="cb31-5"><a href="#cb31-5" aria-hidden="true" tabindex="-1"></a></span>
- <span id="cb31-6"><a href="#cb31-6" aria-hidden="true" tabindex="-1"></a> <span class="co">// use default clocks</span></span>
- <span id="cb31-7"><a href="#cb31-7" aria-hidden="true" tabindex="-1"></a> <span class="co">// turn on all the GPIO's</span></span>
- <span id="cb31-8"><a href="#cb31-8" aria-hidden="true" tabindex="-1"></a> RCC<span class="op">-></span>AHBENR <span class="op">=</span> <span class="bn">0x005e0014</span><span class="op">;</span></span>
- <span id="cb31-9"><a href="#cb31-9" aria-hidden="true" tabindex="-1"></a> <span class="co">// enable SysCfg</span></span>
- <span id="cb31-10"><a href="#cb31-10" aria-hidden="true" tabindex="-1"></a> RCC<span class="op">-></span>APB2ENR <span class="op">=</span> <span class="bn">0x00004801</span><span class="op">;</span></span>
- <span id="cb31-11"><a href="#cb31-11" aria-hidden="true" tabindex="-1"></a></span>
- <span id="cb31-12"><a href="#cb31-12" aria-hidden="true" tabindex="-1"></a></span>
- <span id="cb31-13"><a href="#cb31-13" aria-hidden="true" tabindex="-1"></a> <span class="co">// enable the two LEDs as outputs</span></span>
- <span id="cb31-14"><a href="#cb31-14" aria-hidden="true" tabindex="-1"></a> GPIOA<span class="op">-></span>MODER <span class="op">=</span> <span class="op">(</span>GPIOA<span class="op">-></span>MODER <span class="op">&</span> <span class="bn">0xFFFFFCFF</span><span class="op">)</span> <span class="op">|</span> <span class="bn">0x00000100</span><span class="op">;</span> <span class="co">// port A bit 4</span></span>
- <span id="cb31-15"><a href="#cb31-15" aria-hidden="true" tabindex="-1"></a> GPIOF<span class="op">-></span>MODER <span class="op">=</span> <span class="op">(</span>GPIOF<span class="op">-></span>MODER <span class="op">&</span> <span class="bn">0xFFFFF3FF</span><span class="op">)</span> <span class="op">|</span> <span class="bn">0x00000400</span><span class="op">;</span> <span class="co">// port F bit 5</span></span>
- <span id="cb31-16"><a href="#cb31-16" aria-hidden="true" tabindex="-1"></a> <span class="co">// and the push button as input + pulldown</span></span>
- <span id="cb31-17"><a href="#cb31-17" aria-hidden="true" tabindex="-1"></a> GPIOB<span class="op">-></span>PUPDR <span class="op">=</span> <span class="op">(</span>GPIOB<span class="op">-></span>PUPDR <span class="op">&</span> <span class="bn">0xF3FFFFFF</span><span class="op">)</span> <span class="op">|</span> <span class="bn">0x08000000</span><span class="op">;</span> <span class="co">// pulldown on 13</span></span>
- <span id="cb31-18"><a href="#cb31-18" aria-hidden="true" tabindex="-1"></a></span>
- <span id="cb31-19"><a href="#cb31-19" aria-hidden="true" tabindex="-1"></a> <span class="co">// keep the clocking system simple: just use the 8MHz HSI everywhere</span></span>
- <span id="cb31-20"><a href="#cb31-20" aria-hidden="true" tabindex="-1"></a> SysTick<span class="op">-></span>LOAD <span class="op">=</span> <span class="dv">10000</span><span class="op">;</span> <span class="co">// 10 msec</span></span>
- <span id="cb31-21"><a href="#cb31-21" aria-hidden="true" tabindex="-1"></a> SysTick<span class="op">-></span>VAL <span class="op">=</span> <span class="dv">0</span><span class="op">;</span></span>
- <span id="cb31-22"><a href="#cb31-22" aria-hidden="true" tabindex="-1"></a> SysTick<span class="op">-></span>CTRL <span class="op">=</span> <span class="dv">3</span><span class="op">;</span> <span class="co">// count at 1usec, use interrupts</span></span>
- <span id="cb31-23"><a href="#cb31-23" aria-hidden="true" tabindex="-1"></a> </span>
- <span id="cb31-24"><a href="#cb31-24" aria-hidden="true" tabindex="-1"></a> <span class="co">/* to configure an interrupt on the stm32f0xx,</span></span>
- <span id="cb31-25"><a href="#cb31-25" aria-hidden="true" tabindex="-1"></a><span class="co"> - enable the EXTI->IMR for the pin</span></span>
- <span id="cb31-26"><a href="#cb31-26" aria-hidden="true" tabindex="-1"></a><span class="co"> - set EXTI->RTSR for select rising edge</span></span>
- <span id="cb31-27"><a href="#cb31-27" aria-hidden="true" tabindex="-1"></a><span class="co"> - set the SYSCFG->EXTICRx pin to route it </span></span>
- <span id="cb31-28"><a href="#cb31-28" aria-hidden="true" tabindex="-1"></a><span class="co"> - enable the gating bit in the NVIC register </span></span>
- <span id="cb31-29"><a href="#cb31-29" aria-hidden="true" tabindex="-1"></a><span class="co"> - don't forget to ack each interrupt at EXTI->PR</span></span>
- <span id="cb31-30"><a href="#cb31-30" aria-hidden="true" tabindex="-1"></a><span class="co"> */</span></span>
- <span id="cb31-31"><a href="#cb31-31" aria-hidden="true" tabindex="-1"></a> EXTI<span class="op">-></span>IMR <span class="op">=</span> <span class="bn">0x2000</span><span class="op">;</span> <span class="co">// enable interrupt from line 13</span></span>
- <span id="cb31-32"><a href="#cb31-32" aria-hidden="true" tabindex="-1"></a> EXTI<span class="op">-></span>RTSR <span class="op">=</span> <span class="bn">0x2000</span><span class="op">;</span> <span class="co">// interrupt on rising edge</span></span>
- <span id="cb31-33"><a href="#cb31-33" aria-hidden="true" tabindex="-1"></a> SYSCFG<span class="op">-></span>EXTICR<span class="op">[</span><span class="dv">3</span><span class="op">]</span> <span class="op">=</span> <span class="bn">0x0010</span><span class="op">;</span> <span class="co">// select prot B for exti-13</span></span>
- <span id="cb31-34"><a href="#cb31-34" aria-hidden="true" tabindex="-1"></a> NVIC<span class="op">-></span>ISER<span class="op">[</span><span class="dv">0</span><span class="op">]</span> <span class="op">=</span> <span class="bn">0x00E1</span><span class="op">;</span> <span class="co">// enable in NVIC: gpio & watchdog</span></span>
- <span id="cb31-35"><a href="#cb31-35" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
- </details>
- <div class="sourceCode" id="cb32"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb32-1"><a href="#cb32-1" aria-hidden="true" tabindex="-1"></a><span class="dt">void</span> main<span class="op">(</span><span class="dt">void</span><span class="op">)</span> <span class="op">{</span></span>
- <span id="cb32-2"><a href="#cb32-2" aria-hidden="true" tabindex="-1"></a></span>
- <span id="cb32-3"><a href="#cb32-3" aria-hidden="true" tabindex="-1"></a> eint<span class="op">();</span></span>
- <span id="cb32-4"><a href="#cb32-4" aria-hidden="true" tabindex="-1"></a></span>
- <span id="cb32-5"><a href="#cb32-5" aria-hidden="true" tabindex="-1"></a> <span class="co">// dispatcher loop</span></span>
- <span id="cb32-6"><a href="#cb32-6" aria-hidden="true" tabindex="-1"></a> <span class="cf">while</span><span class="op">(</span><span class="dv">1</span><span class="op">)</span> <span class="op">{</span></span>
- <span id="cb32-7"><a href="#cb32-7" aria-hidden="true" tabindex="-1"></a> <span class="dt">int</span> j<span class="op">;</span></span>
- <span id="cb32-8"><a href="#cb32-8" aria-hidden="true" tabindex="-1"></a> <span class="cf">for</span> <span class="op">(</span>j<span class="op">=</span><span class="dv">0</span><span class="op">;</span> j<span class="op"><</span>NUM_EVENTS<span class="op">;</span> j<span class="op">++)</span> <span class="op">{</span></span>
- <span id="cb32-9"><a href="#cb32-9" aria-hidden="true" tabindex="-1"></a> <span class="cf">while</span> <span class="op">(</span>events<span class="op">[</span>j<span class="op">]==</span>EVT_NONE<span class="op">)</span></span>
- <span id="cb32-10"><a href="#cb32-10" aria-hidden="true" tabindex="-1"></a> <span class="op">{}</span></span>
- <span id="cb32-11"><a href="#cb32-11" aria-hidden="true" tabindex="-1"></a> ledTask<span class="op">(</span>events<span class="op">[</span>j<span class="op">]);</span></span>
- <span id="cb32-12"><a href="#cb32-12" aria-hidden="true" tabindex="-1"></a> respondToButtonTask<span class="op">(</span>events<span class="op">[</span>j<span class="op">]);</span></span>
- <span id="cb32-13"><a href="#cb32-13" aria-hidden="true" tabindex="-1"></a> events<span class="op">[</span>j<span class="op">]</span> <span class="op">=</span> EVT_NONE<span class="op">;</span></span>
- <span id="cb32-14"><a href="#cb32-14" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
- <span id="cb32-15"><a href="#cb32-15" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
- <span id="cb32-16"><a href="#cb32-16" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span>
- <span id="cb32-17"><a href="#cb32-17" aria-hidden="true" tabindex="-1"></a></span>
- <span id="cb32-18"><a href="#cb32-18" aria-hidden="true" tabindex="-1"></a><span class="co">/*********** task code, with states ************/</span></span>
- <span id="cb32-19"><a href="#cb32-19" aria-hidden="true" tabindex="-1"></a><span class="kw">enum</span> <span class="op">{</span>LED_ON<span class="op">,</span> LED_OFF<span class="op">};</span></span>
- <span id="cb32-20"><a href="#cb32-20" aria-hidden="true" tabindex="-1"></a><span class="kw">enum</span> <span class="op">{</span>RTB_IDLE<span class="op">,</span> RTB_ON<span class="op">};</span> <span class="co">// states</span></span>
- <span id="cb32-21"><a href="#cb32-21" aria-hidden="true" tabindex="-1"></a><span class="dt">static</span> uint8 rtbState <span class="op">=</span> RTB_IDLE<span class="op">;</span></span>
- <span id="cb32-22"><a href="#cb32-22" aria-hidden="true" tabindex="-1"></a><span class="dt">static</span> uint16 rtbTimerCount <span class="op">=</span> <span class="dv">0</span><span class="op">;</span></span>
- <span id="cb32-23"><a href="#cb32-23" aria-hidden="true" tabindex="-1"></a><span class="dt">const</span> uint16 BUTTON_LED_ON_TIME <span class="op">=</span> <span class="dv">150</span><span class="op">;</span></span>
- <span id="cb32-24"><a href="#cb32-24" aria-hidden="true" tabindex="-1"></a></span>
- <span id="cb32-25"><a href="#cb32-25" aria-hidden="true" tabindex="-1"></a><span class="dt">void</span> respondToButtonTask<span class="op">(</span>uint8 evt<span class="op">)</span> <span class="op">{</span></span>
- <span id="cb32-26"><a href="#cb32-26" aria-hidden="true" tabindex="-1"></a> <span class="cf">switch</span><span class="op">(</span>rtbState<span class="op">)</span> <span class="op">{</span></span>
- <span id="cb32-27"><a href="#cb32-27" aria-hidden="true" tabindex="-1"></a> <span class="cf">case</span> RTB_IDLE<span class="op">:</span></span>
- <span id="cb32-28"><a href="#cb32-28" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> <span class="op">(</span>evt <span class="op">==</span> EVT_BUTTON<span class="op">)</span> <span class="op">{</span></span>
- <span id="cb32-29"><a href="#cb32-29" aria-hidden="true" tabindex="-1"></a> rtbState <span class="op">=</span> RTB_ON<span class="op">;</span></span>
- <span id="cb32-30"><a href="#cb32-30" aria-hidden="true" tabindex="-1"></a> rtbTimerCount <span class="op">=</span> <span class="dv">0</span><span class="op">;</span></span>
- <span id="cb32-31"><a href="#cb32-31" aria-hidden="true" tabindex="-1"></a> gpio_set<span class="op">(</span>LED<span class="op">,</span> LED_ON<span class="op">);</span></span>
- <span id="cb32-32"><a href="#cb32-32" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
- <span id="cb32-33"><a href="#cb32-33" aria-hidden="true" tabindex="-1"></a> <span class="cf">break</span><span class="op">;</span></span>
- <span id="cb32-34"><a href="#cb32-34" aria-hidden="true" tabindex="-1"></a> <span class="cf">case</span> RTB_ON<span class="op">:</span></span>
- <span id="cb32-35"><a href="#cb32-35" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> <span class="op">(</span>evt <span class="op">==</span> EVT_TICK<span class="op">)</span> <span class="op">{</span></span>
- <span id="cb32-36"><a href="#cb32-36" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> <span class="op">(++</span>rtbTimerCount <span class="op">></span> BUTTON_LED_ON_TIME<span class="op">)</span> <span class="op">{</span></span>
- <span id="cb32-37"><a href="#cb32-37" aria-hidden="true" tabindex="-1"></a> gpio_set<span class="op">(</span>LED<span class="op">,</span> LED_OFF<span class="op">);</span></span>
- <span id="cb32-38"><a href="#cb32-38" aria-hidden="true" tabindex="-1"></a> rtbState <span class="op">=</span> RTB_IDLE<span class="op">;</span></span>
- <span id="cb32-39"><a href="#cb32-39" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
- <span id="cb32-40"><a href="#cb32-40" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
- <span id="cb32-41"><a href="#cb32-41" aria-hidden="true" tabindex="-1"></a> <span class="cf">break</span><span class="op">;</span></span>
- <span id="cb32-42"><a href="#cb32-42" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
- <span id="cb32-43"><a href="#cb32-43" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span>
- <span id="cb32-44"><a href="#cb32-44" aria-hidden="true" tabindex="-1"></a></span>
- <span id="cb32-45"><a href="#cb32-45" aria-hidden="true" tabindex="-1"></a><span class="dt">const</span> uint16 LED_ON_TIME <span class="op">=</span> <span class="dv">150</span><span class="op">;</span></span>
- <span id="cb32-46"><a href="#cb32-46" aria-hidden="true" tabindex="-1"></a><span class="dt">const</span> uint16 LED_OFF_TIME <span class="op">=</span> <span class="dv">50</span><span class="op">;</span></span>
- <span id="cb32-47"><a href="#cb32-47" aria-hidden="true" tabindex="-1"></a><span class="dt">static</span> uint8 ledState <span class="op">=</span> LED_OFF<span class="op">;</span></span>
- <span id="cb32-48"><a href="#cb32-48" aria-hidden="true" tabindex="-1"></a><span class="dt">static</span> uint16 ledTimerCount <span class="op">=</span> <span class="dv">0</span><span class="op">;</span></span>
- <span id="cb32-49"><a href="#cb32-49" aria-hidden="true" tabindex="-1"></a></span>
- <span id="cb32-50"><a href="#cb32-50" aria-hidden="true" tabindex="-1"></a><span class="dt">void</span> ledTask<span class="op">(</span>uint8 evt<span class="op">)</span> <span class="op">{</span></span>
- <span id="cb32-51"><a href="#cb32-51" aria-hidden="true" tabindex="-1"></a> <span class="cf">switch</span><span class="op">(</span>ledState<span class="op">)</span> <span class="op">{</span></span>
- <span id="cb32-52"><a href="#cb32-52" aria-hidden="true" tabindex="-1"></a> <span class="cf">case</span> LED_OFF<span class="op">:</span></span>
- <span id="cb32-53"><a href="#cb32-53" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> <span class="op">(</span>evt <span class="op">==</span> EVT_TICK<span class="op">)</span> <span class="op">{</span></span>
- <span id="cb32-54"><a href="#cb32-54" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> <span class="op">(++</span>ledTimerCount <span class="op">></span> LED_OFF_TIME<span class="op">)</span> <span class="op">{</span></span>
- <span id="cb32-55"><a href="#cb32-55" aria-hidden="true" tabindex="-1"></a> gpio_set<span class="op">(</span>LED2<span class="op">,</span> LED_ON<span class="op">);</span></span>
- <span id="cb32-56"><a href="#cb32-56" aria-hidden="true" tabindex="-1"></a> ledTimerCount <span class="op">=</span> <span class="dv">0</span><span class="op">;</span></span>
- <span id="cb32-57"><a href="#cb32-57" aria-hidden="true" tabindex="-1"></a> ledState <span class="op">=</span> LED_ON<span class="op">;</span></span>
- <span id="cb32-58"><a href="#cb32-58" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
- <span id="cb32-59"><a href="#cb32-59" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
- <span id="cb32-60"><a href="#cb32-60" aria-hidden="true" tabindex="-1"></a> <span class="cf">break</span><span class="op">;</span></span>
- <span id="cb32-61"><a href="#cb32-61" aria-hidden="true" tabindex="-1"></a> <span class="cf">case</span> LED_ON<span class="op">:</span></span>
- <span id="cb32-62"><a href="#cb32-62" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> <span class="op">(</span>evt <span class="op">==</span> EVT_TICK<span class="op">)</span> <span class="op">{</span></span>
- <span id="cb32-63"><a href="#cb32-63" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> <span class="op">(++</span>ledTimerCount <span class="op">></span> LED_ON_TIME<span class="op">)</span> <span class="op">{</span></span>
- <span id="cb32-64"><a href="#cb32-64" aria-hidden="true" tabindex="-1"></a> gpio_set<span class="op">(</span>LED2<span class="op">,</span> LED_OFF<span class="op">);</span></span>
- <span id="cb32-65"><a href="#cb32-65" aria-hidden="true" tabindex="-1"></a> ledTimerCount <span class="op">=</span> <span class="dv">0</span><span class="op">;</span></span>
- <span id="cb32-66"><a href="#cb32-66" aria-hidden="true" tabindex="-1"></a> ledState <span class="op">=</span> LED_OFF<span class="op">;</span></span>
- <span id="cb32-67"><a href="#cb32-67" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
- <span id="cb32-68"><a href="#cb32-68" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
- <span id="cb32-69"><a href="#cb32-69" aria-hidden="true" tabindex="-1"></a> <span class="cf">break</span><span class="op">;</span></span>
- <span id="cb32-70"><a href="#cb32-70" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
- <span id="cb32-71"><a href="#cb32-71" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
- <details>
- <summary>
- vector table
- </summary>
- <div class="sourceCode" id="cb33"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb33-1"><a href="#cb33-1" aria-hidden="true" tabindex="-1"></a><span class="co">/* vector table */</span></span>
- <span id="cb33-2"><a href="#cb33-2" aria-hidden="true" tabindex="-1"></a><span class="pp">#define STACK_TOP </span><span class="bn">0x20002000</span></span>
- <span id="cb33-3"><a href="#cb33-3" aria-hidden="true" tabindex="-1"></a><span class="dt">void</span> default_isr<span class="op">(</span><span class="dt">void</span><span class="op">)</span> <span class="op">{}</span></span>
- <span id="cb33-4"><a href="#cb33-4" aria-hidden="true" tabindex="-1"></a><span class="kw">extern</span> <span class="dt">void</span> _start<span class="op">(</span><span class="dt">void</span><span class="op">);</span></span>
- <span id="cb33-5"><a href="#cb33-5" aria-hidden="true" tabindex="-1"></a></span>
- <span id="cb33-6"><a href="#cb33-6" aria-hidden="true" tabindex="-1"></a><span class="dt">void</span> <span class="op">(*</span>myvectors<span class="op">[])(</span><span class="dt">void</span><span class="op">)</span> __attribute__ <span class="op">((</span>section<span class="op">(</span><span class="st">".vectors"</span><span class="op">)))=</span> <span class="op">{</span></span>
- <span id="cb33-7"><a href="#cb33-7" aria-hidden="true" tabindex="-1"></a> <span class="op">(</span><span class="dt">void</span><span class="op">(*)(</span><span class="dt">void</span><span class="op">))</span> STACK_TOP<span class="op">,</span> <span class="co">// stack pointer</span></span>
- <span id="cb33-8"><a href="#cb33-8" aria-hidden="true" tabindex="-1"></a> _start<span class="op">,</span> <span class="co">// code entry point</span></span>
- <span id="cb33-9"><a href="#cb33-9" aria-hidden="true" tabindex="-1"></a> default_isr<span class="op">,</span> <span class="co">// handle non-maskable interrupts</span></span>
- <span id="cb33-10"><a href="#cb33-10" aria-hidden="true" tabindex="-1"></a> default_isr<span class="op">,</span> <span class="co">// handle hard faults</span></span>
- <span id="cb33-11"><a href="#cb33-11" aria-hidden="true" tabindex="-1"></a> <span class="dv">0</span><span class="op">,</span><span class="dv">0</span><span class="op">,</span><span class="dv">0</span><span class="op">,</span><span class="dv">0</span><span class="op">,</span> <span class="co">/* 10...1f */</span></span>
- <span id="cb33-12"><a href="#cb33-12" aria-hidden="true" tabindex="-1"></a> <span class="dv">0</span><span class="op">,</span><span class="dv">0</span><span class="op">,</span><span class="dv">0</span><span class="op">,</span><span class="dv">0</span><span class="op">,</span> <span class="co">/* 20...2f */</span></span>
- <span id="cb33-13"><a href="#cb33-13" aria-hidden="true" tabindex="-1"></a> <span class="dv">0</span><span class="op">,</span><span class="dv">0</span><span class="op">,</span><span class="dv">0</span><span class="op">,</span>timer_isr<span class="op">,</span> <span class="co">/* 30...3f */</span></span>
- <span id="cb33-14"><a href="#cb33-14" aria-hidden="true" tabindex="-1"></a> <span class="dv">0</span><span class="op">,</span><span class="dv">0</span><span class="op">,</span><span class="dv">0</span><span class="op">,</span><span class="dv">0</span><span class="op">,</span></span>
- <span id="cb33-15"><a href="#cb33-15" aria-hidden="true" tabindex="-1"></a> <span class="dv">0</span><span class="op">,</span>button_isr<span class="op">,</span>button_isr<span class="op">,</span>button_isr<span class="op">,</span> <span class="co">/* 50...5f */</span></span>
- <span id="cb33-16"><a href="#cb33-16" aria-hidden="true" tabindex="-1"></a> <span class="dv">0</span><span class="op">,</span><span class="dv">0</span><span class="op">,</span><span class="dv">0</span><span class="op">,</span><span class="dv">0</span><span class="op">,</span> <span class="co">/* 60...6f */</span></span>
- <span id="cb33-17"><a href="#cb33-17" aria-hidden="true" tabindex="-1"></a> <span class="dv">0</span><span class="op">,</span><span class="dv">0</span><span class="op">,</span><span class="dv">0</span><span class="op">,</span><span class="dv">0</span><span class="op">,</span> <span class="co">/* 70...7f */</span></span>
- <span id="cb33-18"><a href="#cb33-18" aria-hidden="true" tabindex="-1"></a> <span class="dv">0</span><span class="op">,</span><span class="dv">0</span><span class="op">,</span><span class="dv">0</span><span class="op">,</span><span class="dv">0</span><span class="op">,</span> <span class="co">/* 80...8f */</span></span>
- <span id="cb33-19"><a href="#cb33-19" aria-hidden="true" tabindex="-1"></a> <span class="dv">0</span><span class="op">,</span><span class="dv">0</span><span class="op">,</span><span class="dv">0</span><span class="op">,</span><span class="dv">0</span><span class="op">,</span> <span class="co">/* 90...9f */</span></span>
- <span id="cb33-20"><a href="#cb33-20" aria-hidden="true" tabindex="-1"></a> <span class="dv">0</span><span class="op">,</span><span class="dv">0</span><span class="op">,</span><span class="dv">0</span><span class="op">,</span><span class="dv">0</span><span class="op">,</span> <span class="co">/* a0...af */</span></span>
- <span id="cb33-21"><a href="#cb33-21" aria-hidden="true" tabindex="-1"></a> <span class="dv">0</span><span class="op">,</span><span class="dv">0</span><span class="op">,</span><span class="dv">0</span><span class="op">,</span><span class="dv">0</span><span class="op">,</span> <span class="co">/* b0...bf */</span></span>
- <span id="cb33-22"><a href="#cb33-22" aria-hidden="true" tabindex="-1"></a> <span class="dv">0</span><span class="op">,</span><span class="dv">0</span><span class="op">,</span><span class="dv">0</span><span class="op">,</span><span class="dv">0</span><span class="op">,</span> <span class="co">/* c0...cf */</span></span>
- <span id="cb33-23"><a href="#cb33-23" aria-hidden="true" tabindex="-1"></a> <span class="dv">0</span><span class="op">,</span><span class="dv">0</span><span class="op">,</span><span class="dv">0</span><span class="op">,</span><span class="dv">0</span><span class="op">,</span> <span class="co">/* d0...df */</span></span>
- <span id="cb33-24"><a href="#cb33-24" aria-hidden="true" tabindex="-1"></a> <span class="dv">0</span><span class="op">,</span><span class="dv">0</span><span class="op">,</span><span class="dv">0</span><span class="op">,</span><span class="dv">0</span><span class="op">,</span> <span class="co">/* e0...ef */</span></span>
- <span id="cb33-25"><a href="#cb33-25" aria-hidden="true" tabindex="-1"></a> <span class="dv">0</span><span class="op">,</span><span class="dv">0</span><span class="op">,</span><span class="dv">0</span><span class="op">,</span><span class="dv">0</span><span class="op">,</span> <span class="co">/* f0...ff */</span></span>
- <span id="cb33-26"><a href="#cb33-26" aria-hidden="true" tabindex="-1"></a> <span class="dv">0</span><span class="op">,</span><span class="dv">0</span><span class="op">,</span><span class="dv">0</span><span class="op">,</span><span class="dv">0</span><span class="op">,</span> <span class="co">/* 100.10f */</span></span>
- <span id="cb33-27"><a href="#cb33-27" aria-hidden="true" tabindex="-1"></a> <span class="dv">0</span><span class="op">,</span><span class="dv">0</span><span class="op">,</span><span class="dv">0</span><span class="op">,</span><span class="dv">0</span> <span class="co">/* 110.11f */</span></span>
- <span id="cb33-28"><a href="#cb33-28" aria-hidden="true" tabindex="-1"></a><span class="op">};</span></span></code></pre></div>
- </details>
- <h2 id="substates">Substates</h2>
- <p>For things like flashing LED’s, you could create two states, and
- toggle between them, like this:</p>
- <p><img src="rtb_state_4.png" /></p>
- <hr>
- <p>Or, you could simply have an internal (static, not-on-the-stack)
- variable which can keep track of the LED toggle. The state diagram then
- simplifies:</p>
- <div class="sourceCode" id="cb34"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb34-1"><a href="#cb34-1" aria-hidden="true" tabindex="-1"></a>uint8 rtbState <span class="op">=</span> RTB_IDLE<span class="op">;</span> <span class="co">// major state, RTB_IDLE/RTB_FLASHING</span></span>
- <span id="cb34-2"><a href="#cb34-2" aria-hidden="true" tabindex="-1"></a>uint8 rtbSubState <span class="op">=</span> RTB_FLASH_OFF<span class="op">;</span> <span class="co">// minor state, toggles LED on/off</span></span></code></pre></div>
- <p>Alternatively, you could use the <em>timer counter</em> variable, and
- make changes at the half-way point through the count. This simplifies
- the substate design to this:</p>
- <p><img src="rtb_state_5.png" class="imageright" /></p>
- <div class="sourceCode" id="cb35"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb35-1"><a href="#cb35-1" aria-hidden="true" tabindex="-1"></a><span class="dt">const</span> uint16 FLASH_CYCLE_TIME <span class="dv">150</span><span class="op">;</span></span>
- <span id="cb35-2"><a href="#cb35-2" aria-hidden="true" tabindex="-1"></a><span class="dt">const</span> uint16 FLASH_ON_TIME <span class="dv">40</span><span class="op">;</span></span>
- <span id="cb35-3"><a href="#cb35-3" aria-hidden="true" tabindex="-1"></a>uint8 rtbState <span class="op">=</span> STATE_IDLE<span class="op">;</span></span>
- <span id="cb35-4"><a href="#cb35-4" aria-hidden="true" tabindex="-1"></a><span class="dt">void</span> rtbTaskCode<span class="op">(</span><span class="dt">char</span> event<span class="op">)</span> <span class="op">{</span></span>
- <span id="cb35-5"><a href="#cb35-5" aria-hidden="true" tabindex="-1"></a> <span class="dt">static</span> uint16 flashCount<span class="op">=</span><span class="dv">0</span><span class="op">;</span></span>
- <span id="cb35-6"><a href="#cb35-6" aria-hidden="true" tabindex="-1"></a></span>
- <span id="cb35-7"><a href="#cb35-7" aria-hidden="true" tabindex="-1"></a> <span class="cf">switch</span><span class="op">(</span>taskState<span class="op">)</span> <span class="op">{</span></span>
- <span id="cb35-8"><a href="#cb35-8" aria-hidden="true" tabindex="-1"></a> <span class="cf">case</span> RTB_IDLE<span class="op">:</span></span>
- <span id="cb35-9"><a href="#cb35-9" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> <span class="op">(</span>event <span class="op">==</span> EVT_BUTTON<span class="op">)</span> <span class="op">{</span></span>
- <span id="cb35-10"><a href="#cb35-10" aria-hidden="true" tabindex="-1"></a> rtbState <span class="op">=</span> STATE_FLASHING<span class="op">;</span></span>
- <span id="cb35-11"><a href="#cb35-11" aria-hidden="true" tabindex="-1"></a> flashCount <span class="op">=</span> FLASH_CYCLE_TIME<span class="op">;</span></span>
- <span id="cb35-12"><a href="#cb35-12" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
- <span id="cb35-13"><a href="#cb35-13" aria-hidden="true" tabindex="-1"></a> <span class="cf">break</span><span class="op">;</span></span>
- <span id="cb35-14"><a href="#cb35-14" aria-hidden="true" tabindex="-1"></a> <span class="cf">case</span> STATE_FLASHING<span class="op">:</span></span>
- <span id="cb35-15"><a href="#cb35-15" aria-hidden="true" tabindex="-1"></a> <span class="co">// count down flashCount, toggle LED halfway through</span></span>
- <span id="cb35-16"><a href="#cb35-16" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> <span class="op">(</span>event <span class="op">==</span> EVT_TICK<span class="op">)</span> <span class="op">{</span></span>
- <span id="cb35-17"><a href="#cb35-17" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> <span class="op">(--</span>flashCount <span class="op">==</span> <span class="dv">0</span><span class="op">)</span> <span class="op">{</span></span>
- <span id="cb35-18"><a href="#cb35-18" aria-hidden="true" tabindex="-1"></a> setLED<span class="op">(</span>OFF<span class="op">);</span></span>
- <span id="cb35-19"><a href="#cb35-19" aria-hidden="true" tabindex="-1"></a> flashCount <span class="op">=</span> FLASH_CYCLE_TIME<span class="op">;</span></span>
- <span id="cb35-20"><a href="#cb35-20" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span> <span class="cf">else</span> <span class="cf">if</span> <span class="op">(</span>flashCount <span class="op">==</span> FLASH_ON_TIME<span class="op">)</span> <span class="op">{</span></span>
- <span id="cb35-21"><a href="#cb35-21" aria-hidden="true" tabindex="-1"></a> setLED<span class="op">(</span>ON<span class="op">);</span></span>
- <span id="cb35-22"><a href="#cb35-22" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
- <span id="cb35-23"><a href="#cb35-23" aria-hidden="true" tabindex="-1"></a> <span class="co">// ... make sure to turn LED off when leaving this state</span></span>
- <span id="cb35-24"><a href="#cb35-24" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> <span class="op">(</span>event <span class="op">==</span> EVT_BUTTON<span class="op">)</span> <span class="op">{</span></span>
- <span id="cb35-25"><a href="#cb35-25" aria-hidden="true" tabindex="-1"></a> setLED<span class="op">(</span>OFF<span class="op">);</span></span>
- <span id="cb35-26"><a href="#cb35-26" aria-hidden="true" tabindex="-1"></a> rtbState <span class="op">=</span> RTB_IDLE<span class="op">;</span></span>
- <span id="cb35-27"><a href="#cb35-27" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span> </span>
- <span id="cb35-28"><a href="#cb35-28" aria-hidden="true" tabindex="-1"></a> <span class="cf">break</span><span class="op">;</span></span>
- <span id="cb35-29"><a href="#cb35-29" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
- <span id="cb35-30"><a href="#cb35-30" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
- <span id="cb35-31"><a href="#cb35-31" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
- <h2 id="state-machine-initialization">State Machine Initialization</h2>
- <p>Notice that there’s no initialization of states in this code. It
- <em>would</em> be very handy if we knew when to initialize all the
- lights and buzzers and match up the various states. Perhaps the
- <code>main()</code> code could add a single event into the event queue
- at power-up, perhaps <code>EVT_INIT</code>. It might be added like
- this:</p>
- <div class="sourceCode" id="cb36"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb36-1"><a href="#cb36-1" aria-hidden="true" tabindex="-1"></a><span class="dt">void</span> main<span class="op">(</span><span class="dt">void</span><span class="op">)</span> <span class="op">{</span></span>
- <span id="cb36-2"><a href="#cb36-2" aria-hidden="true" tabindex="-1"></a> newEvent<span class="op">(</span>EVT_INIT<span class="op">);</span> <span class="co">// early, before interrupts, so we know it's first in line</span></span>
- <span id="cb36-3"><a href="#cb36-3" aria-hidden="true" tabindex="-1"></a> eint<span class="op">();</span></span>
- <span id="cb36-4"><a href="#cb36-4" aria-hidden="true" tabindex="-1"></a> <span class="cf">while</span><span class="op">(</span><span class="dv">1</span><span class="op">)</span> <span class="op">{</span></span>
- <span id="cb36-5"><a href="#cb36-5" aria-hidden="true" tabindex="-1"></a> <span class="op">...</span> dispatcher code <span class="op">...</span></span>
- <span id="cb36-6"><a href="#cb36-6" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
- <span id="cb36-7"><a href="#cb36-7" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
- <p>Then, in the state code, you can catch that event and set up whatever
- might be required</p>
- <div class="sourceCode" id="cb37"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb37-1"><a href="#cb37-1" aria-hidden="true" tabindex="-1"></a>uint8 myState<span class="op">;</span></span>
- <span id="cb37-2"><a href="#cb37-2" aria-hidden="true" tabindex="-1"></a><span class="dt">void</span> stateCode<span class="op">(</span>uint8 event<span class="op">)</span> <span class="op">{</span></span>
- <span id="cb37-3"><a href="#cb37-3" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> <span class="op">(</span>event<span class="op">==</span>EVT_INIT<span class="op">)</span> <span class="op">{</span></span>
- <span id="cb37-4"><a href="#cb37-4" aria-hidden="true" tabindex="-1"></a> <span class="co">// ... do setup code</span></span>
- <span id="cb37-5"><a href="#cb37-5" aria-hidden="true" tabindex="-1"></a> myState <span class="op">=</span> FIRST_STATE<span class="op">;</span></span>
- <span id="cb37-6"><a href="#cb37-6" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span><span class="op">;</span></span>
- <span id="cb37-7"><a href="#cb37-7" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
- <span id="cb37-8"><a href="#cb37-8" aria-hidden="true" tabindex="-1"></a> <span class="co">// ... regular state machine code</span></span>
- <span id="cb37-9"><a href="#cb37-9" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
- <h2 id="messages">Messages</h2>
- <p>Now that we have an event/dispatcher system, we can also also use it
- to send information asychronously between tasks. For example, if a
- keyboard processing task needs to inform its siblings, it can create
- <em>messages</em> which can be injected into the event queue.</p>
- <p>Notice the change of terminology, where <em>message</em> indicates
- that the entry was created by code procesing, rather than an
- interrupt.</p>
- <h3 id="postmessage">PostMessage</h3>
- <p>We need a service routine to add messages to the event queue:
- <code>postMessage()</code>. This call is very smimilar
- <code>newEvent()</code> which should only be called from interrupts.</p>
- <figure>
- <img src="postMessage.png"
- alt="cd-eject task uses postMessage() to broadcast a message" />
- <figcaption aria-hidden="true">cd-eject task uses postMessage() to
- broadcast a message</figcaption>
- </figure>
- <div class="sourceCode" id="cb38"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb38-1"><a href="#cb38-1" aria-hidden="true" tabindex="-1"></a><span class="dt">void</span> postMessage<span class="op">(</span>uint8 message<span class="op">);</span></span></code></pre></div>
- <p>Notice that <code>postMessage</code> can’t return any information,
- because it’s not processed immediately; the <em>message</em> is added to
- the <em>event queue</em> to be processed at a later time.</p>
- <details>
- <summary>
- Reality Check
- </summary>
- In this tiny system, <code>postMessage()</code> is exactly the same as
- <code>newEvent()</code>. The reason that I use different names is that
- some more sophisticated operating systems require that these two
- functions are <em>not</em> identical.
- </details>
- <h3 id="sendmessage">SendMessage</h3>
- <p>In some cases we may want the sibling task to process the information
- immediately. This means:</p>
- <ul>
- <li>we can be assured that the sibling task has fully processed the
- information</li>
- <li>does not involve the dispatcher; but does require extra room on the
- stack</li>
- <li>we <em>could</em> get a return value from the sibling task; since
- it’s implemented as a subroutine, it’s allowed to return a value</li>
- </ul>
- <figure>
- <img src="sendMessage.png"
- alt="cd-eject task uses sendMessage() to call a sibling task" />
- <figcaption aria-hidden="true">cd-eject task uses sendMessage() to call
- a sibling task</figcaption>
- </figure>
- <p>This of course means that the prototype for task functions would
- change from</p>
- <div class="sourceCode" id="cb39"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb39-1"><a href="#cb39-1" aria-hidden="true" tabindex="-1"></a><span class="dt">void</span> taskCode<span class="op">(</span>uint8 event<span class="op">);</span></span>
- <span id="cb39-2"><a href="#cb39-2" aria-hidden="true" tabindex="-1"></a>to</span>
- <span id="cb39-3"><a href="#cb39-3" aria-hidden="true" tabindex="-1"></a>uint8 taskCode<span class="op">(</span>uint8 event<span class="op">);</span></span></code></pre></div>
- <p>The service routine to send a message like this would look like:</p>
- <div class="sourceCode" id="cb40"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb40-1"><a href="#cb40-1" aria-hidden="true" tabindex="-1"></a>uint8 SendMessage<span class="op">(</span>int8 taskPointer<span class="op">(</span>uint8<span class="op">),</span> uint8 message<span class="op">)</span> <span class="op">{</span></span>
- <span id="cb40-2"><a href="#cb40-2" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> taskPointer<span class="op">(</span>message<span class="op">);</span></span>
- <span id="cb40-3"><a href="#cb40-3" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
- <h3 id="usage">Usage</h3>
- <p>Why might one want to send a <em>message</em> between tasks?</p>
- <p>Suppose you have a rotary encoder, which sends quadrant signals,
- which need to be interpreted as <em>clockwise</em> and <em>counter
- clockwise</em>. You could have one task devoted to determining the
- direction of the knob (and <a href="#debouncing">debouncing</a>), and
- have it send clean EVT_CW and EVT_CCW <em>messages</em> to its sibgling
- tasks.</p>
- <p>Another possible use of <em>messages</em> is to create alternate
- souces of input. For example, a device which has a temperature set by
- “up/down” buttons on a front panel could receive the same controls from
- an infra-red remote, or even a serial port (perhaps for testing).</p>
- <p>And why <code>sendMessage()</code>? Perhaps you need to have a
- sibling task change states, or process information <em>before</em> you
- continue in your task. Imagine there is a slave WiFi chip that needs to
- be powered up <em>before</em> you send it a request….you could use
- <code>sendMessage()</code> to activate the power, and then continue,
- knowing that your peripheral is available.</p>
- <h2 id="ideas-for-tasks">Ideas for Tasks</h2>
- <p>I taught this coding technique at the college level, and a typical
- end-of-term assignment was to code up a simulated automobile
- entertainment system, which included about 6-10 concurrent tasks:</p>
- <ul>
- <li>task blue light to show bluetooth connectivity</li>
- <li>volume up/down sense and display</li>
- <li>radio channel select buttons
- <ul>
- <li>step single freq, or high-speed scan</li>
- </ul></li>
- <li>power on/off management</li>
- <li>remote control of volume/radio-select from the steering wheel (uart
- link)</li>
- <li>backlight for night-time viewing (auto sense from a light
- sensor)</li>
- <li>service-required LED, flashing</li>
- <li>a beeper to confirm keypresses (30ms) and alarm (700ms/100ms)</li>
- <li>a clock up/down set button
- <ul>
- <li>3 buttons: “set min/hr” and “+” and “-”</li>
- </ul></li>
- </ul>
- <p>Each list item above corresponds to one task. My students were able
- to code this up with about 35k of source, which compiles to about 8k of
- code and 256B of RAM (on an STM32)</p>
- <h2 id="more-state-machines">More State Machines</h2>
- <h3 id="radio-tuner">Radio Tuner</h3>
- <p>Suppose we have a <code>+</code> and <code>-</code> button for tuning
- a radio. A press of either will move the radio frequency by one step. If
- the button is held for more than 0.5 seconds, the radio should change to
- a rapid scroll through all available frequencies.</p>
- <p>Suppose we have events coming at 100msec, and that the radio
- frequencies are 88.7 up to 107.9, with steps at 0.1. And finally,
- suppose there are 5 available events:</p>
- <div class="sourceCode" id="cb41"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb41-1"><a href="#cb41-1" aria-hidden="true" tabindex="-1"></a><span class="kw">enum</span> <span class="op">{</span>EVT_NONE<span class="op">,</span> EVT_TICK<span class="op">,</span> EVT_UP_PRESS<span class="op">,</span> EVT_UP_RELEASE<span class="op">,</span> EVT_DN_PRESS<span class="op">,</span> EVT_DN_RELEASE<span class="op">};</span></span></code></pre></div>
- <figure>
- <img src="radio_tuner.png" alt="state machine for radio tuner" />
- <figcaption aria-hidden="true">state machine for radio
- tuner</figcaption>
- </figure>
- <div class="sourceCode" id="cb42"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb42-1"><a href="#cb42-1" aria-hidden="true" tabindex="-1"></a><span class="dt">const</span> <span class="dt">float</span> MIN_FREQ<span class="op">=</span><span class="fl">88.7</span><span class="op">,</span> MAX_FREQ<span class="op">=</span><span class="fl">107.9</span><span class="op">,</span> INC_FREQ<span class="op">=</span><span class="dv">0</span><span class="er">.1</span><span class="op">;</span></span>
- <span id="cb42-2"><a href="#cb42-2" aria-hidden="true" tabindex="-1"></a><span class="dt">float</span> radioFreq <span class="op">=</span> MIN_FREQ<span class="op">;</span></span>
- <span id="cb42-3"><a href="#cb42-3" aria-hidden="true" tabindex="-1"></a>uint16 radioTimer <span class="op">=</span> <span class="dv">0</span><span class="op">;</span></span>
- <span id="cb42-4"><a href="#cb42-4" aria-hidden="true" tabindex="-1"></a><span class="dt">const</span> uint16 radioTimeout <span class="op">=</span> <span class="dv">5</span><span class="op">;</span> <span class="co">// 0.5 seconds</span></span>
- <span id="cb42-5"><a href="#cb42-5" aria-hidden="true" tabindex="-1"></a></span>
- <span id="cb42-6"><a href="#cb42-6" aria-hidden="true" tabindex="-1"></a><span class="kw">enum</span> <span class="op">{</span>IDLE<span class="op">,</span> FREQ_UP<span class="op">,</span> FREQ_UP_FAST<span class="op">,</span> FREQ_DN<span class="op">,</span> FREQ_DN_FAST<span class="op">};</span></span>
- <span id="cb42-7"><a href="#cb42-7" aria-hidden="true" tabindex="-1"></a>uint8 state <span class="op">=</span> IDLE<span class="op">;</span></span>
- <span id="cb42-8"><a href="#cb42-8" aria-hidden="true" tabindex="-1"></a><span class="dt">void</span> radioTask<span class="op">(</span>uint8 event<span class="op">)</span> <span class="op">{</span></span>
- <span id="cb42-9"><a href="#cb42-9" aria-hidden="true" tabindex="-1"></a> <span class="cf">switch</span><span class="op">(</span>state<span class="op">)</span> <span class="op">{</span></span>
- <span id="cb42-10"><a href="#cb42-10" aria-hidden="true" tabindex="-1"></a> <span class="cf">case</span> IDLE<span class="op">:</span></span>
- <span id="cb42-11"><a href="#cb42-11" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> <span class="op">(</span>event <span class="op">==</span> EVT_UP_PRESS<span class="op">)</span> <span class="op">{</span></span>
- <span id="cb42-12"><a href="#cb42-12" aria-hidden="true" tabindex="-1"></a> radioFreq <span class="op">+=</span> INC_FREQ<span class="op">;</span></span>
- <span id="cb42-13"><a href="#cb42-13" aria-hidden="true" tabindex="-1"></a> checkRadioFreq<span class="op">();</span></span>
- <span id="cb42-14"><a href="#cb42-14" aria-hidden="true" tabindex="-1"></a> radioTimer <span class="op">=</span> radioTimeout<span class="op">;</span></span>
- <span id="cb42-15"><a href="#cb42-15" aria-hidden="true" tabindex="-1"></a> state <span class="op">=</span> FREQ_UP<span class="op">;</span></span>
- <span id="cb42-16"><a href="#cb42-16" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
- <span id="cb42-17"><a href="#cb42-17" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> <span class="op">(</span>event <span class="op">==</span> EVT_DN_PRESS<span class="op">)</span> <span class="op">{</span></span>
- <span id="cb42-18"><a href="#cb42-18" aria-hidden="true" tabindex="-1"></a> radioFreq <span class="op">-=</span> INC_FREQ<span class="op">;</span></span>
- <span id="cb42-19"><a href="#cb42-19" aria-hidden="true" tabindex="-1"></a> checkRadioFreq<span class="op">();</span></span>
- <span id="cb42-20"><a href="#cb42-20" aria-hidden="true" tabindex="-1"></a> radioTimer <span class="op">=</span> radioTimeout<span class="op">;</span></span>
- <span id="cb42-21"><a href="#cb42-21" aria-hidden="true" tabindex="-1"></a> state <span class="op">=</span> FREQ_DN<span class="op">;</span></span>
- <span id="cb42-22"><a href="#cb42-22" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
- <span id="cb42-23"><a href="#cb42-23" aria-hidden="true" tabindex="-1"></a> <span class="cf">break</span><span class="op">;</span></span>
- <span id="cb42-24"><a href="#cb42-24" aria-hidden="true" tabindex="-1"></a> <span class="cf">case</span> FREQ_UP<span class="op">:</span></span>
- <span id="cb42-25"><a href="#cb42-25" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> <span class="op">(</span>event <span class="op">==</span> EVT_UP_RELEASE<span class="op">)</span> </span>
- <span id="cb42-26"><a href="#cb42-26" aria-hidden="true" tabindex="-1"></a> state <span class="op">=</span> IDLE<span class="op">;</span></span>
- <span id="cb42-27"><a href="#cb42-27" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> <span class="op">(</span>event <span class="op">==</span> EVT_TICK<span class="op">)</span> <span class="op">{</span></span>
- <span id="cb42-28"><a href="#cb42-28" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> <span class="op">(--</span>radioTimer <span class="op">==</span> <span class="dv">0</span><span class="op">)</span> </span>
- <span id="cb42-29"><a href="#cb42-29" aria-hidden="true" tabindex="-1"></a> state <span class="op">=</span> FREQ_UP_FAST<span class="op">;</span></span>
- <span id="cb42-30"><a href="#cb42-30" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
- <span id="cb42-31"><a href="#cb42-31" aria-hidden="true" tabindex="-1"></a> <span class="cf">break</span><span class="op">;</span></span>
- <span id="cb42-32"><a href="#cb42-32" aria-hidden="true" tabindex="-1"></a> <span class="cf">case</span> FREQ_UP_FAST<span class="op">:</span></span>
- <span id="cb42-33"><a href="#cb42-33" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> <span class="op">(</span>event <span class="op">==</span> EVT_TICK<span class="op">)</span> <span class="op">{</span></span>
- <span id="cb42-34"><a href="#cb42-34" aria-hidden="true" tabindex="-1"></a> radioFreq <span class="op">+=</span> INC_FREQ<span class="op">;</span></span>
- <span id="cb42-35"><a href="#cb42-35" aria-hidden="true" tabindex="-1"></a> checkRadioFreq<span class="op">();</span></span>
- <span id="cb42-36"><a href="#cb42-36" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
- <span id="cb42-37"><a href="#cb42-37" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> <span class="op">(</span>event <span class="op">==</span> EVT_UP_RELEASE<span class="op">)</span> </span>
- <span id="cb42-38"><a href="#cb42-38" aria-hidden="true" tabindex="-1"></a> state <span class="op">=</span> IDLE<span class="op">;</span></span>
- <span id="cb42-39"><a href="#cb42-39" aria-hidden="true" tabindex="-1"></a> <span class="cf">break</span><span class="op">;</span></span>
- <span id="cb42-40"><a href="#cb42-40" aria-hidden="true" tabindex="-1"></a> <span class="co">// and the same for FREQ_DN and FREQ_DN_FAST</span></span>
- <span id="cb42-41"><a href="#cb42-41" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
- <span id="cb42-42"><a href="#cb42-42" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span>
- <span id="cb42-43"><a href="#cb42-43" aria-hidden="true" tabindex="-1"></a></span>
- <span id="cb42-44"><a href="#cb42-44" aria-hidden="true" tabindex="-1"></a><span class="co">// manage rollover</span></span>
- <span id="cb42-45"><a href="#cb42-45" aria-hidden="true" tabindex="-1"></a><span class="dt">void</span> checkRadioFreq<span class="op">()</span> <span class="op">{</span></span>
- <span id="cb42-46"><a href="#cb42-46" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> <span class="op">(</span>radioFreq <span class="op">></span> MAX_FREQ<span class="op">)</span></span>
- <span id="cb42-47"><a href="#cb42-47" aria-hidden="true" tabindex="-1"></a> radioFreq <span class="op">=</span> MIN_FREQ<span class="op">;</span></span>
- <span id="cb42-48"><a href="#cb42-48" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> <span class="op">(</span>radioFreq <span class="op"><</span> MIN_FREQ<span class="op">)</span></span>
- <span id="cb42-49"><a href="#cb42-49" aria-hidden="true" tabindex="-1"></a> radioFreq <span class="op">=</span> MAX_FREQ<span class="op">;</span></span>
- <span id="cb42-50"><a href="#cb42-50" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
- <p>This may seem like a lot of code, but it compiles to something quite
- small. It is non-blocking, and easily maintained and modified. And it
- works.</p>
- </body>
- </html>
|