32 x Servo/IO Hat


New 3D model showing the right angle connectors and the updated “Servo” that now is renamed to 32 x IO Hat. I have done some mechanical changes and added a lot of protection electronics. As mentioned earlier this is my test ground for the VM and each channel have multiple capabilities – hence the “IO” rather than Servo.

  • 32 channel Servo Controller
  • 32 channel digital in
  • 32 channel digital out
  • 32 channel low resolution PWM
  • 2 channel analogue out
  • 12 channel analogue in
  • or any combination of the above

I have three Hat’s that will stack nicely

  • 16/32 channel IO (shown here)
  • 8x H-Bridge + 8 x IO
  • 5 port RS-485/CAN

I do in addition have the 7 x Stepper, but I am not happy with the connectors on that one. The 28BYJ-48 stepper motors comes with a 5-pin connector that is nice and exist as right-angle, but I can only manage 3 of these in stacked position – To be honest I am considering ditching this Hat design.

As we move forward I will add more hat’s and more distributed sensors and controllers to allow larger and more complex systems to be created. I want to create a few robots + I want to automate my home for the fun of it. But, all of this is worthless without an easier way to apply Distributed logic so I will continue to dig into the PLAIN concept for now.


I did earlier create named Event’s that could be called with a Raise statement. The last example created an Event digitalIn[2] where the “name” is a declared object register and the event is automatically called if C or PLAIN assembly changes that exact bit.

Event digitalIn[2]
            // ...

 I like the concept, of Automatic events, but I need to let the syntax mature a little. Is this event to be called before or after we change the bit? Is it to be called only on Write or also on Read? And what about parameters?

Event DI() On digitalIn[2..3]

The modified syntax above name the event to enable it to be raised or called, and it add the On keyword to allow allocation of automatic events.

Event DI() on digitalIn[2..3] After Write

This add the syntax After|Before Read|Write allowing us to control how it is called. Adding parameters is also straight forward since we have a name and a (). Parameters will either be the same as declared in the C implementation or none at all – need to work on this part.

Event On Ch4.AnalogueIn

 The event above is however a challenge because it is connected to an ADC that very well might be sampled at 2.4Mhz. Any attempt on calling an event at that frequency will overload our MCU, so we need a buffer mechanism if we actually want to sample this fast, or add a filter on the ADC. A filter in this case is a max frequency and a threshold requiring a minimum change before it is raised as an event. These capabilities needs to be added to the C interface – I don’t think I want to control this on the Event declaration – not sure.

VM – Library

The previous example used a lot of lines to outline the register mapping and I don’t want to be declaring all this for every app I create. Basically I just want to write something like this:

use System
use lib32io

Assign ch1.chMode = Mode.Servo
Assign ch2.chMode = Mode.DigitalIn
Assign ch3.chMode = Mode.DigitalIn

Event digitalIn[2]
            // ...

Event digitalIn[3]
            // ...

This example assumes that object “Channel” and C bindings are declared in lib32io that is part of the repository. This will work, but I need the assembler to be smart since lib32io will declare more content than we want to include on this module, so everything that is not used will be removed by the assembler to keep both module and object mapping at a minimum.

Notice that I now have introduced “library” to distinguish from “module”. Module creates a new application, library declare stuff that can be included in modules. The example above could also be written like this:


VM Test Ground

I decided to use the 32 x IO (Servo, Analogue, Digital) as my test ground for the VM – the firmware will in this case include easyIPC (SPI in this case) and a 32 channel programmable IO controller. Each of the channels have capabilities like:

  • Servo with a 14 bit pulse resolution.
  • PWM out with 14 bit resolution. This is a bit-banged PWM that can be used on all channels.
  • Digital Signal Input
  • Digital Signal Output

 Some of the channels will have the following:

  • High resolution/frequency PWM signals
  • Analogue Input
  • Analogue output.

This firmware is an excellent test-ground because it includes highly programmable logic and a hard real-time core on the bit-banging part.

