Sunday, March 2, 2014

The vs1063 untethered

Late last year I started playing with the vs1063 DSP chip, making a little Audio Player. Quite a lot of tinkering is available for in/output on such a project, so it can be an interesting way to play around with various electronics stuff without it feeling like 10 das blinken lights tutorials in a row.

Over this time I tried using a 5 way joystick for input as well as some more unconventional mechanisms. The Blackberry Trackballer from SparkFun is my favourite primary input device for the player. Most of the right hand side board is to make using that trackballer simpler, boiling it down to a nice I2C device without the need for tight timing or 4 interrupt lines on the Arduino.




The battery in the middle area of the screen is the single 16850 protected battery cell that runs the whole show. The battery leads via a switch to a 3.7v -> 5v step up to provide a clean power input. Mid way down the right middle board is a low dropout 3v3 regulator from which the bulk of the board is running. The SFE OLED character display wants its 5v, and the vs1063 breakout board, for whatever reason, wants to regulate its own 3v3 from a 5v input. Those are the two 5v holdouts on the board.

Last blog post I was still using a Uno as the primary microcontroller. I also got rid of that Uno and moved to an on board 328 running at 8Mhz and 3v3. Another interesting leaning experience was finding when something 'felt' like it needed a cap. At times the humble cap combo is a great way to get things going again after changing a little part of the board. After the last clean up it should all fit onto 3 boards again, so might then also fit back into the red box. Feels a bit sad not to have broken the red box threshold anymore :|

Loosely the board comes out somewhat like the below... there are things missing from the fritzing layout, but its a good general impression. The trackballer only needs power, gnd, I2C, and MR pin connections. With reasonable use of SMD components the whole thing should shrivel down to the size of the OLED PCB.

Without tuning the code to sleep the 328 or turn off the attin84 + OLED screen (oled is only set all black), it uses about 65mA while playing. I'm running the attiny84 and OLED from an output pin on the 4050 level shifter, so I can drop power to them completely if I want. I can expect above 40hrs continuous play without doing any power optimization. So its all going to improve from there ;)

Tuesday, February 4, 2014

attiny screen driver for parallel controlled character displays

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.

Wednesday, January 29, 2014

Ripple counting trackballer hall effects

Sparkfun sells a breakout with a blackberry trackballer on it and 4 little hall effect sensors. One complete ball rotation generates 11 hall events. So the up, down, left, and right pins will pulse 11 times a rotation. The original thought was to hook those up to DPins on the arduino and use an interrupt that covered that block of pins to count these hall events as they came in. Then in loop() one could know how many events had happened since the last check and work from there. Feeling like doing it more in hardware though, I turned to the 74HC393 which has two 4bit ripple counters in it. Since there were four lines I wanted to count I needed two 393 chips. The output (the count) from the 393 is offered in four lines per count (its a 4 bit counter). So I then took those outputs and fed them into the MCP23x17 pin muxer which has 16 in/out pins on it. I used the I2C version of the MCP chip in this case. So it then boils down to reading the chip when you like and pulsing the MR pin on the 393s to reset the counters.


In the example sketch I pushed to github, I have a small list of choices that you can scroll through and if you stop scrolling for "a while" then it selects the current entry. Which just happens to be the exact use case I am planning to put this hardware to next. Apart from feel good factor, this design should have less chance of missing events if you already have interrupt handlers which themselves might take a while to execute.

Tuesday, December 17, 2013

The kernel of an Arduino audio player

The vs10x3 DSP chips allow you to decode or encode various audio formats. This includes codec to mp3, ogg vorbis, as well as decoding flac and support for many other formats. So naturally I took the well trodden path and decided to use an arduino to build a "small" audio player. The breadboard version is shown below. The show is driven by the Uno on the right. The vs1063 is on a Sparkfun breakout board in the top of image. The black thing you see to the right of the vs1063 is an audio plug. The bottom IC in the line on the right is an attiny84. But wait you say, don't you already have the Uno to be the arduino in the room? But yes I say, because the attiny84 is a SPI slave device which is purely a "display driver" for the 4 bit parallel OLED screen at the bottom. Without having to play tricks overriding the reset pin, the tiny84 has just enough for SPI (4), power (2), and the OLED (7) so it's perfect for this application.

The next chip up from the attiny84 is an MCP23017 which connects to the TWI bus and provides 16 digital pins to play with. There is a SPI version of the muxer, the 23S17. The muxer works well for buttons and chip selects which are not toggled frequently. It seems either my library for the MCP is slow or using TWI for CS can slow down SPI operations where selection/deselection is in a tight loop.

Above the MCP is a 1Mbit SPI RAM chip. I'm using that as a little cache and to store the playlist for ultra quick access. There is only so much you can do with the 2kb of sram on the arduino. Above the SPI ram is a 4050 and SFE bidirectional level shifter. The three buttons in bottom left allow you to control the player reasonably effectively. Though I'm waiting for my next red box day for more controller goodies to arrive.

