소스 검색

Major release, lots of graphics, the text flows better now

Pat Beirne 2 주 전
커밋
a842113249
44개의 변경된 파일3825개의 추가작업 그리고 0개의 파일을 삭제
  1. 1858 0
      CooperativeMultitasking.md
  2. 22 0
      beer_vat.dot
  3. BIN
      beer_vat.png
  4. BIN
      blb.jpg
  5. 54 0
      build/crt1.c
  6. 203 0
      build/demo.stm32f0xx.c
  7. 206 0
      build/demo.stm32f1xx.c
  8. 25 0
      build/makefile
  9. 45 0
      build/stm32.lnk
  10. 564 0
      build/stm32f030.h
  11. 632 0
      build/stm32f10x.h
  12. BIN
      dispatch1.jpg
  13. BIN
      dispatch2.jpg
  14. 14 0
      door.dot
  15. BIN
      door.png
  16. 15 0
      door2.dot
  17. BIN
      door2.png
  18. 11 0
      fridge_state.dot
  19. BIN
      fridge_state.png
  20. 10 0
      led_state.dot
  21. BIN
      led_state.png
  22. 10 0
      led_state_simple.dot
  23. BIN
      led_state_simple.png
  24. 25 0
      makefile
  25. BIN
      postMessage.png
  26. 19 0
      radio_tuner.dot
  27. BIN
      radio_tuner.png
  28. 10 0
      rtb_state.dot
  29. BIN
      rtb_state.png
  30. 11 0
      rtb_state_2.dot
  31. BIN
      rtb_state_2.png
  32. 10 0
      rtb_state_3.dot
  33. BIN
      rtb_state_3.png
  34. 14 0
      rtb_state_4.dot
  35. BIN
      rtb_state_4.png
  36. 11 0
      rtb_state_5.dot
  37. BIN
      rtb_state_5.png
  38. BIN
      sendMessage.png
  39. 20 0
      vending.dot
  40. BIN
      vending.png
  41. 22 0
      window_complex.dot
  42. BIN
      window_complex.png
  43. 14 0
      window_state.dot
  44. BIN
      window_state.png

+ 1858 - 0
CooperativeMultitasking.md

