英锐恩单片机论坛,Microchip单片机,模拟器件,接口电路,麦肯单片机,单片机应用交流

 找回密码
 立即注册
搜索
电子烟方案单片机单片机开发深圳单片机开发
单片机方案国产单片机8位单片机电子烟方案开发
查看: 5107|回复: 2
打印 上一主题 下一主题

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

[复制链接]
跳转到指定楼层
1#
发表于 2009-4-25 17:12:46 | 只看该作者 回帖奖励 |正序浏览 |阅读模式
所谓高质量就是:1,高可靠。2,C代码简洁并且编译后代码简洁。3,函数接口方便,通用性强。4,代码容易维护,修改,阅读。
本文的目的是让PICC新手了解PICC的运用,C的技巧,C对PIC’MCU的技巧,以及拿到一个新课题怎么下手等方面。
做任何课题之前,必须了解对象硬件的特性以及操作指令。
这里是12864的中文文挡:
一:怎么下手:
1,先读懂阅读文挡。
2,规划与MCU的接口脚。用16F877A。
引脚
引脚名称












1
VSS
0V
电源地
2
VDD
+5V
电源电压
3
VLCD
0~-10V
LCD驱动负电压,要求VDD-VLCD=13V
4
RS
H/L
读/写操作选择信号
5
R/W
H/L
寄存器选择信号
6
E
H/L
使能信号
7
DB0
H/L
八位三态并行数据总线
8
DB1
9
DB2
10
DB3
11
DB4
12
DB5
13
DB6
14
DB7
15
CS1
H/L
片选信号,当CS1=H时,液晶左半屏显示
16
CS2
H/L
片选信号,当CS2=H时,液晶右半屏显示
17
/RES
H/L
复位信号,低有效
18
VEE
-10V
输出-10V的负电压(单电源供电)
19
LED+(EL)
+5V
背光电源,Idd≤300mA
20
LED-(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_IO  TRISD                             //由于数据线是双向传输,所以要定义控制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口输入。
}

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
3#
 楼主| 发表于 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[12][8]={               //为什么要用2维数组,因为这样便于选择目标字符,函数接口非常直观、灵活。

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

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



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

display8x8(0, 0, a[4 ]);     //a[4]表示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_IO  TRISD                             //由于数据线是双向传输,所以要定义控制D口的方向

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

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



另外为了保证整个硬件系统运行的可靠性,一定要把IO口在空闲时间处理成输入形式,也就是悬空。这样一是避免偶然的干扰而烧硬件,二是降低工耗,三是。。。。,反正好处多多,百益而无一害。
回复 支持 反对

使用道具 举报

2#
 楼主| 发表于 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[12][8]={
{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列显示字符0

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

display8x8(7, 8, a[10 ]);                   //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;

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

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

关闭

站长推荐上一条 /1 下一条

小黑屋|公司首页|Microchip单片机,模拟器件,接口电路,麦肯单片机,单片机应用交流 ( 粤ICP备09008620号 )

GMT+8, 2024-11-24 04:42 , Processed in 0.057054 second(s), 24 queries .

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表