Displaying custom fonts or images on an LCD screen using a microcontroller usually requires quite a bit of work. We’ve used some readily available tools to make this a bit easier for your next project. Our python script will convert BMP files into a header file ready for use with AVR microcontrollers. We’ll walk you through it after the break.
For this tutorial we will be using the GNU Image Manipulation Program in conjunction with Python. We are working on an Ubuntu 9.04 system but because these are cross-platform tools you should be able to do this on any OS.
What the script does:
The Python script takes one or more 1-bit color palette indexed BMP images, cuts out the header and any unused column data, and outputs a header file with the information stored in a one dimensional array in PROGMEM. This data can then be read out of the array and manipulated in the AVR code for use in whatever format you need for your display. This can be used for generating fonts, or converting larger images.
Generate the BMP files:
Open the GIMP and create a new file with the dimensions that you require. Height is up to you, but the width should be in multiples of 8 to correspond to the 8-bit wide storage scheme. In this case, we’re interested in generating a set of fonts that will display in a 24×30 pixel area.
Using the font tool, select your desired font and add your character. Adjust the size and location until if fills the canvas. You should make sure that the Antialiasing checkbox of the font tool is not selected.
BMP files are saved from bottom to top, we need to invert the image for our purposes. Do this by clicking the Image menu, go to Transform, and select “Flip Vertically”. We also need to make this an indexed image. To do so, click on the Image menu at the top, go to Mode and select “Indexed…”. From this menu, choose “Use black and white(1-bit) palette”. Now save the file as a BMP image. In our case, we saved it as 4.bmp. Repeat this for each character you wish to include in your new font header file.
Use the script:
Download our bmp2header.py file.
$ python bmp2header.py *.bmp Please enter how many bytes (8-bits) wide the image data needs to be: 3 Generating header file with a byte width of: 3 bytes Successfully generated: my_header.h
Run the file, with your BMP images as the command line arguments. You will be asked to input the desired column width for the images. Our example image is 24 pixels wide so we want header data to be 3 bytes wide (24-pixels/8-bits = 3 bytes). You can see from the output that my_header.h was successfully created by the script.
Here are the contents of that file (in this case, data for the ‘4’ character):
#include <avr/pgmspace.h> static const char PROGMEM my_header[]={ //4 0x1f, 0x00, 0x00, 0x3f, 0x80, 0x00, 0x3f, 0x80, 0x00, 0x3f, 0x80, 0x00, 0x3f, 0x80, 0x00, 0x3f, 0x80, 0x00, 0x3f, 0x80, 0x00, 0x3f, 0x80, 0x00, 0x3f, 0x80, 0x00, 0x3f, 0x80, 0x00, 0x3f, 0x80, 0x00, 0x3f, 0x80, 0x00, 0x3f, 0x80, 0x00, 0x3f, 0x80, 0x00, 0x3f, 0x80, 0x00, 0x3f, 0x80, 0x00, 0x3f, 0x80, 0x00, 0x3f, 0x81, 0xfc, 0x3f, 0x81, 0xfc, 0x3f, 0x81, 0xfc, 0x3f, 0xff, 0xfc, 0x3f, 0xff, 0xfc, 0x3f, 0xff, 0xfc, 0x3f, 0xff, 0xfc, 0x00, 0x01, 0xfc, 0x00, 0x01, 0xfc, 0x00, 0x01, 0xfc, 0x00, 0x01, 0xfc, 0x00, 0x01, 0xfc, 0x00, 0x01, 0xfc };
In the header file, each BMP that is processed by the script will have its filename appended as a comment before the HEX output. Our data for 4.bmp is displayed in 3 columns of bytes with 30 rows. This matches up with the 24×30 aspect ratio we were looking for. If you have an output much larger than this, you either didn’t used a 1-bit indexed image, or something when wrong when the script asked you to input your column width.
Accessing data from the header file:
Covering how to use this header data is beyond the scope of this tutorial. Below is the code we used to write to the display in the image at the top of this article. Our screen is written to by declaring the area we want to write to, then sending a stream of bit data for that area. We provide this for reference purposes only:
#include my_header.h void Other_Num(unsigned char num, unsigned char x, unsigned char y) { //Setup screen area for writing: LCD_Out(0x2A, 1); LCD_Out(x, 0); LCD_Out(x+23, 0); LCD_Out(0x2B, 1); LCD_Out(y, 0); LCD_Out(y+29, 0); LCD_Out(0x2C, 1); unsigned char temp; for (unsigned char i=0; i<90; i++) //Read one column of char at a time { temp = pgm_read_byte((char *)((int)my_header + (i + (90*num)))); //Get column from progmem for (unsigned char k=0; k<8; k++) { if (temp & 1<<(7-k)) LCD_Out(blue, 0); else LCD_Out(white, 0); } } }
Conclusion
Using this method make generating font sets quit a bit easier. We were able to generate five different numeric sets (0-9) in about 45 mintues. We hope this helps with your next project. Don’t forget to include pictures of your new fonts in the comments.
Just curious, does anyone use the GIMP option to export C code/headers? If so, what are your thoughts on these two methods?
Jon, I use GIMP option to export C code/headers. It’s more useful than anything because unlike anything, GIMP and C compiler are always at hand. The pixel packing the one that’s required, but writing a code that rearranges a few bits is faster than finding the right tool that does it out of the box.
Err… I meant “…the pixel packing isn’t always the one that’s required…”
I’ve never really looked at how all the standard file formats are structured so I never really paid attention to what the convention was, but I thought it was odd that the data was packed to a row of 8 columns. When I did my own stuff like this I’d structure it with packed columns, so that you ended up with heights that were a multiple of 8. I figured that was more intuitive especially considering that’s how most LCD drivers are set up, but admittedly I can see some advantages to both.
I used the “LCD Font Maker” but it still has some issues that irritated me. It was a useful tool though when I had to change LCD but wanted to use the old fonts, the controller on the new LCD took data in funky format also. Coordinates ware from bottom left to top right and ware offset a few pixel’s so bottom 0,0 was infact 4,2. In the end I still ended up touching up the fonts manually editing the hex tables. Specially when your doing gray-scale. It’s just impossible to truly represent what it will look like on the LCD looking at CRT monitor or even another LCD monitor. It might look ok on the monitor and then proceeds to look like @$$ on the LCD.
This is good but I think ImageMagick or GraphicsMagick can be made to do this and either will handle just about any image format. And with these, you can rasterise any font installed on your system. You might still need a bit of script to format it neatly as a header/.asm table and to pack your paletized colors, but you won’t be limited to just bmp’s.
For Windows, a program called The Dot Factory does something very similar to this.
Thanks for this detailed tutorial!
Microchip provides a nice little tool in his Graphics Library that converts normal TTF fonts to C arrays or even assembler.
You just have to decide the height and then doing copy&paste.
@Jon
@svofski
Indeed. GIMP works perfectly for me.
I don’t know if what you’re programming on has object-oriented capabilities, but static variable arrays are the devil.
I’ll get on this as soon as I can get my LCD to display proper colours :(
this plugin is much better :-)
http://codinglab.blogspot.com/2009/03/gimp-plugin-to-export-bitmaps-for.html
Since the posts asks for pics I figured I’d post some samples of mine (using a different process however).
http://rob.mora-tek.com/img/fontsample.gif
That’s blown up to double size, 4 fonts in total (the last two are different sizes of the same font).
Have you ever heard about XPM? You can include it into the C code.
http://en.wikipedia.org/wiki/X_PixMap
gimp supports it, just click “save as” and type image.xpm to output filename form…
For those with a Windows box, FontMaker is a free download. It does a nice job converting TrueType fonts and/or bitmaps (including grids of characters) in AVR-GCC C headers:
http://www.eurekelettronica.it/prodotti/touch8/download/download.htm
Anyone know if a utility exists that can output a 16-bit rgb (5-6-5 format) hex file from a bmp image?
The only utilities I’ve been able to find either output a c header (not a binary hex file suitable for flashing), or they don’t support 5-6-5 rgb!
An export procedure for the Nokia 3310 LCD:
http://1024.cjb.net/2009/11/graphics-nokia-3310-lcd-avr/
This is a very late reply to this thread but it might help someone….checkout this Python plug-in for the gimp:
http://e2e.ti.com/support/microcontrollers/stellaris_arm_cortex-m3_microcontroller/f/473/t/67340.aspx
It creates a C header for a gimp image and its quite easy to modify this to output any format you like.