I've pushed some of the code to control the OLED screen from the attiny up to github, for example at:
https://github.com/monkeyiq/attiny_oled_server_spi2
I'll probably do a more just write up about those source files and the whole display driver subsystem at some stage soon.

Sunday, December 8, 2013

Asymmetric Multiprocessing at the MCU level

I recently got a 16x2 character OLED display from sparkfun. Unfortunately that display had a parallel interface so wanted to mop up at least 7 digital pins on the controlling MCU. Being a software engineer rather than an electrical engineer the problem looked like one that needed another small microcontoller to solve it. The attiny84 is a 14 pin IC which has USI SPI and, once you trim off 4 pins for SPI, 3 for power, gnd, and reset, leaves you 7 digital pins to do your bidding with. This just so happens to be the same 7 pins one needs to drive the OLED over its 4 bit parallel interface!


Driving an OLED screen over SPI using an attiny84 as a display driver from Ben Martin on Vimeo.

Shown in the video above is the attiny84 being used as a display driver by an Arduino Uno. Sorry about having the ceiling fan on during capture. On the Uno side, I have a C++ "shim" class that has the same interface as the class used to drive the OLED locally. The main difference is you have to tell the shim class which pin to use as chip select when it wants to talk to the attiny84. On the attiny commands come in over SPI and the real library that can drive the OLED screen is used to issue the commands to the screen.

The fuss about that green LED is that when it goes out, I've cut the power to the attiny84 and the OLED. Running both the later with a reasonable amount of text on screen uses about 20mA, with the screen completely dark and the tiny asleep that drops to 6mA. That can go down to less than 2mA if I turn off the internal power in the OLED. Unfortunately I haven't worked out how to turn the power back on again other than resetting the power to the OLED completely. But being able to drop power completely means that the display is an optional extra and there is no power drain if there is nothing that I want to see.

Another way to go with this is using something like an MCP23S17 chip as a pin muxer and directly control the screen from the Uno. The two downsides to that design are the need to modify the real OLED library to use the pin muxer over SPI, and that you don't gain the use of a dedicated MCU to drive the screen. An example of the later is adding a command to scroll through 10 lines of text. The Uno could issue that command and then forget about the display completely while the attiny handles the details of scrolling and updating the OLED.

Some issues of doing this were working out how to tell the tiny to go into SPI slave mode, then getting non garbage from the SPI bus when talking to the tiny, and then working out acceptable delays for key times. When you send a byte to the tiny the ISR that accepts that byte will take "some time" to complete. Even if you are using a preallocated circular array to dispense with the new byte as quickly as possible, the increment and modulo operations take time. Time that can be noticeable when the tiny is clocked at 8mhz and the Uno at 16mhz and you ramp up the SPI clock speed without mercy.

As part of this I also made a real trivial SPI calculator for the attiny84. By trivial I mean store, add, and print are the only operations and there is only a single register for the current value. But it does show that the code that interacts with the SPI bus on the client and server side gets what one would expect to get from a sane adding machine. I'll most likely be putting this code up on github once I clean it up a little bit.

Friday, November 15, 2013

Sparkfun vs1063 DSP breakout

The Sparkfun vs1063 breakout gives you a vs1063 chip with a little bit of supporting circuit. You have to bring your own microcontroller, sdcard or data source, and level shifting.


One thing which to my limited knowledge seems unfortunate is that the VCC on the breakout has to be 5v. There are voltage regulators on the vs1063 breakout which give it 3.3v, 2.8v and 1.8v. Since all vregs are connected to VCC and it wants to make its own 3v3 then I think you have to give 5v as VCC on the breakout board.

With the microsd needing to run on 3.3v I downshifted the outbound SPI ports, the sdcard chip select, and the few input pins to the vs1063 board. Those are the two little red boards on the breadboard. The sdcard is simply on a breakout which does no level shifting itself. The MISO pin is good to go without shifting because a 3.3v will trip as high on a 5v line. Likewise the interrupt pin DREQ which goes to pin 2 on the Uno doesn't have any upshifting.

I had a few issues getting the XDCS, XCS, and DREQ to all play well from the microcontroller. A quick and nasty hack was to attach that green LED in the middle of the photo to the interrupt line so I could see when it was tripped. During playback it gives a PWM effect as 32byte blocks of data are shuffled to the vs1063 as it keeps playing. The DREQ is fired when the vs1063 can take at least another 32 bytes of data from the SPI bus to it's internal buffer. Handy to have the arduino doing other things and also servicing that interrupt as needed.

I'm hoping to use a head to tail 3v3 design for making a mobile version of the circuit. I would have loved to use 2xAA batteries, but might have to go another way for power. Unfortunately the OLED screen I have is 5v but there are 3v3 onces of those floating around so I can get a nice modern low power display on there.

The next step is likely to prototype UI control for this. Which I'll probably use the 5v OLED in the meantime to get a feel for how things will work. I get the feeling that an attiny might sit between the main arduino and the parallel OLED screen so it can be addressed on the SPI bus too. Hmm, attiny going into major power save mode until chip selected back into life.

