winnie 发表于 2009-4-25 17:12:46

用PICC写高质量的12864显示C代码

所谓高质量就是:1,高可靠。2,C代码简洁并且编译后代码简洁。3,函数接口方便,通用性强。4,代码容易维护,修改,阅读。
本文的目的是让PICC新手了解PICC的运用,C的技巧,C对PIC’MCU的技巧,以及拿到一个新课题怎么下手等方面。
做任何课题之前,必须了解对象硬件的特性以及操作指令。
这里是12864的中文文挡:
一:怎么下手:
1,先读懂阅读文挡。
2,规划与MCU的接口脚。用16F877A。
引脚号引脚名称级
别引










述1VSS0V电源地
2VDD+5V电源电压
3VLCD0~-10VLCD驱动负电压,要求VDD-VLCD=13V
4RSH/L读/写操作选择信号
5R/WH/L寄存器选择信号
6EH/L使能信号
7DB0H/L八位三态并行数据总线
8DB19DB210DB311DB412DB513DB614DB715CS1H/L片选信号,当CS1=H时,液晶左半屏显示
16CS2H/L片选信号,当CS2=H时,液晶右半屏显示
17/RESH/L复位信号,低有效
18VEE-10V输出-10V的负电压(单电源供电)
19LED+(EL)+5V背光电源,Idd≤300mA
20LED-(EL)0V
可以看出有8个数据接口DB0-7,规划成和877A的PORTD相连。
另外有5条控制线,规划成和877A的PORTC相连。
于是首先写好如下的C代码:
#define CS1   RC3
#define CS2   RC4
#define RS    RC0
#define RW    RC1
#define E   RC2                                             //上面是5条控制线的接法
#define Lcd_IO   PORTD                               //数据线接PORTD
#define SetLcd_IOTRISD                           //由于数据线是双向传输,所以要定义控制D口的方向
#define SetLcd_CON TRISC                         //便于维护修改
3,MCU执行最初总是要先设定IO口等寄存器。
第一个函数就是IO口初始化函数:
void ioint(){
PORTC=0;                              
PORTD=0;                                     //先清0 C、D口,避免干扰
SetLcd_CON =0B11100000;          //把C口控制线设成输出。
SetLcd_IO=0xff;                              //D口输入。
}

winnie 发表于 2009-4-25 17:13:49

续1:用PICC写高质量的12864显示C代码
二:对LCD的低层操作。

对LCD操作有2种数据,一是控制指令,二是显示数据。

指 令 名 称
控制信号
控   制   代   码

RS
R/W
D7
D6
D5
D4
D3
D2
D1
D0

显示开关设置
0
0
0
0
1
1
1
1
1
D

显示起始行设置
0
0
1
1
L5
L4
L3
L2
L1
L0

页面地址设置
0
0
1
0
1
1
1
P2
P1
P0

列地址设置
0
0
0
1
C5
C4
C3
C2
C1
C0

读取状态字
0
1
BUSY
0
ON/OFF
RESET
0
0
0
0

写显示数据
1
0
数      据

读显示数据
1
1
数      据



并且在送数据之前要判断LCD是否处于忙状态。于是就先写判断LCD状态的函数:

void check_busy(){
CS1=1;CS2=1;
RS=0;RW=1;                           //按照上表,设定好读取LCD状态字的控制线。

do{
E=0;
E=1;      
}while(Lcd_IO&0x80);               //判断最高位状态,如果是1,则反复送E时续,直到LCD空闲

CS1=0;CS2=0;
E=0;                                          //及时拉低E,使DB口处于高阻状态,提高可靠性
if(L_lcd)
CS1=1;
if(R_lcd)
CS2=1;                                     //通过全局BIT变量L_Lcd\R_lcd传递LCD左右屏幕参数
SetLcd_IO=0;                            //使IO口处于输出状态,为向LCD写数据作准备。
}

后面当然就是向LCD写数据和指令的函数了:

void LCD_Write_Com(unsigned char val)
{
check_busy();                   //检查LCD是否空闲
RS=0;RW=0;                  //根据上表设定控制脚,CS1、2在check_busy();里事先有设定
E=1;
Lcd_IO=val;                      //E时续下降沿输入数据
E=0;
SetLcd_IO=0xff;            //写完数据,立即把IO口设定成高阻,提高可靠性。
}

void LCD_Write_Dat(unsigned char val)
{
check_busy();
RS=1;RW=0;
E=1;
Lcd_IO=val;
E=0;
SetLcd_IO=0xff;
}



续2:用PICC写高质量的12864显示C代码
四:LCD显示之前当然要对其清屏,避免出现色癍。

清屏幕就是对LCD内部DRAM全写0。

void LCD_Clr(void)
{
unsigned char i,j;
L_lcd=1,R_lcd=1;                              //用2个全局BIT变量传递左右屏幕,因为check_busy()函数里面有可能

                                                          //改变CS1、CS2

// LCD_Write_Com(0x3e);               //关闭LCD显示,因为清屏幕极快,不要这句也罢。
LCD_Write_Com(0xc0);                  
for(j=8;j>0;j--){
LCD_Write_Com(0xb8|j);               
LCD_Write_Com(0x40);
for(i=64;i>0;i--){
   LCD_Write_Dat(0x00);                        //对8个页面的DRAM全部送0
}
}
LCD_Write_Com(0x3f);                         //打开LCD显示
}



五:上面4个步骤就是LCD的低层操作了。下面就是显示字符的应用函数:

先要明白LCD的显示字符的原理,一般字符取磨都是按照8x8,8x16,16x16来的,分别适合小字体阿拉伯数字,大字体阿拉伯数字,汉字。

事先要把显示的字符取摸:

如:8x8的0-9,:

