The Nokia 3310 LCD is well known in the electronics community; I decided to have some fun and get it to do things the designers probably weren’t intending it to ever do! 😛
This LCD is a monochrome graphics LCD with a resolution of 84×48 and while relatively old (1998), it’s brilliant for its very low price.
This controller uses the PCD8544 controller IC has an easy to use SPI protocol and handles all the hard work of driving this display (multiplexing, generating bias voltages, ect.) Using the display is relatively easy with any microcontroller, I used a breakout board for ease of access on a breadboard/stripboard.
Adafruit has written a nice Arduino library and tutorial for using this display. It was perfect for my needs, as their library can draw text, lines, rectangles, circles and other shapes easily to the display, plus all these functions are built into the library. I will not explain how to wire this display up as their tutorial explains it well.
I soldered up a simple shield for driving the display, and a few buttons. However, these are not normal buttons but capacitive buttons! The reason why I made these was because I did not have any normal buttons, plus it is very cool to use! The piece of code I used for the buttons was found over here at the Arduino site, and is connected directly to the buttons. No pullup resistors were used in this case.
The buttons were connected to the following pins:
- Select File – Pin 19 (analog A5)
- Toggle FPS – Pin 18 (analog A4)
- Play – Pin 17 (analog A3)
Here are some images of the shield I made (I apologise for the messy soldering, I needed this done in a hurry):
First of all I wanted to make a simple text scroller. This was easy, and the following code was used in the loop() routine to scroll some text, with display being the display object:
String text = "Test of scrolling text!";
word textWidth = text.length()*6+85;
for(int i=0; i display.clearDisplay();
This worked very well, with smooth text being scrolled across the screen. However, I wanted more.
I had seen somewhere of getting text to scroll while each character ‘wobbles’.
This effect was easy to achieve.
Instead of printing out a whole string to the display, you print out each character of the string one by one, and change the Y axis of each one for the wobble effect.
To get the ‘wobble’, a sine wave was added to each character’s Y axis, each with a small offset to make the sinusoid slightly different for every letter. I then made a few lines at the bottom to follow the pattern of another sine wave at the bottom of the screen just for something to fill the extra space with.
The effects turned out brilliantly. (Arduino code for the Wobbly Text at the bottom of the page)
This was fun, but I still wanted more. Then I came accross this video. It has an ATMega88 getting video off an SD card and playing it on a monochrome LCD at a good frame rate, and states that the program was written in ASM.
The video looked like it was using some form of 1-bit dithering to be visible on a monochrome display, and was very recognisable. I thought that this was definitely possible, and I was going to give it a try in C++ first and try redoing it in ASM later on, and did not know what frame rate I would get, but I assumed the worst.
I had also purchased an Ethernet shield from Dealextreme. This has the Wiznet chip which is used in the Official Ethernet shields, and works well. It has a SD card slot which would work well for this project. Best of all, it is less than $14 which is almost one third the price of the official version.
First of all I came up with a format for saving my data. The encoded data file would consist of ASCII characters. If the ASCII character was from
0x3F in hex (the first 6 bits, or from
B111111) then it would be treated as data.
If I wanted the first 6 pixels of the first row on the frame buffer to appear in this binary pattern:
101010 then I would set the character to be
0x2A in hex, or
B101010 in binary, and increases the X coordinate by 6. If I repeat this character, it would set the next 6 pixels to 101010, and so on. This is what is used to set the image of the display.
When this is done 14 times, I end up with 84 pixels which is exactly the width of one of the rows of the display. Perfect fit!
Now, if the ASCII character is over
0x3F it is a control character. This is a command that can trigger a certain function in the program.
Here are the control characters:
0x7B– New line – Increments the Y-axis by one, use this when you have written all 14 data characters for the row to go on to the next one and carry on sending your data.
0x7C– Reset the X and Y coordinates – Use this when you have filled up the whole display, and need to start at the beginning point for a new image.
0x7D– Update display – This sends the data in the frame buffer to the display and displays it.
With these commands it is possible to display images or video.
I wrote a Processing script to render a simple 3D Cube and save each frame to a file in the sketch’s project folder using the protocol above (code at the bottom of the page).
I made it save the data to a file called data.dat.
Now I formatted my MicroSD card on Fat32 as the format, and copied the data.dat file onto it.
Writing the Arduino video player program was the most difficult part. I used the SD library included with Arduino to read the files of the SD card and wrote up a file chooser what uses the buttons on the shield, and a routine to read data from the chosen file off the SD card, draw the images and handle the commands.
Everything worked out OK in the end after a day of work, and I had a working product. However, I still needed to finish my goal of displaying video.
Next I did some research on Dithering. I then discovered Windell Oskay’s writeup on how dithering works and some code he wrote to use Dithering in Processing. The example converted real-time video from the webcam to 1-bit dithered images using the Atkinson Dithering algorithm. This was exactly what I needed. (thanks Windell!)
I then modified the program with a output resolution of 84×48, and gave it a video as the input file. (If you want to use a different file, make sure it is 84×48 with ‘high contrast’ for best results with the dithering, although any resolution should still work fine.
For the video, I decided to play the music video of Rick Astley’s “Never Gonna Give You Up”, primarily because of its ‘perfect contrast’ for 1-bit dithering, and also because it’s a fun internet meme 😉
After tackling many more bugs, crashes and other issues I finally had the program working and saving the data file. (yes, download at the bottom of the page)
All I then needed to do was then copy the file to the card and then watch the video!
The results were very impressive. After making the program more efficient I got it to run at a stable 15 frames per second!
I was simply amazed at how good the video looked and how it ran. I had used C++ to do this – No ASM, and it looked almost the same as the one I had seen previously in the other video. However, it should be noted that the other version had a higher resolution display and was running at half the clock speed (8Mhz compared to Arduino’s 16Mhz). There is definitely room for improvement that I will do at some stage. Most of the RAM on the Arduino is used up, but there is around 340 bytes available which should allow extra features. I am still using Adafruit’s library for the display, simply due to its inbuilt framebuffer and ease to draw pixels.
(video is at the top of the page)
In the end, it has been a good experience doing this project and I am impressed by my results. I hope these resources may be useful to others. There are definitely a lot of improvements that need to be made as there are many inefficiencies, but I will fix these at a later stage. Right now I am satisfied that it works perfectly.
Please note that the Processing sketches were written for Processing version 1.5.1. It will not work directly on the Processing 2.0+ betas, however it should be rather easy to port it over.
If you see any files with a
.txt extension, just rename it to a
.dat extension. There is no difference with the file content. I used it for debugging.