Persistence of Vision

With 20 LEDs in a row a persistence of vision display is something that just has to happen. But how to implement it most efficiently? It could be done from scratch but I will try to reuse as much open source stuff as possible to get a lean and mean implementation. I also will use „direct port manipulation“. Althoug this is a somewhat advanced technique it is very appropriate for this application. It will make the sketch much simpler.

So let’s see what a POV display actually has to do. It will display a bit pattern for some time and then replace it by a new pattern. Often the timing is synchronized to some movement of the display. In this case I will just use some fixed delays. This leaves the question how to store and output the bit patterns. Of course 20 bits will fit into 3 bytes. And of course rows of 3 bytes can be stored in some array.

But there are some issues. Extracting the bits and pushing them through digitalWrite is slow and somehow anoying. Especially since the 20 pins are mapped to exactly 3 ports. All 3 ports are accessible by writing just 1 byte. This technique is known as direct port manipulation. It may make the sketch incompatible to some Arduino clones that are not based on the Atmega 328 family. This can be fixed with some macros but I sacrifice compatibility for clarity.

Another issue is how avr-gcc handles arrays. By default arrays are copied from FLASH memory into RAM. Due to the tight RAM constraints of the controllers this is something that I definitely want to avoid. Fortunately there exist the pgmspace libraries which provide the PROGMEM macro. With this macro the compiler will NOT copy the array to RAM. It follows that the array’s contents can not be accessed as usual. Instead the content must be read with some helper function.

Enough said. Here is how such a sketch will look like.

//
//	www.blinkenlight.net
//
//	Copyright 2011 Udo Klein
//
//	This program is free software: you can redistribute it and/or modify
//	it under the terms of the GNU General Public License as published by
//	the Free Software Foundation, either version 3 of the License, or
//	(at your option) any later version.
//
//	This program is distributed in the hope that it will be useful,
//	but WITHOUT ANY WARRANTY; without even the implied warranty of
//	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
//	GNU General Public License for more details.
//
//	You should have received a copy of the GNU General Public License
//	along with this program. If not, see http://www.gnu.org/licenses/


#include <MsTimer2.h>
#include <avr/io.h>
#include <avr/pgmspace.h>

uint8_t const pov_pattern[] PROGMEM = {
                                  0b000000, 0b000000, 0b00000000, // line   1: ....................
                                  0b000000, 0b000000, 0b00000000, // line   2: ....................
                                  0b000000, 0b000000, 0b00000000, // line   3: ....................
                                  0b000000, 0b000000, 0b00000000, // line   4: ....................
                                  0b000000, 0b000000, 0b00000000, // line   5: ....................
                                  0b000000, 0b000000, 0b00000000, // line   6: ....................
                                  0b000000, 0b011110, 0b00000000, // line   7: .......XXXX.........
                                  0b000001, 0b111110, 0b00000000, // line   8: .....XXXXXX.........
                                  0b000011, 0b111110, 0b00000000, // line   9: ....XXXXXXX.........
                                  0b000111, 0b111100, 0b00000000, // line  10: ...XXXXXXX..........
                                  0b001111, 0b110000, 0b00000000, // line  11: ..XXXXXX............
                                  0b001111, 0b100000, 0b00000000, // line  12: ..XXXXX.............
                                  0b011111, 0b000000, 0b00000000, // line  13: .XXXXX..............
                                  0b011110, 0b000000, 0b00000000, // line  14: .XXXX...............
                                  0b011110, 0b000000, 0b00111110, // line  15: .XXXX.........XXXXX.
                                  0b011100, 0b000000, 0b01111110, // line  16: .XXX.........XXXXXX.
                                  0b111100, 0b000000, 0b01111110, // line  17: XXXX.........XXXXXX.
                                  0b111100, 0b000000, 0b01111110, // line  18: XXXX.........XXXXXX.
                                  0b111100, 0b000000, 0b01111100, // line  19: XXXX.........XXXXX..
                                  0b111000, 0b000000, 0b00000000, // line  20: XXX.................
                                  0b111000, 0b000000, 0b00000000, // line  21: XXX.................
                                  0b111000, 0b000000, 0b00000000, // line  22: XXX.................
                                  0b111000, 0b000000, 0b00000000, // line  23: XXX.................
                                  0b111100, 0b000000, 0b01111100, // line  24: XXXX.........XXXXX..
                                  0b111100, 0b000000, 0b01111110, // line  25: XXXX.........XXXXXX.
                                  0b111110, 0b000000, 0b01111110, // line  26: XXXXX........XXXXXX.
                                  0b011111, 0b000000, 0b01111110, // line  27: .XXXXX.......XXXXXX.
                                  0b001111, 0b000000, 0b00111100, // line  28: ..XXXX........XXXX..
                                  0b001111, 0b100000, 0b00000000, // line  29: ..XXXXX.............
                                  0b000111, 0b111000, 0b00000000, // line  30: ...XXXXXX...........
                                  0b000011, 0b111100, 0b00000000, // line  31: ....XXXXXX..........
                                  0b000001, 0b111100, 0b00000000, // line  32: .....XXXXX..........
                                  0b000000, 0b111100, 0b00000000, // line  33: ......XXXX..........
                                  0b000000, 0b000000, 0b00000000, // line  34: ....................
                                  0b000000, 0b000000, 0b00000000, // line  35: ....................
                                  0b000000, 0b000000, 0b00000000, // line  36: ....................
                                  0b000000, 0b000000, 0b00000000, // line  37: ....................
                                  0b000000, 0b000000, 0b00000000, // line  38: ....................
                                  0b000000, 0b000000, 0b00000000, // line  39: ....................
                                  0b000000, 0b000000, 0b00000000, // line  40: ....................
                                };