Lets draft some PLAIN Assembly code:

Enum uint32 Mode

Object Channel 
            Mode chMode = Mode.Servo
            uint32 position=0
            uint32 frequency = 0
            uint32 duty = 0
            uint32 analogueOut = 0
            uint32 analogueIn = 0

Channel Ch1
Channel Ch2
Channel Ch3
Channel Ch4

Bit digitalIn[32]
Bit digitalOut[32]

This is a draft of how I want PLAIN Assembly to see the easyIPC objects. Ch1.Mode will be located at register 0x8000, but we can also locate data on selected registers as follows

Channel Ch1 at R(0x8010)

In this example I force Ch1 to be located at Register 0x8010.

The more tricky part is the integration with C/C++ code. The challenge is that the module and data we transfer must match what the C/C++ firmware expects. This last mapping is done by the real-time linker in the firmware that receive the above as a “PLAIN Module Specification”. If it fail it will need to reject the module. In theory it should never fail as the assembler should stop us, but it is always the possibility of firmware version mismatch. This part will need to be strict or we will just dig into loads of debug problems.

Map Channel to C(Channel)
Map digitalIn to C(DigIn)
Map digitalOut to C(DigOut)

Assign Ch1.chMode = Mode.Servo
Assign Ch2.chMode = Mode.DigitalIn
Assign Ch3.chMode = Mode.DigitalIn

Event digitalIn[2]
            Assign Ch1.position = 0

Event digitalIn[3]
            Assign Ch1.position = 100

This example configure Ch1 as a Servo and Ch2, Ch3 as digital input pins. The Map statements link the objects to associated C/C++ code. The Assembler (and real-time linker) will check these exact names and parameters – parameters must match on name and data type.

The Event statement “digitalIn[2]” declare that any change to bit 2 in the digitalIn will cause a call to this bit of logic. This will be called regardless if it is C/C++ or PLAIN assembly that makes the change.

 I think this will work, but I need to let it mature to catch up missing bits and see if I can optimize this. Notice that while this example is ca 40 lines in assembly we would be talking about ca 7 instructions in the resulting code (+ initialization). The initialization will in this case be nothing since 0 is the default value on all registers. The VM will automatically reset this before execution to avoid that we have random, default values. “Event digitalIn[2]” will generate 2 instructions – an Assign and a Return.

But, keep in mind that this draft is work in progress. I will implement this next to see how it actually works out in real life.


20170130_195322 20170130_195347

Two new PCB’s arrived. The large green is another Hat – the 8xDC Motor Driver. The small red one is the STM32F030F4 breakout. I am using www.Elecrow.com on all my PCB’s because they have proven to be very reliable. I ordered 10 of these small PCB’s and well – the bag contains 40 – and the PCB quality is good. I will get Down to testing these Boards in due time.

PLAIN VM – Updated

Wrapping up the VM I question some of the instructions so I reviewed the list once more. I will needed to add more of the If instructions back. I will probably also need to add bit operators later – I actually need to wrap up the VM and Write some actual code to see if miss something.

NOP No Operation.
For FOR Loop
Assign Assign operation
While WHILE Loop
Loop Generic Loop
Exit  Terminate a process
Raise Raise an event. The same as calling a function without creating a stack Return entry.
Call Call a function and create a stack Return entry. A function can be PLAIN or C/C++ function.
Ifeq, Ifneq, Ifgt, Ifls, Ifgte, Iflse If statements. If takes an Expression and insert a Assign instruction to evaluate the Expression. All other compare two registers.
Switch Switch

I hope to have a working demo of the VM in a few days.

PLAIN – Math & Expressions

We have so far demonstrated PLAIN Assembly with little difference to an actual 3.gen language. We will see the low-level assembly – instruction by instruction – a bit more as we dig into math and expressions.

As we are an assembler we need to maintain a principle of 1:1 between code and actual instructions. The challenge is that I would still like to write code like this:

If (A > B+C/D)
    // do something

