• 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++;
    }
    }
    }

  • 相关阅读:
    py基础之模块与包
    py装饰器,生成器,迭代器
    py函数式编程
    py基础之列表生成式
    算法基础之递归算法
    Py基础之函数
    py基础之无序列表
    py基础之数据类型及基本语法
    jsp报错问题汇总
    mysql问题汇总
  • 原文地址:https://www.cnblogs.com/wolf-man/p/6823279.html
Copyright © 2020-2023  润新知