12864液晶——读写、划点、划线、汉字、32*16的字符

//左半屏幕和右半屏幕的列号是一样的,页号也是一样的。
//选择整个屏幕,在给DDRAM中写数据时,会同时写到两个屏幕中,即两个屏幕中将会显示一样的数据。
//在清屏的时候可以选择整个屏幕。
//在滚动的时候可以选择整个屏幕,此时如果分别选屏幕滚动,可以实现两个屏幕滚动方向相反。

#define LCD_OFF 0x3E //关显示
#define LCD_ON 0x3F//开显示

#define Add_X 0xB8 //页初始地址,共8页
#define Add_Y 0x40 //Y初始地址,0到63,0x40到0x7f
#define Add_Z 0xC0 //DDRAM的初始地址

#define UPLINE 0x01 //上划线就是每个字节的第一位都是1
#define UNDERLINE 0x80//下划线就是每个字节的最后一位都是1

#define LCD12864_DATA_PORT P0//数据端口DB0~7接P0口

sbit LCD12864_E=P2^4; //E使能端
sbit LCD12864_RW=P2^3; //RW为0是写,为1是读 
sbit LCD12864_RS=P2^2; //RS为0输入的为命令,为1输入的为数据
sbit LCD12864_CS1=P2^0; //CS1,低电平有效
sbit LCD12864_CS2=P2^1; //CS2,低电平有效
sbit LCD12864_RST=P2^5; //复位端口

void delayus(unsigned int us)//延时函数
{
while(us--);
}

void LCDSel(unsigned char sel)//选择屏幕
{

switch(sel)

{

case 0: LCD12864_CS1=0;LCD12864_CS2=0;break; //选择两个屏幕

case 1: LCD12864_CS1=0;LCD12864_CS2=1;break; //选择左半屏幕

case 2: LCD12864_CS1=1;LCD12864_CS2=0;break; //选择右半屏幕

default:break;

}

}

void WaitLCD()//检测忙
{

unsigned char flag;

LCD12864_DATA_PORT=0xFF;//P0口全部置1

LCD12864_RW=1;

LCD12864_RS=0;

LCD12864_E=1;

LCD12864_E=1;

LCD12864_E=0;

LCD12864_DATA_PORT=0xFF; //读有效数据

LCD12864_RW=1;

LCD12864_RS=0;

LCD12864_E=1;

do{

flag=LCD12864_DATA_PORT;

LCD12864_DATA_PORT=0xFF;

}while(!((flag&0x80)==0x80));

LCD12864_E=0;

}

void WriteDatToLCD12864(unsigned char dat)//写数据函数
{

// WaitLCD();

LCD12864_RS=1; //the data

LCD12864_RW=0; //write

LCD12864_DATA_PORT=dat;

LCD12864_E=1;

LCD12864_E=0;

}

void WriteCmdToLCD12864(unsigned char cmd)//写命令函数
{

// WaitLCD();

LCD12864_RS=0; //the command

LCD12864_RW=0; //write

LCD12864_DATA_PORT=cmd;

LCD12864_E=1;

LCD12864_E=0;

}

unsigned char ReadDatFromLCD12864(void)//读数据
{

unsigned char dat;

WaitLCD();

LCD12864_DATA_PORT=0xFF; //读空操作

LCD12864_RS=1; //the data

LCD12864_RW=1; //read

LCD12864_E=1;

LCD12864_E=1;

LCD12864_E=0;

LCD12864_DATA_PORT=0xFF; //读有效数据

LCD12864_RS=1;

LCD12864_RW=1;

LCD12864_E=1;

dat=LCD12864_DATA_PORT;

LCD12864_E=0;

return dat;

}

void LCD12864_init(void)//初始化12864
{

LCD12864_RST=0;//液晶复位

delayus(50);

LCD12864_RST=1;

LCDSel(0); //选择整个屏幕

WriteCmdToLCD12864(LCD_OFF);//关显示

WriteCmdToLCD12864(LCD_ON);//开显示

}