A classic assembly would not allow these expressions but rather force us to do math with separate instructions and we could have implemented something like this:


This would actually be slow in a VM since we are an interpreter, but also to close to a native assembly language for my taste. The result I want to try out is a horse trade – I accept that my IF statement use 2 instructions and create a new instruction dedicated to calculate Math and boolean expressions.

Assign R1 = B+C/D
If (A > R1)
            // do something

This would be the actual assembly, but I am happy to let the assembler insert Assign statements in advance of If, Call and Raise as needed as a  trade-off.

The assign instruction is dedicated to calculate math expressions written in a natural, readable format. The assembler will parse the expression and build a math table that in run-time calculate the expression that is stored in R1 – the math table is the 2nd parameter in the Assign instruction. We will still need to interpret the Math table, but as this now is dedicated for Math I can also do tricks to optimize speed. But, Math and Expressions are a VM’s weakest link with regards to performance.

PLAIN – Module

PLAIN VM consist of multiple Modules. A Module have it’s own unique name, easyIPC Object map, Stack and can execute independently from other Modules. One Module can interact with other Modules or call them on blocking/non-blocking schemes.

All code in PLAIN Assembly must be part of a module and a module start execution with the first statement it finds

Use System
Module MyModule
            println ("Hello World")

This is the “Hello World” example in PLAIN. We could even write the following:

use System
println ("Hello World")

In this last example we just add our code to a global module. As we assemble this we also declare what repository to use, and the use statement administrate what modules we access. This is designed to be easy once set up, but still give you full control of what the assembler does and how it assemble Your modules. It also enables portability by replacing the repository – no changes to Assembly code.

A repository is a XML based database that we need to write. This describe the VM, add-on modules in C/C++ and PLAIN etc. “use System” assume we have a module named “System” with an exact description of this. Your object file will contain this as a “specification” that is downloaded with your module. The real-time linker will reject modules with missing dependencies.

PLAIN – Events 2

Events and state engine support is a key feature in PLAIN where we do things different. It basically convert the entire application into a state engine where processing is top-down based on events. I am not sure how much of this I want to support directly in the VM, but I will Experiment a bit.

An Event is basically a function that needs to be called with a Raise statement. But, unlike a Call it will not return and instructions after “Raise” is illegal as they never will be executed since we will return to the last Call.

Event is added at the end of a function where you also can add parameters, options and a default body

Func MyFunc
            // function body
Event MyEvent (parameters) options
            // default body

Options are Static, Mandatory, Optional.

A static event can not be overridden.

A mandatory event must be overridden.

An optional event (default) can be overridden by the choice of the developer.

The keyword Event is only legal on the end of a function where it also “end” the previous function/event body or after a Call where it will catch mandatory and optional events.

One important feature about event is the “do nothing” rule – if an event is raised and not caught nothing should happen. The Assembler should generate an error, but the VM should execute the Raise and behave as if it was Raise Continue.

PLAIN – Events

Events have so far only been a mechanism in the high level language, but I want to support it in the assembly. Events are as mentioned return conditions. We create functions with multiple exit paths where each exit is a “return” with its own event code and separate list of return parameters. To enable this we need a “Raise” statement, but we also need a “Event” to catch this in the calling code.

Func MyFunc
            Raise MyFunc2

Func MyFunc2
            Raise Error
Event Error
            // Default processing
Call MyFunc
Event Error
            // Overrides error processing

In this example we call “MyFunc” that Raise MyFunc2 that again Raise Error. The returning event will in this case be Error. Raise behaves like a Call, but do not create a return entry on the stack. A call creates a stack return entry causing processing to continue with the next instruction – raise preserve the existing stack entry.

If a function reaches End without raising an event that is the same as “Raise Continue” that can be processed with “Event Continue”. The Event statement in the function declare the event and its parameters.

Notice that while Call and Raise are actual assembly instructions, Func, Event and End are syntax needed by the assembler only. Need to dig into how I detect Events thought … 

At this point I need to write my little VM to consolidate the design concept …