Step 13: Program the PIC

Picture of Program the PIC
POV GEN - waveform.png
The board now complete, we have only to give it a program to run, and we will be ready to set our creation free into the wild!

The PIC can be programmed either in C++ or assembly language. I chose C++, to mostly to save time, but also because I didn't necessarily want to commit to this architecture, and admittedly, assembly languages are no fun! For simplicity, I developed the board using the DIP PIC12F509, which is pin compatible with the 508. It is also larger and easier to manipulate for testing purposes, and I found that it had more memory than I needed, so I was able to downgrade the chip for the final product.

How the POV Card works

The card is off until the momentary on switch is held down. On power up, the program simulates a little rolling ball with a binary counter that rolls in and out with the tilt of the card. After roughly four tilts back and forth, it then switches to Persistence Of Vision mode and cycles through a series of programmed patterns. Currently, it says my name, followed by a neat square wave pattern that acts as a separator, and then my phone number. When the tilt switch closes, the program recognizes that as the start of a wave, and plays the current pattern one time through, then waits until another wave starts. As long as the wave does not double back too soon, and the pattern is not too long, there will be no overlap as the pattern will be blank until it resets.

The Patterns

Each pattern is an array of bytes, each byte a column in the sequence. The time that each column (frame) in the pattern is left on and the time between frames determines the speed of the display, and how close the frames are spaced when the card is waved. Basically, it changes the width of the characters and the pattern as a whole. Furthermore, the speed at which you wave the card also plays a part in the spacing, but not enough to make it illegible; since it changes frames at a constant rate, the variation is not enough to matter, but is enough to make it look dynamic. Neat, huh?

To create the patterns, I modified a script I found  written by Andrew Mason. My version is customized to allow for alternate byte formatting and number of LEDS, but the LED placement is fixed, for the time being. You can make any pattern you want, provided it is small enough to fit in the PICs memory. The rate that the pattern plays back at can be changed in the code, as well.

The Program

The code is very simple. Without PWM or interrupts, there were not very many tricky things I could do, even if I wanted to. Conserving space and optimizing made it a little more interesting, though, given the tweaking necessary to make the code fit in the PICs tiny memory.

To play a pattern, the PIC simply checks for the tilt switch to be activated, then loops through a pattern. It sets the GPIO outputs to the current byte in the array, which represents a frame, waits a set period, then increments the array. Once the pattern has played all the way through, it blanks the display and waits until the tilt switch has been activated. This indicates another wave has started, and it goes through the pattern once more. 

The frame code looks like this, without quotes : "0b00000000,". The 0b delineates a byte and the comma separates each byte from the next. This is all placed inside an array of constants, since it won't be changing during the playback and that will save RAM. Five of the bits in the array indicate an LED output, while one is unused and one is a read-only bit used for the input pin. The bits used are: 0bXX12X543, where the number indicates the pin.

The Code

#include <htc.h>
#include <stdlib.h>

const char waveform[] = { /* 0bXX12X543 - sets pin at number, to output high, e.g.: 
0b00100111, complete array code omitted for space; see attached .c file for complete code */ };
const char binary[] = { /* code omitted */ };
const char name[] = {/* code omitted */};
void init(void) {
	OPTION = 0b01000111;
	//sets pin 2,3,5,6,7 output, pin 4 input
	TRIS = 0b00001000;
void display_roll (const char p[], const char pattern_length) {
	char i = 0;
	for (unsigned int j = 0; j < 350; j++) {
		//plays pattern forwards or backwards depending on tilt
		if (GP3 == 0 && i < pattern_length) { i++; } else if (GP3 == 1 && i > 0) { i--; } 
		GPIO = p[i]; //sets LED outputs to current frame in current pattern
		_delay(16000); //frame delay in number of cpu cycles
void display_pattern (const char p[], const char pattern_length, const char loops, const char speed) { 
	for (char i = 0; i < loops; i++) {
		//exits display either at swing restart
		for (char j = 0; j < pattern_length && GP3 == 1; j++) {
			GPIO = p[j];
			//frame delay in multiples of 10 microseconds :)
			for (char k = 0; k < speed; k++) { _delay(40); }
		GPIO = 0b00000000; //blank output so pattern doesn't overlap while swinging back to start
		while (GP3 == 1); //if still waving, wait until swing restarts and switch contacts 
		_delay(5000); //lazy debounce
void main(void) {
	display_roll(binary, 31);
	while (1) {
	/* function variables are: pattern name, length of pattern (in bytes/frames), 
	number of loops, speed of display/framerate */
		display_pattern(name, 69, 110, 25); 
		display_pattern(waveform, 71, 100, 25);
		display_pattern(number, 61, 110, 25);
Programming the Board

All code was written in Notepad++, an excellent free multi-language editor. You will also need the MPLAB IDE with a copy of the HI-TECH C Compiler. Both are available in free versions.

You will need a copy of Microchip's PICKit 2 programming software. With the chip connected and the iCP01 plugged in, set the device family to "Baseline" and select PIC12F508 from the device dropdown. Then load the hex file and press the write button.

  1. PIC12F508/509/16F505 Datasheet
  2. Hi-Tech C 9.81 Compiler User Guide
  3. POVgen online LED Pattern Generator
  4. Hi-Tech C Compliler
  5. MPLAB Integrated Development Environment

Tools & Supplies
muscleflex1 year ago

still no video?

Fayezslim2 years ago
Plz upload the hex file for downloading it plz
cstewart0002 years ago
Wow man, great instructable!!!
beckettman4 years ago
I am working on my own card and encountered a problem with Hi-Tech C version 9.81. For some reason they do not have a declaration for "TRIS" in the header files. They will probably fix soon but in the meantime you just add this declaration before the main body of the code:

"volatile control unsigned char TRIS @ 0x006;"

Thanks for the instructable. Great Idea!!
sponges (author)  beckettman4 years ago
There's always something... Yes, I forgot to mention that the default device specific header files are sometimes lacking. I also had some problems when they changed their formatting on legacy devices for the PIC12F508/509 in the latest version of MPLAB. Thanks for catching that!
jplate14 years ago
totally awesome. do you take orders? id buy a set quantity. like 200? if your price is right for me.
Where can i buy some? Thanks for the Instructable. I wish i had the time to do all of this it looks like fun.

Zimoich4 years ago
I agree.

Would be one of the best instructables with the final touch of a video of it in action.
The writeup was detailed but very understandable.

Now I want to redo my etching lab. =)
juanoporras4 years ago
Wow, one of the most complete instructables i have seen, congratulations!.

the only thing missing is a video of the card in action. ;)

Peace, thanks for sharing