const char a={
{0,62,65,65,62,0,0,0},            //0
{0,66,127,64,0,0,0,0},            //1
{0,98,81,73,70,0,0,0},
{0,34,73,73,54,0,0,0},
{0,56,38,127,32,0,0,0},
{0,79,73,73,49,0,0,0},
{0,62,73,73,50,0,0,0},
{0,3,113,9,7,0,0,0},
{0,54,73,73,54,0,0,0},
{0,38,73,73,62,0,0,0},             //9
{0,0,0,204,204,0,0,0},             //:
{0,0,0,0,0,0,0,0}                      //最后全0,是为了对某个位置的数字清0
};



显示8x8的函数:

void display8x8(uchar x,uchar y,const char *p){         //x表示屏幕的 x行(0-7),y表示屏幕的y列(0-15)
uchar i;                                                                  
y<<=3;
L_lcd=1;R_lcd=0;
if(y&0x40){
L_lcd=0;R_lcd=1;                                                   //判断y,来选择左右屏幕
}
y&=0x3f;
LCD_Write_Com(0xb8|x);
LCD_Write_Com(0x40|y);
for(i=8;i>0;i--){
LCD_Write_Dat(*p++);
}
}

调用这个应用函数示例:

display8x8(0, 0, a);               //在屏幕0行0列显示字符0

display8x8(0, 15, a);                  // 0行15列显示9

display8x8(7, 8, a);                   //7行8列显示:号

下面分别是8x16,16x16的函数:

void display8x16(unsigned char x,unsigned char y,const char *p){   
unsigned char i;
x<<=1;
y<<=3;
L_lcd=1;R_lcd=0;
if(y&0x40){
L_lcd=0;R_lcd=1;
}
y&=0x3f;

      LCD_Write_Com(0xb8|x);
LCD_Write_Com(0x40|y);
for(i=8;i>0;i--){
LCD_Write_Dat(*p++);
}
LCD_Write_Com(0xb9|x);
LCD_Write_Com(0x40|y);
for(i=8;i>0;i--){
LCD_Write_Dat(*p++);
}
}

void display16x16(uchar x,uchar y,const char *p){
uchar i;
x<<=1;
y<<=4;
L_lcd=1;R_lcd=0;
if(y&0x40){
L_lcd=0;R_lcd=1;
}
y&=0x3f;         
LCD_Write_Com(0xb8|x);
LCD_Write_Com(0x40|y);
for(i=16;i>0;i--){
LCD_Write_Dat(*p++);
}
LCD_Write_Com(0xb9|x);
LCD_Write_Com(0x40|y);
for(i=16;i>0;i--){
LCD_Write_Dat(*p++);
}
}

当然8x16和16x16的字符取摸要按照规定来,存放格式雷同上面的a二维数组。

注意,8x16函数只能显示4x16 个字符,16x16 只能显示4x8个字符,调用函数时候,x,y参数不能超过这个数值。

最后不要忘了在代码前加上全局BIT变量:

bit L_lcd,R_lcd;

此代码经过产品的长期考验,事实证明是非常可靠的。至于代码是否简洁,大家编译后就知道了。

winnie 发表于 2009-4-25 17:14:22

用PICC写高质量的12864显示C代码中的一些技巧
do{
E=0;
E=1;      
}while(Lcd_IO&0x80);               // Lcd_IO&0x80意思是判断IO口的第7位,编译后只有一个位测试指令

                                                   //如果判断第6位,就是Lcd_IO&0B01000000;以此类推。



if(L_lcd)
CS1=1;
if(R_lcd)
CS2=1;                                    //为什么不用CS1=L_lcd;CS2=R_lcd;呢?因为对于bit变量最好用if来写,编译后代码

                                                   //要简洁许多。            



for(j=8;j>0;j--){
for(i=64;i>0;i--){                      //许多循环体都用的是递减到0的形式,这样编译后代码最简洁。


   y<<=3;                                 //用左移3位的方法来代替乘以8,非常省空间的技巧

if(y&0x40){                               //判断y是否大于63,即测试y的6位


y&=0x3f;                                 //对y取63的摸,如果y是64,则y取摸后成了0。
   

LCD_Write_Com(0xb8|x);      //0xb8是指令,把x和它相或,就可以把x贴入指令中。         



const char a={               //为什么要用2维数组,因为这样便于选择目标字符,函数接口非常直观、灵活。

                                                //用一维数组的话就费神许多,并且2者代码量是一样的。

               //数组用const修饰,是放在ROM中的,除非你喜欢吃RAM,况且PIC没有足够的RAM给你



void display8x8(uchar x,uchar y,const char *p){//调用函数,传递字符是传递字符的指针,注意const是指向ROM的。

display8x8(0, 0, a);   //a表示2维数组的5个字符的首地址,如果不太清楚标C的数组,那你一定要重新温习。



#define CS1   RC3
#define CS2   RC4
#define RS    RC0
#define RW    RC1
#define E   RC2                                             //上面是5条控制线的接法
#define Lcd_IO   PORTD                               //数据线接PORTD
#define SetLcd_IOTRISD                           //由于数据线是双向传输,所以要定义控制D口的方向

#define SetLcd_CON TRISC                         //便于维护修改

整个代码内部没有涉及具体的IO口,IO口的定义全在上面这几句里,如果你想把并口改PORTB,就简单改上面几句就可以了,而不需要满篇代码去修改。



另外为了保证整个硬件系统运行的可靠性,一定要把IO口在空闲时间处理成输入形式,也就是悬空。这样一是避免偶然的干扰而烧硬件,二是降低工耗,三是。。。。,反正好处多多,百益而无一害。
页: [1]
查看完整版本: 用PICC写高质量的12864显示C代码