Introduction: Watch Futurama on an 8x8 Pixel Screen
tired of hi-def? (booo!!!)
here's how to convert otherwise reasonable quality video into pixelated garbage and play it on a 2 color 8x8 led matrix, with no sound and only moderate sync.
ingredients:
- (1) 8x8 2 color led matrix
- (1) atmel avr atmega168
- (2) 74hc595 shift register
- (1) 3.3V regulator
- (1) a linux system
this is a mid level avr project, in that it assumes ( does not explain ) how to get a program onto a chip. it's pretty easy once you've done it though, so don't worry. to see how to actually load up a program, The Real Elliot has a nice introduction.
onto the show!
Attachments
Step 1: Have Linux, Avr-gcc, Python, Mplayer...
you'll need linux to use this method, because i used common linuxy things in it. these things are, in no particular order:
1. avr-gcc: needed to make c code into avr code --> wiki stuff about it
2. python: a surprisingly nice programming language --> official site
3. python image library: used here to turn nice video into tiny specs of light without nearly as much hassle as that sounds like. --> pil
4. mplayer: used to turn video into stills --> mplayer
5. mencoder: (optional) change the frame rate of video --> same place as mplayer
i think that is all of the dependancies.
Step 2: Circuit Overview
shift registers:
we use 2 of them, one for green and one for red.
the 74hc595 shift register is a simple latched device that converts serial 1's and 0's to parallel 1's and 0's. the 'don't clear' pin is held high while the serial data is clocked in, then the latch pin is set high triggering the output of the parallel data. dropping the 'don't clear' pin empties the output register and gets ready for fresh data.
all that means is the chip acts as a friendly robot that patiently waits until you have said 8 things while touching it on the shoulder. and then when you punch it in the stomach the robot says them all at once out of its 8 mouths. just slap it upside the head and it forgets, ready to go again.
Step 3: Circuit Overview: Led Matrix
this particular led matrix has a green and a red led in each of the 64 positions. if you light them both at once you get a sort of yellowyorange color.
the only thing special about this matrix is that it is the one i had when i did this. a HUGE improvement in the final result would be to use an RGB led matrix and an additional shift register, but i don't have 64 RGB leds lying around.
moving on:
say the green shift register outputs some fancy pattern like 10110011, that would light up the matrix like this:
g x g g x x g g
g x g g x x g g
g x g g x x g g
g x g g x x g g
g x g g x x g g
g x g g x x g g
g x g g x x g g
g x g g x x g g
(x means off)
as long as all of the ground pins were held low. same deal with the red register.
so to achieve animation, just hold all of the ground pins high except for the one that is on the line you want to draw to.
x x x x x x x x
x x x x x x x x
g x g g x x g g
x x x x x x x x
etc.
Step 4: Atmega168 == Brains
the atmega168 has a bunch of storage space and 28 pins, which is more than we need. by default it runs at 1MHz on an internal RC oscillator, which is not very stable. but that's ok.
PORTD:
to control the ground pins on the matrix, they are wired to PORTD of the avr. in code this will use the &= ~ (see step 6) type of method to keep all pins HI except the one for the row we want to display.
PORTB:
the latch will be on pin 0. the shift register clock is pin 3, green serial is pin2, red serial is pin4, and the no_clear is on pin1.
Step 5: Put It Together
oh my, there are some wires involved.
Step 6: Code for Avr
the avr's job is pretty easy. the overall procedure is as follows:
1. read a byte from memory and write it to the red shift register
2. read a byte from memory and write it to the green shift register
3. set the appropriate pin low to ground the row we want to display
4. throw the latch to trigger the shift register outputs
5. wait.
6. clear the shift registers and repeat.
easy.
this is all done in code using a lot of bit shifts and bit masks. a quick refresher:
the << operator means shift the thing before, over by the amount after.
in an 8bit world,
(1<<3) means: 0000 1000
(1<<0) means: 0000 0001
(1<<7) means: 1000 0000
a |= means to apply an OR to the thing before and after, saving it in the before's spot. this is how you make sure that a bit is set high, or that a corresponding pin is on.
so if
a = 0100, and b = 0001,
then
a |= b changes a to 0101 and leaves b the same. b is usually called a mask
a tilde ~ in front of a value negates it:
a = 0110 1111
a = ~a changes a to 1001 0000
finally &= is just like |= except it's an AND instead of an OR:
a = 0110
b = 0100 and ~b = 1011
so
a &= ~b changes a to 0010. this is how you make sure that a pin is off.
Step 7: Draw a Frame
unsigned char green_vals[][8] PROGMEM = { { 0x01, 0xff, 0x80, 0xff, etc...unsigned char red_vals[][8] PROGMEM = { { 0x01, 0x02, 0x03, 0x04, etc...
remember that the matrix is only 8 leds across, meaning that each entry in those arrays corresponds to a complete description of an entire row. so looping through 8 of them in succession is a frame...
for (i=0; i<8; i++){ set_frame( red_vals[ 0 ][ i ], green_vals[ 0 ][ i ], (1<<i) ); show_frame( TIME_TO_SHOW_LINE );}
except that won't work, because of PROGMEM up there... just call pgm_read_byte() on red_vals and green_vals to fix it.
set_frame( pgm_read_byte( &red_vals[ 0 ][ i ] ), pgm_read_byte( &green_vals[ 0 ][ i ] ), (1<<i) );
the set_frame() function is pretty simple:
/* send frame line data to shift registers and set appropriate grounder pin */void set_frame ( unsigned char red, unsigned char green, unsigned char gnd ){ /* just a loop variable */ unsigned char i = 0; /* set up red, loop through all 8 bits */ for (i=0; i<8; i++) { if ( red & (1<<i) ) PORTB |= red_ser; // if it's a one then serial pin hi else PORTB &= ~red_ser; // otherwise make sure it's low if ( green & (1<<i) ) PORTB |= green_ser; // if it's a one then pin hi else PORTB &= ~green_ser; // otherwise make sure it's low PORTB |= ser_sck; // clock up PORTB &= ~ser_sck; // clock down } PORTB &= ~red_ser; // be sure to leave red's ser line low PORTB &= ~green_ser; // leave green serial pin low /* set the grounder pin for this frame line to low */ PORTD = ~gnd; // upsidedown logic... }
Step 8: Great, But Where's the Data?
the led_matrix_formatted.py script is very simple thanks to the python imaging library.
since we want to change the palette of images the first thing to do is generate a palette array, into which we place the palette that will replace the palette of the soon to be paletted image. palette palette palette, palette palette image.
using some loops that array gets filled up with black, red, yellow, and green.
then we cycle through the command line args, which hopefully contain a bunch of images. each image is resized, converted to 'indexed' (palette) mode, and given the new palette that was generated above.
(btw, you can mess with that palette a great deal to achieve a variety of final looks)
then using more of the bit masking method seen in step 6, two long strings are formed and written to a file for copy/pasting.
Attachments
Step 9: Python Makes It Easy...
first dig up a video file that you want to use ( i used Futurama s05e10 the Farnsworth Paradox )
second generate a bunch of jpegs. to do so type this is an empty directory:
mplayer -ss 00:17:20 -vo jpeg:quality=50 ~/tv/futurama.mpgthe '-ss 00:17:20' seeks to 17 minutes 20 seconds in the file (funny part) and the '-vo jpeg:quality=50' tells mplayer that you want to output mid quality jpegs instead of say, video on your screen. i never tested that quality setting, but given what comes next i don't think it matters.
third run this handy python script: `python ./led_matrix_formatted_data.py ./*.jpg` to create the "RGout.t" file, which is full of data to paste into the c code file.
fourth copy and paste the contents of RGout.t into led_matrix_template.c, and save it as matrix.c.
fifth compile and load the program onto your avr with a programmer. be sure to check the makefile to set the type of programmer you have! (btw i didn't write that makefile, i found it on google and modified it.)
sixth watch pixelly cartoons!
Step 10: Post Scripts
for people who build this i have included a couple of RGout.t 's for their viewing pleasure:
the intro to Futurama, the intro to Harvey Birdaman (including car chase!), and of course 00:17:20 from Farnsworth's Paradox.
as they are 64 pixel descriptions of tv, i assume that they fall under fair use at this point. the shows certainly aren't recoverable in any way from the data provided.
also, for people who don't build the device but would like to watch funny pixelly cartoons (or the tv news, whatever) anyhow, the python script contains commented out lines to make small bitmaps of the converted images.
typing `convert -delay 5 ./*.bmp Animated.gif` in the directory after running the uncommented script will produce an animated gif of the images which can then be played however you like, but i recommend `mplayer -fs Animated.gif` to get a fullscreen experience. ( you'll notice that the commented lines expand the images up to 256x256 pixels. this is not nearly as cool as watching the 8x8 versions blown up to fullscreen. check it out)
36 Comments
9 years ago on Step 4
Hi, can the grounds also be driven using any shift register? BTW nice pencilmanship!
12 years ago on Introduction
cool but don't u think it's misssing a arduino
13 years ago on Step 2
ha. I agree, i was like.. hmm? then stomach punch - bam got it! Thanks. We need you to write a dictionary for everything that needs an explanation. :)
13 years ago on Step 2
Really great explanation. Keep on going!
14 years ago on Introduction
What movie/show is that?
14 years ago on Introduction
does the display actually flicker like that or is that just a similar refresh rate to the camarea
Reply 14 years ago on Introduction
probably the camera
14 years ago on Step 2
Awesome explanation. I wish you where my robotics teacher in high school. lol
Reply 14 years ago on Introduction
I wish I had a robotics teacher.
Reply 14 years ago on Introduction
lol mee 2
Reply 14 years ago on Introduction
is that a whippet?
Reply 14 years ago on Introduction
lol ur the second person to ask. no its not mine but its an odd mix breed. i dont remember what it is. but i suppose it looks sort of like that due to the angle i took the photo from.
Reply 14 years ago on Introduction
I only asked because I saw the other post ,and I thought it would be funny and/or annoying if I asked too. I looked up whippets, an they do look similar, but I don't think it is a whippet.
Reply 14 years ago on Introduction
haha lol
Reply 14 years ago on Introduction
Yeah, It worked!
14 years ago on Introduction
You could add shades of color by storing more than 1 bit of data per subpixel per frame. That 5ms delay for each line is plenty of time to do some pseudo-PWM. It would make the data much larger though.
16 years ago
how would you go about making the screen a 8x6. Meaning that it would be 8 matrix's wide and 6 matrix's tall. How do you interface all the matrix's to act as one big screen
Reply 14 years ago on Introduction
To simply scale this design up, you would need 48 of the matrixes, obviously. The circuit would be laid out as if you had one long 384x8-pixel matrix, even though you would arrange them physically in a rectangle. You would need an additional shift register for each matrix, and all the shift registers would need to be chained together to cascade. And you would need a row driver capable of putting out enough current to drive 384 LEDs at once.
That is not a particularly feasible design. The largest display I have seen like that was 4x2 * 8x8, and that was stretching it. You would probably want to build a 8x1 * 8x8 display, then get more creative in stacking those to avoid ridiculous input pin counts.
Reply 16 years ago
do you mean 64x48 pixels? you could do that by adding a few more shift registers and adjusting the code to notice them.
15 years ago on Introduction
if anyone wants to watch futurama and other shows like american dad the boondocks and robot chicken all seasons and episodes from these show u should try http://newamericandadepisodes.com/ also has funny clips and pics