void SetX(unsigned char x)//页选择0~7
{

WriteCmdToLCD12864(Add_X+x);

}

void SetY(unsigned char y)//ADD_Y是0x40,列初始地址,Y地址自动加一,DDRAM中的写入一字节数据的对应关系是竖着的八位,低位在上面,高位在下面。两个半屏幕的列号都是从0~63。当选中两个屏幕时,操作是对两个屏幕同时操作,并且进行一样的操作。由选屏、页和列就能指定唯一的一字节单元,只能写入一字节,不能一位一位的写,行号是指DDRAM和屏幕显示用的。
{

WriteCmdToLCD12864(Add_Y+y);

}

void SetZ(unsigned char z)//ADD_Z是0xC0,起始行地址,自动加一,SetZ(0)表示DDRAM中的第0行对应屏幕中的第1行,改变对应的行可以实现滚屏的效果。写数据是把DB0~DB7一字节的数据写到DDRAM中,Y地址指针自动加1,读数据是从DDRAM中读一个字节数据,Y地址指针自动加1。改变SetZ(z)中z的值只是改变了DDRAM与屏幕的对应关系。DDRAM中的数据到屏幕上的显示是一行一行进行的,因此有个自动加1。
{

WriteCmdToLCD12864(Add_Z+z);

}

void ClearLCD()//清屏
{

int i,j;

LCDSel(0);//选择两个屏幕

for(j=0;j<8;j++)

{

WriteCmdToLCD12864(LCD_ON);//开显示

SetX(j); //从0到7选择每一页

WriteCmdToLCD12864(Add_Y); //写入列的初始地址,列地址会自动加1,diffrent from SetY(0),SetY(64);

SetZ(0);//DDEAM和屏幕的对应关系是DDRAM中第0行对应屏幕的第1行。

for (i=0;i<64;i++)

{

WriteDatToLCD12864(0x00);//选中了两个半屏,向两个半屏相同列号处同时写入0x00,清屏。

}

}

}