void blink() {
	static uint16_t index = 0;

	PORTC = pgm_read_byte(pov_pattern+(index++));
	PORTB = pgm_read_byte(pov_pattern+(index++));
	PORTD = pgm_read_byte(pov_pattern+(index++));

	if (index >= sizeof(pov_pattern)) { index = 0; }
}

void setup() {
	DDRD = 0b11111111; // set digital  0- 7 to output
	DDRB = 0b00111111; // set digital  8-13 to output
	DDRC = 0b00111111; // set digital 14-19 to output (coincidences with analog 0-5)

	MsTimer2::set(2, blink);
	MsTimer2::start();
}

void loop() { }

Notice that the sketch does not loop. Instead it uses the MsTimer2 library to trigger blinking. This library is not included by default with Arduino. You have to download it from the playground and install it. Installation means just unpacking it into the library directry. If the Arduino IDE is open during „installation“ it must be restarted to pick up the library.

It would be easily possible to get the same effect with delays. However I like this library a lot. It provides some rudimentary multitasking. Thus the main loop is empty. Although the animation is running the controller is „free“ for doing other computations. If no „other“ computations are required it would also be possible to sleep it most of the time to reduce power consumption.

So now we can have some nice light painting.

Smilies

Smilies

The picture above was taken with a digital SLR mounted on a tripod. The shutter was open for 10s. I was just waving the shield in front of the camera. This is very easy and needs very little tries to get great results.

Before you start to edit my sketch to create your own persistence of vision displays read on to learn how to generate pov sketches.
You may also want to learn about my new and improved version of the sketch above.

Advertisement

7 Responses to Persistence of Vision

  1. Bruno Hoegger says:

    Hi … the “persistence of Vision” file does not compile with the Arduino IDE-1.6.1 but it compiles without problems with Arduino IDE-1.0.1 ==> here the error message for IDE-1.6.1 :
    sketch_mar27b.ino:24:23: error: variable ‘pov_pattern’ must be const in order to be put into read-only section by means of ‘__attribute__((progmem))’
    Error compiling.

  2. Thanks for pointing this out. The fixes are here: https://github.com/udoklein/blinkenlight.

  3. Bruno Hoegger says:

    POV sketch: “Persistence of Vision”, chapter 17 is now ok compiling with the Arduino IDE-1.6.1 … super … thanks … ! But there are some other sketches in some of the FRANZIS (Dr. Klein, Lichteffekte … ) sketches with some errors in the #include … concerning names missspelling … “uppercase letters” … the Compiler will not find the include files … !

  4. Bruno Hoegger says:

    POV-sketches: Dr. Udo Klein, FRANZIS, “Lichteffekte … ” in both sketches POV-17 and also POV-18 the array must be defined by: “uint_t const pov_pattern[ ] PROGMEM = { …” so that the Arduino IDE-1.6.1 Compiler will translate the sketch properly … ! Why the Arduino IDE-1.0.1 Compiler was so tolerant … I do not know it … !

  5. Travis says:

    Im getting the errors

    sketch_aug15b.ino: In function ‘void setup()’:
    sketch_aug15b:82: error: ‘MsTimer2’ has not been declared

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.