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.
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.
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.
Thanks for pointing this out. The fixes are here: https://github.com/udoklein/blinkenlight.
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 … !
Feedback of the form “something has some error” is not really helpful. Could you please be more specific which sketch has an error?
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 … !
Im getting the errors
sketch_aug15b.ino: In function ‘void setup()’:
sketch_aug15b:82: error: ‘MsTimer2’ has not been declared
This indicates that you did not install MsTimer2. Please install this library and try again.