//左上角第一个点为原点,向下Y为轴,向右为X轴
//x:0~63~127 列号 y:0~63行号
//由于是由页和列控制的,因此此处给出的x必须转化为页,即除以8即可,由于是处理一个点,还要得到这一页的第几行,即对8求余
//flag : 0:擦除某个点, 1:显示某个点 ,其本质就是赋值,赋值0是清除一个点,赋值1是显示一个点
unsigned char code Tab[]={0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80};//一个字节中的8个位,此处要控制点,因此要处理八位中的一位。
void Dot(char x,char y,bit flag)
{
unsigned char dat = 0;
if(x<64)//x属于0~63就是左半屏幕,x是向右的X轴坐标,即在左半屏幕x是列号,在右半屏幕x-64是列号
{
LCDSel(1);//选择左半屏幕
SetX(y>>3); //y是行号,y属于0~63,除以8就得到当前的页,得到页号就可以设置页地址
SetY(x); //由列号设置列地址
dat = ReadDatFromLCD12864();//读数据,由于是一个字节一个字节操作的,别的点不能改变,因此要读数据,从而来保证其余7位的值
if(flag)//flag为1显示某个点,点显示为黑色的,液晶中每个字体的显示都是黑色的
{
dat = dat|(Tab[y&7]);//对8求余:y%8或y&7。对8求余得到当前页的第y%8行,由列和行可以确定唯一一个点。与0求或不变,与1求或变为1。
}
else
{
dat = dat&(~(Tab[y&7]));//清除这个点,与0求与为0,与1求与不变。
}
SetY(x); //列地址会自动加1,改变的点还要写入DDRAM中,因此将列地址仍设置为列号为x的列
WriteDatToLCD12864(dat);//写数据,仅仅只改变了一个点的状态
}
else if(x<128)//右半屏幕
{
LCDSel(2);//选择右半屏幕
SetX(y>>3);//设置页地址
SetY(x-64);//列地址,右屏幕的列号是从0到63
dat = ReadDatFromLCD12864();//读数据,由于是一个字节一个字节操作的,别的点不能改变,因此要读数据
if(flag)
{
dat=dat|(Tab[y&7]);
}
else
{
dat=dat&(~(Tab[y&7]));
}
SetY(x-64);//右半屏幕的列地址也是0~63
WriteDatToLCD12864(dat);//写数据

}

//左上角第一个点为原点,向下Y为轴,向右为X轴
//在两个点之间划线,draw a line between point(x1,y1) and point(x2,y2)
//flag为0是erase擦除线,为1是画线
void Line(unsigned char x1,unsigned char y1,unsigned char x2,unsigned char y2,bit flag)
{
unsigned char i;
unsigned char temp;//临时变量,用于交换两个数
float k;//k为斜率
//if中是划竖线
if(x1==x2)//坐标系是左上角第一个点为原点,向下Y为轴,向右为X轴,x1等于x2则这两个点在同一列
{
if(y1>y2)//如果y1大于y2,交换y1和y2的值
{
temp = y1;
y1 = y2;
y2 = temp;
}
for(i=y1;i<=y2;i++)//y1等于y2就是画点
{
Dot(x1,i,flag);//列相同,从y1到y2划点形成线
}
}
//else中是划横线和斜线
else
{
if(x1>x2)//为了保证k为正值,当然也可以用绝对值函数
{
temp = x1;
x1 = x2;
x2 = temp;
}
if(y1>y2)
{
temp = y1;
y1 = y2;
y2 = temp;
}
k = (float)(y2-y1)/(float)(x2-x1);//计算斜率,k为0是划横线,由于x1不等于x2,所以不会是画点
temp = x2-x1;
for(i=0;i<temp;i++)
{
Dot(x1+i,(unsigned char)(y1+k*i),flag);
}
}
}

void Rect(unsigned char x1,unsigned char y1,unsigned char x2,unsigned char y2,bit flag)//画矩形
{

Line(x1,y1,x2,y1,flag);//y1行所在横线

Line(x2,y1,x2,y2,flag);//x2列所在竖线

Line(x2,y2,x1,y2,flag);//y2行所在横线

Line(x1,y2,x1,y1,flag);//x1列所在竖线

}

//汉字是16*16
//x是页0~7,y是列0~127
//n为要显示汉字的个数
//upline为0表示有上划线,underline为0表示有下划线
//flag为0表示汉字反白显示
void hz_disp(unsigned char x,unsigned char y,unsigned char n,unsigned char code *hz,bit flag,bit upline,bit underline)
{
unsigned char i,j;
for (j=0;j<n;j++)//要显示n个汉字,汉字编号0~n-1,j表示当前汉字编号
{
//显示上半个汉字
//每页是8行,汉字是16*16的,因此一个汉字分成上下两部分显示,即8*16,8行16列。
for(i=0;i<16;i++)//一个汉字占32个字节,这是前16个字节,每个字节占DDRAM中的一列
{
//点的位置是在左半屏幕还是右右半屏幕
if((y+(j<<4)+i)<64)//y表示要显示的起始列号,j表示当前汉字编号,j要乘以16,i表示当前汉字中0~16行的第i行,因此y+j<<4+i才是当前要写入数据的列号
{
LCDSel(1);//选择左半屏幕
WriteCmdToLCD12864(LCD_ON);//开显示
SetX(x);//设置页
SetZ(0);//DDRAM和屏幕的对应关系
SetY(y+(j<<4)+i);//设置列地址
if(upline)//如果没有上划线
{
if(flag)//汉字不反白显示
{
WriteDatToLCD12864(hz[(j<<5)+i]);//一个汉字占32个单元,j要乘以32,即左移5位
}
else//汉字反白显示
{
WriteDatToLCD12864(~hz[(j<<5)+i]);
}
}
else//如果有上划线
{
if(flag)//不反白显示
{
WriteDatToLCD12864(hz[(j<<5)+i]|UPLINE);//将每个字节的最低位置为1
}
else//反白显示
{
WriteDatToLCD12864(~hz[(j<<5)+i]|UPLINE);//将每个字节的最低位置为1 
}
}
}
else if((y+(j<<4)+i)<128)//在右半屏幕
{
LCDSel(2);//选择右半屏幕
WriteCmdToLCD12864(LCD_ON);//开显示
SetX(x);//设置页地址
SetZ(0);//DDRAM和屏幕对应关系
SetY(y+(j<<4)+i-64);//设置列地址,右半屏需要减去64
if(upline)
{
if(flag) WriteDatToLCD12864(hz[(j<<5)+i]);
else WriteDatToLCD12864(~hz[(j<<5)+i]);
}
else
{
if(flag) WriteDatToLCD12864(hz[(j<<5)+i]|UPLINE);
else WriteDatToLCD12864(~hz[j<<5+i]|UPLINE); 
}
}
}
//显示下半个汉字
for(i=16;i<32;i++)//一个汉字占32个单元,这是后16个单元
{
if((y+(j<<4)+i-16)<64)//汉字在左半屏幕
{
if(x+1<8)//页编号是0~7,共8页,如果x+1小于8,这说明这个汉字不会最后一页显示汉字的上半部分,第一页显示汉字的下半部分

LCDSel(1);//选择左半屏幕
WriteCmdToLCD12864(LCD_ON);//开显示
SetX(x+1);//设置页地址
SetZ(0);//DDRAM和屏幕对应关系
SetY(y+(j<<4)+i-16);//设置列地址
if(underline)//如果没有下划线
{
if(flag) WriteDatToLCD12864(hz[j<<5+i]);
else WriteDatToLCD12864(~hz[j<<5+i]);
}
else//有下划线
{
if(flag) WriteDatToLCD12864(hz[j<<5+i]|UNDERLINE);//将每个字节的最高位置为1
else WriteDatToLCD12864(~hz[j<<5+i]|UNDERLINE);
}
}
}
else if((y+(j<<4)+i-16)<127)//下半部分的汉字在右半屏幕
{
if(x+1<8)

LCDSel(2);
WriteCmdToLCD12864(LCD_ON);
SetX(x+1);
SetZ(0);
SetY(y-64+(j<<4)+i-16);
if(underline)
{
if(flag) WriteDatToLCD12864(hz[j<<5+i]);
else WriteDatToLCD12864(~hz[j<<5+i]);
}
else
{
if(flag) WriteDatToLCD12864(hz[j<<5+i]|UNDERLINE);
else WriteDatToLCD12864(~hz[j<<5+i]|UNDERLINE); 
}
}
}
}
}
}