Saturday, November 9, 2013

RePaper 2.7 inch epaper goodness from the BeagleBone

A little while back I bought a rePaper 2.7 inch eInk display. While the smaller, down to 1.4 inch screens have few enough pixels to be driven from an Arduino, the 264x176 screen should need around 5.5k for a single frame buffer, and you need two buffers to "wax on, wax off" the image on the display in order to update. The short story is that these displays work nicely from the BeagleBone Black. You have to have a fairly recent kernel in order to get the right sys files for the driver. Hint: if you have no "duty" file for your pwm then you have too old of a kernel.

So the first image I chose to display after the epd_test was a capture of fontforge editing Cantarell Regular. Luckily, I've made no changes to the splineset so my design skills are not part of the image. The rendering of splines in the charview of fontforge uses antialiasing, as it was switched over to cairo around a year ago. As the eInk display is monochrome the image displayed is dithered back to 1 bit.

With the real time collaboration support in fontforge this does raise the new chance to see a font being rendered on eInk as you design it (or hint it). I'm not sure how many fonts are being designed with eInk as the specific consumption ground. If you are interested in font design, checkout Crafting Type which uses fontforge to create new type, and you should also be able to see the collaboration and HTML preview modes in action.

Getting the actual eInk display to go from the BeagleBone had a few steps. Firstly, I managed to completely fill up the 2gb of eMMC where my Angstrom was installed. So now I'm running the whole show off a high speed 8gb sandisk card. I spent a little extra cash on a faster card, its one of the extreme super panda + extra adjective sandisk ones. The older kernel I had didn't have a duty file for the PWM pin that the driver wanted to use. Now I that I have a fully updated beaglebone black boot area I have that file. FWIW I'm on kernel version 3.8.13-r23a.49.

Trying out the epd_test initially showed me some broken lines and after a little bit what looked like a bit of the cat from the test image. After rechecking the wireup a few times I looked at the code and saw it was expecting a 2 inch screen. That happens in a few places in the code. So I changed those to reflect my hardware. Then the test loop ran as expected!

The next step was getting the FUSE driver installed (change for size needed too). Then the python demos could run. And thus the photo above was made. My next step is to create a function to render cairo to /dev/epd/display in order to drive the display directly from a cairo app.

A huge thank you to rePaper for making this so simple to get going. The drivers for Raspberry and Beagle are up on their github page. I had been looking at the Arduino driver and it's SPI code thinking about porting that over to Linux, but now that's not necessary! I might design some cape love for this, perhaps with a 14 pin IDC connector on it for eInk attaching. Shouldn't look much worse than last night's SPI only monster, though something etched would be nicer.



The 2.7 inch changes are below, the first one is just slightly more verbose error reporting. You'll also want to set EPD_SIZE=2.7 in /etc/init.d/epd-fuse.

diff --git a/PlatformWithOS/BeagleBone/gpio.c b/PlatformWithOS/BeagleBone/gpio.c
index b3ded6f..d1df3df 100644
--- a/PlatformWithOS/BeagleBone/gpio.c
+++ b/PlatformWithOS/BeagleBone/gpio.c
@@ -767,7 +767,7 @@ static bool PWM_enable(int channel, const char *pin_name) {
                                usleep(10000);
                        }
                        if (pwm[channel].fd < 0) {
-                               fprintf(stderr, "PWM failed to appear\n"); fflush(stderr);
+                               fprintf(stderr, "PWM failed to appear pin:%s file:%s\n", pin_name, pwm[channel]
                                free(pwm[channel].name);
                                pwm[channel].name = NULL;
                                break;  // failed
diff --git a/PlatformWithOS/demo/EPD.py b/PlatformWithOS/demo/EPD.py
index da1ef12..41cc6c1 100644
--- a/PlatformWithOS/demo/EPD.py
+++ b/PlatformWithOS/demo/EPD.py
@@ -48,8 +48,8 @@ to use:

     def __init__(self, *args, **kwargs):
         self._epd_path = '/dev/epd'
-        self._width = 200
-        self._height = 96
+        self._width = 264
+        self._height = 176
         self._panel = 'EPD 2.0'
         self._auto = False

diff --git a/PlatformWithOS/driver-common/epd_test.c b/PlatformWithOS/driver-common/epd_test.c
index e2f2b5a..afe3cb8 100644
--- a/PlatformWithOS/driver-common/epd_test.c
+++ b/PlatformWithOS/driver-common/epd_test.c
@@ -72,7 +72,7 @@ int main(int argc, char *argv[]) {
        GPIO_mode(reset_pin, GPIO_OUTPUT);
        GPIO_mode(busy_pin, GPIO_INPUT);

-       EPD_type *epd = EPD_create(EPD_2_0,
+       EPD_type *epd = EPD_create(EPD_2_7,
                                   panel_on_pin,
                                   border_pin,
                                   discharge_pin,