Hier entsteht eine kleine Bibliothek für Grafik-LCDs. Angefangen habe ich mit einem Nokia 3310 LCD (PCD8544 Controller). Eine kleine Demonstration kann man in dem Video sehen. Die Bibliothek wird in nächster Zeit noch ein paar Funktionen dazu bekommen (es fehlen noch viele Funktionen, wie z.B. Linien zeichnen, Rechtecke, Kreise etc.). Bei dem verwendeten Nokia 3310 LCD handelt sich um ein Ersatz-Display (kann auf Ebay bestellt werden). Der sichtbare Bereich des Displays beträgt 96×64 Pixel (der Display-Speicher ist aber etwas größer). WICHTIG: Das originale Nokia 3310 LCD hat eine Auflösung von 84×48 Pixel! Die Ansteuerung (besonders die Initialsierung) ist bei diesem Display ziemlich einfach, da es nicht viele Einstellmöglichkeiten gibt, beschränkt sich also auf ein paar Befehle. Angesteuert wird das LCD über einen MSP430G2553 (per SPI).
Video
Schaltplan
Source-Code
Der komplette Source-Code: DOWNLOAD SOURCE
Zwei Ausschnitte: main.c und lib_lcd.c
main.c
/*******************************
* __
* /\ \
* __ _ \_\ \ __ ___
* /\ \/'\ /'_' \ /'__'\ /'___\
* \/> <//\ \ \ \/\ __//\ \__/
* /\_/\_\ \___,_\ \____\ \____\
* \//\/_/\/__,_ /\/____/\/____/
*
* Author: declis (xdec.de)
********************************/
#include <msp430g2553.h>
#include "lib_lcd.h"
#include "delay.h"
#include "bitmaps.h"
#include "data.h"
void main(void)
{
signed int x=0,y=-184,num=0,x1=0,y1=0;
WDTCTL=WDTPW+WDTHOLD;
BCSCTL1=CALBC1_8MHZ; // SMCLK=~8MHz
DCOCTL=CALDCO_8MHZ;
init_USCI();
init_LCD();
// SCENE 00 - "Countdown" ----------------------------
wait_ms(500);
countdown('3');
countdown('2');
countdown('1');
wait_ms(500);
write_string(3,2,gogo,5);
wait_ms(1000);
fill_display(lcd_width,lcd_height,0);
// ---------------------------------------------------
// SCENE 01 - "Motoko Vertical-Scroller" -------------
while(y!=16)
{
draw_bitmap(0,y++,ghost_w,ghost_h,ghost);
wait_ms(80);
}
write_string(0,0,moto,1);
wait_ms(2000);
while(y!=-85)
{
draw_bitmap(0,y--,ghost_w,ghost_h,ghost);
wait_ms(80);
}
wait_ms(2000);
// ---------------------------------------------------
// SCENE 02 - "Motoko Horizontal-Overlap-Scroller" ---
y=0;
while(x!=-lcd_width)
{
draw_bitmap(x--,-85,ghost_w,ghost_h,ghost);
draw_bitmap(y++,-85,ghost_w,ghost_h,ghost);
}
while(x<=lcd_width)
{
draw_bitmap(x++,-85,ghost_w,ghost_h,ghost);
draw_bitmap(y--,-85,ghost_w,ghost_h,ghost);
}
wait_ms(500);
// ---------------------------------------------------
// SCENE 03 - "String-Typer" -------------------------
string_typer(3,0,powa0,0,typer_ms);
string_typer(3,1,powa1,1,typer_ms);
string_typer(3,3,powa2,0,typer_ms);
wait_ms(typer_ms);
draw_bitmap(0,29,boo_w,boo_h,boo);
wait_ms(typer_ms);
string_typer(3,4,powa3,3,typer_ms);
string_typer(3,7,powa4,0,typer_ms);
wait_ms(4000);
fill_display(lcd_width,lcd_height,0);
// ---------------------------------------------------
// SCENE 04 - "Bitmap-FadeIn-Destruction -------------
x=0;
while(x<=29)
{
draw_bitmap(0,0,x++,ichi_h,ichi);
wait_ms(300);
}
wait_ms(1000);
// ---------------------------------------------------
// SCENE 05 - "Classic-Scrolling" --------------------
x=lcd_width;
y=lcd_width;
y1=sizeof(sine)-1;
x1=sizeof(sine)-21;
num=6*sizeof(scroll0);
while(x!=-num)
{
if(!y1) y1=sizeof(sine)-1;
if(!x1) x1=sizeof(sine)-1;
f_scroller_normal(x--,0,scroll1);
if(y!=-num)
{
f_scroller_normal(y,56,scroll0);
y-=2;
}
else y=lcd_width;
draw_bitmap(0,0,ichi_w,ichi_h-8,ichi);
draw_bitmap(35,sine[y1--]/2+8,boo_w,boo_h,boo);
draw_bitmap(70,sine[x1--]/2+8,boo_w,boo_h,boo);
wait_ms(50);
}
// ---------------------------------------------------
// SCENE 06 - "FadeOut Scene 05" ---------------------
wait_ms(500);
x=0;
y=8;
while(y<=lcd_height)
{
if(x>=-ichi_w)
draw_bitmap(x--,0,ichi_w,ichi_h,ichi);
draw_bitmap(35,sine[y1]/2+y,boo_w,boo_h,boo);
draw_bitmap(70,sine[x1]/2+y++,boo_w,boo_h,boo);
wait_ms(70);
}
// ---------------------------------------------------
// SCENE 07 - "Sine-Scroller" / "END" ----------------
x=lcd_width;
num=6*sizeof(scroll3);
write_string(3,6,end,2);
while(x!=-num)
{
f_scroller_func(x--,0,scroll3,sine,sizeof(sine)-1);
write_h_string(0,0,powa3,1);
write_h_string(15,0,powa3,1);
wait_ms(70);
fill_display(lcd_width,32,0);
}
// ---------------------------------------------------
fill_display(lcd_width,lcd_height,0);
while(1);
}
lib_lcd.c
#include <msp430g2553.h>
#include "ascii_char.h"
#include "lib_lcd.h"
#include "delay.h"
#define max_f_size 8 // cache size for "font resize function"
unsigned char byte,bit_num;
void write_h_string(unsigned char x, unsigned char y, const char *string, unsigned char f_size)
{
while(*string!=0)
{
write_char(x,y,*string++,f_size);
if(f_size==1) y+=f_size+1;
else if(f_size>1) y+=f_size;
else y++;
}
}
void string_typer(unsigned char x, unsigned char y, const char *string, unsigned char f_size, unsigned int ms)
{
while(*string!=0)
{
write_char(x,y,*string++,f_size);
if(f_size>1) x+=f_size;
else x++;
wait_ms(ms);
}
}
void f_scroller_func(signed int x, signed int y, const char *string, const char *func, unsigned char func_size)
{
byte=0;
while(*string!=0&&x<lcd_width)
{
if(x>func_size) byte=x-func_size; // use abs() here
else byte=x;
draw_bitmap(x,y+func[byte],f_width,8,ascii_table[*string++]);
set_instruction(1,0); // clear byte before
x+=f_width+space_char;
byte++;
}
}
void f_scroller_normal(signed int x, signed int y, const char *string)
{
while(*string!=0&&x<lcd_width)
{
draw_bitmap(x,y,f_width,8,ascii_table[*string++]);
set_instruction(1,0); // clear byte before
x+=f_width+space_char;
}
}
void countdown(unsigned char num)
{
signed char size=8;
while(size!=-1)
{
write_char(4,0,num,size--);
wait_ms(111);
fill_display(96,64,0);
}
}
void draw_bitmap(signed int x, signed int y, unsigned int b_width, unsigned int b_height, const char *bmap)
{
unsigned int h_index=0,w_index=0,width_max=0,x_off=0,y_off=0;
byte=0;
if(y+(signed int)b_height<0) return; // outside display
if(x+(signed int)b_width<0) return;
if(y>=lcd_height) return;
if(x>=lcd_width) return;
if(x<0)
{
w_index=x*-1; // set bmap x-offset
x_off=w_index;
x=0; // start at display position x=0 with x-offset
}
if(b_height%8) b_height+=8;
b_height/=8;
if(x+(signed int)b_width>=lcd_width) // width overflow check
width_max=lcd_width-x;
else width_max=b_width;
if(y<0)
{
y*=-1;
y_off=y/8;
b_height-=y_off;
while(h_index<b_height)
{
set_cursor(x,h_index);
while(w_index<width_max)
{
byte=bmap[w_index+((h_index+y_off)*b_width)]>>(y%8);
if(h_index+1!=b_height)
byte|=bmap[w_index+((h_index+y_off+1)*b_width)]<<(8-(y%8));
set_instruction(1,byte);
w_index++;
byte=0;
}
h_index++;
w_index=x_off;
if(h_index>=lcd_height_b) return;
}
}
else
{
y_off=y/8;
while(h_index<=b_height)
{
set_cursor(x,y_off+h_index);
while(w_index<width_max)
{
if(h_index!=b_height)
byte=bmap[w_index+(h_index*b_width)]<<(y%8);
if(h_index)
byte|=(bmap[w_index+((h_index-1)*b_width)]>>(8-(y%8))); // byte before
set_instruction(1,byte);
w_index++;
byte=0;
}
h_index++;
w_index=x_off;
if(h_index+y_off>=lcd_height_b) return;
if(h_index==b_height&&!(y%8)) return;
}
}
}
void convert_font_size(unsigned char x, unsigned char y, unsigned char character, unsigned char f_size)
{
unsigned char x_char=0,bit_num_b=0,size=0,px_size=0,y_pos_new=0,x_pos_new=0;
unsigned char cache[max_f_size],i=0;
byte=0;
bit_num=0;
if (f_size==1) size=2;
else size=f_size;
while(x_char<f_width) // test byte, starting at 0 to f_width (font width)
{
while(bit_num<8) // test bit 0..7 of current byte
{
if(ascii_table[character][x_char]&(1<<bit_num)) // if bit=1 in byte...
{
while(px_size<size) // duplicate bits (f_size*f_size)
{
if(bit_num_b>7&&px_size>0) // byte overflow, new byte
{
set_cursor(x+x_pos_new,y+y_pos_new++); // set cursor to next y-address
set_instruction(1,byte); // send byte
bit_num_b=0; // reset bit counter
cache[i++]=byte; // save byte in cache
byte=0; // reset byte
}
byte|=(1<<bit_num_b); // set bit in byte
px_size++; // increment pixel duplicate counter
bit_num_b++; // calculate new bit position
}
px_size=0; // reset pixel duplicate counter
}
else bit_num_b+=size; // bit=0, calculate new bit position in byte
if(bit_num_b>7) // byte overflow, new byte
{
set_cursor(x+x_pos_new,y+y_pos_new++);
set_instruction(1,byte);
bit_num_b-=8;
cache[i++]=byte;
byte=0;
}
bit_num++; // test next byte in array
}
y_pos_new=0; // reset y-offset
x_pos_new++; // increment x-position
i=0; // reset cache counter
if(f_size==1) size=0; // double height font (only for f_size=1)
else size--; // first row is ready, only size-1
while(size--)
{
while(i<f_size)
{
set_cursor(x+x_pos_new,y+y_pos_new++);
set_instruction(1,cache[i++]);
}
i=0;
y_pos_new=0;
x_pos_new++;
}
x_char++;
if(f_size==1) size=2;
else size=f_size;
i=0;
bit_num=0;
}
}
void write_string(unsigned char x, unsigned char y, const char *string, unsigned char f_size)
{
x*=(f_width+space_char);
set_cursor(x,y);
while(*string!=0)
{
if(f_size)
{
convert_font_size(x,y,*string++,f_size);
x+=(f_size*f_width+space_char);
}
else
{
send_data_array(ascii_table[*string++],f_width);
y=space_char;
while(y--)
set_instruction(1,0);
}
}
}
void write_char(unsigned char x, unsigned char y, unsigned char character, unsigned char f_size)
{
x*=(f_width+space_char);
set_cursor(x,y);
if(f_size)
convert_font_size(x,y,character,f_size);
else
send_data_array(ascii_table[character],f_width);
}
void send_data_array(const char *d_array, unsigned char size)
{
while(size--)
set_instruction(1,*d_array++);
}
void set_cursor(unsigned char x, unsigned char y)
{
set_instruction(0,0x80+x);
set_instruction(0,0x40+y);
}
void fill_display(unsigned char width, unsigned char height, unsigned char byte)
{
height/=8;
while(height--)
{
set_cursor(0,height);
while(width--)
set_instruction(1,byte);
width=lcd_width;
}
}
void set_instruction(unsigned char register_sel, unsigned char number)
{
if(register_sel)
P1OUT|=DC;
else P1OUT&=~DC;
P1OUT&=~CS; // start condition
while(IFG2&UCB0TXIFG); // TX buffer ready?
UCB0TXBUF=number; // start transmission
}
void init_LCD(void)
{
wait_ms(100);
P1OUT|=RES;
set_instruction(0,0x21); // function set, extended instruction set (h=1)
set_instruction(0,0xCF); // set Vop, contrast
set_instruction(0,0x04); // temperature control, set coefficient
set_instruction(0,0x17); // set bias system
set_instruction(0,0x20); // function set, basic instruction set (h=0)
set_instruction(0,0x0C); // display control, normal mode
fill_display(lcd_width,lcd_height_real,0); // display RAM is undefined after reset, clean dat shit
}
void init_USCI(void)
{
P1DIR|=RES+DC+CS;
P1OUT&=~RES+DC;
P1OUT&=~CS;
P1SEL|=SDIN+SCLK;
P1SEL2|=SDIN+SCLK;
UCB0CTL1|=UCSWRST; // USCI in reset state
// SPI Master, 8bit, MSB first, synchronous mode
UCB0CTL0|=UCMST+UCSYNC+UCCKPH+UCMSB;
UCB0CTL1|=UCSSEL_2; // USCI CLK-SRC=SMCLK=~8MHz
UCB0CTL1&=~UCSWRST; // USCI released for operation
IE2|=UCB0TXIE; // enable TX interrupt
IFG2&=~UCB0TXIFG;
_EINT(); // enable interrupts
}
#pragma INTERRUPT (USCI)
#pragma vector=USCIAB0TX_VECTOR
void USCI(void)
{
P1OUT|=CS; // transmission done
IFG2&=~UCB0TXIFG; // clear TXIFG
}
Good work dude. It works fine for nokia 2100 lcd(96×64) with little modification. Thanks..
Hi,
Excellent design.
Do you know what changes are needed to make the Nokia 2100 work?
Thanks.