//字母和数字是16行8列的
//x:行0~7 y:列0~127
//asc: 指向标准交换码ASCII
//string: 指向要显示的字符串
//flag: 0 反白显示
//online: 0 带上划线,underline : 0带下划线
//n: the number of the string
void en_disp(unsigned char x,unsigned char y,unsigned char n,unsigned char code *asc,const unsigned char *string,bit flag,bit online,bit underline)
{
unsigned char i,j,loc;
for (j=0;j<n;j++)//从第一个字符开始处理,字符串string长度为n
{
loc = string[j]-0x30;//0~9和:的ASCII码是从0x30到0x3A,:正好在9后面,0~9和:的ASCII码在Asc[]数组中对应,loc是指示第几个元素,loc*16才是Asc[]数组中对应的位置,因为每个字符占16个单元,如果建立一个二维数据更容易处理一些。
for(i=0;i<8;i++)//ASCII码的上半部分
{
if((y+(j<<3)+i)<64)//y是列地址,每一个字符占8列,j<<3表示前面有j个字符占了8*j列,还要加上i,即当前字符的第i列
{
LCDSel(1);//选择左半屏幕
WriteCmdToLCD12864(LCD_ON);//开显示
SetX(x);//设置页地址
SetZ(0);
SetY(y+(j<<3)+i);//设置列地址
if(online)//没有上划线
{
if(flag) WriteDatToLCD12864(asc[loc<<4+i]);//不反白显示 
else WriteDatToLCD12864(~asc[loc<<4+i]);//反白显示
}
else
{
if(flag) WriteDatToLCD12864(asc[loc<<4+i]|UPLINE);
else WriteDatToLCD12864(~asc[loc<<4+i]|UPLINE);
}
}
else if((y+(j<<3)+i)<128)
{
LCDSel(2);
WriteCmdToLCD12864(LCD_ON);
SetX(x);
SetZ(0);
SetY(y-64+(j<<3)+i);
if(online)
{
if(flag) WriteDatToLCD12864(asc[loc<<4+i]);
else WriteDatToLCD12864(~asc[loc<<4+i]);
}
else
{
if(flag) WriteDatToLCD12864(asc[loc<<4+i]|UPLINE);
else WriteDatToLCD12864(~asc[loc<<4+i]|UPLINE);
}
}
}
for(i=8;i<16;i++)//显示下半个字母
{
if((y+(j<<3)+i-8)<64)

if(x+1<8)
{
LCDSel(1);
WriteCmdToLCD12864(LCD_ON);
SetX(x+1);
SetZ(0);
SetY(y+(j<<3)+i-8);
if(underline)
{
if(flag) WriteDatToLCD12864(asc[loc<<4+i]);
else WriteDatToLCD12864(~asc[loc<<4+i]);
}
else
{
if(flag) WriteDatToLCD12864(asc[loc<<4+i]|UNDERLINE);
else WriteDatToLCD12864(~asc[loc<<4+i]|UNDERLINE);
}

}
}
else if((y+(j<<3)+i-8)<128)
{
if(x+1<8)

LCDSel(2);
WriteCmdToLCD12864(LCD_ON);
SetX(x+1);
SetZ(0);
SetY(y-64+(j<<3)+i-8);
if(underline)
{
if(flag) WriteDatToLCD12864(asc[loc<<4+i]);
else WriteDatToLCD12864(~asc[loc<<4+i]);
}
else
{
if(flag) WriteDatToLCD12864(asc[loc<<4+i]|UNDERLINE);
else WriteDatToLCD12864(~asc[loc<<4+i]|UNDERLINE);
}
}
}
}

}

