Skip to main content

Nokia 3310 display fun

Recently I wrote a driver for a nokia 3310 (pcd8544 based) lcd display which I had lying around.Again, I decided to create my own driver just to learn something about it and to see how much effort it will require - turned out that this driver is pretty easy to handle. Despite the fact that it's suggested to use 3V3 logic (maximum accepted voltage accordingly to the datasheet it's 7V), it works great with arduino powered with 5V and using 5V logic outputs.

Adafruit's Library

I had a look on Adafruit's library for that display just as a reference (github) and it seems very primitive to be honest - it allocates a huge display buffer without asking the user whether it is really needed (it consumes more than 25% of available RAM on Arduino) and what's even worse it uses bit banged SPI to communicate with the display. Why ? Why bother when there is fast & reliable hardware bus available on that MCU (and of course like all Arduino IDE libraries it's written in C++ which makes things even more slower). And it doesn't come with any font, so in fact the user must take care about it himself. Not being very humble - the implementation in libpca is far more superior.

Hello World

Just to test the driver & the display - let's first play around with a code template and generate some easy graphics (and of course mandatory for every project - "Hello World" program)




Simple, isn't it ? This code in a couple of lines initializes the SPI bus in the simplest manner, initializes the display itself, installs it as an STDOUT output and prints the "Hello World" string. That's fancy, plain and simple, no unnecesary C++ mumbo jumbo (don't get me wrong I love C++, but the way it is used in the Arduino libraries - it's a joke really). This is how it looks on the display:

I'll use that code as a template and will enhance it further on to generate some other effects. I'll be simply replacing the "Hello World" printf line with some other code.

Let's start with something simple - checker board. Each byte in the display represents 8 rows, 48 rows are distributed amongst 6 bytes. The memory organization of the display looks the following way:


Which means that if we'll fill the display with a pattern of 0b10101010 (0xaa) - we'll get something like a very high grained stripes. Let's test that. Replacing the 37th line (printf("Hello world !!!");) line with the following code:

pcd8544_fill(&lcd, 0xaa);

You'll get:


Ok, let's do something fancier. There's an old trick which I love to use (back from the dos 7.0 world and the famous software interrupt 13 which brought you into the 320x240 graphics mode) - generating the Sierpinski's triangle with just logical AND operation. Let's replace the line 37th again with the following code:


The result:



Yeah, that's nice, but we can still do better without much effort. Each pixel on the display can be either turned on or off - it's a 1-bit color depth. In other words it's physically impossible to display any shade of grey. But this is not a problem really - that limitation has been overcome long time ago with dithering alghorithms - we could implement simple error diffusion dithering or floyd-steinberg dithering to get awesome effects - but that would require some more effort than I really want to spend at the moment. Let's focus on an ordered dithering using an 8x8 Bayer lookup table - thanks to that method we'll be able to effectively simulate a 6-bit color depth (0 - 63) on a 1-bit color depth capable hardware and guess what - yes libpca implements that method for your convenience as well all you have to do is simply use it.

I recommend having a look on those articles if you are really interested in dithering - it's a lot of fun.


First, let's generate a gradient. This will be the code:

And the effect:



Ok. That's pretty nice. We can tell how this method works - it generates different patterns (with different intensity) in order to simulate a particular shade - it's not as good as floyd-steinberg but good enough and what's most important - fast enough.

Let's see if good old atmega is fast enough to generate another old-school graphics effect - famous plasma effect This will be a challenging task for small Atmega - this effect heavilly depends on the sinus function and sqrt, power of two functions - everything of which our CPU is not very good at. Let's try anyway. 

The general alghorithm for the basic plasma effect is:

for every x
for every y
color = sin( sqrt(x*x + y*y) );
putpixel (x,y,color);

Surprisingly previously I was using a "buffer" variable as a display buffer, but for the plasma effect it's not really needed. First thing which I've done was generating the sin function lookup table:

    for (uint16_t i = 0; i < 256; i++) {
        sin_table[i] = (cos(i/2)*sin(i))*31 + 32;
    }


In order to avoid two loops, one which generates the image in the buffer and the other one which copies the data to the display I'm trying to do everything in one loop (just to make it quicker). The complete code looks the following way:



What I'm trying to achieve is to generate a byte of image (vertical) and copy it to the display, so in fact I'm using a single byte as a processing buffer. I'm using the ordered dithering function the same way as previously. This code on my Arduino is able to generate the plasma effect with something around 2, 3 frames per second. Have a look:




The code is available on github. You need libpca:

git clone git@github.com:dagon666/avr_Libpca pca

and the my avr projects repository:

git clone git@github.com:dagon666/avr_ArduinoProjects projects

In the projects directory you'll find a directory called nokia_spi.

A snapshot is available also here.
 



Comments

Post a Comment

Popular posts from this blog

RTC without RTC ? How good is my crystal ?

Clock - I think that it's the most popular idea for a project with the MCU . It's not that hard from both software and hardware side to realize but not that trivial as well, not mentioning the fact that once realized it sits somewhere in the house, constantly reminding about itself and brings a kind of satisfaction :). There are a lot of options really to implement, a buzzer alarm, perhaps a thermometer, display and buttons servicing, perhaps maybe even serial port usage for NTP synchronization. But ... The most challenging thing - like in every clock is the time reference source. There are a couple of options: well calibrated frequency source acting as an interrupt source Real Time Clock module with external crystal ( < 20 ppm ) internal clock These days RTC chips are so cheap and widely available that they are really the only reasonable choice, but if we're only planning to play around a bit with the software without paying much attention to accura...

Simple Serial Port Command Line Interface (CLI)

It's often very useful to have some sort of interface through which a basic management can be done of our application. Whether you're designing a giant Marquee with a LED display or a plotter or you simply need to get some diagnostics from your device occasionally, a simple CLI system can come very handy.  Of course, designing something similar to "bash" or any other unix shell is completely out of scope since those applications are huge and are simply an overkill for our needs. It's pretty simple though to create some basic, yet flexible & easilly extensible CLI system. First thing needed is a command type definition. This will bind a keyword with an actual underlying routine executed for that keyword typed in the CLI . typedef struct _t_cmd { const char *cmd; void (*fh)(void*); } t_cmd; The command type is pretty simple. There's the CLI command/keyword pointer, it holds a pointer to the execution function and that's it. OK, so...

Arduino PWM Audio DAC

This post is an addendum to the previous one. To make our lives (as a software developers) even more easier it is possible to recreate a real audio without even having to built the R-2R ladder. Atmega328 comes with a PWM capable timers and they're good enough to produce audio quality analog signal. PWM fundamentals I'm not going to go into details about PWM digital to analog conversion, on this blog I focus on my code and do not intend to cover the theoretical background very thoroughly. Please take some time and familiarize yourself with the PWM theory explained on those sites: Open Music - PWM DAC Open Music - Dual PWM Open Music - Distortion Analysis Open Music - PWM Tutorial In general every PWM DAC can be described by a PWM frequency - f pwm (the frequency of the squarewave generated - which is constant) and the PWM bit resolution - bitres (which describes the granularity with which we can control the duty cycle). All of three Arduino's...