|
@@ -5,7 +5,7 @@ email: <patb@pbeirne.com>
|
|
date: 2025/01/15
|
|
date: 2025/01/15
|
|
license: MIT
|
|
license: MIT
|
|
---
|
|
---
|
|
-
|
|
|
|
|
|
+<style> h2 { border-top: solid thin #899;} </style>
|
|
# Part 1: Cooperative Multitasking
|
|
# Part 1: Cooperative Multitasking
|
|
|
|
|
|
<!-- ## Introduction -->
|
|
<!-- ## Introduction -->
|
|
@@ -22,7 +22,7 @@ The core of *this* technique is covered in
|
|
[Part 1](#part-1-cooperative-multitasking)
|
|
[Part 1](#part-1-cooperative-multitasking)
|
|
of this paper.
|
|
of this paper.
|
|
If you just want to see how it all comes together,
|
|
If you just want to see how it all comes together,
|
|
-jump to [Final Implementaion](#final-implementation).
|
|
|
|
|
|
+jump to [Final Implementation](#final-implementation).
|
|
|
|
|
|
[Part 2](#part-2) contains enhancements and variations,
|
|
[Part 2](#part-2) contains enhancements and variations,
|
|
probably useful reading if you decide to adopt
|
|
probably useful reading if you decide to adopt
|
|
@@ -86,7 +86,7 @@ to make this happen?
|
|
|
|
|
|
## First Page
|
|
## First Page
|
|
|
|
|
|
-Let's look an a pseudocode overview of what we want to do:
|
|
|
|
|
|
+Let's look at the pseudocode overview of what we want to do:
|
|
|
|
|
|
```C
|
|
```C
|
|
void initialize(void) {
|
|
void initialize(void) {
|
|
@@ -137,12 +137,12 @@ In order to have a responsive system, it would make sense to use the
|
|
|
|
|
|
In the task described, we need to respond to a button press.
|
|
In the task described, we need to respond to a button press.
|
|
So let's connect the button to
|
|
So let's connect the button to
|
|
-an input pin and enable it to repsond to a button press with interrupt code.
|
|
|
|
|
|
+an input pin and enable it to respond 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.
|
|
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
|
|
Each interrupt causes the execution of *interrupt handler* code. For this project, it might look
|
|
-somthing like this:
|
|
|
|
|
|
+something like this:
|
|
|
|
|
|
```C
|
|
```C
|
|
enum {EVT_NONE, EVT_TICK, EVT_BUTTON};
|
|
enum {EVT_NONE, EVT_TICK, EVT_BUTTON};
|
|
@@ -198,7 +198,7 @@ Implemented, it can be a *byte*, or an *int* or an even larger structure.
|
|
But in these small microcontrollers, let's use a *byte*.
|
|
But in these small microcontrollers, let's use a *byte*.
|
|
|
|
|
|
```C
|
|
```C
|
|
-volatile uint8 event;
|
|
|
|
|
|
+volatile uchar event;
|
|
```
|
|
```
|
|
|
|
|
|
These *events* will be created in interrupt level of the code,
|
|
These *events* will be created in interrupt level of the code,
|
|
@@ -218,7 +218,7 @@ and *non-zero* to represent
|
|
an event/message.
|
|
an event/message.
|
|
|
|
|
|
```C
|
|
```C
|
|
-volatile uint8 event;
|
|
|
|
|
|
+volatile uchar event;
|
|
enum {EVT_NONE, EVT_TICK, EVT_BUTTON}; // 0,1,2
|
|
enum {EVT_NONE, EVT_TICK, EVT_BUTTON}; // 0,1,2
|
|
```
|
|
```
|
|
|
|
|
|
@@ -244,13 +244,13 @@ How do we send the information (events/messages) from the interrupt
|
|
service routine to the
|
|
service routine to the
|
|
`main()` code? We used shared memory.
|
|
`main()` code? We used shared memory.
|
|
|
|
|
|
-One way would be to have a global `volatile uint8` location into which
|
|
|
|
|
|
+One way would be to have a global `volatile uchar` location into which
|
|
we drop the *event* information. But
|
|
we drop the *event* information. But
|
|
having only one socket for that would be a bit naive;
|
|
having only one socket for that would be a bit naive;
|
|
what happens if a timer tick and a button press
|
|
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?
|
|
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]`
|
|
|
|
|
|
+It makes more sense to have an array: `volatile uchar events[NUM_EVENTS]`
|
|
where NUM_EVENTS is on the order of 5..10.
|
|
where NUM_EVENTS is on the order of 5..10.
|
|
That would give us 50-100msec to catch up in case there's a
|
|
That would give us 50-100msec to catch up in case there's a
|
|
pileup of events/messages.
|
|
pileup of events/messages.
|
|
@@ -263,8 +263,8 @@ The `newEvent(evt)` routine simply adds the `evt` to the array `events[]`.
|
|
Something like this might work:
|
|
Something like this might work:
|
|
|
|
|
|
```C
|
|
```C
|
|
-void newEvent(uint8 evt) {
|
|
|
|
- static uint8 nextEvent;
|
|
|
|
|
|
+void newEvent(uchar evt) {
|
|
|
|
+ static uchar nextEvent;
|
|
events[nextEvent++] = evt;
|
|
events[nextEvent++] = evt;
|
|
if (nextEvent == NUM_EVENTS)
|
|
if (nextEvent == NUM_EVENTS)
|
|
nextEvent = 0;
|
|
nextEvent = 0;
|
|
@@ -283,7 +283,7 @@ Here's a more realistic version:
|
|
|
|
|
|
```C
|
|
```C
|
|
void newEvent(char evt) {
|
|
void newEvent(char evt) {
|
|
- static uint8 nextEvent; // keep track of where we are in queue
|
|
|
|
|
|
+ static uchar nextEvent; // keep track of where we are in queue
|
|
disable_irq(); // critical section
|
|
disable_irq(); // critical section
|
|
events[nextEvent++] = evt; // insert event into queue
|
|
events[nextEvent++] = evt; // insert event into queue
|
|
if (nextEvent == NUM_EVENTS) // loop back to the start of queue
|
|
if (nextEvent == NUM_EVENTS) // loop back to the start of queue
|
|
@@ -312,7 +312,7 @@ To see the real code for the dispatcher, [jump ahead](#dispatcher-details).
|
|
The `main()` code illustrated here calls the tasks
|
|
The `main()` code illustrated here calls the tasks
|
|
as subroutines, sending each one a copy of the event number.
|
|
as subroutines, sending each one a copy of the event number.
|
|
|
|
|
|
-As I mentioned earlier, *events* can also be refered to as *messages*.
|
|
|
|
|
|
+As I mentioned earlier, *events* can also be referred to as *messages*.
|
|
We can also refer to this process "sending
|
|
We can also refer to this process "sending
|
|
a message to the task"
|
|
a message to the task"
|
|
|
|
|
|
@@ -345,11 +345,11 @@ the event, speeding up the whole system.
|
|
|
|
|
|
## Tasks
|
|
## Tasks
|
|
|
|
|
|
-In this environment, the code that impliments a *task* is simply a
|
|
|
|
|
|
+In this environment, the code that implements a *task* is simply a
|
|
subroutine that accepts an *event/message*.
|
|
subroutine that accepts an *event/message*.
|
|
|
|
|
|
```C
|
|
```C
|
|
-void taskCode(uint8 event) {
|
|
|
|
|
|
+void taskCode(uchar event) {
|
|
... process the event information ...
|
|
... process the event information ...
|
|
}
|
|
}
|
|
```
|
|
```
|
|
@@ -382,11 +382,11 @@ The above diagram can be implemented in this code:
|
|
|
|
|
|
```C
|
|
```C
|
|
enum {RTB_IDLE, RTB_ON}; // RTB_IDLE=0, RTB_ON=1
|
|
enum {RTB_IDLE, RTB_ON}; // RTB_IDLE=0, RTB_ON=1
|
|
-static uint8 rtbState = RTB_IDLE;
|
|
|
|
|
|
+static uchar rtbState = RTB_IDLE;
|
|
static uint16 rtbTimerCount = 0;
|
|
static uint16 rtbTimerCount = 0;
|
|
const uint16 TIMER_LIMIT = 150;
|
|
const uint16 TIMER_LIMIT = 150;
|
|
|
|
|
|
-void respondToButtonTask(uint8 evt) {
|
|
|
|
|
|
+void respondToButtonTask(uchar evt) {
|
|
switch(rtbState) {
|
|
switch(rtbState) {
|
|
case RTB_IDLE:
|
|
case RTB_IDLE:
|
|
if (evt == EVT_BUTTON) {
|
|
if (evt == EVT_BUTTON) {
|
|
@@ -440,12 +440,12 @@ The code for this might be:
|
|
|
|
|
|
```C
|
|
```C
|
|
enum {LED_ON, LED_OFF};
|
|
enum {LED_ON, LED_OFF};
|
|
-static uint8 ledState = LED_OFF;
|
|
|
|
|
|
+static uchar ledState = LED_OFF;
|
|
static uint16 ledTimerCount = 0;
|
|
static uint16 ledTimerCount = 0;
|
|
const uint16 LED_ON_TIME = 100;
|
|
const uint16 LED_ON_TIME = 100;
|
|
const uint16 LED_OFF_TIME = 100;
|
|
const uint16 LED_OFF_TIME = 100;
|
|
|
|
|
|
-void ledTask(uint8 evt) {
|
|
|
|
|
|
+void ledTask(uchar evt) {
|
|
switch(ledState) {
|
|
switch(ledState) {
|
|
case LED_OFF:
|
|
case LED_OFF:
|
|
if (evt == EVT_TICK) {
|
|
if (evt == EVT_TICK) {
|
|
@@ -569,11 +569,11 @@ Let's put all of the above together, for an SMT32F Cortex M0, in *C* code.
|
|
```C
|
|
```C
|
|
/***** declarations ****/
|
|
/***** declarations ****/
|
|
#define NUM_EVENTS 10
|
|
#define NUM_EVENTS 10
|
|
-volatile uint8 events[NUM_EVENTS];
|
|
|
|
|
|
+volatile uchar events[NUM_EVENTS];
|
|
|
|
|
|
-void newEvent(uint8 e);
|
|
|
|
-void ledTask(uint8 evt);
|
|
|
|
-void respondToButtonTask(uint8 evt);
|
|
|
|
|
|
+void newEvent(uchar e);
|
|
|
|
+void ledTask(uchar evt);
|
|
|
|
+void respondToButtonTask(uchar evt);
|
|
|
|
|
|
/********** interrupts **************/
|
|
/********** interrupts **************/
|
|
|
|
|
|
@@ -593,8 +593,8 @@ void button_isr(void) {
|
|
*
|
|
*
|
|
* @param the event
|
|
* @param the event
|
|
*/
|
|
*/
|
|
-void newEvent(uint8 e) {
|
|
|
|
- static uint8 nextEvent;
|
|
|
|
|
|
+void newEvent(uchar e) {
|
|
|
|
+ static uchar nextEvent;
|
|
dint(); // critical section
|
|
dint(); // critical section
|
|
events[nextEvent++] = e;
|
|
events[nextEvent++] = e;
|
|
if (nextEvent==NUM_EVENTS)
|
|
if (nextEvent==NUM_EVENTS)
|
|
@@ -623,11 +623,11 @@ void main(void) {
|
|
/*********** task code, with states ************/
|
|
/*********** task code, with states ************/
|
|
enum {LED_ON, LED_OFF};
|
|
enum {LED_ON, LED_OFF};
|
|
enum {RTB_IDLE, RTB_ON}; // states
|
|
enum {RTB_IDLE, RTB_ON}; // states
|
|
-static uint8 rtbState = RTB_IDLE;
|
|
|
|
|
|
+static uchar rtbState = RTB_IDLE;
|
|
static uint16 rtbTimerCount = 0;
|
|
static uint16 rtbTimerCount = 0;
|
|
const uint16 LED_ON_TIME = 150;
|
|
const uint16 LED_ON_TIME = 150;
|
|
|
|
|
|
-void respondToButtonTask(uint8 evt) {
|
|
|
|
|
|
+void respondToButtonTask(uchar evt) {
|
|
switch(rtbState) {
|
|
switch(rtbState) {
|
|
case RTB_IDLE:
|
|
case RTB_IDLE:
|
|
if (evt == EVT_BUTTON) {
|
|
if (evt == EVT_BUTTON) {
|
|
@@ -649,10 +649,10 @@ void respondToButtonTask(uint8 evt) {
|
|
|
|
|
|
const uint16 LED_ON_TIME = 150;
|
|
const uint16 LED_ON_TIME = 150;
|
|
const uint16 LED_OFF_TIME = 50;
|
|
const uint16 LED_OFF_TIME = 50;
|
|
-static uint8 ledState = LED_OFF;
|
|
|
|
|
|
+static uchar ledState = LED_OFF;
|
|
static uint16 ledTimerCount = 0;
|
|
static uint16 ledTimerCount = 0;
|
|
|
|
|
|
-void ledTask(uint8 evt) {
|
|
|
|
|
|
+void ledTask(uchar evt) {
|
|
switch(ledState) {
|
|
switch(ledState) {
|
|
case LED_OFF:
|
|
case LED_OFF:
|
|
if (evt == EVT_TICK) {
|
|
if (evt == EVT_TICK) {
|
|
@@ -714,7 +714,7 @@ LED timer on each key press:
|
|
<div style="display: flex;">
|
|
<div style="display: flex;">
|
|
\
|
|
\
|
|
```C
|
|
```C
|
|
-void rtbTask(uint8 event) {
|
|
|
|
|
|
+void rtbTask(uchar event) {
|
|
switch(rtbState) {
|
|
switch(rtbState) {
|
|
case RTB_IDLE:
|
|
case RTB_IDLE:
|
|
if (event == EVT_BUTTON) {
|
|
if (event == EVT_BUTTON) {
|
|
@@ -746,7 +746,7 @@ Or have a 2nd press of the button cause the LED to extinguish early:
|
|
<div style="display: flex;">
|
|
<div style="display: flex;">
|
|
\
|
|
\
|
|
```C
|
|
```C
|
|
-void rtbTask(uint8 event) {
|
|
|
|
|
|
+void rtbTask(uchar event) {
|
|
switch(rtbState) {
|
|
switch(rtbState) {
|
|
case RTB_IDLE:
|
|
case RTB_IDLE:
|
|
if (event == EVT_BUTTON) {
|
|
if (event == EVT_BUTTON) {
|
|
@@ -783,7 +783,7 @@ it: (see also [substates](#substates))
|
|
\
|
|
\
|
|
|
|
|
|
```C
|
|
```C
|
|
-void rtbTask(uint8 event) {
|
|
|
|
|
|
+void rtbTask(uchar event) {
|
|
switch(rtbState) {
|
|
switch(rtbState) {
|
|
case RTB_IDLE:
|
|
case RTB_IDLE:
|
|
if (event == EVT_BUTTON) {
|
|
if (event == EVT_BUTTON) {
|
|
@@ -888,7 +888,7 @@ If a task occasionally runs into the 10's of msec, the event queue will
|
|
handle buffering the events until they can be processed.
|
|
handle buffering the events until they can be processed.
|
|
|
|
|
|
Under no circumstances should a task take more than 100msec. Use a new state,
|
|
Under no circumstances should a task take more than 100msec. Use a new state,
|
|
-and return from the tast. Process the new state later.
|
|
|
|
|
|
+and return from the task. Process the new state later.
|
|
<hr>
|
|
<hr>
|
|
</details>
|
|
</details>
|
|
|
|
|
|
@@ -915,9 +915,9 @@ and the code: arrows leaving a state correspond to an `if()` phrase.
|
|
|
|
|
|
```C
|
|
```C
|
|
enum {WINDOW_IDLE, WINDOW_UP, WINDOW_DOWN};
|
|
enum {WINDOW_IDLE, WINDOW_UP, WINDOW_DOWN};
|
|
-static uint8 windowState = WINDOW_IDLE;
|
|
|
|
|
|
+static uchar windowState = WINDOW_IDLE;
|
|
|
|
|
|
-void windowTask(uint8 evt) {
|
|
|
|
|
|
+void windowTask(uchar evt) {
|
|
switch(windowState) {
|
|
switch(windowState) {
|
|
case WINDOW_IDLE:
|
|
case WINDOW_IDLE:
|
|
if (evt == EVT_BUTTON_UP) {
|
|
if (evt == EVT_BUTTON_UP) {
|
|
@@ -962,7 +962,7 @@ code from the previous version.
|
|
<details><summary>Reality Check</summary>
|
|
<details><summary>Reality Check</summary>
|
|
This is such a simple task, with only a few I/O pins involved. In theory, a
|
|
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
|
|
cheap microcontroller could control a dozen windows, each appearing to operate
|
|
-independantly.
|
|
|
|
|
|
+independently.
|
|
|
|
|
|
In the code, one wouldn't need to create a dozen tasks......just create an index
|
|
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
|
|
into the same code and invoke it in a way that makes it appear as an
|
|
@@ -1020,7 +1020,7 @@ And here is the corresponding code.
|
|
|
|
|
|
```C
|
|
```C
|
|
enum {FRIDGE_CLOSED, FRIDGE_OPEN, FRIDGE_BEEPING};
|
|
enum {FRIDGE_CLOSED, FRIDGE_OPEN, FRIDGE_BEEPING};
|
|
-uint8 fridgeState;
|
|
|
|
|
|
+uchar fridgeState;
|
|
uint16 fridgeTimer;
|
|
uint16 fridgeTimer;
|
|
const uint16 FRIDGE_OPEN_LIMIT = 9000; // 90 seconds at 10msec tick
|
|
const uint16 FRIDGE_OPEN_LIMIT = 9000; // 90 seconds at 10msec tick
|
|
void fridgeTask(char event) {
|
|
void fridgeTask(char event) {
|
|
@@ -1135,18 +1135,18 @@ Here's the state diagram:
|
|
|
|
|
|

|
|

|
|
|
|
|
|
-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.
|
|
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
|
|
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
|
|
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.
|
|
for 4 seconds. The rest of the problem is as stated above.
|
|
|
|
|
|
|
|
+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.
|
|
|
|
+
|
|
Here's the modified state diagram:
|
|
Here's the modified state diagram:
|
|
|
|
|
|
-
|
|
|
|
|
|
+
|
|
|
|
|
|
### Beer Vat
|
|
### Beer Vat
|
|
|
|
|
|
@@ -1198,16 +1198,17 @@ Event e = {MSG_KEYPRESS, KEY_A};
|
|
```
|
|
```
|
|
|
|
|
|
In the old Windows system, *events/messages* were realized as a 16 bit number,
|
|
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.
|
|
|
|
|
|
+with extra 16 and 32 bit numbers glued to it for extra information.
|
|
|
|
|
|
```C
|
|
```C
|
|
typedef struct MSG {
|
|
typedef struct MSG {
|
|
UINT message;
|
|
UINT message;
|
|
|
|
+ WPARAM wParam;
|
|
LPARAM lParam;
|
|
LPARAM lParam;
|
|
}; // some extra detail removed
|
|
}; // some extra detail removed
|
|
```
|
|
```
|
|
For example,
|
|
For example,
|
|
-`WM_CHAR=0x0102` indicates that a key was pressed, with the extra 32bit `lParam`
|
|
|
|
|
|
+`message == WM_CHAR == 0x0102` indicates that a key was pressed, with the `wParam`
|
|
carrying the information about *which* key.
|
|
carrying the information about *which* key.
|
|
<hr>
|
|
<hr>
|
|
|
|
|
|
@@ -1253,8 +1254,8 @@ 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,
|
|
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.
|
|
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
|
|
|
|
|
|
+In all the above examples, the `timer_isr()` code is trivial, just `newEvent(EVT_TICK)`.
|
|
|
|
+Suppose we add code to the `timer_isr()` so that it can process timer counting on
|
|
behalf of the tasks......
|
|
behalf of the tasks......
|
|
|
|
|
|
### Timers as a Resource
|
|
### Timers as a Resource
|
|
@@ -1262,9 +1263,9 @@ behalf of the tasks......
|
|
Let's create a centralized service called `setTimer(timer_index, timer_count)`.
|
|
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
|
|
A task can call this service with a unique `timer_index` and a requested count. The
|
|
-`timer_irq()` uses a pool of timer registers, and will count out the ticks
|
|
|
|
|
|
+`timer_isr()` uses a pool of timer registers, and will count out the ticks
|
|
on behalf of the task, and when the tick
|
|
on behalf of the task, and when the tick
|
|
-count is finished, the `timer_irq()` code can generate a unique event, perhaps
|
|
|
|
|
|
+count is finished, the `timer_isr()` code can generate a unique event, perhaps
|
|
`EVT_TIMER_n`.
|
|
`EVT_TIMER_n`.
|
|
|
|
|
|
So the state code can then look something like this:
|
|
So the state code can then look something like this:
|
|
@@ -1294,7 +1295,7 @@ void stateCode(char event) {
|
|
This makes the state code simpler to read,
|
|
This makes the state code simpler to read,
|
|
hiding all the increments/decrements and limit testing.
|
|
hiding all the increments/decrements and limit testing.
|
|
|
|
|
|
-The overhead for this ends up in the `timer_irq()` code,
|
|
|
|
|
|
+The overhead for this ends up in the `timer_isr()` code,
|
|
and might look something like this:
|
|
and might look something like this:
|
|
|
|
|
|
```C
|
|
```C
|
|
@@ -1303,7 +1304,7 @@ static uint16 timers[NUM_TIMERS];
|
|
enum {EVENT_TIMER_1=EVT_TIMER_OFFSET, EVENT_TIMER_2, EVENT_TIMER_3}; // 100, 101...
|
|
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,....
|
|
enum {TIMER_1, TIMER_2, TIMER_3}; // 0,1,2,....
|
|
|
|
|
|
-void timer_irq() {
|
|
|
|
|
|
+void timer_isr() {
|
|
newEvent(EVT_TICK); // the main tick, fires every time
|
|
newEvent(EVT_TICK); // the main tick, fires every time
|
|
for (i=0; i<NUM_TIMERS) { // the system timers, fires on completion
|
|
for (i=0; i<NUM_TIMERS) { // the system timers, fires on completion
|
|
if (timers[i]>0) {
|
|
if (timers[i]>0) {
|
|
@@ -1321,7 +1322,7 @@ void setTimer(int timerIndex, unsigned int timerCount) {
|
|
|
|
|
|
<details><summary>Reality Check</summary>
|
|
<details><summary>Reality Check</summary>
|
|
On a typical microcontroller running at 24MHz, with 5 timers, this adds about
|
|
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
|
|
|
|
|
|
+2 microseconds of extra time to the `timer_isr()` code, which typically runs every
|
|
10 or 100msec. It simplifies the task code, makes it more legible
|
|
10 or 100msec. It simplifies the task code, makes it more legible
|
|
and probably reduces bugs that may appear by duplication of code.
|
|
and probably reduces bugs that may appear by duplication of code.
|
|
|
|
|
|
@@ -1339,7 +1340,7 @@ INTERRUPT timer_isr(void) {
|
|
|
|
|
|
// .... state code ...
|
|
// .... state code ...
|
|
static int targetTime;
|
|
static int targetTime;
|
|
-void stateCode(uint8 event) {
|
|
|
|
|
|
+void stateCode(uchar event) {
|
|
switch(state) {
|
|
switch(state) {
|
|
case STATE1:
|
|
case STATE1:
|
|
//..... set a target
|
|
//..... set a target
|
|
@@ -1399,15 +1400,15 @@ void gpio_set(uint32 bitPosition, bool value) {
|
|
```C
|
|
```C
|
|
/***** events ****/
|
|
/***** events ****/
|
|
#define NUM_EVENTS 10
|
|
#define NUM_EVENTS 10
|
|
-volatile uint8 events[NUM_EVENTS];
|
|
|
|
|
|
+volatile uchar events[NUM_EVENTS];
|
|
enum { EVT_NONE,
|
|
enum { EVT_NONE,
|
|
EVT_TICK,
|
|
EVT_TICK,
|
|
EVT_BUTTON};
|
|
EVT_BUTTON};
|
|
-void newEvent(uint8 e);
|
|
|
|
|
|
+void newEvent(uchar e);
|
|
|
|
|
|
/********** tasks ***********/
|
|
/********** tasks ***********/
|
|
-void ledTask(uint8 evt);
|
|
|
|
-void respondToButtonTask(uint8 evt);
|
|
|
|
|
|
+void ledTask(uchar evt);
|
|
|
|
+void respondToButtonTask(uchar evt);
|
|
|
|
|
|
/********** interrupts **************/
|
|
/********** interrupts **************/
|
|
volatile uint32 tick; // increasing at 100 ticks/sec
|
|
volatile uint32 tick; // increasing at 100 ticks/sec
|
|
@@ -1503,11 +1504,11 @@ void main(void) {
|
|
/*********** task code, with states ************/
|
|
/*********** task code, with states ************/
|
|
enum {LED_ON, LED_OFF};
|
|
enum {LED_ON, LED_OFF};
|
|
enum {RTB_IDLE, RTB_ON}; // states
|
|
enum {RTB_IDLE, RTB_ON}; // states
|
|
-static uint8 rtbState = RTB_IDLE;
|
|
|
|
|
|
+static uchar rtbState = RTB_IDLE;
|
|
static uint16 rtbTimerCount = 0;
|
|
static uint16 rtbTimerCount = 0;
|
|
const uint16 BUTTON_LED_ON_TIME = 150;
|
|
const uint16 BUTTON_LED_ON_TIME = 150;
|
|
|
|
|
|
-void respondToButtonTask(uint8 evt) {
|
|
|
|
|
|
+void respondToButtonTask(uchar evt) {
|
|
switch(rtbState) {
|
|
switch(rtbState) {
|
|
case RTB_IDLE:
|
|
case RTB_IDLE:
|
|
if (evt == EVT_BUTTON) {
|
|
if (evt == EVT_BUTTON) {
|
|
@@ -1529,10 +1530,10 @@ void respondToButtonTask(uint8 evt) {
|
|
|
|
|
|
const int LED_ON_TIME = 150;
|
|
const int LED_ON_TIME = 150;
|
|
const int LED_OFF_TIME = 50;
|
|
const int LED_OFF_TIME = 50;
|
|
-static uint8 ledState = LED_OFF;
|
|
|
|
|
|
+static uchar ledState = LED_OFF;
|
|
static uint16 ledTimerCount = 0;
|
|
static uint16 ledTimerCount = 0;
|
|
|
|
|
|
-void ledTask(uint8 evt) {
|
|
|
|
|
|
+void ledTask(uchar evt) {
|
|
switch(ledState) {
|
|
switch(ledState) {
|
|
case LED_OFF:
|
|
case LED_OFF:
|
|
if (evt == EVT_TICK) {
|
|
if (evt == EVT_TICK) {
|
|
@@ -1606,8 +1607,8 @@ simplifies:
|
|
|
|
|
|
|
|
|
|
```C
|
|
```C
|
|
-uint8 rtbState = RTB_IDLE; // major state, RTB_IDLE/RTB_FLASHING
|
|
|
|
-uint8 rtbSubState = RTB_FLASH_OFF; // minor state, toggles LED on/off
|
|
|
|
|
|
+uchar rtbState = RTB_IDLE; // major state, RTB_IDLE/RTB_FLASHING
|
|
|
|
+uchar rtbSubState = RTB_FLASH_OFF; // minor state, toggles LED on/off
|
|
```
|
|
```
|
|
|
|
|
|
Alternatively, you could use the *timer counter*
|
|
Alternatively, you could use the *timer counter*
|
|
@@ -1620,7 +1621,7 @@ variable, and make changes at the half-way point through the count.
|
|
```C
|
|
```C
|
|
const uint16 FLASH_CYCLE_TIME 150;
|
|
const uint16 FLASH_CYCLE_TIME 150;
|
|
const uint16 FLASH_ON_TIME 40;
|
|
const uint16 FLASH_ON_TIME 40;
|
|
-uint8 rtbState = STATE_IDLE;
|
|
|
|
|
|
+uchar rtbState = STATE_IDLE;
|
|
void rtbTaskCode(char event) {
|
|
void rtbTaskCode(char event) {
|
|
static uint16 flashCount=0;
|
|
static uint16 flashCount=0;
|
|
|
|
|
|
@@ -1677,8 +1678,8 @@ Then, in the state code, you can catch that event and set up whatever might
|
|
be required
|
|
be required
|
|
|
|
|
|
```C
|
|
```C
|
|
-uint8 myState;
|
|
|
|
-void stateCode(uint8 event) {
|
|
|
|
|
|
+uchar myState;
|
|
|
|
+void stateCode(uchar event) {
|
|
if (event==EVT_INIT) {
|
|
if (event==EVT_INIT) {
|
|
// ... do setup code
|
|
// ... do setup code
|
|
myState = FIRST_STATE;
|
|
myState = FIRST_STATE;
|
|
@@ -1707,7 +1708,7 @@ only be called from interrupts.
|
|

|
|

|
|
|
|
|
|
```C
|
|
```C
|
|
-void postMessage(uint8 message);
|
|
|
|
|
|
+void postMessage(uchar message);
|
|
```
|
|
```
|
|
|
|
|
|
Notice that `postMessage` can't return any information, because it's not
|
|
Notice that `postMessage` can't return any information, because it's not
|
|
@@ -1726,7 +1727,8 @@ In some cases we may want the sibling task to process the information immediatel
|
|
This means:
|
|
This means:
|
|
|
|
|
|
- we can be assured that the sibling task has fully processed the information
|
|
- 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
|
|
|
|
|
|
+- does not involve the dispatcher
|
|
|
|
+- requires extra room on the stack, since we're calling within a call
|
|
- we *could* get a return value from the sibling task; since it's implemented as
|
|
- we *could* get a return value from the sibling task; since it's implemented as
|
|
a subroutine, it's allowed to return a value
|
|
a subroutine, it's allowed to return a value
|
|
|
|
|
|
@@ -1735,15 +1737,15 @@ a subroutine, it's allowed to return a value
|
|
To return a value means that the prototype for task functions would change from
|
|
To return a value means that the prototype for task functions would change from
|
|
|
|
|
|
```C
|
|
```C
|
|
-void taskCode(uint8 event);
|
|
|
|
|
|
+void taskCode(uchar event);
|
|
to
|
|
to
|
|
-uint8 taskCode(uint8 event);
|
|
|
|
|
|
+uchar taskCode(uchar event);
|
|
```
|
|
```
|
|
|
|
|
|
The service routine to send a message like this would look like:
|
|
The service routine to send a message like this would look like:
|
|
|
|
|
|
```C
|
|
```C
|
|
-uint8 SendMessage(int8 taskPointer(uint8), uint8 message) {
|
|
|
|
|
|
+uchar SendMessage(int8 taskPointer(uchar), uchar message) {
|
|
return taskPointer(message);
|
|
return taskPointer(message);
|
|
}
|
|
}
|
|
```
|
|
```
|
|
@@ -1752,7 +1754,8 @@ uint8 SendMessage(int8 taskPointer(uint8), uint8 message) {
|
|
|
|
|
|
Why might one want to *send a message* between tasks?
|
|
Why might one want to *send a message* between tasks?
|
|
|
|
|
|
-Suppose you have a rotary encoder, which sends quadrant signals, which need
|
|
|
|
|
|
+**PostMessage()** Suppose you have a rotary encoder, which sends
|
|
|
|
+quadrant signals, which need
|
|
to be interpreted as *clockwise* and *counter clockwise*. You could have one
|
|
to be interpreted as *clockwise* and *counter clockwise*. You could have one
|
|
task devoted to determining the direction of the knob
|
|
task devoted to determining the direction of the knob
|
|
(and [debouncing](#debouncing)), and have it *send* (or *post*) clean
|
|
(and [debouncing](#debouncing)), and have it *send* (or *post*) clean
|
|
@@ -1763,7 +1766,7 @@ For example, a device which has
|
|
a temperature set by "up/down" buttons on a front panel could receive the same
|
|
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).
|
|
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
|
|
|
|
|
|
+**SendMessage()** Perhaps you need to have a sibling task change
|
|
states, or process information *before* you continue in your task. Imagine
|
|
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
|
|
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
|
|
a request....you could use `sendMessage()` to activate the power, and then
|
|
@@ -1819,8 +1822,8 @@ uint16 radioTimer = 0;
|
|
const uint16 radioTimeout = 5; // 0.5 seconds
|
|
const uint16 radioTimeout = 5; // 0.5 seconds
|
|
|
|
|
|
enum {IDLE, FREQ_UP, FREQ_UP_FAST, FREQ_DN, FREQ_DN_FAST};
|
|
enum {IDLE, FREQ_UP, FREQ_UP_FAST, FREQ_DN, FREQ_DN_FAST};
|
|
-uint8 state = IDLE;
|
|
|
|
-void radioTask(uint8 event) {
|
|
|
|
|
|
+uchar state = IDLE;
|
|
|
|
+void radioTask(uchar event) {
|
|
switch(state) {
|
|
switch(state) {
|
|
case IDLE:
|
|
case IDLE:
|
|
if (event == EVT_UP_PRESS) {
|
|
if (event == EVT_UP_PRESS) {
|