//显示一个32行16列的字符,占用4页
//line是页
//column是列
//flag是反白标识
void Show16X32(unsigned char page,unsigned char column,unsigned char *pt,bit flag)
{
unsigned char i,j;
for(j=0;j<4;j++)
{
SetX(page+j); //设置页地址
LCDSel(1); //选左半屏
SetY(column); //一个字符占用4页,但每页的列首地址都是相同的,设置列地址 
for(i=0;i<16;i++)//一个字符占16列
{
if((column+i)>=64) //如果列大于等于64

LCDSel(2);//选右屏幕
SetY(column+i-64);//设置列地址,列地址会自动加一
}
if(flag) WriteDatToLCD12864(*pt);//不反白显示
else WriteDatToLCD12864(~(*pt)); 
pt++;
}
}
}

时间: 2024-08-01 06:26:44

12864液晶——读写、划点、划线、汉字、32*16的字符的相关文章

树莓派+12864液晶视频播放实验

树莓派+12864液晶视频播放实验 51单片机学习笔记:ST7920控制器的12864液晶使用总结 总结: 1. 控制芯片不同,液晶接口定义,或者寄存器定义也可能不同 2. 显示方式有并行和串行,串行方式据说不能读数据寄存器(DR),那指令暂存器IR是否可读? 3. 含字库芯片显示字符时不必对字符取模了,但字库有可能缺斤少两,就是说有一部分字(哪怕是常用字),在字库中没有,如果你第一次测试代码就遇到该字在字库中没有的情况,建议去买体育彩票,支持体育运动! 4. 对芯片的结构地址一定要理解清楚,个

