Interrupts can be very useful: as the name says, they signal when something has happened while you’re busy doing something else. In embedded systems they can tell you when a a timer expires, when a pin level changes, and when an I/O operation completes. Which happens to be exactly how JeeH uses them.
Musings
Sync mode drivers
As described in the previous article, JeeH’s “tasks” implement a limited form of concurrency. This includes “async” drivers to deal with interrupts. But asynchronous I/O requests are somewhat inconvenient: once such a request has been started, you need to return from the task to process completion events (to guarantee atomicity, incoming events for a task can only be picked up when that task is not running).
Multitasking
JeeH supports a limited form of multitasking. There is only a single stack: there are no threads to suspend or resume, but also no need to allocate separate stacks to each one. This single stack expands downwards with the rest of unused RAM available for heap allocation. Just as in any single-tasking bare-metal application.
It's been a while
How time flies … (and there’s a reason: we’ve moved to a new apartment).
I’ve started working on JeeH again (which is now at version 7). There have been several substantial changes, affecting all parts of the project:
Goodbye threads, hello workers
The big picture in JeeH is still not right: I don’t like the way device drivers
run in an exception-centric “handler” mode, whereas the rest of the application
uses “thread” mode. This distinction was needed to provide atomic guards around
certain parts of JeeH’s core data structures. This also affects the app-supplied
lowestPower
and resumePower
functions, used to enter sleep modes “when there
is no work”. It’s a bit odd that we need to be in a special mode when …
idling!
JeeH 6.1.0
JeeH version 6.1.0 release notes and highlights:
This is a minor release. I’m pushing this release out to prepare for some new ideas - mostly aimed at reducing overhead and complexity, and at improving JeeH’s low-power capabilities.
The current low-power design lets JeeH decide when to enter a low-power mode,
based on jeeh::lowestPower
and jeeh::resumePower
functions which can be
tweaked for a specific scenario. One problem is that these two functions have to
run in “handler” mode, i.e. as exceptions, which has very specific “rules of
engagement”. Another more pressing problem is that this code is simply … not
working correctly in all cases!
JeeH 6.0.0
JeeH version 6.0.0 release notes and highlights:
This is a major new release of JeeH. A lot of things have changed:
-
Now you see them, now you don’t: tasks are gone again. The routing of task messages was too complex. Threads are unchanged. Messages can still be sent to threads and to device drivers.
-
Instead of waiting for messages coming back as reply, the sender can now set up a callback method, which is called when the reply is received. For this to work, a thread needs to have
while (true) sys::recv();
as its main loop. There is a (template) function to store a callback into a message:
Four ways to SPI
One of the problems I want to address in JeeH, is how to best interface with peripherals: built-in as well as connected via a common bus, e.g. I2C or SPI. There are two sides to this: talking to built-in hardware via device registers, and talking through built-in hardware to a connected module / chip.
Hardware register access #
Talking to the built-in hardware is a matter of reading and writing the
hardware registers at specific addresses. This is already solved in JeeH with
the use of IoReg<...>
definitions. To configure the “B” port of the GPIO
hardware in an STM32 for example, JeeH defines a GPIOB
object (a constant type
really) which can be referenced as an array. Here’s how to set its pin 8 high:
To buffer or not to buffer
The DMA-based UART driver in JeeH is an interesting example of the interaction between hardware, memory use, and blocking behaviour.
Reading data #
In JeeH, the way to read bytes from the UART is to send a message to its device
driver, and wait for its reply. The mTag
field is 'R'
, with the mLen
and
mPtr
field starting off as zero. The driver uses a small ring buffer
internally, which is filled in via DMA as bytes arrive. The CPU is not involved.
The next task is Tasks
As mentioned in my previous Threads vs Async I/O musings, threads are no longer the main concurrency mechanism I’m after, tasks are. Threads are still present in JeeH (and they actually work), but I’m not so keen on having to allocate stacks for each thread, nor on deciding up front how large they need to be.
So what is a task? #
Tasks are a bit different. I’m using the same sys::send
and sys::recv
mechanism for them as threads and device drivers, but they run as part of the
thread which created them. Sending a message to a task behaves as if the
message is sent to its owning thread and then forwarded to that specific task.