1 - Making a start
The first step was to take a close look at the real arc and find out, how it works. No, I'm not trying to reverse-engineer it (which is not possible when you only have this picture). It's just that looking at work of others and trying to figure out, why they did it this way can save you a lot of trouble - and cost, when it comes to making pcbs over and over again, because your design was faulty.
The start had to be done with electronics. And with the most central part of a monome arc:
How do you drive a whole bunch of leds with a microcontroller? There are different approaches, each with its pros and cons
- Driving them with the i/o-pins of a microcontroller. Usually you only do this, when you have unused pins. And because we have to drive 256 leds for an arc 4, this clearly is not an option
- Driving them with shift registers. A sipo-shift-register is an easy and cheap way to add more i/o-pins to your microcontroller. You can ideally add an inifite number of outputs, with the only sacrifice beeing a longer time to update them. This is an option, because its easy, it can drive a lot of leds and the display can be refreshed reasonably fast.
- Driving them with a matrix. A matrix arranges leds in (usually but not necessarily) two dimensions and iterates through each line. It is a good way to drive a very large number of leds with only few outputs. Unfortunately this needs high switching speeds so it won't flicker. Even a simple matrix needs very efficient programming if it has to be controlled by a micro controller. Thus, the framerates will not be very high with common uCs.
A matrix driver can be combined with shift registers to save pins on your uC, but this will lower the refresh rates even more.
- Driving the leds with special ICs. This seems to be a common approach for many people as it is very easy to realise. ICs like the TLC5940 will allow you to run 16 leds in 4096 dimming intensity levels, interfacing the chip with a simple synchronous serial interface. However, depending on special chips that might be discontinued tomorrow is not always a good idea. Also, such chips are usually harder to get and more expensive.
But simply driving leds in an on/off fashion is not enough for an arc. The arc features 16 levels of brightness control for each led independently. This is usually done with pulse width modulation because it is easy and does not require more hardware. For 16 levels of light intensity, the PWM period can be split into 16 pieces. For level 0, the led is off all the time, for level 1 it is on for the period of one piece, for level 2 -> two pieces and so on. Doing this fast enough (about 100-150 times a second) will appear as different levels of brightness for the human eye.
However the human eye has a non-linear curve of brightness-sensitivity. Doubling the amount of light emmitted by an led does not double the perceived brightness for us. A good article about this can be found here (german only...). So to get something about 16 individual levels of brightness, it actually needs at least 128 levels of PWM intensity. And here is the point, where refresh rates become the game-changer.
- So driving leds with uC-pins is not an option because of limited i/o-pins.
- Driving them with a matrix is cool, but once you start to include pwm as well, it won't be possible with a simple uC and will most likely require to write programms for highest efficiency using assembly language. And remember, the micro controllers have to react to incoming data from the encoder and the computer as well. Maybe it woul be possible to do - but only with a whole lot of effort.
- Driving them with special ics that do all the work for you is very simple, but not efficient in terms of price and availability.
- Driving them with shift registers seems to be a good choice to try.
When looking at the real arc, it seems like they chose to do the same. You will find 8 shift registers per wheel, with their outputs connected to an led with a resistor to throttle the current. (in the image mentioned above, the sipo-shift registers are on the back of the pcb and the resistors can be found on the front as resistor networks in a "star" format - this is what I read from the picture). I might be wrong here, though.
However, my first approach was a test board to check, if it will be possible to achieve 16 levels of "gamma corrected" brightness with an AVR uC and 8 shift registers.
My idea was to build a little test setup that can drive the leds required for one wheel. Before i started wiring it up, i did some rough calculations:
Updating the display requires shifting out 8 Bytes, one for each shift register. With a system clock of 20Mhz, one CPU cycle is 50ns. Shifting out one bit via the serial interface takes two cycles for the "low" part of the clock pulse and two cycles for the high part of the clock pulse, this sums up to 4 cycles per bit. One byte therefor requires 32 cycles of pure data transfer. To start transmitting the next byte, it is necessary to jump into an ISR ("interrupt service routine" - special part of the programm thats called on a system event) and afterwards jump out of it again to resume what it did before the call. Both jumping "operations" sum up to at least 3us for saving context data and another 3us for restoring it afterwards. So transmitting one byte takes about 7,6us. As a result, transmitting the whole set of 8 bytes will take 61us.
At the desired 100Hz PWM frequency with 128 steps per cycle, this would need a refresh every 78us. Within this short period of time, the uC does several things:
- jumping into an ISR (=3us)
- calculating the next byte to send out and preparing that byte for transmission to the shift registers (time not measured)
- jumping out of th ISR (=3us)
- repeating steps 1-3 another 7 times for the other 7 bytes
- working on the normal program (e.g. scanning for button presses or animating the display)
The first thing that's problematic: From the 78us the CPU spends 62% with jumping into and out of the ISR. Within this time it does simply nothing except saving context data to RAM and loading it back from RAM at the end of an ISR call. There is about 30us time to do other calculations - this equals to 600 cycles. 600 cycles sounds like a lot, but it's actually little. These 600 cycles must be used
- to calculate which leds must be switched on in the next PWM cycle,
- to communicate with the computer its connected to and
- to read values from the rotary encoder
Even though thats quite tough to realize within this short amount of time, i nevertheless decided to make a simple test circuit on the breadboard. I used an ATMega8 and clocked it with 20MHz (it's made for 16MHz but can run at higher frequencies as long as no special modules are used). I connected two shift registers to its serial data output and coded some software using the programming language C.
This video gives you a brief summary of what happened.
Even though I already used several techniques to speed things up a little (e.g. look-up-tables) I was not able to realize the algorithm within the required time-frame. I had to lower the refresh speed a bit to leave get more processing time within one PWM cycle.
There might still be room for improvement. But overall I found this solution was not flexible enough and would at least require programming in assembly language to run noticeably faster. Especially the fact that I had not yet tried to make communications to the computer made me believe that this approach is not robust enough. The led output looked okay, but a bit flickery. One more reason to try something different.