This is the "details" post for controlling a 7 bit parallel OLED character display using an attiny as a display driver. See the previous post for an overview of the setup and video.
And now... the code. Apologies for some of the names of directories.
Instead of branching and whatnot in git I just copied the code to
library(n+1) and added the next feature in line as I went. I might at
some stage do a write up detailing the stepping stones to get from the
bare attiny84 to the code below.
To make it simpler, I'll call the "main" arduino the Uno. That is the
one what wants to run the show. The screen is attached to the attiny84
which I'll just call the tiny84.
This is the code that one uses on the Uno:
oled_clientspireal.ino
It is designed to look very close to the example it was based on (linked
in the second line of its header comment).
The attiny84 runs this
attiny_oled_server_spi2.ino
The heavy lifting is done in SimpleMessagePassing5 which I link next.
The main part of loop() is to process each full message that we get from
SimpleMessagePassing5. There is a timeout() logic in there after that
which allows the tiny84 to somewhat turn off the screen after a period
of no activity on the SPI bus. noDisplay() doesn't turn off the OLED
internal power, so you are only down to about 6mA there.
The SimpleMessagePassing5 does two main things (coupling SPI into
message byte boundaries in a single file, the academics would be
unhappy)
SimpleMessagePassing5.cpp
Earlier version of SMP did
USICR |= (1 << USIOIE);
in init(). That line turns on the ISR for USI SPI overflow. But that is
itself now done in a pin change ISR so that the USI is only active when
the attiny84 has been chip selected.
serviceInput() is a fairly basic state machine, mopping up bytes until
we have a whole "message" from the SPI master. If there is a complete
message available then takeMessage() will return true. It is then up to
the caller to do smp.buffer().get() to actually grab and disregard the
bytes that comprise a message.
I'm guessing that at some stage I'd add a "Message" class that
takeMessage could then return. The trick will be to do that without
needing to copy from smp.buffer() so that sram is kept fairly low.
The Uno uses the shim class
Shim_CharacterOLEDSPI2.cpp
which just passes the commands along to the attiny84 for execution.
This all seems to smell a bit like it wants to use something like Google
protocol buffers for marshaling but the hand crafted code works :) In
some ways using GPB for such a simple interface as CharacterOLEDSPI2
might be well be considered over engineering, but I'm still tempted.