12864液晶并行方式显示

12864液晶使用的16*16点阵.128个字符(8*16点阵)及64*256点阵显示RAM(GDRAM).与外部CPU接口採用并行或串行两种控制方式. 在12864上显示0-9的随机数 第二行显示www.csdn.blog 第三行显示"求是07的博客" 第四行显示"欢迎光临" 下面是基本的代码 #include <reg52.h> #include <intrins.h> #include <stdlib.h> #define

汉字转16进制ASCII码

using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace TestConsole { class Program { static void Main(string[] args) { byte[] b = ASCIIEncoding.Default.GetBytes("阿"); foreach (var s in b) { Console.WriteL

URL参数中汉字转换为16进制Unicode形式

导入JAR包: import java.net.*; 把汉字转换为16进制Unicode形式: String xw="新闻中心"; String name=URLEncoder.encode(xw,"utf-8"); 把16进制Unicode形式字符串转换为汉字: URLDecoder.decode("%E6%96%B0%E9%97%BB%E4%B8%AD%E5%BF%83","utf-8"); 注意:从A(UTF-8)页面中

C#编程入门--将指定字符串中的汉字转换为拼音缩写,其中非汉字保留为原字符

将指定字符串中的汉字转换为拼音缩写,其中非汉字保留为原字符 #region 将指定字符串中的汉字转换为拼音缩写,其中非汉字保留为原字符 /// <summary> /// 将指定字符串中的汉字转换为拼音缩写,其中非汉字保留为原字符 /// </summary> /// <param name="text"></param> /// <returns></returns> public static string G

汉字转换为16进制

汉字转换为16进制.%C0%EE%CE%C4%B4%CF  对应  李文聪一个汉字对应两个十六进制源码如下:int main(){  int i;  char* str = "李文聪";  char res_tmp[100];  char res[100] = {"%"};   for (i = 0; i < strlen(str); i++)  {      sprintf(res_tmp + 3*i, "%02X ", (unsigne

js 识别汉字和全角字符

遇到个问题,需要检测输入框中的字符长度,如果是汉字,算作2个字节??? 然后我就在想,我怎么去识别他是不是汉字呢???首先想到的可能就是 js 的 charCodeAt 方法, ASCII 码,然后就疑惑,那么怎么把汉字和 ASCII 码对应起来呢,想不通,遂百度,发现转发方法的人挺多,但都不说原理,难道都明白???原理还是我来说吧 比较通用的方法都是这样子: 代码 var a="好"; isChinese=!!a.match(/[^\x00-\xff]/ig); //或者 isChi

PHP正则匹配6到16位字符组合(且只能为数字、字母、下划线)

php正则匹配6到16位的字符串. 只允许包含数字.字母.下划线组成的6到16位字符,符合返回ture,否则返回false. 解答: 6到16位,正则可以这样写:{6,16}. 任意的字符6到16位的正则表达式是这样:.{6,16} 仅允许数字.字母.下划线的组合,正则为:[0-9_a-zA-Z]   整合一下,完整的正则就是: ^[_0-9a-z]{6,16}$ 以下是应用此正则验证密码的例子. php; auto-links:false;"><?php /** * php正则验证

js判断输入字符串长度(汉字算两个字符,字母数字算一个):例如 要求输入12的字,24个字节

<html> <head> <title>js判断输入字符串长度(汉字算两个字符,字母数字算一个)</title> <style type="text/css"> .pbt { margin-bottom: 10px; } .ie6 .pbt .ftid a, .ie7 .pbt .ftid a { margin-top: 1px; } .cl:after { clear: both; content: ".&quo