@@ -0,0 +1,1858 @@
+---
+title: Cooperative Multitasking
+author: Pat Beirne 
+email: <patb@pbeirne.com>
+date: 2025/01/15
+license: MIT
+---
+
+# Part 1: Cooperative Multitasking
+
+<!-- ## Introduction -->
+
+Small microcontrollers are often required to handle several 
+tasks, sometimes with overlapping
+phases. This paper will lead you through the creation of a small 
+***operating system*** which can be implemented on a tiny microcontroller, 
+without the fancy time-slicing that is 
+offered by sophisticated operating systems 
+like [Linux](https://linux.org) and [FreeRTos](https://freertos.org).
+
+The core of *this* technique is covered in 
+[Part 1](#part-1-cooperative-multitasking)
+of this paper. 
+If you just want to see how it all comes together, 
+jump to [Final Implementaion](#final-implementation). 
+
+[Part 2](#part-2) contains enhancements and variations, 
+probably useful reading if you decide to adopt
+this programming technique in your projects.
+
+## Intro
+
+### Abstract
+
+This paper will take you through the creation of a tiny *operating system* 
+that can be implemented on a small microcontroller.
+
+### Audience
+
+These techniques can be applied by anyone with experience in ***C***, 
+***Python*** 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. 
+
+> The ***Reality Check*** dropdowns in this article provide extra, often
+> practical supplementary reading.
+
+<details><summary>Reality Check</summary>
+The technique described here is also called ***event driven programming***.
+
+This technique was used in the original Window (1995),
+including some of the system calls: `sendMessage(), postMessage() and setTimer()`.
+
+This event-driven technique is applicable to a whole host 
+of small microcontrollers, including
+
+- MSP430
+- Cortex M0, M0+ (SAM, STM32, PY32, Cypress, Kinetis, HT32, XMC, LPC81x)
+- AtMega, AtTiny
+- 8051 (SiliconLabs, Nuvoton, HT85)
+- RL78 (Renesas)
+- Pic 12/14/16
+- Risc (ch32v)
+- STM8
+
+*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.)*
+
+</details>
+
+## The Problem
+
+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?
+
+## First Page
+
+Let's look an a pseudocode overview of what we want to do:
+
+```C
+void initialize(void) {
+  setup_hardware();
+  setup_interrupts();
+}
+
+INTERRUPT void irq(void) {
+  create_event();
+  acknowledge_interrupt();
+}
+
+void main(void) {
+  initialize();
+
+  while (1) {
+    if (event) {
+      flashLedTask(event);
+      respondToButtonTask(event);
+    }
+  }
+}
+```
+
+That's about it. 
+
+Of course, we will need to write the code for `setup_hardware()` 
+and `setup_interrupts()`. 
+And `flashLedTask()` and `respondToButtonTask()`. And create the *magic* 
+that allows `event` information to flow.
+Don't worry, it will all be laid out in the following pages. 
+
+If you're concerned with complexity, feel free to jump ahead to the 
+[final implementation](#final-implementation) page to see real, tested, code.
+
+Between here and there, I'll walk you step-by-step through building 
+the structure and implementation.
+
+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.
+
+## Interrupts
+
+In order to have a responsive system, it would make sense to use the 
+*interrupt* capabilities of these small micrcontrollers.
+
+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.
+
+We also need to keep track of time.....so let's hook up a system timer to another interrupt.
+
+Each interrupt causes the execution of *interrupt handler* code. For this project, it might look
+somthing like this:
+
+```C
+enum {EVT_NONE, EVT_TICK, EVT_BUTTON};
+
+INTERRUPT timer_isr(void) {
+  newEvent(EVT_TICK);
+}
+
+INTERRUPT button_isr(void) {
+  newEvent(EVT_BUTTON);  // see notes about button bounce and acknowledgement
+}
+```
+
+The interrupt handlers are both very simple. They just create a unique *event* 
+and let the system 
+know about it. Next, let's talk about *events*.
+
+<details>
+<summary>Reality check</summary>
+
+In some microcontrollers, interrupts must be *acknowledged*. 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 *not*
+need an ack, so the above code example for `timer_isr()` is complete.
+
+When a pushbutton or switch is connected to a microcontroller, 
+the first bit of activity will
+cause the interrupt and execute the `button_isr()` code. 
+However, real buttons produce about 5msec of 
+*bounce* and this will cause subsequent interrupts unless 
+they are somehow filtered out. There are
+lots of ways to handle *bounce*, 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.
+
+As a general rule, *input* pins should be observed either by 
+interrupt service routine (ISR), or scanned
+periodically by the timer ISR. *Outputs* should be 
+controlled in the task code, which we'll see below.
+
+</details>
+
+## Events
+
+An *event* in this context is a small bit of information that appears 
+asynchronously in the system.
+Implemented, it can be a *byte*, or an *int* or an even larger structure. 
+But in these small microcontrollers, let's use a *byte*.
+
+```C
+volatile uint8 event;
+```
+
+These *events* will be created in interrupt level of the code, 
+and processed at the main level. We use the `event` object to send
+information between these levels, so we have to mark it 
+[`volatile`](https://en.wikipedia.org/wiki/Volatile_(computer_programming)).
+
+In this paper, the word *message* and *event* are equivalent. 
+*Message* has the sense of 
+"a bit of communication from another part of the program" 
+and *event* has the sense of "something
+external just happened". 
+At this point in the design, they both mean the same thing.
+
+By convention, let's use *zero* to indicate the absence of an event/message, 
+and *non-zero* to represent
+an event/message.
+
+```C
+volatile uint8 event;
+enum {EVT_NONE, EVT_TICK, EVT_BUTTON}; // 0,1,2
+```
+
+<details>
+<summary>Reality check</summary>
+
+<hr>
+For this project, let's suppose the timer ticks happen every 10ms (100 per second).
+<hr>
+
+</details>
+
+So now we start to get an idea of what kind of information 
+the *interrupt handler* code generates.
+Now let's look at how that information gets sent to the rest of the code: 
+`newEvent()`.
+
+## newEvent()
+
+Here's a block diagram of the message flow ![](dispatch1.jpg)
+
+How do we send the information (events/messages) from the interrupt 
+service routine to the 
+`main()` code? We used shared memory.
+
+One way would be to have a global `volatile uint8` location into which 
+we drop the *event* 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?
+
+It makes more sense to have an array: `volatile uint8 events[NUM_EVENTS]` 
+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.
+
+At the beginning, before anything happens, we need to make sure the 
+`events[]` is full of zeros (EVT_NONE), indicating that it's empty.
+
+The `newEvent(evt)` routine simply adds the `evt` to the array `events[]`.
+
+Something like this might work:
+
+```C
+void newEvent(uint8r evt) {
+  static unsigned uint8 nextEvent;
+  events[nextEvent++] = evt;
+  if (nextEvent == NUM_EVENTS)
+    nextEvent = 0;
+}
+```
+<details>
+<summary>Reality check</summary>
+
+There is a problem with the above code. What happens if a `key_isr()` 
+is running, and is halfway through its
+call to `newEvent()` when a `timer_isr()` happens, 
+and reenters the `newEvent()` routine. This will get
+really messed up. So we need to wrap *this particular* code in a critical section. 
+
+Here's a more realistic version:
+
+```C
+void newEvent(char evt) {
+  static unsigned char nextEvent;   // keep track of where we are in queue
+  disable_irq();                    // critical section
+  events[nextEvent++] = evt;        // insert event into queue
+  if (nextEvent == NUM_EVENTS)      // loop back to the start of queue
+    nextEvent = 0;
+  enable_irq();           // end critical section, probably <100us of blockage
+}
+```
+</details>
+
+In the next section, we'll show how the `main()` code pulls the events 
+out of the array, and leaves an EVT_NONE in its place.
+
+
+## Dispatch
+
+The `main()` code can simply watch the `events[]` to see when an entry 
+goes non-zero (!=EVT_NONE). When that
+happens, `main()` will pull out the *event/message* from the array, 
+and call the task subroutines. In this case,
+`flashLedTask()` and `respondToButtonTask()`.
+
+To see the real code for the dispatcher, [jump ahead](#dispatcher-details).
+
+<details><summary>Terminology</summary>
+
+The `main()` code illustrated here calls the tasks 
+as subroutines, sending each one a copy of the event number. 
+
+As I mentioned earlier, *events* can also be refered to as *messages*. 
+We can also refer to this process "sending
+a message to the task"
+
+So, for this paper, these are equivalent
+
+- calling a task subroutine with the event information
+- sending a message to a task
+
+</details>
+
+![Call task routines with event](dispatch2.jpg)
+
+<details>
+
+<summary>Reality check</summary>
+
+It may seem wasteful to send *all* events to *all* 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.
+
+More sophisticated event systems do, in fact, filter and sort events. 
+For example, Windows only sends `MOUSE_MOVE` 
+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.
+
+</details>
+
+## Tasks
+
+In this environment, the code that impliments a *task* is simply a 
+subroutine that accepts an *event/message*.
+
+```C
+void taskCode(uint8 event) {
+  ... process the event information ...
+}
+```
+
+The subroutine should be designed to flow-through as quickly as possible, 
+without any pauses/waits/delays.
+
+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 [state machine](#state-machine).
+
+## State Machine
+
+A *state machine* in this environment is a subroutine which can be 
+called many times, and it remembers
+in which *state* 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.
+
+Here's a state diagram for the task which reacts to a button press by
+flashing an LED.
+
+![simple state diagram](rtb_state.png)
+
+How does the *state code* remember what state it's in between invocations?
+We can use a `static` variable. A `static` is stored in main memory (*not 
+on the stack*), persists between calls and is initialized to zero.
+
+The above diagram can be implemented in this code:
+
+```C
+enum {RTB_IDLE, RTB_ON};          // RTB_IDLE=0, RTB_ON=1
+static uint8  rtbState      = RTB_IDLE;
+static uint16 rtbTimerCount = 0;
+const  uint16 TIMER_LIMIT   = 150;
+
+void respondToButtonTask(uint8 evt) {
+  switch(rtbState) {
+    case RTB_IDLE:
+      if (evt == EVT_BUTTON) {
+        rtbState = RTB_ON;
+        rtbTimerCount = TIMER_LIMIT;
+        gpio_set(LED, ON);
+      }
+      break;
+    case RTB_ON:
+      if (evt == EVT_TICK) {
+        if (--rtbTimerCount == 0) {
+          gpio_set(LED, OFF);
+          rtbState = RTB_IDLE;
+        }
+      }
+      break;
+    }
+}
+```
+
+Each time this routine is called, it checks the *event* that it was given, 
+and sometimes processes it. And sometimes it changes state.
+
+Here's a few things to notice:
+
+- The code *flows through*. It does not stop or wait for anything. 
+- Each arrow in the net diagram corresponds to a phrase in the code. The *tail* of the arrow corresponds
+to an `if` statement
+- The code ignores *events* that are not relevant.
+
+<details>
+<summary>Reality check</summary>
+
+The *state* variable must be `static` or in the global memory space. 
+*NEVER put a state variable on the stack!* i.e. as a local variable.
+
+The tick counter could equally well count up from zero to threshold. See the
+discussion about [shared timers](#timers-as-a-resource)
+</details>
+
+
+## Another State Machine
+
+The other task of this project simply flashes the LED on and off.
+
+
+![](led_state_simple.png)
+
+The code for this might be:
+
+
+```C
+enum {LED_ON, LED_OFF};
+static uint8 ledState       = LED_OFF;
+static uint16 ledTimerCount = 0;
+const  uint16 LED_ON_TIME   = 100;
+const  uint16 LED_OFF_TIME  = 100;
+
+void ledTask(uint8 evt) {
+  switch(ledState) {
+    case LED_OFF:
+      if (evt == EVT_TICK) {
+        if (--ledTimerCount == 0) {
+          gpio_set(LED, ON);
+          ledTimerCount = LED_ON_TIME;
+          ledState = LED_ON;
+        }
+      }
+      break;
+    case LED_ON:
+      if (evt == EVT_TICK) {
+        if (--ledTimerCount == 0) {
+          gpio_set(LED, OFF);
+          ledTimerCount = LED_OFF_TIME;
+          ledState = LED_OFF;
+        }
+      }
+      break;
+    }
+}
+```
+
+<details><summary>Discussion</summary>
+
+Perhaps a more accurate state diagram might be:
+
+![](led_state.png)
+
+As you build the code from the diagram, I *strongly* suggest that you use
+a `switch(currentState) ....case STATE_1` approach to the task routine.
+If you try and code it with multiple `if(currentState==STATE_1)`, you will
+find yourself in a tangle of spaghetti; and if you forget a critical `else`, 
+nothing will work as you expect.
+
+```C
+if (state==1) {
+  if (event = EVT_1) {
+    P3OUT = 27;	// turn on some hardware
+    state = 2;	// and change state
+  }
+} 
+if (state==2) {	// <<<----- BAD BAD
+  P3OUT = 14;
+  state = 3;    // <<<--- dont' change state twice in the same event
+}
+...
+```
+rather
+```C
+switch(state) {
+  case 1:
+    if (event = EVT_1) {
+      P3OUT = 27;   // turn on some hardware
+      state = 2;
+    }
+    break;
+  case 2:
+    P3OUT = 14;
+    state = 3;
+  }
+  ...
+}
+```
+
+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.
+
+</details>
+
+
+
+## Dispatcher Details
+
+The last piece of the puzzle is the `main()` code which observes the `events[]` array and calls the tasks.
+
+The `events[]` 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.
+
+```C
+void main() {
+  initialize();
+
+  while(1) {
+    int i;
+    for (i=0; i<NUM_EVENTS; i++) {
+      while (events[i]==EVT_NONE)
+        {}
+      ledTask(events[i]);
+      respondToButtonTask(events[i]);
+      events[i] = EVT_NONE;
+    }
+  }
+}
+```
+
+Each non-zero event is "sent" to each task.....by means of a subroutine call. 
+Once all the tasks have 
+been invoked, the `event` is thrown away and its slot in the array is set to zero.
+
+<details>
+<summary>Reality check</summary>
+
+The `main()` code above needs to run with interrupts enabled.
+
+There's lots of ways to structure the 'wait for event' loop. 
+For example, when you detect that the 
+`events[]` 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.
+
+</details>
+
+
+## Final Implementation
+
+Let's put all of the above together, for an SMT32F Cortex M0, in *C* code.
+
+```C
+/***** declarations ****/
+#define NUM_EVENTS 10
+volatile uint8 events[NUM_EVENTS];
+
+void newEvent(uint8 e);
+void ledTask(uint8 evt);
+void respondToButtonTask(uint8 evt);
+
+/********** interrupts **************/
+
+void timer_isr(void) {
+  newEvent(EVT_TICK);
+  // this interrupt is auto-ack'd
+}
+
+void button_isr(void) {
+  newEvent(EVT_BUTTON);
+  EXTI->PR |= KEY_IRQ_ACK_MASK;    // the hardware requires that we acknowledge 
+}
+
+/** newEvent
+ * add the event to the event queue
+ * wrapped in critical section
+ * 
+ * @param the event
+ */
+void newEvent(uint8 e) {
+  static uint8 nextEvent;
+  dint(); // critical section
+  events[nextEvent++] = e;
+  if (nextEvent==NUM_EVENTS)
+    nextEvent = 0;
+  eint();
+}
+
+/****** main() and dispatcher *********/
+void main(void) {
+
+  eint();
+
+  // dispatcher loop
+  while(1) {
+    int j;
+    for (j=0; j<NUM_EVENTS; j++) {
+       while (events[j]==EVT_NONE)
+         {}
+       ledTask(events[j]);
+       respondToButtonTask(events[j]);
+       events[j] = EVT_NONE;
+    }
+  }
+}
+
+/*********** task code, with states ************/
+enum {LED_ON, LED_OFF};
+enum {RTB_IDLE, RTB_ON};    // states
+static uint8  rtbState      = RTB_IDLE;
+static uint16 rtbTimerCount = 0;
+const  uint16 LED_ON_TIME   = 150;
+
+void respondToButtonTask(uint8 evt) {
+  switch(rtbState) {
+    case RTB_IDLE:
+      if (evt == EVT_BUTTON) {
+        rtbTimerCount = 0;
+        rtbState      = RTB_ON;
+        gpio_set(LED, LED_ON);
+      }
+      break;
+    case RTB_ON:
+      if (evt == EVT_TICK) {
+        if (++rtbTimerCount > LED_ON_TIME) {
+          gpio_set(LED, LED_OFF);
+          rtbState = RTB_IDLE;
+        }
+      }
+      break;
+    }
+}
+
+const  uint16 LED_ON_TIME   = 150;
+const  uint16 LED_OFF_TIME  = 50;
+static uint8 ledState       = LED_OFF;
+static uint16 ledTimerCount = 0;
+
+void ledTask(uint8 evt) {
+  switch(ledState) {
+    case LED_OFF:
+      if (evt == EVT_TICK) {
+        if (++ledTimerCount > LED_OFF_TIME) {
+          gpio_set(LED2, LED_ON);
+          ledTimerCount = 0;
+          ledState      = LED_ON;
+        }
+      }
+      break;
+    case LED_ON:
+      if (evt == EVT_TICK) {
+        if (++ledTimerCount > LED_ON_TIME) {
+          gpio_set(LED2, LED_OFF);
+          ledTimerCount = 0;
+          ledState      = LED_OFF;
+        }
+      }
+      break;
+    }
+}
+
+```
+
+This is the end of the main presentation. With the above techniques, you
+can make a tiny microprocessor appear to multitask.
+
+For further tips, read the next section. The interesting topics are:
+
+- [more state code examples](#state-machine-examples)
+  - with implementation
+- [how to initialize a state machine](#state-machine-initialization)
+- [inter-task communication](#messages)
+- [unified timers](#timers)
+
+<details><summary>Reality Check</summary>
+
+What's missing from this code sample is the initialization of the 
+hardware........some kind of `setup()` 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 [here](build/demo.c)
+
+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 *dozens* of tasks, even on sub-$1 processors.
+</details>
+
+# Part 2
+
+## Variations
+
+<style>
+.imageleft {float: left;}
+.imageright {float: right;}
+</style>
+
+Now that the infrastructure is in place, it's easy to expand or modify 
+the code for changes in the project definition.
+
+For example, suppose we want the `respondToButtonTask()` to restart the 
+LED timer on each key press:
+
+![](rtb_state_2.png){.imageleft}
+
+```C
+void rtbTask(uint8 event) {
+  switch(rtbState) {
+    case RTB_IDLE:
+      if (event == EVT_BUTTON) {
+        gpio_set(LED, LED_ON);
+        rtbState = RTB_ON;
+        rtbTimer = RTB_TIMEOUT;
+      }
+      break;
+    case RTB_ON:
+      if (event == EVT_BUTTON) { 
+        rtbTimer = RTB_TIMEOUT;
+      }
+      if (event == EVT_TICK) {
+        if (--rtbTimer == 0) {
+          gpio_set(LED, LED_OFF);
+          rtbState = RTB_IDLE;
+        }
+      }
+      break;
+  }
+}
+```
+
+<hr>
+
+Or have a 2nd press of the button cause the LED to extinguish early:
+
+![](rtb_state_3.png){.imageleft}
+
+```C
+void rtbTask(uint8 event) {
+  switch(rtbState) {
+    case RTB_IDLE:
+      if (event == EVT_BUTTON) {
+        gpio_set(LED, LED_ON);
+        rtbState = RTB_ON;
+        rtbTimer = RTB_TIMEOUT;
+      }
+      break;
+    case RTB_ON:
+      if (event == EVT_BUTTON) { 
+        gpio_set(LED, LED_OFF);
+        rtbState = RTB_IDLE;
+      }
+      if (event == EVT_TICK) {
+        if (--rtbTimer == 0) {
+          gpio_set(LED, LED_OFF);
+          rtbState = RTB_IDLE;
+        }
+      }
+      break;
+  }
+}
+```
+
+<hr>
+
+
+How about have the button start a flash sequence, and a 2nd press stops
+it: (see also [substates](#substates))
+
+![](rtb_state_4.png){.imageright}
+
+```C
+void rtbTask(uint8 event) {
+  switch(rtbState) {
+    case RTB_IDLE:
+      if (event == EVT_BUTTON) {
+        gpio_set(LED, LED_ON);
+        rtbState = RTB_ON;
+        rtbTimer = RTB_TIMEOUT;
+      }
+      break;
+    case RTB_ON:
+      if (event == EVT_BUTTON) { 
+        gpio_set(LED, LED_OFF);
+        rtbState = RTB_IDLE;
+      }
+      if (event == EVT_TICK) {
+        if (--rtbTimer == 0) {
+          gpio_set(LED, LED_OFF);
+          rtbTimer = RTB_FLASH_TIME;
+          rtbState = RTB_OFF;
+        }
+      }
+      break;
+    case RTB_OFF:
+      if (event == EVT_BUTTON) { 
+        gpio_set(LED, LED_OFF);
+        rtbState = RTB_IDLE;
+      }
+      if (event == EVT_TICK) {
+        if (--rtbTimer == 0) {
+          gpio_set(LED, LED_ON);
+          rtbTimer = RTB_FLASH_TIME;
+          rtbState = RTB_ON;
+        }
+      }
+      break;
+  }
+}
+```
+
+<hr>
+
+Each of these diagrams corresponds to trivial changes in the state code.
+
+
+## Working with Arduino
+
+If you're from the Arduino world, you have no doubt seen the similarity
+between this OS plan and the Arduino infrastructure.
+
+```C
+setup() 
+  {}    // initialize and start up the devices and services
+
+loop()
+  {}    // code for continuous operation
+```
+
+You can certainly merge this paper's operating system into the Arduino
+architecture:
+
+```C
+char events[];
+
+interrupt_service_routines() {
+  newEvent(evt); // insert events/messages into events[]
+}   
+
+static int nextEvent;
+void newEvent(char evt) 
+  {} // same as above; put evt into the events[]
+
+setup()
+  {}
+
+loop() {
+  static int nextTaskEvent;
+  if (events[nextTaskEvent]!=EVT_NONE) {        // check for non-zero events[],
+    task1(events[nextTaskEvent]);     // and call the task routines
+    task2(events[nextTaskEvent]);
+    events[nextTaskEvent) = EVT_NONE; // free the slot, fill with 0
+    if (++nextTaskEvent > NUM_EVENTS)
+      nextTaskEvent = 0;              // and loop through the array
+  }
+}  
+
+void task1(char evt) {}
+void task2(char evt) {}
+    
+```
+
+
+## Tasks
+
+The fundamental guideline for tasks is that *they do not stop*. 
+Control flows through and out
+the bottom, returning to the dispatcher quickly.
+
+<details><summary>Reality Check</summary>
+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.
+
+If a task occasionally runs into the 10's of msec, the event queue will 
+handle buffering the events until they can be processed.
+
+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>
+
+
+## State Machine Examples
+
+### Car Window
+
+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.
+
+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.
+
+Here's a possible state diagram.
+![window motor control](window_state.png)
+
+And here's the matching code. Note the correspondence between the diagram
+and the code: arrows leaving a state correspond to an `if()` phrase.
+
+```C
+enum {WINDOW_IDLE, WINDOW_UP, WINDOW_DOWN};
+static uint8 windowState = WINDOW_IDLE;
+
+void windowTask(uint8 evt) {
+  switch(windowState) {
+    case WINDOW_IDLE:
+      if (evt == EVT_BUTTON_UP) {
+        gpio_set(MOTOR, UP);
+        windowState = WINDOW_UP;
+      }
+      if (evt == EVT_BUTTON_DOWN) {
+        gpio_set(MOTOR, DOWN);
+        windowState = WINDOW_DOWN;
+      }
+      break;
+    case WINDOW_UP:
+      if (evt == EVT_MOTOR_SENSE || 
+          evt == EVT_BUTTON_RELEASE) {
+        gpio_set(MOTOR, OFF);
+        windowState = WINDOW_IDLE;
+      }
+      break;
+    case WINDOW_DOWN:
+      if (evt == EVT_MOTOR_SENSE || 
+          evt == EVT_BUTTON_RELEASE) {
+        gpio_set(MOTOR, OFF);
+        windowState = WINDOW_IDLE;
+      }
+  }
+}
+```
+
+
+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). 
+
+Here is a possible state diagram. Notice the significant re-use of
+code from the previous version. 
+
+![window control with auto open/close, (_ma) motor activated (_ms) motor stopped](window_complex.png)
+
+<details><summary>Reality Check</summary>
+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. 
+
+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:
+
+```
+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:
+      ...
+      ...
+  }
+}
+```
+</details>
+
+
+### Fridge Door
+
+A simple state machine can control the interior light of a fridge. Here's the use-case:
+
+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.
+
+Here is the state diagram.
+
+![](fridge_state.png)
+
+And here is the corresponding code. 
+
+```C
+enum {FRIDGE_CLOSED, FRIDGE_OPEN, FRIDGE_BEEPING};
+uint8  fridgeState;
+uint16 fridgeTimer;
+const uint16 FRIDGE_OPEN_LIMIT = 9000; // 90 seconds at 10msec tick
+void fridgeTask(char event) {
+  switch(fridgeState) {
+    case FRIDGE_CLOSED:
+      if (event == EVT_OPEN) {
+        set_io(LIGHT, ON);
+        fridgeTimer = FRIDGE_OPEN_LIMIT;
+        fridgeState = FRIDGE_OPEN;
+      }
+      break;
+    case FRIDGE_OPEN:
+      if (event == EVT_CLOSE) {
+        set_io(LIGHT, OFF);
+        fridgeState = FRIDGE_CLOSED;
+      }
+      if (evt == EVT_TICK) {
+        if (--fridgeTimer == 0) {
+          set_io(ALARM, ON);
+          fridgeState = FRIDGE_BEEPING;
+        }
+      }
+      break;
+    case FRIDGE_BEEPING:
+      if (event == EVT_CLOSE) {
+        set_io(ALARM, OFF);
+        set_io(LIGHT, OFF);
+        fridgeState = FRIDGE_CLOSED;
+      }
+    break;
+  }
+}
+
+```
+
+
+#### Refactor for `changeState()`
+
+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 *arrows* of the state diagram. Like this:
+
+```C
+// this outer code deals with the arrows on the state diagram
+void fridgeTask(char event) {
+  switch (fridgeState) {
+    case FRIDGE_CLOSED:
+      if (event == EVT_OPEN) changeFridgeState(FRIDGE_OPEN);
+      break;
+    case FRIDGE_OPEN:
+      if (event == EVT_CLOSE) changeFridgeState(FRIDGE_CLOSED);
+      if (event == EVT_FRIDGE_TIMEOUT) changeFridgeState(FRIDGE_BEEPING);
+      break;
+    case FRIDGE_BEEPING:
+      if (event == EVT_CLOSE) changeFridgeState(FRIDGE_CLOSED);
+      break;
+  }
+}
+```
+while the inner code deals with the actions required for *entry* and
+*exit* from each state
+```C
+void changeFridgeState(char newState) {
+  static char oldState = FRIDGE_CLOSED;
+  // do all the state-leaving actions
+  switch(oldState) {
+    case FRIDGE_CLOSED:
+      set_io(LIGHT, ON);
+      setTimer(FRIDGE_TIMER, FRIDGE_OPEN_LIMIT);
+      break;
+    case FRIDGE_OPEN:
+      break;
+    case FRIDGE_BEEPING:
+      set_io(ALARM, OFF);
+      break;
+  }
+  // change state
+  fridgeState = oldState = newState;
+  // and do the state-entry code
+  switch(newState) {
+    case FRIDGE_CLOSED:
+      set_io(LIGHT, OFF);
+      setTimer(FRIDGE_TIMER, 0);
+      break;
+    case FRIDGE_OPEN:
+      break;
+    case FRIDGE_BEEPING:
+      set_io(ALARM, ON);
+      break;
+  }
+}
+```
+
+
+### Door Opener
+ 
+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.
+
+After the door is locked, send a report to the master control via the WiFi.
+
+Here's the state diagram:
+
+![RFID operated door lock](door.png)
+
+I use `EVT_` type events to indicate that they originate in hardware, probably
+at the interrupt level; and `MSG_` type events to indicate they come from a
+software source, perhaps a sibling task.
+
+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.
+
+Here's the modified state diagram:
+
+![RFID operated door, with remove key verify](door2.png)
+
+### Beer Vat
+
+Suppose we have to move a servo motor to lift the lid from a brewing vat, 
+to release excess pressure.
+
+Inputs: pressure valve, manual operation button
+Outputs: servo, LED, beeper
+
+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.
+
+If the manual operation button is pressed, go directly to "operate servo" as above. 
+![state machine for beer vat pressure release valve](beer_vat.png)
+
+If the manual button is pressed while the lid is open, close immediately.
+
+### Vending Machine
+
+When idle, wait for payment tap. If selection button is pressed before payment,
+display cost to inform the user for 3 seconds.
+
+After payment tap, request the user select item.
+
+Operate dispense motor. 
+
+Wait for object to be removed from output bin.
+
+![states for vending machine](vending.png)
+
+
+## Events
+
+In all the above, events were implemented as a simple `unsigned char`, allowing
+255 different event types.
+
+There's no reason we couldn't use an `unsigned short` or even an `int`. Further,
+a 16 bit event number could be designed to be 8 bits of event type, and 8 bits
+of supplementary event information:
+
+```C
+typedef struct Event {
+  unsigned char type;
+  unsigned char info;
+} Event;
+
+Event e = {MSG_KEYPRESS, KEY_A};
+```
+
+In the old Windows system, *events/messages* were realized as a 16 bit number,
+with an extra 32 bit number glued to it for extra information. 
+
+```C
+typedef struct MSG {
+  UINT   message;
+  LPARAM lParam;
+};    // some extra detail removed
+```
+For example, 
+`WM_CHAR=0x0102` indicates that a key was pressed, with the extra 32bit `lParam`
+carrying the information about *which* key. 
+<hr>
+
+
+
+## Timers
+
+The simplest state timer is made with a static variable associated with the state code.
+
+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.
+
+For instance, to create a timer that waits 100 timer ticks, you could use:
+
+```C
+static int state;
+static int stateTimer;
+void stateCode(char event) {
+  switch(state) {
+    case STATE_IDLE:
+      if (event = EVENT_TRIGGER) {
+        stateTimer = 100;
+        state = STATE_DELAY;
+      }
+      break;
+    case STATE_DELAY:
+      if (event = EVENT_TICK) {
+        if (--stateTimer == 0) {
+          // the timer is finished
+          state = STATE_NEXT;
+        }
+      }
+      break;
+    // ....
+  }
+}
+
+
+In the above example, you could equally well have set the timer to start at zero
+and increment until it hits the desired limit (100 in this case).
+
+In a moderate sized project, timers like this will proliferate throughout the code,
+making it awkward to read. One solution to this is to centralize the timers.
+
+In all the above examples, the `timer_irq()` code is trivial, just `newEvent(EVT_TICK)`.
+Suppose we add code to the `timer_irq()` so that it can process timer counting on
+behalf of the tasks......
+
+### Timers as a Resource
+
+Let's create a centralized service called `setTimer(timer_index, timer_count)`. 
+
+A task can call this service with a unique `timer_index` and a requested count. The
+`timer_irq()` will then count out the ticks on behalf of the task, and when the tick
+count is finished, the `timer_irq()` code can generate a unique event, perhaps
+`EVT_TIMER_n`. 
+
+So the state code can then look something like this:
+
+```C
+static int state;
+void stateCode(char event) {
+  switch(state) {
+    case STATE_IDLE:
+      if (event = EVENT_TRIGGER) {
+        setTimer(TIMER_1,100);
+        state = STATE_DELAY;
+      }
+      break;
+    case STATE_DELAY:
+      if (event = EVENT_TIMER_1) {
+          // the timer is finished
+          state = STATE_NEXT;
+        }
+      }
+      break;
+    // ....
+  }
+}
+```
+
+This makes the state code much simpler to read, 
+hiding all the increments/decrements and limit testing.
+
+The overhead for this ends up in the `timer_irq()` code, 
+and might look something like this:
+
+```C
+static unsigned int timers[NUM_TIMERS];
+#define EVT_TIMER_OFFSET 100
+enum {EVENT_TIMER_1=EVT_TIMER_OFFSET, EVENT_TIMER_2, EVENT_TIMER_3}; // 100, 101...
+enum {TIMER_1, TIMER_2, TIMER_3};   // 0,1,2,....
+
+void timer_irq() {
+  newEvent(EVT_TICK);           // the main tick, fires every time
+  for (i=0; i<NUM_TIMERS) {     // the system timers, fires on completion
+    if (timers[i]>0) {
+      if (--timers[i]==0)
+        newEvent(i + EVT_TIMER_OFFSET);
+    }
+  }
+}
+
+/* common service, available to all tasks */
+void setTimer(int timerIndex, unsigned int timerCount) {
+  timers[timerIndex] = timerCount;
+}
+```
+
+<details><summary>Reality Check</summary>
+On a typical microcontroller running at 24MHz, with 5 timers, this adds about
+2 microseconds of extra time to the `timer_irq()` 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.
+
+Another possible design for timers is to have the main `timer_isr()` increment
+a global atomic `voloatile int timer` variable, and the tasks can observe this
+timer and set a target based on that.
+
+```C
+volatile int timer;         // on most microcontrollers, access to an int
+                            // will be atomic
+INTERRUPT timer_isr(void) {
+  timer++;
+  newEvent(EVT_TICK);
+}
+
+// .... state code ...
+static int targetTime;
+void stateCode(uint8 event) {
+  switch(state) {
+    case STATE1:
+      //..... set a target
+      targetTimer = time + DELAY_TIME;
+      state = state2;
+      //.....
+    case STATE2:
+      if (event == EVT_TICK) {
+        if (time == targetTimer) {
+          //.... we've waited long enough
+        }
+      }
+    //...
+  }
+}
+
+```
+
+</details>
+
+
+
+
+
+
+
+
+
+## Real Code
+
+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.
+
+<details><summary>board specific defines</summary>
+
+```C
+#include "stm32f030.h"
+
+/****** project hardware ******/
+// on this demo board, there is a push button on PB13 and
+// an LED on PA4 and PF5
+
+#define LED  0x10 // port A bit 4  these LED are LOW=lit-up
+#define LED2 0x50020 // port F bit 5
+GPIO_TypeDef * GROUP[] = {GPIOA, GPIOB, GPIOC, GPIOD, 0, GPIOF};  // 0,0x10000,0x20000, etc
+void gpio_set(uint32 bitPosition, bool value) {
+  vu32* group = &((GROUP[bitPosition >> 16])->ODR);
+  bitPosition &= 0xFFFF;
+  if (value)
+    *group |= bitPosition;
+  else
+    *group &= ~bitPosition;
+}
+```
+</details>
+
+```C
+/***** events ****/
+#define NUM_EVENTS 10
+volatile uint8 events[NUM_EVENTS];
+enum { EVT_NONE,
+      EVT_TICK,
+      EVT_BUTTON};
+void newEvent(uint8 e);
+
+/********** tasks ***********/
+void ledTask(uint8 evt);
+void respondToButtonTask(uint8 evt);
+
+/********** interrupts **************/
+volatile uint32 tick;   // increasing at 100 ticks/sec
+                        // takes a year to roll-over
+
+void timer_isr(void) {
+  tick++;
+  newEvent(EVT_TICK);
+  // this interrupt is auto-ack'd
+}
+
+void button_isr(void) {
+  newEvent(EVT_BUTTON);
+  EXTI->PR |= 0x3001;    // ack it 
+}
+
+/** newEvent
+ * add the event to the event queue
+ * wrapped in critical section
+ * 
+ * @param the event
+ */
+void newEvent(char e) {
+        static uint nextEvent;
+        dint(); // critical section
+        events[nextEvent++] = e;
+        if (nextEvent==NUM_EVENTS)
+                nextEvent = 0;
+        eint();
+}
+```
+</details>
+
+<details><summary>hardware initialization</summary>
+
+```C
+/***** init ******/
+/* called by newlib startup */
+void _init(void) {
+  // startup code
+
+  // use default clocks
+  // turn on all the GPIO's
+  RCC->AHBENR  = 0x005e0014;
+  // enable SysCfg
+  RCC->APB2ENR = 0x00004801;
+
+
+  // enable the two LEDs as outputs
+  GPIOA->MODER = (GPIOA->MODER & 0xFFFFFCFF) | 0x00000100; // port A bit 4
+  GPIOF->MODER = (GPIOF->MODER & 0xFFFFF3FF) | 0x00000400; // port F bit 5
+  // and the push button as input + pulldown
+  GPIOB->PUPDR = (GPIOB->PUPDR & 0xF3FFFFFF) | 0x08000000; // pulldown on 13
+
+  // keep the clocking system simple: just use the 8MHz HSI everywhere
+  SysTick->LOAD = 10000;                // 10 msec
+  SysTick->VAL  = 0;
+  SysTick->CTRL = 3;                    // count at 1usec, use interrupts
+ 
+  /* to configure an interrupt on the stm32f0xx,
+    - enable the EXTI->IMR for the pin
+    - set EXTI->RTSR for select rising edge
+    - set the SYSCFG->EXTICRx pin to route it 
+    - enable the gating bit in the NVIC register 
+    - don't forget to ack each interrupt at EXTI->PR
+  */
+  EXTI->IMR         = 0x2000;        // enable interrupt from line 13
+  EXTI->RTSR        = 0x2000;        // interrupt on rising edge
+  SYSCFG->EXTICR[3] = 0x0010;   // select prot B for exti-13
+  NVIC->ISER[0]     = 0x00E1;        // enable in NVIC: gpio & watchdog
+}
+```
+</details>
+
+```C
+void main(void) {
+
+  eint();
+
+  // dispatcher loop
+  while(1) {
+    int j;
+    for (j=0; j<NUM_EVENTS; j++) {
+       while (events[j]==EVT_NONE)
+         {}
+       ledTask(events[j]);
+       respondToButtonTask(events[j]);
+       events[j] = EVT_NONE;
+    }
+  }
+}
+
+/*********** task code, with states ************/
+enum {LED_ON, LED_OFF};
+enum {RTB_IDLE, RTB_ON};    // states
+static uint8 rtbState           = RTB_IDLE;
+static uint16 rtbTimerCount     = 0;
+const uint16 BUTTON_LED_ON_TIME = 150;
+
+void respondToButtonTask(uint8 evt) {
+  switch(rtbState) {
+    case RTB_IDLE:
+      if (evt == EVT_BUTTON) {
+        rtbState      = RTB_ON;
+        rtbTimerCount = 0;
+        gpio_set(LED, LED_ON);
+      }
+      break;
+    case RTB_ON:
+      if (evt == EVT_TICK) {
+        if (++rtbTimerCount > BUTTON_LED_ON_TIME) {
+          gpio_set(LED, LED_OFF);
+          rtbState = RTB_IDLE;
+        }
+      }
+      break;
+    }
+}
+
+const uint16 LED_ON_TIME  = 150;
+const uint16 LED_OFF_TIME = 50;
+static uint8 ledState       = LED_OFF;
+static uint16 ledTimerCount = 0;
+
+void ledTask(uint8 evt) {
+  switch(ledState) {
+    case LED_OFF:
+      if (evt == EVT_TICK) {
+        if (++ledTimerCount > LED_OFF_TIME) {
+          gpio_set(LED2, LED_ON);
+          ledTimerCount = 0;
+          ledState      = LED_ON;
+        }
+      }
+      break;
+    case LED_ON:
+      if (evt == EVT_TICK) {
+        if (++ledTimerCount > LED_ON_TIME) {
+          gpio_set(LED2, LED_OFF);
+          ledTimerCount = 0;
+          ledState      = LED_OFF;
+        }
+      }
+      break;
+    }
+}
+
+```
+
+<details><summary>vector table</summary>
+
+```C
+/* vector table */
+#define STACK_TOP 0x20002000
+void default_isr(void) {}
+extern void _start(void);
+
+void (*myvectors[])(void) __attribute__ ((section(".vectors")))= {
+    (void(*)(void)) STACK_TOP,     // stack pointer
+    _start,        // code entry point
+    default_isr,   // handle non-maskable interrupts
+    default_isr, // handle hard faults
+    0,0,0,0,                /* 10...1f */
+    0,0,0,0,                /* 20...2f */
+    0,0,0,timer_isr,        /* 30...3f */
+    0,0,0,0,
+    0,button_isr,button_isr,button_isr, /* 50...5f */
+    0,0,0,0,                /* 60...6f */
+    0,0,0,0,                /* 70...7f */
+    0,0,0,0,                /* 80...8f */
+    0,0,0,0,                /* 90...9f */
+    0,0,0,0,                /* a0...af */
+    0,0,0,0,                /* b0...bf */
+    0,0,0,0,                /* c0...cf */
+    0,0,0,0,                /* d0...df */
+    0,0,0,0,                /* e0...ef */
+    0,0,0,0,                /* f0...ff */
+    0,0,0,0,                /* 100.10f */
+    0,0,0,0                 /* 110.11f */
+};
+```
+</details>
+
+## Substates
+
+For things like flashing LED's, you could create two states, 
+and toggle between them, like this:
+
+![](rtb_state_4.png)
+
+<hr>
+
+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: 
+
+
+```C
+uint8 rtbState = RTB_IDLE;              // major state, RTB_IDLE/RTB_FLASHING
+uint8 rtbSubState = RTB_FLASH_OFF;      // minor state, toggles LED on/off
+```
+
+Alternatively, you could use the *timer counter*
+variable, and make changes at the half-way point through the count.
+ This simplifies the substate design to this:
+
+![](rtb_state_5.png){.imageright}
+
+```C
+const uint16 FLASH_CYCLE_TIME 150;
+const uint16 FLASH_ON_TIME 40;
+uint8 rtbState = STATE_IDLE;
+void rtbTaskCode(char event) {
+  static uint16 flashCount=0;
+
+  switch(taskState) {
+    case RTB_IDLE:
+      if (event == EVT_BUTTON) {
+        rtbState = STATE_FLASHING;
+        flashCount = FLASH_CYCLE_TIME;
+      }
+      break;
+    case STATE_FLASHING:
+      // count down flashCount, toggle LED halfway through
+      if (event == EVT_TICK) {
+        if (--flashCount == 0) {
+          setLED(OFF);
+          flashCount = FLASH_CYCLE_TIME;
+        } else if (flashCount == FLASH_ON_TIME) {
+          setLED(ON);
+        }
+      // ... make sure to turn LED off when leaving this state
+      if (event == EVT_BUTTON) {
+        setLED(OFF);
+        rtbState = RTB_IDLE;
+      } 
+      break;
+    }
+  }
+}
+
+```
+
+## State Machine Initialization
+
+
+Notice that there's no initialization of states in this code. 
+It *would* be very handy if we 
+knew when to initialize all the lights and buzzers and match up the various
+states. Perhaps the `main()` code could add a single event into the event queue 
+at power-up, perhaps `EVT_INIT`. It might be added like this:
+
+```C
+void main(void) {
+  newEvent(EVT_INIT);  // early, before interrupts, so we know it's first in line
+  eint();
+  while(1) {
+      ... dispatcher code ...
+  }
+}
+```
+
+Then, in the state code, you can catch that event and set up whatever might
+be required
+
+```C
+uint8 myState;
+void stateCode(uint8 event) {
+  if (event==EVT_INIT) {
+    // ... do setup code
+    myState = FIRST_STATE;
+    return;
+  }
+  // ... regular state machine code
+}
+```
+
+## Messages
+
+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 *messages* which can be injected into the event queue.
+
+Notice the change of terminology, where *message* indicates that the entry 
+was created by code procesing, rather than an interrupt.
+
+### PostMessage
+
+We need a service routine to add messages to the event queue: 
+`postMessage()`. This call is very smimilar `newEvent()` which should 
+only be called from interrupts.
+
+![cd-eject task uses postMessage() to broadcast a message](postMessage.png)
+
+```C
+void postMessage(uint8 message);
+```
+
+Notice that `postMessage` can't return any information, because it's not 
+processed immediately; the *message* is added to the *event queue* to be 
+processed at a later time.
+
+<details><summary>Reality Check</summary>
+In this tiny system, `postMessage()` is exactly the same as `newEvent()`. The
+reason that I use different names is that some more sophisticated operating
+systems require that these two functions are *not* identical. 
+</details>
+
+### SendMessage
+
+In some cases we may want the sibling task to process the information immediately. 
+This means:
+
+- we can be assured that the sibling task has fully processed the information
+- does not involve the dispatcher; but does require extra room on the stack
+- we *could* get a return value from the sibling task; since it's implemented as
+a subroutine, it's allowed to return a value
+
+![cd-eject task uses sendMessage() to call a sibling task](sendMessage.png)
+
+This of course means that the prototype for task functions would change from 
+
+```C
+void taskCode(uint8 event);
+to
+uint8 taskCode(uint8 event);
+```
+
+The service routine to send a message like this would look like:
+
+```C
+uint8 SendMessage(int8 taskPointer(uint8), uint8 message) {
+  return taskPointer(message);
+}
+```
+
+### Usage
+
+Why might one want to send a *message* between tasks?
+
+Suppose you have a rotary encoder, which sends quadrant signals, which need
+to be interpreted as *clockwise* and *counter clockwise*. You could have one
+task devoted to determining the direction of the knob 
+(and [debouncing](#debouncing)), and have it send clean 
+EVT_CW and EVT_CCW *messages* to its sibgling tasks.
+
+Another possible use of *messages* 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).
+
+And why `sendMessage()`? Perhaps you need to have a sibling task change
+states, or process information *before* you continue in your task. Imagine
+there is a slave WiFi chip that needs to be powered up *before* you send it
+a request....you could use `sendMessage()` to activate the power, and then
+continue, knowing that your peripheral is available.
+
+## Ideas for Tasks
+
+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:
+
+- task blue light to show bluetooth connectivity
+- volume up/down sense and display
+- radio channel select buttons
+  - step single freq, or high-speed scan
+- power on/off management
+- remote control of volume/radio-select from the steering wheel (uart link)
+- backlight for night-time viewing (auto sense from a light sensor)
+- service-required LED, flashing
+- a beeper to confirm keypresses (30ms) and alarm (700ms/100ms)
+- a clock up/down set button
+  - 3 buttons: "set min/hr" and "+" and "-"
+
+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)
+
+
+
+## More State Machines
+
+### Radio Tuner
+
+Suppose we have a `+` and `-` 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. 
+
+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:
+
+```C
+enum {EVT_NONE, EVT_TICK, EVT_UP_PRESS, EVT_UP_RELEASE, EVT_DN_PRESS, EVT_DN_RELEASE};
+```
+
+![state machine for radio tuner](radio_tuner.png)
+
+```C
+const float MIN_FREQ=88.7, MAX_FREQ=107.9, INC_FREQ=0.1;
+float radioFreq = MIN_FREQ;
+uint16 radioTimer = 0;
+const uint16 radioTimeout = 5;  // 0.5 seconds
+
+enum {IDLE, FREQ_UP, FREQ_UP_FAST, FREQ_DN, FREQ_DN_FAST};
+uint8 state = IDLE;
+void radioTask(uint8 event) {
+  switch(state) {
+    case IDLE:
+      if (event == EVT_UP_PRESS) {
+        radioFreq += INC_FREQ;
+        checkRadioFreq();
+        radioTimer = radioTimeout;
+        state = FREQ_UP;
+      }
+      if (event == EVT_DN_PRESS) {
+        radioFreq -= INC_FREQ;
+        checkRadioFreq();
+        radioTimer = radioTimeout;
+        state = FREQ_DN;
+      }
+      break;
+    case FREQ_UP:
+      if (event == EVT_UP_RELEASE) 
+        state = IDLE;
+      if (event == EVT_TICK) {
+        if (--radioTimer == 0) 
+          state = FREQ_UP_FAST;
+      }
+      break;
+    case FREQ_UP_FAST:
+      if (event == EVT_TICK) {
+        radioFreq += INC_FREQ;
+        checkRadioFreq();
+      }
+      if (event == EVT_UP_RELEASE) 
+        state = IDLE;
+      break;
+    // and the same for FREQ_DN and FREQ_DN_FAST
+  }
+}
+
+// manage rollover
+void checkRadioFreq() {
+  if (radioFreq > MAX_FREQ)
+    radioFreq = MIN_FREQ;
+  if (radioFreq < MIN_FREQ)
+    radioFreq = MAX_FREQ;
+}
+```
+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.
+

+ 22 - 0
beer_vat.dot

@@ -0,0 +1,22 @@
+/* beer vat pressure release valve */
+digraph {
+  pad=0.5; bgcolor="lightgrey"; nodesep=1; ranksep=0.3;
+  node [shape="box"; style="filled";  fillcolor="cyan";  ];
+  edge [labelfontsize=10; minlen=2; labeldistance=2 ];
+  CLOSED [label="CLOSED\npressure valve closed"];
+  FLASHING [label="FLASHING\nstart blinking LED"]; 
+  BEEPING [label="BEEPING\nturn on the alarm sound"]; 
+  OPENING [label="OPENING\nslowly open the relief valve"]; 
+  CLOSING [label="CLOSING\nslowly close the relief valve"]; 
+  CLOSED -> FLASHING [taillabel="EVT_PRESSURE";]
+  FLASHING -> BEEPING  [taillabel="EVT_TIMER";]
+  BEEPING -> OPENING [taillabel="EVT_TIMER";]
+  OPENING -> OPEN [taillabel="EVT_TIMER";]
+  OPEN -> CLOSING [taillabel="EVT_TIMER";]
+  CLOSING -> CLOSED [taillabel="EVT_TIMER";]
+  CLOSED -> OPENING [taillabel="EVT_BUTTON";]
+  FLASHING -> OPENING [taillabel="EVT_BUTTON";]
+  BEEPING -> OPENING [taillabel="EVT_BUTTON";]
+  OPEN -> CLOSING [taillabel="EVT_BUTTON";]
+}
+

BIN
beer_vat.png


BIN
blb.jpg


+ 54 - 0
build/crt1.c

@@ -0,0 +1,54 @@
+/* startup code for Cortex-M3*/
+#define USE_LIBC 1
+#pragma GCC optimize("Os")
+
+extern unsigned long _etext, _sdata, _edata, _end;
+extern void main(void);
+extern void __libc_init_array(void);
+
+/* come here on power up, copy the .data and clear the .bss */
+ __attribute__ ((noreturn)) void _start(void){
+	unsigned long *pulSrc, *pulDest;
+
+	/* Copy the data segment initializers from flash to SRAM */
+	pulSrc  = &_etext;
+	pulDest = &_sdata;
+
+	for(; pulDest < &_edata; )
+		*(pulDest++) = *(pulSrc++);
+	/* Zero fill the bss segment. */
+	for(; pulDest < &_end; )
+		*(pulDest++) = 0;
+#ifdef USE_LIBC
+	/* initialize the libc */
+	__libc_init_array();
+#endif
+	/* jump to user program */
+	while (1)
+		main();
+}
+
+#ifdef USE_LIBC
+#include <sys/errno.h>
+extern unsigned int _stack_bottom;
+#define RAM_END (void*)&_stack_bottom
+ /*
+  * sbrk -- changes heap size size. Get nbytes more
+  *         RAM. We just increment a pointer in what's
+  *         left of memory on the board.
+  */
+void * _sbrk(nbytes)
+      int nbytes;
+ {
+   static void * heap_ptr = (void*) &_end;
+   void *        base;
+
+   if (heap_ptr + nbytes < RAM_END) {
+     base = heap_ptr;
+     heap_ptr += nbytes;
+     return (base);
+   }  
+   errno = ENOMEM;
+   return ((void*)-1);
+ }
+#endif

+ 203 - 0
build/demo.stm32f0xx.c

@@ -0,0 +1,203 @@
+#include "stm32f030.h"
+
+/****** project hardware ******/
+// on this demo board, there is a push button on PB13 and
+// an LED on PA4 and PF5
+
+#define LED  0x10 // port A bit 4  these LED are LOW=lit-up
+#define LED2 0x50020 // port F bit 5
+GPIO_TypeDef * GROUP[] = {GPIOA, GPIOB, GPIOC, GPIOD, 0, GPIOF};  // 0,0x10000,0x20000, etc
+void gpio_set(uint32 bitPosition, bool value) {
+  vu32* group = &((GROUP[bitPosition >> 16])->ODR);
+  bitPosition &= 0xFFFF;
+  if (value)
+    *group |= bitPosition;
+  else
+    *group &= ~bitPosition;
+}
+
+
+/***** declarations ****/
+#define NUM_EVENTS 10
+volatile uint8 events[NUM_EVENTS];
+
+void newEvent(uint8 e);
+void ledTask(uint8 evt);
+void respondToButtonTask(uint8 evt);
+
+enum { EVT_NONE,
+      EVT_TICK,
+      EVT_BUTTON};
+
+/********** interrupts **************/
+volatile uint32 tick;	// increasing at 100 ticks/sec
+			            // takes a year to roll-over
+
+void timer_isr(void) {
+  tick++;
+  newEvent(EVT_TICK);
+  // this interrupt is auto-ack'd
+}
+
+void button_isr(void) {
+  newEvent(EVT_BUTTON);
+  EXTI->PR |= 0x3001;    // ack it 
+}
+
+/** newEvent
+ * add the event to the event queue
+ * wrapped in critical section
+ * 
+ * @param the event
+ */
+void newEvent(uint8 e) {
+	static uint nextEvent;
+	dint();	// critical section
+	events[nextEvent++] = e;
+	if (nextEvent==NUM_EVENTS)
+		nextEvent = 0;
+	eint();
+}
+
+/***** init ******/
+/* called by newlib startup */
+void _init(void) {
+  // startup code
+
+  // use default clocks
+  // turn on all the GPIO's
+  RCC->AHBENR  = 0x005e0014;
+  // enable SysCfg
+  RCC->APB2ENR = 0x00004801;
+
+
+  // enable the two LEDs as outputs
+  GPIOA->MODER = (GPIOA->MODER & 0xFFFFFCFF) | 0x00000100; // port A bit 4
+  GPIOF->MODER = (GPIOF->MODER & 0xFFFFF3FF) | 0x00000400; // port F bit 5
+  // and the push button as input + pulldown
+  GPIOB->PUPDR = (GPIOB->PUPDR & 0xF3FFFFFF) | 0x08000000; // pulldown on 13
+
+  // keep the clocking system simple: just use the 8MHz HSI everywhere
+  SysTick->LOAD = 10000;		// 10 msec
+  SysTick->VAL  = 0;
+  SysTick->CTRL = 3;			// count at 1usec, use interrupts
+ 
+  /* to configure an interrupt on the stm32f0xx,
+    - enable the EXTI->IMR for the pin
+    - set EXTI->RTSR for select rising edge
+    - set the SYSCFG->EXTICRx pin to route it 
+    - enable the gating bit in the NVIC register 
+    - don't forget to ack each interrupt at EXTI->PR
+  */
+  EXTI->IMR         = 0x2000;        // enable interrupt from line 13
+  EXTI->RTSR        = 0x2000;        // interrupt on rising edge
+  SYSCFG->EXTICR[3] = 0x0010;   // select prot B for exti-13
+  NVIC->ISER[0]     = 0x00E1;        // enable in NVIC: gpio & watchdog
+
+  gpio_set(LED,1); // TODO:
+  gpio_set(LED2,1);
+}
+
+
+void main(void) {
+
+	eint();
+
+	while(1) {
+		uint j;
+		for (j=0; j<NUM_EVENTS; j++) {
+			while (events[j]==EVT_NONE)
+              {}
+            ledTask(events[j]);
+            respondToButtonTask(events[j]);
+			events[j] = EVT_NONE;
+		}
+	}
+}
+
+/*********** task code, with states ************/
+enum {LED_ON, LED_OFF};
+enum {RTB_IDLE, RTB_ON};    // states
+static uint8 rtbState           = RTB_IDLE;
+static uint16 rtbTimerCount     = 0;
+const uint16 BUTTON_LED_ON_TIME = 150;
+
+void respondToButtonTask(uint8 evt) {
+  switch(rtbState) {
+    case RTB_IDLE:
+      if (evt == EVT_BUTTON) {
+        rtbState      = RTB_ON;
+        rtbTimerCount = 0;
+        gpio_set(LED, LED_ON);
+      }
+      break;
+    case RTB_ON:
+      if (evt == EVT_TICK) {
+        if (++rtbTimerCount > BUTTON_LED_ON_TIME) {
+          gpio_set(LED, LED_OFF);
+          rtbState = RTB_IDLE;
+        }
+      }
+      break;
+    }
+}
+
+const int LED_ON_TIME  = 150;
+const int LED_OFF_TIME = 50;
+static uint8 ledState       = LED_OFF;
+static uint16 ledTimerCount = 0;
+
+void ledTask(uint8 evt) {
+  switch(ledState) {
+    case LED_OFF:
+      if (evt == EVT_TICK) {
+        if (++ledTimerCount > LED_OFF_TIME) {
+          gpio_set(LED2, LED_ON);
+          ledTimerCount = 0;
+          ledState      = LED_ON;
+        }
+      }
+      break;
+    case LED_ON:
+      if (evt == EVT_TICK) {
+        if (++ledTimerCount > LED_ON_TIME) {
+          gpio_set(LED2, LED_OFF);
+          ledTimerCount = 0;
+          ledState      = LED_OFF;
+        }
+      }
+      break;
+    }
+}
+
+
+
+
+/* vector table */
+#define STACK_TOP 0x20002000
+void default_isr(void) {}
+extern void _start(void);
+
+void (*myvectors[])(void) __attribute__ ((section(".vectors")))= {
+	(void(*)(void)) STACK_TOP,     // stack pointer
+	_start,        // code entry point
+	default_isr,   // handle non-maskable interrupts
+	default_isr, // handle hard faults
+	0,0,0,0,                /* 10...1f */
+	0,0,0,0,                /* 20...2f */
+	0,0,0,timer_isr,        /* 30...3f */
+	0,0,0,0,
+	0,button_isr,button_isr,button_isr, /* 50...5f */
+	0,0,0,0,                /* 60...6f */
+	0,0,0,0,                /* 70...7f */
+	0,0,0,0,                /* 80...8f */
+	0,0,0,0,                /* 90...9f */
+	0,0,0,0,                /* a0...af */
+	0,0,0,0,                /* b0...bf */
+	0,0,0,0,                /* c0...cf */
+	0,0,0,0,                /* d0...df */
+    0,0,0,0,  	            /* e0...ef */
+	0,0,0,0,                /* f0...ff */
+	0,0,0,0,				/* 100.10f */
+	0,0,0,0                 /* 110.11f */
+};

+ 206 - 0
build/demo.stm32f1xx.c

@@ -0,0 +1,206 @@
+#include "stm32f10x.h"
+
+/****** project hardware ******/
+#define PCOUT   GPIOC->ODR    // for output LEDs PC8 PC9
+#define PAIN    GPIOA->IDR    // for input push button PA0
+#define PADDR_HIGH GPIOA->CRH
+#define PADDR_LOW  GPIOA->CRL
+#define PBDDR_HIGH GPIOB->CRH
+#define PBDDR_LOW  GPIOB->CRL
+#define PCDDR_HIGH GPIOC->CRH
+#define PCDDR_LOW  GPIOC->CRL
+
+#define LED 8 // port C, bit 8
+#define LED2 9
+inline void gpio_set(int bitPosition, int value) {
+  if (value)
+    PCOUT |= (1<<bitPosition);
+  else
+    PCOUT &= ~(1<<bitPosition);
+}
+
+/************ events ************/
+void newEvent(char e);
+#define NUM_EVENTS 10
+volatile char events[NUM_EVENTS];	// starts full of zeros EVT_NONE
+
+#define EVT_NONE 0
+#define EVT_TICK 1
+#define EVT_BUTTON 2
+
+/********** tasks ***********/
+void ledTask(char evt);
+void respondToButtonTask(char evt);
+
+/********** interrupts **************/
+volatile int tick;	// increasing at 100 ticks/sec
+			// takes a year to roll-over
+
+#define eint() asm volatile("cpsie i")
+#define dint() asm volatile("cpsid i")
+
+void timer_isr(void) {
+	tick++;
+	newEvent(EVT_TICK);
+	// this interrupt is auto-ack'd
+}
+
+void button_isr(void) {
+  newEvent(EVT_BUTTON);
+  EXTI->PR |= 1;    // ack both 
+}
+
+/** newEvent
+ * add the event to the event queue
+ * wrapped in critical section
+ * 
+ * @param the event
+ */
+void newEvent(char e) {
+	static unsigned char nextEvent;
+    if (events[nextEvent]) 
+      return;
+	dint();	// critical section
+	events[nextEvent++] = e;
+	if (nextEvent==NUM_EVENTS)
+		nextEvent = 0;
+	eint();
+}
+
+/***** init ******/
+/* called by newlib startup */
+void _init(void) {
+  // startup code
+
+  // turn on all the GPIO's
+  RCC->APB2ENR = 0x74A3D; // enable the GPIO devices and timers
+  RCC -> APB1ENR = 0x1ff; // enable timers 2-14
+  AFIO -> MAPR |= 0x02008C00;   // remap osc to PD0/1 and tim3 to PB4/5/0/1
+
+  PADDR_HIGH = 0x44444444;      // PortA all inputs
+  PCDDR_HIGH = 0x11111111;      // PortC all outputs
+
+  // keep the clocking system simple: just use the 8MHz HSI everywhere
+  SysTick->LOAD = 10000;		// 10 msec
+  SysTick->Val  = 0;
+  SysTick->CTRL = 3;			// count at 1usec, use interrupts
+  
+  /* configure the blue button on the discovery module, PA0 */
+  EXTI->IMR |= 1;         // enable interrupt from line 0
+  EXTI->RTSR |= 1;        // interrupt on rising edge
+  AFIO->EXTICR[0] |= 0x0;       // select port A
+
+  /* enable all interrupt sources */
+  NVIC->ISER[0] = 0xFFFFFFFF;     // enable in NVIC
+  NVIC->ISER[1] = 0x00FFFFFF;     // enable in NVIC
+
+  gpio_set(9,0);
+  gpio_set(8,0);   
+	
+}
+
+
+void main(void) {
+
+	eint();
+
+	while(1) {
+		int j;
+		for (j=0; j<NUM_EVENTS; j++) {
+			while (events[j]==EVT_NONE)
+				{}
+
+            ledTask(events[j]);
+            respondToButtonTask(events[j]);
+			events[j] = EVT_NONE;
+		}
+	}
+}
+
+/*********** state code ************/
+#define RTB_IDLE 0
+#define RTB_ON 1
+#define TIMER_LIMIT 150
+static int rtbState = RTB_IDLE;
+static int rtbTimerCount = 0;
+
+void respondToButtonTask(char evt) {
+  switch(rtbState) {
+    case RTB_IDLE:
+      if (evt == EVT_BUTTON) {
+        rtbState = RTB_ON;
+        gpio_set(LED, 1);
+      }
+      break;
+    case RTB_ON:
+      if (evt == EVT_TICK) {
+        if (++rtbTimerCount > TIMER_LIMIT) {
+          gpio_set(LED, 0);
+          rtbState = RTB_IDLE;
+          rtbTimerCount = 0;
+        }
+      }
+      break;
+    }
+}
+
+#define LED_OFF 0
+#define LED_ON 1
+#define LED_ON_TIME 150
+#define LED_OFF_TIME 50
+static int ledState = LED_OFF;
+static int ledTimerCount = 0;
+
+void ledTask(char evt) {
+  switch(ledState) {
+    case LED_OFF:
+      if (evt == EVT_TICK) {
+        if (++ledTimerCount > LED_OFF_TIME) {
+          gpio_set(LED2, LED_ON);
+          ledTimerCount = 0;
+          ledState = LED_ON;
+        }
+      }
+      break;
+    case LED_ON:
+      if (evt == EVT_TICK) {
+        if (++ledTimerCount > LED_ON_TIME) {
+          gpio_set(LED2, LED_OFF);
+          ledTimerCount = 0;
+          ledState = LED_OFF;
+        }
+      }
+      break;
+    }
+}
+
+
+
+/* vector table */
+#define STACK_TOP 0x20002000
+void default_isr(void) {}
+extern void _start(void);
+
+void (*myvectors[])(void) __attribute__ ((section(".vectors")))= {
+	(void(*)(void)) STACK_TOP,     // stack pointer
+	_start,        // code entry point
+	default_isr,   // handle non-maskable interrupts
+	default_isr, // handle hard faults
+	0,0,0,0,                /* 10...1f */
+	0,0,0,0,                /* 20...2f */
+	0,0,0,timer_isr,        /* 30...3f */
+	0,0,0,0,                /* 40...4f */
+	0,0,button_isr,0,       /* 50...5f */
+	0,0,0,0,                /* 60...6f */
+	0,0,0,0,                /* 70...7f */
+	0,0,0,0,                /* 80...8f */
+	0,0,0,0,                /* 90...9f */
+	0,0,0,0,                /* a0...af */
+	0,0,0,0,                /* b0...bf */
+	0,0,0,0,                /* c0...cf */
+	0,0,0,0,                /* d0...df */
+    0,0,0,0,  	            /* e0...ef */
+	0,0,0,0,                /* f0...ff */
+	0,0,0,0,				/* 100.10f */
+	0,0,0,0                 /* 110.11f */
+};

+ 25 - 0
build/makefile

@@ -0,0 +1,25 @@
+
+CC=arm-none-eabi-gcc
+CFLAGS=-Wall -mcpu=cortex-m0 -mthumb -nostartfiles -g -Os 
+LDFLAGS=-T stm32.lnk -static 
+#  some recommend -lnosys --specs=nosys.spec, but they don't seem effective
+
+SOURCES=demo.c crt1.c 
+OBJECTS=$(SOURCES:.c=.o)
+
+.c.o:
+	$(CC) $(CFLAGS) -c $< 
+
+demo: $(OBJECTS) 
+	$(CC) $(CFLAGS) $(LDFLAGS) $(OBJECTS) -o $@
+
+all:
+	klm 
+
+clean:
+	rm $(OBJECTS)
+	
+load: demo 
+	arm-none-eabi-objcopy $< -O binary demo.bin
+	# st-flash write demo.bin 0x08000000
+	stm32loader -p /dev/ttyUSB0 -e -w -v -V demo.bin

+ 45 - 0
build/stm32.lnk

@@ -0,0 +1,45 @@
+ /* Linker script for stm32f030r8 64k-flash + 8k-ram */
+
+ENTRY(_start)
+
+MEMORY
+{
+  rom (rx) : ORIGIN = 0x08000000, LENGTH = 64K
+  ram (rw) : ORIGIN = 0x20000000, LENGTH =  8K
+}
+
+_stack = ORIGIN(ram) + LENGTH(ram);
+_stack_bottom = _stack - 512;
+
+SECTIONS {
+	.text : {
+		KEEP(*(.vectors)) 
+		*(.text*)
+		*(.rodata*)
+		*(.init)
+		__init_array_start = .;
+		*(.init_array)
+		__init_array_end = .;
+		*(.fini .fini_array)
+	} >rom 
+
+	. = ALIGN(4);
+
+	PROVIDE(_etext = .);
+	
+	.data : {
+		_sdata = .;
+		*(.data*)  *(.jcr)
+		_edata = .;
+	} >ram AT >rom
+
+	.bss : {
+		__bss_start__ = .; 
+		*(.bss*)
+		*(COMMON)
+		__bss_end__ = .; 
+		_end = .;
+	} >ram
+}	
+
+

+ 564 - 0
build/stm32f030.h

@@ -0,0 +1,564 @@
+/**
+*
+* File Name          : stm32f030.h
+* Author             : Pat Beirne
+* Version            : 12.0
+* Date               : 01/08/2014
+* Copyright          : 2014 Pat Beirne
+* License            : GPL v3
+*
+*/
+
+#ifndef __STM32F030_H
+#define __STM32F030_H
+
+/******************************************************************************/
+/*								Data Types Needed							  */
+/******************************************************************************/
+
+typedef signed long  s32;
+typedef signed short s16;
+typedef signed char  s8;
+
+typedef signed long  const sc32;  /* Read Only */
+typedef signed short const sc16;  /* Read Only */
+typedef signed char  const sc8;   /* Read Only */
+
+typedef volatile signed long  vs32;
+typedef volatile signed short vs16;
+typedef volatile signed char  vs8;
+
+typedef volatile signed long  const vsc32;  /* Read Only */
+typedef volatile signed short const vsc16;  /* Read Only */
+typedef volatile signed char  const vsc8;   /* Read Only */
+
+typedef unsigned long  u32;
+typedef unsigned short u16;
+typedef unsigned char  u8;
+
+typedef unsigned long  const uc32;  /* Read Only */
+typedef unsigned short const uc16;  /* Read Only */
+typedef unsigned char  const uc8;   /* Read Only */
+
+typedef volatile unsigned long  vu32;
+typedef volatile unsigned short vu16;
+typedef volatile unsigned char  vu8;
+
+typedef volatile unsigned long  const vuc32;  /* Read Only */
+typedef volatile unsigned short const vuc16;  /* Read Only */
+typedef volatile unsigned char  const vuc8;   /* Read Only */
+
+/******************************************************************************/
+/*                         Peripheral registers structures                    */
+/******************************************************************************/
+
+/*------------------------ Analog to Digital Converter -----------------------*/
+typedef struct
+{
+  vu32 ISR;		//0
+  vu32 IER;
+  vu32 CR;
+  vu32 CFGR1;
+  vu32 CFGR2;	//0x10
+  vu32 SMPR;
+  vu32 RESERVED0;
+  vu32 RESERVED1;
+  vu32 TR;		//0x20
+  vu32 RESERVED2;
+  vu32 CHSELR;	
+  vu32 RESERVED4[5];
+  vu32 DR;		//0x40
+  vu32 CCR;
+} ADC_TypeDef;
+
+
+/*------------------------ DMA Controller ------------------------------------*/
+typedef struct
+{
+  vu32 CCR;
+  vu32 CNDTR;
+  vu32 CPAR;
+  vu32 CMAR;
+} DMA_Channel_TypeDef;
+
+typedef struct
+{
+  vu32 ISR;
+  vu32 IFCR;
+} DMA_TypeDef;
+
+/*------------------------ External Interrupt/Event Controller ---------------*/
+typedef struct
+{
+  vu32 IMR;
+  vu32 EMR;
+  vu32 RTSR;
+  vu32 FTSR;
+  vu32 SWIER;
+  vu32 PR;
+} EXTI_TypeDef;
+
+/*------------------------ FLASH and Option Bytes Registers ------------------*/
+typedef struct
+{
+  vu32 ACR;
+  vu32 KEYR;
+  vu32 OPTKEYR;
+  vu32 SR;
+  vu32 CR;
+  vu32 AR;
+  vu32 RESERVED;
+  vu32 OBR;
+  vu32 WRPR;
+} FLASH_TypeDef;
+
+typedef struct
+{
+  vu16 RDP;
+  vu16 USER;
+  vu16 Data0;
+  vu16 Data1;
+  vu16 WRP0;
+  vu16 WRP1;
+} OB_TypeDef;
+
+/*------------------------ General Purpose and Alternate Function IO ---------*/
+typedef struct
+{
+  vu32 MODER;
+  vu32 OTYPER;
+  vu32 OSPEEDR;
+  vu32 PUPDR;
+  vu32 IDR;
+  vu32 ODR;
+  vu32 BSRR;
+  vu32 LCKR;
+  vu32 AFRL;
+  vu32 AFRH;
+  vu32 BRR;
+} GPIO_TypeDef;
+
+
+/*------------------------ Inter-integrated Circuit Interface ----------------*/
+typedef struct
+{
+  vu32 CR1;
+  vu32 CR2;
+  vu16 OAR1;
+  u16 RESERVED2;
+  vu16 OAR2;
+  u16 RESERVED3;
+  vu32 TIMINGR;	// 0x10
+  vu32 TIMEOUTR;
+  vu32 ISR;
+  vu32 ICR;
+  vu8 PECR;			// 0x20
+  vu8 RESERVED4[3];
+  vu8 RXDR;
+  vu8 RESERVED5[3];
+  vu8 TXDR;
+  vu8 RESERVED6[3];
+} I2C_TypeDef;
+
+/*------------------------ Independent WATCHDOG ------------------------------*/
+typedef struct
+{
+  vu32 KR;
+  vu32 PR;
+  vu32 RLR;
+  vu32 SR;
+  vu32 WINR;
+} IWDG_TypeDef;
+
+/*------------------------ Nested Vectored Interrupt Controller --------------*/
+typedef struct
+{
+  vu32 ISER[2];
+  u32 RESERVED0[30];
+  vu32 ICER[2];
+  u32 RSERVED1[30];
+  vu32 ISPR[2];
+  u32 RESERVED2[30];
+  vu32 ICPR[2];
+  u32 RESERVED3[30];
+  vu32 IABR[2];
+  u32 RESERVED4[62];
+  vu32 IPR[11];
+} NVIC_TypeDef;
+
+typedef struct
+{
+  vuc32 CPUID;
+  vu32 ICSR;
+  vu32 VTOR;
+  vu32 AIRCR;
+  vu32 SCR;
+  vu32 CCR;
+  vu32 SHPR[3];
+  vu32 SHCSR;
+  vu32 CFSR;
+  vu32 HFSR;
+  vu32 DFSR;
+  vu32 MMFAR;
+  vu32 BFAR;
+  vu32 AFSR;
+} SCB_TypeDef;
+
+/*------------------------ Power Control -------------------------------------*/
+typedef struct
+{
+  vu32 CR;
+  vu32 CSR;
+} PWR_TypeDef;
+
+/*------------------------ Reset and Clock Control ---------------------------*/
+typedef struct
+{
+  vu32 CR;
+  vu32 CFGR;
+  vu32 CIR;
+  vu32 APB2RSTR;
+  vu32 APB1RSTR;	// 0x10
+  vu32 AHBENR;
+  vu32 APB2ENR;
+  vu32 APB1ENR;
+  vu32 BDCR;		// 0x20
+  vu32 CSR;
+  vu32 AHBRSTR;
+  vu32 CRGR2;
+  vu32 CFGR3;		// 0x30
+  vu32 CR2;
+} RCC_TypeDef;
+
+/*------------------------ Real-Time Clock -----------------------------------*/
+typedef struct
+{
+  vu32 TR;
+  vu32 DR;
+  vu32 CR;
+  vu32 ISR;
+  vu32 PRER;	// 0x10
+  u32  RESERVED0[2];
+  vu32 ALRMAR;
+  u32  RESERVED1;	// 0x20
+  vu32 WPR;
+  vu32 SSR;
+  vu32 SHIFTR;	
+  vu32 TSTR;	// 0x30
+  vu32 TSDR;
+  vu32 TSSSR;
+  vu32 CALR;
+  vu32 TAFCR;	// 0x40
+  vu32 ALRMASSR;
+} RTC_TypeDef;
+
+/*------------------------ Serial Peripheral Interface -----------------------*/
+typedef struct
+{
+  vu16 CR1;
+  u16 RESERVED0;
+  vu16 CR2;
+  u16 RESERVED1;
+  vu16 SR;
+  u16  RESERVED2;
+  vu16 DR;
+  u16  RESERVED3;
+  vu16 CRCPR;
+  u16 RESERVED4;
+  vu16 RXCRCR;
+  u16  RESERVED5;
+  vu16 TXCRCR;
+  u16  RESERVED6;
+} SPI_TypeDef;
+
+/*------------------------ SysCfg --------------------------------------------*/
+typedef struct 
+{
+	vu32 CFGR1;
+    vu32 reserved;
+	vu32 EXTICR[4];
+	vu32 CFGR2;
+} SYSCFG_TypeDef;
+
+/*------------------------ SystemTick ----------------------------------------*/
+typedef struct
+{
+  vu32 CTRL;
+  vu32 LOAD;
+  vu32 VAL;
+  vuc32 CALIB;
+} SysTick_TypeDef;
+
+/*------------------------ Advanced Control Timer ----------------------------*/
+typedef struct
+{
+  vu16 CR1;
+  u16 RESERVED0;
+  vu16 CR2;
+  u16 RESERVED1;
+  vu16 SMCR;
+  u16 RESERVED2;
+  vu16 DIER;
+  u16 RESERVED3;
+  vu16 SR;
+  u16 RESERVED4;
+  vu16 EGR;
+  u16 RESERVED5;
+  vu16 CCMR1;
+  u16 RESERVED6;
+  vu16 CCMR2;
+  u16 RESERVED7;
+  vu16 CCER;
+  u16 RESERVED8;
+  vu16 CNT;
+  u16 RESERVED9;
+  vu16 PSC;
+  u16 RESERVED10;
+  vu16 ARR;
+  u16 RESERVED11;
+  vu16 RCR;
+  u16 RESERVED12;
+  vu16 CCR1;
+  u16 RESERVED13;
+  vu16 CCR2;
+  u16 RESERVED14;
+  vu16 CCR3;
+  u16 RESERVED15;
+  vu16 CCR4;
+  u16 RESERVED16;
+  vu16 BDTR;
+  u16 RESERVED17;
+  vu16 DCR;
+  u16 RESERVED18;
+  vu16 DMAR;
+  u16 RESERVED19;
+} TIM1_TypeDef;
+
+/*------------------------ General Purpose Timer -----------------------------*/
+typedef struct
+{
+  vu16 CR1;
+  u16 RESERVED0;
+  vu16 CR2;
+  u16 RESERVED1;
+  vu16 SMCR;
+  u16 RESERVED2;
+  vu16 DIER;
+  u16 RESERVED3;
+  vu16 SR;
+  u16 RESERVED4;
+  vu16 EGR;
+  u16 RESERVED5;
+  vu16 CCMR1;
+  u16 RESERVED6;
+  vu16 CCMR2;
+  u16 RESERVED7;
+  vu16 CCER;
+  u16 RESERVED8;
+  vu16 CNT;
+  u16 RESERVED9;
+  vu16 PSC;
+  u16 RESERVED10;
+  vu16 ARR;
+  u16 RESERVED11[3];
+  vu16 CCR1;
+  u16 RESERVED12;
+  vu16 CCR2;
+  u16 RESERVED13;
+  vu16 CCR3;
+  u16 RESERVED14;
+  vu16 CCR4;
+  u16 RESERVED15[3];
+  vu16 DCR;
+  u16 RESERVED16;
+  vu16 DMAR;
+  u16 RESERVED17;
+} TIM_TypeDef;
+
+/*----------------- Basic timer TIM6 -----------------------------------------*/
+typedef struct 
+{
+	vu16 CR1;
+	u16 RESERVED0;
+	vu16 DIER;
+	u16 RESERVED1;
+	vu16 SR;
+	u16 RESERVED2;
+	vu16 EGR;
+	u16 RESERVED3;
+	vu16 CNT;
+	u16 RESERVED4;
+	vu16 PSC;
+	u16 RESERVED5;
+	vu16 ARR;
+	u16 RESERVED6;
+} TIM6_TypeDef;
+
+/*----------------- Universal Synchronous Asynchronous Receiver Transmitter --*/
+typedef struct
+{
+  vu32 CR1;
+  vu32 CR2;
+  vu16 CR3;
+  u16 RESERVED0;
+  vu16 BRR;
+  u16 RESERVED1;
+  
+  u32 GTPR;
+  vu32 RTOR;
+  vu16 RQR;
+  u16 RESERVED2;
+  vu32 ISR;
+  
+  vu32 ICR;
+  vu8 RDR;
+  u8 RESERVED3[3];
+  vu8 TDR;
+  u8 RESERVED4[3];
+} USART_TypeDef;
+
+/*------------------------ Window WATCHDOG -----------------------------------*/
+typedef struct
+{
+  vu32 CR;
+  vu32 CFR;
+  vu32 SR;
+} WWDG_TypeDef;
+
+
+/******************************************************************************/
+/*                         Peripheral memory map                              */
+/******************************************************************************/
+/* Peripheral and SRAM base address in the alias region */
+#define PERIPH_BB_BASE        ((u32)0x42000000)
+#define SRAM_BB_BASE          ((u32)0x22000000)
+
+/* Peripheral and SRAM base address in the bit-band region */
+#define SRAM_BASE             ((u32)0x20000000)
+#define PERIPH_BASE           ((u32)0x40000000)
+
+/* Peripheral memory map */
+#define APBPERIPH_BASE        PERIPH_BASE
+#define AHB1PERIPH_BASE       (PERIPH_BASE + 0x20000)
+#define AHB2PERIPH_BASE       (PERIPH_BASE + 0x08000000)
+
+
+#define TIM3_BASE             (APBPERIPH_BASE + 0x0400)
+#define TIM6_BASE             (APBPERIPH_BASE + 0x1000)
+#define TIM14_BASE            (APBPERIPH_BASE + 0x2000)
+#define RTC_BASE              (APBPERIPH_BASE + 0x2800)
+#define WWDG_BASE             (APBPERIPH_BASE + 0x2C00)
+#define IWDG_BASE             (APBPERIPH_BASE + 0x3000)
+#define SPI2_BASE             (APBPERIPH_BASE + 0x3800)
+#define USART2_BASE           (APBPERIPH_BASE + 0x4400)
+#define I2C1_BASE             (APBPERIPH_BASE + 0x5400)
+#define I2C2_BASE             (APBPERIPH_BASE + 0x5800)
+#define PWR_BASE              (APBPERIPH_BASE + 0x7000)
+
+#define SYSCFG_BASE				(APBPERIPH_BASE + 0x10000)
+#define EXTI_BASE             (APBPERIPH_BASE + 0x10400)
+#define ADC_BASE              (APBPERIPH_BASE + 0x12400)
+#define TIM1_BASE             (APBPERIPH_BASE + 0x12C00)
+#define SPI1_BASE             (APBPERIPH_BASE + 0x13000)
+#define USART1_BASE           (APBPERIPH_BASE + 0x13800)
+#define TIM15_BASE            (APBPERIPH_BASE + 0x14000)
+#define TIM16_BASE            (APBPERIPH_BASE + 0x14400)
+#define TIM17_BASE            (APBPERIPH_BASE + 0x14800)
+
+#define DMA_BASE              (AHB1PERIPH_BASE + 0x0000)
+#define DMA_Channel1_BASE     (AHB1PERIPH_BASE + 0x0008)
+#define DMA_Channel2_BASE     (AHB1PERIPH_BASE + 0x001C)
+#define DMA_Channel3_BASE     (AHB1PERIPH_BASE + 0x0030)
+#define DMA_Channel4_BASE     (AHB1PERIPH_BASE + 0x0044)
+#define DMA_Channel5_BASE     (AHB1PERIPH_BASE + 0x0058)
+
+#define RCC_BASE              (AHB1PERIPH_BASE + 0x1000)
+#define FLASH_BASE				(AHB1PERIPH_BASE + 0x2000)
+
+#define GPIOA_BASE            (AHB2PERIPH_BASE + 0x0000)
+#define GPIOB_BASE            (AHB2PERIPH_BASE + 0x0400)
+#define GPIOC_BASE            (AHB2PERIPH_BASE + 0x0800)
+#define GPIOD_BASE            (AHB2PERIPH_BASE + 0x0c00)
+#define GPIOF_BASE            (AHB2PERIPH_BASE + 0x1400)
+
+
+/* System Control Space memory map */
+#define SCS_BASE              ((u32)0xE000E000)
+
+#define SysTick_BASE          (SCS_BASE + 0x0010)
+#define NVIC_BASE             (SCS_BASE + 0x0100)
+#define SCB_BASE              (SCS_BASE + 0x0D00)
+
+
+/******************************************************************************/
+/*                         Peripheral declaration                             */
+/******************************************************************************/
+
+#define TIM1                ((TIM1_TypeDef *) TIM1_BASE)
+#define TIM3                ((TIM_TypeDef *) TIM3_BASE)
+#define TIM6                ((TIM6_TypeDef *) TIM6_BASE)
+#define TIM14               ((TIM_TypeDef *) TIM14_BASE)
+#define TIM15               ((TIM_TypeDef *) TIM15_BASE)
+#define TIM16               ((TIM_TypeDef *) TIM16_BASE)
+#define TIM17               ((TIM_TypeDef *) TIM17_BASE)
+
+#define RTC                 ((RTC_TypeDef *) RTC_BASE)
+
+#define WWDG                ((WWDG_TypeDef *) WWDG_BASE)
+#define IWDG                ((IWDG_TypeDef *) IWDG_BASE)
+
+#define USART2              ((USART_TypeDef *) USART2_BASE)
+#define USART1              ((USART_TypeDef *) USART1_BASE)
+
+#define I2C1                ((I2C_TypeDef *) I2C1_BASE)
+#define I2C2                ((I2C_TypeDef *) I2C2_BASE)
+
+#define PWR                 ((PWR_TypeDef *) PWR_BASE)
+#define EXTI                ((EXTI_TypeDef *) EXTI_BASE)
+#define SYSCFG 				((SYSCFG_TypeDef *) SYSCFG_BASE)
+
+#define GPIOA               ((GPIO_TypeDef *) GPIOA_BASE)
+#define GPIOB               ((GPIO_TypeDef *) GPIOB_BASE)
+#define GPIOC               ((GPIO_TypeDef *) GPIOC_BASE)
+#define GPIOD               ((GPIO_TypeDef *) GPIOD_BASE)
+#define GPIOF               ((GPIO_TypeDef *) GPIOF_BASE)
+
+#define ADC1                ((ADC_TypeDef *) ADC1_BASE)
+#define ADC2                ((ADC_TypeDef *) ADC2_BASE)
+
+
+#define SPI1                ((SPI_TypeDef *) SPI1_BASE)
+#define SPI2                ((SPI_TypeDef *) SPI2_BASE)
+
+#define USART1              ((USART_TypeDef *) USART1_BASE)
+#define USART2              ((USART_TypeDef *) USART2_BASE)
+
+#define DMA                 ((DMA_TypeDef *) DMA_BASE)
+#define DMA_Channel1        ((DMA_Channel_TypeDef *) DMA_Channel1_BASE)
+#define DMA_Channel2        ((DMA_Channel_TypeDef *) DMA_Channel2_BASE)
+#define DMA_Channel3        ((DMA_Channel_TypeDef *) DMA_Channel3_BASE)
+#define DMA_Channel4        ((DMA_Channel_TypeDef *) DMA_Channel4_BASE)
+#define DMA_Channel5        ((DMA_Channel_TypeDef *) DMA_Channel5_BASE)
+#define DMA_Channel6        ((DMA_Channel_TypeDef *) DMA_Channel6_BASE)
+#define DMA_Channel7        ((DMA_Channel_TypeDef *) DMA_Channel7_BASE)
+
+#define FLASH               ((FLASH_TypeDef *) FLASH_BASE)
+#define OB                  ((OB_TypeDef *) OB_BASE) 
+
+#define RCC                 ((RCC_TypeDef *) RCC_BASE)
+
+#define SysTick             ((SysTick_TypeDef *) SysTick_BASE)
+
+#define NVIC                ((NVIC_TypeDef *) NVIC_BASE)
+#define SCB                 ((SCB_TypeDef *) SCB_BASE)  
+
+#define eint() asm volatile("cpsie i")
+#define dint() asm volatile("cpsid i")
+
+#endif // __STM32F030_H
+
+typedef unsigned char uint8;
+typedef unsigned short uint16;
+typedef unsigned int uint32;
+typedef unsigned int uint;
+typedef int bool;

+ 632 - 0
build/stm32f10x.h

@@ -0,0 +1,632 @@
+/*******************************************************************************
+*
+* File Name          : stm32f10x.h
+* Author             : MCD Application Team, Paul Robson
+* Version            : V2.0
+* Date               : 20/09/2010
+* Description        : Includes as needed for STM32VLDISCOVERY coding.
+*					   Built from _type.h and _map.h and simplified.
+* Original Copyright : (c) 2007 ST Microelectronics
+*
+********************************************************************************
+* THE PRESENT SOFTWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS
+* WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE TIME.
+* AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY DIRECT,
+* INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING FROM THE
+* CONTENT OF SUCH SOFTWARE AND/OR THE USE MADE BY CUSTOMERS OF THE CODING
+* INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS.
+*******************************************************************************/
+
+#ifndef __STM32F10x_H
+#define __STM32F10x_H
+
+/******************************************************************************/
+/*								Data Types Needed							  */
+/******************************************************************************/
+
+typedef signed long  s32;
+typedef signed short s16;
+typedef signed char  s8;
+
+typedef signed long  const sc32;  /* Read Only */
+typedef signed short const sc16;  /* Read Only */
+typedef signed char  const sc8;   /* Read Only */
+
+typedef volatile signed long  vs32;
+typedef volatile signed short vs16;
+typedef volatile signed char  vs8;
+
+typedef volatile signed long  const vsc32;  /* Read Only */
+typedef volatile signed short const vsc16;  /* Read Only */
+typedef volatile signed char  const vsc8;   /* Read Only */
+
+typedef unsigned long  u32;
+typedef unsigned short u16;
+typedef unsigned char  u8;
+
+typedef unsigned long  const uc32;  /* Read Only */
+typedef unsigned short const uc16;  /* Read Only */
+typedef unsigned char  const uc8;   /* Read Only */
+
+typedef volatile unsigned long  vu32;
+typedef volatile unsigned short vu16;
+typedef volatile unsigned char  vu8;
+
+typedef volatile unsigned long  const vuc32;  /* Read Only */
+typedef volatile unsigned short const vuc16;  /* Read Only */
+typedef volatile unsigned char  const vuc8;   /* Read Only */
+
+/******************************************************************************/
+/*                         Peripheral registers structures                    */
+/******************************************************************************/
+
+/*------------------------ Analog to Digital Converter -----------------------*/
+typedef struct
+{
+  vu32 SR;
+  vu32 CR1;
+  vu32 CR2;
+  vu32 SMPR1;
+  vu32 SMPR2;
+  vu32 JOFR1;
+  vu32 JOFR2;
+  vu32 JOFR3;
+  vu32 JOFR4;
+  vu32 HTR;
+  vu32 LTR;
+  vu32 SQR1;
+  vu32 SQR2;
+  vu32 SQR3;
+  vu32 JSQR;
+  vu32 JDR1;
+  vu32 JDR2;
+  vu32 JDR3;
+  vu32 JDR4;
+  vu32 DR;
+} ADC_TypeDef;
+
+/*------------------------ Backup Registers ----------------------------------*/
+typedef struct
+{
+  u32 RESERVED0;
+  vu16 DR1;
+  u16  RESERVED1;
+  vu16 DR2;
+  u16  RESERVED2;
+  vu16 DR3;
+  u16  RESERVED3;
+  vu16 DR4;
+  u16  RESERVED4;
+  vu16 DR5;
+  u16  RESERVED5;
+  vu16 DR6;
+  u16  RESERVED6;
+  vu16 DR7;
+  u16  RESERVED7;
+  vu16 DR8;
+  u16  RESERVED8;
+  vu16 DR9;
+  u16  RESERVED9;
+  vu16 DR10;
+  u16  RESERVED10;
+  vu16 RTCCR;
+  u16  RESERVED11;
+  vu16 CR;
+  u16  RESERVED12;
+  vu16 CSR;
+  u16  RESERVED13;
+} BKP_TypeDef;
+
+/*------------------------ Controller Area Network ---------------------------*/
+typedef struct
+{
+  vu32 TIR;
+  vu32 TDTR;
+  vu32 TDLR;
+  vu32 TDHR;
+} CAN_TxMailBox_TypeDef;
+
+typedef struct
+{
+  vu32 RIR;
+  vu32 RDTR;
+  vu32 RDLR;
+  vu32 RDHR;
+} CAN_FIFOMailBox_TypeDef;
+
+typedef struct
+{
+  vu32 FR0;
+  vu32 FR1;
+} CAN_FilterRegister_TypeDef;
+
+typedef struct
+{
+  vu32 MCR;
+  vu32 MSR;
+  vu32 TSR;
+  vu32 RF0R;
+  vu32 RF1R;
+  vu32 IER;
+  vu32 ESR;
+  vu32 BTR;
+  u32 RESERVED0[88];
+  CAN_TxMailBox_TypeDef sTxMailBox[3];
+  CAN_FIFOMailBox_TypeDef sFIFOMailBox[2];
+  u32 RESERVED1[12];
+  vu32 FMR;
+  vu32 FM0R;
+  u32 RESERVED2[1];
+  vu32 FS0R;
+  u32 RESERVED3[1];
+  vu32 FFA0R;
+  u32 RESERVED4[1];
+  vu32 FA0R;
+  u32 RESERVED5[8];
+  CAN_FilterRegister_TypeDef sFilterRegister[14];
+} CAN_TypeDef;
+
+/*------------------------ DMA Controller ------------------------------------*/
+typedef struct
+{
+  vu32 CCR;
+  vu32 CNDTR;
+  vu32 CPAR;
+  vu32 CMAR;
+} DMA_Channel_TypeDef;
+
+typedef struct
+{
+  vu32 ISR;
+  vu32 IFCR;
+} DMA_TypeDef;
+
+/*------------------------ External Interrupt/Event Controller ---------------*/
+typedef struct
+{
+  vu32 IMR;
+  vu32 EMR;
+  vu32 RTSR;
+  vu32 FTSR;
+  vu32 SWIER;
+  vu32 PR;
+} EXTI_TypeDef;
+
+/*------------------------ FLASH and Option Bytes Registers ------------------*/
+typedef struct
+{
+  vu32 ACR;
+  vu32 KEYR;
+  vu32 OPTKEYR;
+  vu32 SR;
+  vu32 CR;
+  vu32 AR;
+  vu32 RESERVED;
+  vu32 OBR;
+  vu32 WRPR;
+} FLASH_TypeDef;
+
+typedef struct
+{
+  vu16 RDP;
+  vu16 USER;
+  vu16 Data0;
+  vu16 Data1;
+  vu16 WRP0;
+  vu16 WRP1;
+  vu16 WRP2;
+  vu16 WRP3;
+} OB_TypeDef;
+
+/*------------------------ General Purpose and Alternate Function IO ---------*/
+typedef struct
+{
+  vu32 CRL;
+  vu32 CRH;
+  vu32 IDR;
+  vu32 ODR;
+  vu32 BSRR;
+  vu32 BRR;
+  vu32 LCKR;
+} GPIO_TypeDef;
+
+typedef struct
+{
+  vu32 EVCR;
+  vu32 MAPR;
+  vu32 EXTICR[4];
+} AFIO_TypeDef;
+
+/*------------------------ Inter-integrated Circuit Interface ----------------*/
+typedef struct
+{
+  vu16 CR1;
+  u16 RESERVED0;
+  vu16 CR2;
+  u16 RESERVED1;
+  vu16 OAR1;
+  u16 RESERVED2;
+  vu16 OAR2;
+  u16 RESERVED3;
+  vu16 DR;
+  u16 RESERVED4;
+  vu16 SR1;
+  u16 RESERVED5;
+  vu16 SR2;
+  u16 RESERVED6;
+  vu16 CCR;
+  u16 RESERVED7;
+  vu16 TRISE;
+  u16 RESERVED8;
+} I2C_TypeDef;
+
+/*------------------------ Independent WATCHDOG ------------------------------*/
+typedef struct
+{
+  vu32 KR;
+  vu32 PR;
+  vu32 RLR;
+  vu32 SR;
+} IWDG_TypeDef;
+
+/*------------------------ Nested Vectored Interrupt Controller --------------*/
+typedef struct
+{
+  vu32 ISER[2];
+  u32 RESERVED0[30];
+  vu32 ICER[2];
+  u32 RSERVED1[30];
+  vu32 ISPR[2];
+  u32 RESERVED2[30];
+  vu32 ICPR[2];
+  u32 RESERVED3[30];
+  vu32 IABR[2];
+  u32 RESERVED4[62];
+  vu32 IPR[11];
+} NVIC_TypeDef;
+
+typedef struct
+{
+  vuc32 CPUID;
+  vu32 ICSR;
+  vu32 VTOR;
+  vu32 AIRCR;
+  vu32 SCR;
+  vu32 CCR;
+  vu32 SHPR[3];
+  vu32 SHCSR;
+  vu32 CFSR;
+  vu32 HFSR;
+  vu32 DFSR;
+  vu32 MMFAR;
+  vu32 BFAR;
+  vu32 AFSR;
+} SCB_TypeDef;
+
+/*------------------------ Power Control -------------------------------------*/
+typedef struct
+{
+  vu32 CR;
+  vu32 CSR;
+} PWR_TypeDef;
+
+/*------------------------ Reset and Clock Control ---------------------------*/
+typedef struct
+{
+  vu32 CR;
+  vu32 CFGR;
+  vu32 CIR;
+  vu32 APB2RSTR;
+  vu32 APB1RSTR;
+  vu32 AHBENR;
+  vu32 APB2ENR;
+  vu32 APB1ENR;
+  vu32 BDCR;
+  vu32 CSR;
+} RCC_TypeDef;
+
+/*------------------------ Real-Time Clock -----------------------------------*/
+typedef struct
+{
+  vu16 CRH;
+  u16 RESERVED0;
+  vu16 CRL;
+  u16 RESERVED1;
+  vu16 PRLH;
+  u16 RESERVED2;
+  vu16 PRLL;
+  u16 RESERVED3;
+  vu16 DIVH;
+  u16 RESERVED4;
+  vu16 DIVL;
+  u16 RESERVED5;
+  vu16 CNTH;
+  u16 RESERVED6;
+  vu16 CNTL;
+  u16 RESERVED7;
+  vu16 ALRH;
+  u16 RESERVED8;
+  vu16 ALRL;
+  u16 RESERVED9;
+} RTC_TypeDef;
+
+/*------------------------ Serial Peripheral Interface -----------------------*/
+typedef struct
+{
+  vu16 CR1;
+  u16 RESERVED0;
+  vu16 CR2;
+  u16 RESERVED1;
+  vu16 SR;
+  u16  RESERVED2;
+  vu16 DR;
+  u16  RESERVED3;
+  vu16 CRCPR;
+  u16 RESERVED4;
+  vu16 RXCRCR;
+  u16  RESERVED5;
+  vu16 TXCRCR;
+  u16  RESERVED6;
+} SPI_TypeDef;
+
+/*------------------------ SystemTick ----------------------------------------*/
+typedef struct
+{
+  vu32 CTRL;
+  vu32 LOAD;
+  vu32 VAL;
+  vuc32 CALIB;
+} SysTick_TypeDef;
+
+/*------------------------ Advanced Control Timer ----------------------------*/
+typedef struct
+{
+  vu16 CR1;
+  u16 RESERVED0;
+  vu16 CR2;
+  u16 RESERVED1;
+  vu16 SMCR;
+  u16 RESERVED2;
+  vu16 DIER;
+  u16 RESERVED3;
+  vu16 SR;
+  u16 RESERVED4;
+  vu16 EGR;
+  u16 RESERVED5;
+  vu16 CCMR1;
+  u16 RESERVED6;
+  vu16 CCMR2;
+  u16 RESERVED7;
+  vu16 CCER;
+  u16 RESERVED8;
+  vu16 CNT;
+  u16 RESERVED9;
+  vu16 PSC;
+  u16 RESERVED10;
+  vu16 ARR;
+  u16 RESERVED11;
+  vu16 RCR;
+  u16 RESERVED12;
+  vu16 CCR1;
+  u16 RESERVED13;
+  vu16 CCR2;
+  u16 RESERVED14;
+  vu16 CCR3;
+  u16 RESERVED15;
+  vu16 CCR4;
+  u16 RESERVED16;
+  vu16 BDTR;
+  u16 RESERVED17;
+  vu16 DCR;
+  u16 RESERVED18;
+  vu16 DMAR;
+  u16 RESERVED19;
+} TIM1_TypeDef;
+
+/*------------------------ General Purpose Timer -----------------------------*/
+typedef struct
+{
+  vu16 CR1;
+  u16 RESERVED0;
+  vu16 CR2;
+  u16 RESERVED1;
+  vu16 SMCR;
+  u16 RESERVED2;
+  vu16 DIER;
+  u16 RESERVED3;
+  vu16 SR;
+  u16 RESERVED4;
+  vu16 EGR;
+  u16 RESERVED5;
+  vu16 CCMR1;
+  u16 RESERVED6;
+  vu16 CCMR2;
+  u16 RESERVED7;
+  vu16 CCER;
+  u16 RESERVED8;
+  vu16 CNT;
+  u16 RESERVED9;
+  vu16 PSC;
+  u16 RESERVED10;
+  vu16 ARR;
+  u16 RESERVED11[3];
+  vu16 CCR1;
+  u16 RESERVED12;
+  vu16 CCR2;
+  u16 RESERVED13;
+  vu16 CCR3;
+  u16 RESERVED14;
+  vu16 CCR4;
+  u16 RESERVED15[3];
+  vu16 DCR;
+  u16 RESERVED16;
+  vu16 DMAR;
+  u16 RESERVED17;
+} TIM_TypeDef;
+
+/*----------------- Universal Synchronous Asynchronous Receiver Transmitter --*/
+typedef struct
+{
+  vu16 SR;
+  u16 RESERVED0;
+  vu16 DR;
+  u16 RESERVED1;
+  vu16 BRR;
+  u16 RESERVED2;
+  vu16 CR1;
+  u16 RESERVED3;
+  vu16 CR2;
+  u16 RESERVED4;
+  vu16 CR3;
+  u16 RESERVED5;
+  vu16 GTPR;
+  u16 RESERVED6;
+} USART_TypeDef;
+
+/*------------------------ Window WATCHDOG -----------------------------------*/
+typedef struct
+{
+  vu32 CR;
+  vu32 CFR;
+  vu32 SR;
+} WWDG_TypeDef;
+
+
+/******************************************************************************/
+/*                         Peripheral memory map                              */
+/******************************************************************************/
+/* Peripheral and SRAM base address in the alias region */
+#define PERIPH_BB_BASE        ((u32)0x42000000)
+#define SRAM_BB_BASE          ((u32)0x22000000)
+
+/* Peripheral and SRAM base address in the bit-band region */
+#define SRAM_BASE             ((u32)0x20000000)
+#define PERIPH_BASE           ((u32)0x40000000)
+
+/* Flash refisters base address */
+#define FLASH_BASE            ((u32)0x40022000)
+/* Flash Option Bytes base address */
+#define OB_BASE               ((u32)0x1FFFF800)
+
+/* Peripheral memory map */
+#define APB1PERIPH_BASE       PERIPH_BASE
+#define APB2PERIPH_BASE       (PERIPH_BASE + 0x10000)
+#define AHBPERIPH_BASE        (PERIPH_BASE + 0x20000)
+
+#define TIM2_BASE             (APB1PERIPH_BASE + 0x0000)
+#define TIM3_BASE             (APB1PERIPH_BASE + 0x0400)
+#define TIM4_BASE             (APB1PERIPH_BASE + 0x0800)
+#define RTC_BASE              (APB1PERIPH_BASE + 0x2800)
+#define WWDG_BASE             (APB1PERIPH_BASE + 0x2C00)
+#define IWDG_BASE             (APB1PERIPH_BASE + 0x3000)
+#define SPI2_BASE             (APB1PERIPH_BASE + 0x3800)
+#define USART2_BASE           (APB1PERIPH_BASE + 0x4400)
+#define USART3_BASE           (APB1PERIPH_BASE + 0x4800)
+#define I2C1_BASE             (APB1PERIPH_BASE + 0x5400)
+#define I2C2_BASE             (APB1PERIPH_BASE + 0x5800)
+#define CAN_BASE              (APB1PERIPH_BASE + 0x6400)
+#define BKP_BASE              (APB1PERIPH_BASE + 0x6C00)
+#define PWR_BASE              (APB1PERIPH_BASE + 0x7000)
+
+#define AFIO_BASE             (APB2PERIPH_BASE + 0x0000)
+#define EXTI_BASE             (APB2PERIPH_BASE + 0x0400)
+#define GPIOA_BASE            (APB2PERIPH_BASE + 0x0800)
+#define GPIOB_BASE            (APB2PERIPH_BASE + 0x0C00)
+#define GPIOC_BASE            (APB2PERIPH_BASE + 0x1000)
+#define GPIOD_BASE            (APB2PERIPH_BASE + 0x1400)
+#define GPIOE_BASE            (APB2PERIPH_BASE + 0x1800)
+#define ADC1_BASE             (APB2PERIPH_BASE + 0x2400)
+#define ADC2_BASE             (APB2PERIPH_BASE + 0x2800)
+#define TIM1_BASE             (APB2PERIPH_BASE + 0x2C00)
+#define SPI1_BASE             (APB2PERIPH_BASE + 0x3000)
+#define USART1_BASE           (APB2PERIPH_BASE + 0x3800)
+#define TIM15_BASE            (APB2PERIPH_BASE + 0x4000)
+#define TIM16_BASE            (APB2PERIPH_BASE + 0x4400)
+#define TIM17_BASE            (APB2PERIPH_BASE + 0x4800)
+
+#define DMA_BASE              (AHBPERIPH_BASE + 0x0000)
+#define DMA_Channel1_BASE     (AHBPERIPH_BASE + 0x0008)
+#define DMA_Channel2_BASE     (AHBPERIPH_BASE + 0x001C)
+#define DMA_Channel3_BASE     (AHBPERIPH_BASE + 0x0030)
+#define DMA_Channel4_BASE     (AHBPERIPH_BASE + 0x0044)
+#define DMA_Channel5_BASE     (AHBPERIPH_BASE + 0x0058)
+#define DMA_Channel6_BASE     (AHBPERIPH_BASE + 0x006C)
+#define DMA_Channel7_BASE     (AHBPERIPH_BASE + 0x0080)
+#define RCC_BASE              (AHBPERIPH_BASE + 0x1000)
+
+/* System Control Space memory map */
+#define SCS_BASE              ((u32)0xE000E000)
+
+#define SysTick_BASE          (SCS_BASE + 0x0010)
+#define NVIC_BASE             (SCS_BASE + 0x0100)
+#define SCB_BASE              (SCS_BASE + 0x0D00)
+
+
+/******************************************************************************/
+/*                         Peripheral declaration                             */
+/******************************************************************************/
+
+#define TIM1                ((TIM1_TypeDef *) TIM1_BASE)
+#define TIM2                ((TIM_TypeDef *) TIM2_BASE)
+#define TIM3                ((TIM_TypeDef *) TIM3_BASE)
+#define TIM4                ((TIM_TypeDef *) TIM4_BASE)
+#define TIM15               ((TIM_TypeDef *) TIM15_BASE)
+#define TIM16               ((TIM_TypeDef *) TIM16_BASE)
+#define TIM17               ((TIM_TypeDef *) TIM17_BASE)
+
+#define RTC                 ((RTC_TypeDef *) RTC_BASE)
+
+#define WWDG                ((WWDG_TypeDef *) WWDG_BASE)
+#define IWDG                ((IWDG_TypeDef *) IWDG_BASE)
+
+#define USART2              ((USART_TypeDef *) USART2_BASE)
+#define USART3              ((USART_TypeDef *) USART3_BASE)
+
+#define I2C1                ((I2C_TypeDef *) I2C1_BASE)
+#define I2C2                ((I2C_TypeDef *) I2C2_BASE)
+
+#define CAN                 ((CAN_TypeDef *) CAN_BASE)
+
+#define BKP                 ((BKP_TypeDef *) BKP_BASE)
+
+#define PWR                 ((PWR_TypeDef *) PWR_BASE)
+
+#define AFIO                ((AFIO_TypeDef *) AFIO_BASE)
+
+#define EXTI                ((EXTI_TypeDef *) EXTI_BASE)
+
+#define GPIOA               ((GPIO_TypeDef *) GPIOA_BASE)
+#define GPIOB               ((GPIO_TypeDef *) GPIOB_BASE)
+#define GPIOC               ((GPIO_TypeDef *) GPIOC_BASE)
+#define GPIOD               ((GPIO_TypeDef *) GPIOD_BASE)
+#define GPIOE               ((GPIO_TypeDef *) GPIOE_BASE)
+
+#define ADC1                ((ADC_TypeDef *) ADC1_BASE)
+#define ADC2                ((ADC_TypeDef *) ADC2_BASE)
+
+
+#define SPI1                ((SPI_TypeDef *) SPI1_BASE)
+#define SPI2                ((SPI_TypeDef *) SPI2_BASE)
+
+#define USART1              ((USART_TypeDef *) USART1_BASE)
+
+#define DMA                 ((DMA_TypeDef *) DMA_BASE)
+#define DMA_Channel1        ((DMA_Channel_TypeDef *) DMA_Channel1_BASE)
+#define DMA_Channel2        ((DMA_Channel_TypeDef *) DMA_Channel2_BASE)
+#define DMA_Channel3        ((DMA_Channel_TypeDef *) DMA_Channel3_BASE)
+#define DMA_Channel4        ((DMA_Channel_TypeDef *) DMA_Channel4_BASE)
+#define DMA_Channel5        ((DMA_Channel_TypeDef *) DMA_Channel5_BASE)
+#define DMA_Channel6        ((DMA_Channel_TypeDef *) DMA_Channel6_BASE)
+#define DMA_Channel7        ((DMA_Channel_TypeDef *) DMA_Channel7_BASE)
+
+#define FLASH               ((FLASH_TypeDef *) FLASH_BASE)
+#define OB                  ((OB_TypeDef *) OB_BASE) 
+
+#define RCC                 ((RCC_TypeDef *) RCC_BASE)
+
+#define SysTick             ((SysTick_TypeDef *) SysTick_BASE)
+
+#define NVIC                ((NVIC_TypeDef *) NVIC_BASE)
+#define SCB                 ((SCB_TypeDef *) SCB_BASE)  
+
+#endif

BIN
dispatch1.jpg


BIN
dispatch2.jpg


+ 14 - 0
door.dot

@@ -0,0 +1,14 @@
+/* RFID controlled door lock */
+digraph {
+  pad=0.5; bgcolor="lightgrey"; nodesep=1;
+  node [shape="square"; style="filled";  fillcolor="cyan"];
+  edge [labelfontsize=10; minlen=2; labeldistance=2; ];
+  DOOR_LOCKED -> DOOR_UNLOCKED [taillabel="EVT_RFID_OK"];
+  DOOR_LOCKED -> DOOR_LOCKED [taillabel="EVT_RFID_NOK"];
+  DOOR_UNLOCKED -> REPORTING [taillabel="EVT_TIMER_DONE"];
+  DOOR_LOCKED -> DOOR_UNLOCKED [taillabel="EVT_INTERNAL_BUTTON"];
+  DOOR_UNLOCKED -> DOOR_UNLOCKED [taillabel="EVT_INTERNAL_BUTTON"];
+  REPORTING -> DOOR_LOCKED [taillabel="EVT_REPORT_DONE"];
+  {rank=same; DOOR_LOCKED DOOR_UNLOCKED}
+}
+

BIN
door.png


+ 15 - 0
door2.dot

@@ -0,0 +1,15 @@
+/* RFID controlled door lock */
+digraph {
+  pad=0.5; bgcolor="lightgrey"; nodesep=1;
+  node [shape="square"; style="filled";  fillcolor="cyan"];
+  edge [labelfontsize=10; minlen=2; labeldistance=2;  ];
+  DOOR_LOCKED -> KEY_ANALYSIS [taillabel="EVT_RFID_DETECTED"];
+  KEY_ANALYSIS -> DOOR_UNLOCKED [taillabel="MSG_KEY_OK"];
+  KEY_ANALYSIS -> DOOR_LOCKED [taillabel="MSG_KEY_NOK"];
+  DOOR_UNLOCKED -> REPORTING [taillabel="EVT_TIMER_DONE"];
+  DOOR_LOCKED -> DOOR_UNLOCKED [taillabel="EVT_INTERNAL_BUTTON"];
+  DOOR_UNLOCKED -> DOOR_UNLOCKED [taillabel="EVT_INTERNAL_BUTTON"];
+  REPORTING -> DOOR_LOCKED [taillabel="EVT_REPORT_DONE"];
+  {rank=same; DOOR_LOCKED DOOR_UNLOCKED}
+}
+

BIN
door2.png


+ 11 - 0
fridge_state.dot

@@ -0,0 +1,11 @@
+digraph {
+  pad=0.5; bgcolor="lightgrey"; nodesep=1;
+  node [shape="box"; style="filled";  fillcolor="cyan";  ];
+  edge [labelfontsize=10; minlen=2; labeldistance=3 ];
+  DOOR_CLOSED -> DOOR_OPEN [taillabel="EVT_OPEN"];
+  DOOR_OPEN -> BEEPING [taillabel="EVT_TICK \n&& counter==0"];
+  BEEPING -> DOOR_CLOSED [taillabel="EVT_CLOSE"];
+  DOOR_OPEN -> DOOR_CLOSED  [taillabel="EVT_CLOSE"];
+  {rank=same; DOOR_OPEN BEEPING}
+}
+

BIN
fridge_state.png


+ 10 - 0
led_state.dot

@@ -0,0 +1,10 @@
+digraph {
+  pad=0.5; bgcolor="lightgrey";
+  node [shape="box"; style="filled";  fillcolor="cyan"];
+  edge [labelfontsize=10; minlen=2; labeldistance=5 ];
+  LED_OFF -> LED_ON [taillabel="EVT_TICK \n&& counter==0"];
+  LED_ON -> LED_OFF [taillabel="EVT_TICK \n&& counter==0"];
+  LED_OFF -> LED_OFF [taillabel="EVT_TICK \n&& counter!=0"];
+  LED_ON  -> LED_ON  [taillabel="EVT_TICK \n&& counter!=0"];
+}
+

BIN
led_state.png


+ 10 - 0
led_state_simple.dot

@@ -0,0 +1,10 @@
+digraph {
+  pad=0.5; bgcolor="lightgrey";
+  node [shape="box"; style="filled";  fillcolor="cyan"];
+  edge [labelfontsize=10; minlen=2; labeldistance=4 ];
+  LED_OFF -> LED_ON [taillabel="EVT_TICK \n&& counter==0";
+    tailport=sw; headport=nw];
+  LED_ON -> LED_OFF [taillabel="EVT_TICK \n&& counter==0";
+    tailport=ne; headport=se];
+}
+

BIN
led_state_simple.png


+ 25 - 0
makefile

@@ -0,0 +1,25 @@
+SOURCES=CooperativeMultitasking.md
+IMG_SOURCES=led_state.dot led_state_simple.dot rtb_state.dot fridge_state.dot \
+  rtb_state_2.dot rtb_state_3.dot rtb_state_4.dot rtb_state_5.dot \
+  window_state.dot radio_tuner.dot beer_vat.dot \
+  window_complex.dot door.dot door2.dot vending.dot
+
+
+OBJECTS=$(SOURCES:.md=.html)
+IMG_OBJECTS=$(IMG_SOURCES:.dot=.png)
+
+CC=pandoc -s -t html -B header.md -A footer.md
+CC=pandoc -s -t html --toc --css pandoc.css --toc-depth=3 
+IM=dot -Tpng 
+
+%.html : %.md
+	$(CC) $< -o $@
+
+%.png: %.dot
+	$(IM) $< -o $@
+
+all: $(OBJECTS) $(IMG_OBJECTS)
+	echo "done"
+
+clean:
+	rm $(OBJECTS)

BIN
postMessage.png


+ 19 - 0
radio_tuner.dot

@@ -0,0 +1,19 @@
+/* front panel of a radio */
+digraph {
+  pad=0.5; bgcolor="lightgrey"; nodesep=1;
+  node [shape="box"; style="filled";  fillcolor="cyan";  ];
+  edge [labelfontsize=10; minlen=2; labeldistance=2 ];
+  IDLE -> FREQ_UP [taillabel="EVT_UP_PRESS";]
+  FREQ_UP -> IDLE [taillabel="EVT_UP_RELEASE"]
+  FREQ_UP -> FREQ_UP_FAST [taillabel="EVT_TICK\n&& counter==0"];
+  FREQ_UP_FAST -> IDLE [taillabel="EVT_UP_RELEASE"];
+  FREQ_UP_FAST -> FREQ_UP_FAST [taillabel="EVT_TICK"];
+
+  IDLE -> FREQ_DN [taillabel="EVT_DN_PRESS";]
+  FREQ_DN -> IDLE [taillabel="EVT_DN_RELEASE"]
+  FREQ_DN -> FREQ_DN_FAST [taillabel="EVT_TICK\n&& counter==0"];
+  FREQ_DN_FAST -> IDLE [taillabel="EVT_DN_RELEASE"];
+  FREQ_DN_FAST -> FREQ_DN_FAST [taillabel="EVT_TICK"];
+  {rank=same; FREQ_UP FREQ_DN}
+}
+

BIN
radio_tuner.png


+ 10 - 0
rtb_state.dot

@@ -0,0 +1,10 @@
+digraph {
+  pad=0.5; bgcolor="lightgrey";
+  node [shape="box"; style="filled";  fillcolor="cyan"];
+  edge [labelfontsize=10; minlen=2; labeldistance=4;  ];
+  RTB_IDLE -> RTB_ON [taillabel="EVT_BUTTON"; 
+    tailport=sw; headport=nw];
+  RTB_ON -> RTB_IDLE [taillabel="EVT_TICK \n&& counter==0\n"; 
+    tailport=ne; headport=se];
+}
+

BIN
rtb_state.png


+ 11 - 0
rtb_state_2.dot

@@ -0,0 +1,11 @@
+/* restart LED on each key press */
+
+digraph {
+  pad=0.5; bgcolor="lightgrey"; nodesep=1;
+  node [shape="box"; style="filled";  fillcolor="cyan"];
+  edge [labelfontsize=10; minlen=2; labeldistance=2 ];
+  RTB_IDLE -> RTB_ON [taillabel="EVT_BUTTON                      "];
+  RTB_ON -> RTB_IDLE [taillabel="                EVT_TICK \n               && counter==0"];
+  RTB_ON -> RTB_ON [taillabel="                            EVT_BUTTON"];
+}
+

BIN
rtb_state_2.png


+ 10 - 0
rtb_state_3.dot

@@ -0,0 +1,10 @@
+/* button press also turns LED off */
+digraph {
+  pad=0.5; bgcolor="lightgrey"; nodesep=1;
+  node [shape="box"; style="filled";  fillcolor="cyan"];
+  edge [labelfontsize=10; minlen=2; labeldistance=3 ];
+  RTB_IDLE -> RTB_ON [taillabel="EVT_BUTTON                      "];
+  RTB_ON -> RTB_IDLE [taillabel="                EVT_TICK \n               && counter==0"];
+  RTB_ON -> RTB_IDLE [taillabel="                EVT_BUTTON";];
+}
+

BIN
rtb_state_3.png


+ 14 - 0
rtb_state_4.dot

@@ -0,0 +1,14 @@
+/* start a looped timer*/
+
+digraph {
+  pad=0.5; bgcolor="lightgrey"; nodesep=1;
+  node [shape="box"; style="filled";  fillcolor="cyan"];
+  edge [labelfontsize=10; minlen=2; labeldistance=3 ];
+  RTB_IDLE -> RTB_ON [taillabel="EVT_BUTTON"];
+  RTB_ON -> RTB_OFF [taillabel="EVT_TICK \n&& counter==0"];
+  RTB_ON -> RTB_IDLE [taillabel="EVT_BUTTON"];
+  RTB_OFF -> RTB_ON [taillabel="EVT_TICK           \n&&counter==0                ";]
+  RTB_OFF -> RTB_IDLE [taillabel="EVT_BUTTON"; labeldistance=5];
+  {rank=same; RTB_ON RTB_OFF}
+}
+

BIN
rtb_state_4.png


+ 11 - 0
rtb_state_5.dot

@@ -0,0 +1,11 @@
+/* start a looped timer*/
+
+digraph {
+  pad=0.5; bgcolor="lightgrey"; nodesep=1;
+  node [shape="box"; style="filled";  fillcolor="cyan"];
+  edge [labelfontsize=10; minlen=2; labeldistance=3 ];
+  RTB_IDLE -> RTB_FLASHING [taillabel="EVT_BUTTON"];
+  RTB_FLASHING -> RTB_IDLE [taillabel="EVT_BUTTON"];
+  RTB_FLASHING -> RTB_FLASHING [taillabel="EVT_TICK"];
+}
+

BIN
rtb_state_5.png


BIN
sendMessage.png


+ 20 - 0
vending.dot

@@ -0,0 +1,20 @@
+/* vending machine */
+
+digraph {
+  pad=0.5; bgcolor="lightgrey";
+  node [shape="box"; style="filled";  fillcolor="cyan"];
+  edge [labelfontsize=10; minlen=2; labeldistance=4;  ];
+  VEND_IDLE -> VEND_PRE_SHOW [taillabel="EVT_SELECT_BUTTON"];
+  VEND_PRE_SHOW -> VEND_IDLE [taillabel="EVT_TIMER"];
+  VEND_PRE_SHOW -> VEND_DISPENSE [taillabel="EVT_PAYMENT"];
+  VEND_IDLE -> VEND_WAIT_SELECT [taillabel="EVT_PAYMENT"];
+  VEND_WAIT_SELECT -> VEND_DISPENSE [taillabel="EVT_SELECT_BUTTON"];
+  VEND_DISPENSE -> VEND_PRODUCT_READY [taillabel="EVT_DISPENSE_COMPLETE"];
+  VEND_PRODUCT_READY -> VEND_IDLE [taillabel="EVT_BIN_EMPTY"];
+  VEND_IDLE [label="VEND_IDLE\ndisplay attraction mode"];
+  VEND_PRE_SHOW [label="VEND_PRE_SHOW\nrequest payment"];
+  VEND_WAIT_SELECT [label="VEND_WAIT_SELECT\nrequest selection"];
+  VEND_DISPENSE [label="VEND_DISPENSE\noperate motor"];
+  VEND_PRODUCT_READY [label="VEND_PRODUCT_READY\nuser can remove product"];
+}
+

BIN
vending.png


+ 22 - 0
window_complex.dot

@@ -0,0 +1,22 @@
+/* automobile power window with auto rise/lower */
+
+digraph {
+  pad=0.5; bgcolor="lightgrey"; nodesep=1.2;
+  node [shape="box"; style="filled";  fillcolor="cyan"];
+  edge [labelfontsize=10; minlen=2; labeldistance=3 ];
+  WINDOW_STOPPED_ms -> WINDOW_GOING_UP_ma [taillabel="EVT_UP_BUTTON"];
+  WINDOW_GOING_UP_ma -> WINDOW_UP_TIMING_ms [taillabel="EVT_BUTTON_RELEASE"];
+  WINDOW_UP_TIMING_ms -> WINDOW_STOPPED_ms [taillabel="EVT_TICK && timout"];
+  WINDOW_UP_TIMING_ms -> WINDOW_AUTO_UP_ma [taillabel="EVT_UP_BUTTON"];
+  WINDOW_AUTO_UP_ma -> WINDOW_STOPPED_ms [taillabel="EVT_MOTOR_SENSE"];  
+
+  WINDOW_STOPPED_ms -> WINDOW_GOING_DN_ma [taillabel="EVT_DN_BUTTON"];
+  WINDOW_GOING_DN_ma -> WINDOW_DN_TIMING_ms [taillabel="EVT_BUTTON_RELEASE"];
+  WINDOW_DN_TIMING_ms -> WINDOW_STOPPED_ms [taillabel="EVT_TICK && timout"];
+  WINDOW_DN_TIMING_ms -> WINDOW_AUTO_DN_ma [taillabel="EVT_UP_BUTTON"];
+  WINDOW_AUTO_DN_ma -> WINDOW_STOPPED_ms [taillabel="EVT_MOTOR_SENSE"];  
+
+  WINDOW_GOING_UP_ma -> WINDOW_STOPPED_ms [taillabel="EVT_MOTOR_SENSE"];
+  WINDOW_GOING_DN_ma -> WINDOW_STOPPED_ms [taillabel="EVT_MOTOR_SENSE"];
+}
+

BIN
window_complex.png


+ 14 - 0
window_state.dot

@@ -0,0 +1,14 @@
+/* automobile power window */
+
+digraph {
+  pad=0.5; bgcolor="lightgrey"; nodesep=1.2;
+  node [shape="box"; style="filled";  fillcolor="cyan"];
+  edge [labelfontsize=10; minlen=2; labeldistance=5 ];
+  WINDOW_STOPPED -> WINDOW_GOING_UP [taillabel="EVT_UP_BUTTON"];
+  WINDOW_STOPPED -> WINDOW_GOING_DN [taillabel="           EVT_DN_BUTTON"];
+  WINDOW_GOING_UP -> WINDOW_STOPPED [taillabel="EVT_MOTOR_SENSE\n   \n"];
+  WINDOW_GOING_DN -> WINDOW_STOPPED [taillabel="EVT_MOTOR_SENSE"];
+  WINDOW_GOING_UP -> WINDOW_STOPPED [taillabel="         EVT_BUTTON_RELEASE"];
+  WINDOW_GOING_DN -> WINDOW_STOPPED [taillabel="EVT_BUTTON_RELEASE"];
+}
+

BIN
window_state.png