博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
LCD12864经典驱动(详细注释)
阅读量:4222 次
发布时间:2019-05-26

本文共 7480 字,大约阅读时间需要 24 分钟。

#include "bsp.h"/*    RW = 1 :读数据    RW = 0 : 写数据    RS = 1 :数据D0-D7与显示RAM交互    RS = 0 :数据D0-D7与指令寄存器交互    E  = 1 :读写是能有效(即可以读写)操作的基础    E :下降沿:锁定数据    CS1 = 0:选择LCD的前64位显示    CS2 = 0:选择LCD的后64位显示*//* *LCD检测忙函数 *在RS=0,RW=1模式下*/void chekbusy12864(void){    uchar dat;            //定义uchar变量,接收数据    EX0 = 0;              //禁止外部中断0    LCD_RS_OUT = 0;       //指令模式     LCD_RW_OUT = 1;       //读数据    do    {        P0 = 0x00;        //初始化数据端口        LCD_E_OUT = 1;    //使能,此语句执行后可以对指令寄存器进行指定操作,此处执行后P0口已经读出了指令寄存器的内容        dat = P0 & 0x80;  //判断P0的最高位数据(将最高为标为第8位即数据手册BF位,BF = 0空闲,BF=1忙)        LCD_E_OUT = 0;    //E出现一个下降沿所存P0数据    }while(dat != 0x00);  //如果dat != 0x00为真,继续do-while循环,也就是说P0的最高为不为1时,退出do-while循环    EX0=1;                //允许外部中断0}/* *LCD选屏函数 *输入参数为0时:选择左半屏 *输入参数为1时:选择右半屏 *输入参数为2时:选择双屏*/void CHOOSE_12864_SCREEN(uchar i)  /*i是要写的屏.0是左屏,1是右屏,2是双屏;*/{                                  /*此处在硬件上运行时i的电平全部与程序相反;*/    switch (i)                               {        case 0:         {            LCD_CS1_OUT=0;            LCD_CS2_OUT=1;        }break;                     //比如此处如果要在电路上运行则应该改为CS=1;LCD_CS2_OUT=0;           case 1:         {            LCD_CS1_OUT=1;            LCD_CS2_OUT=0;        }break;        case 2:         {            LCD_CS1_OUT=0;            LCD_CS2_OUT=0;        }break;        default: break;     }}/* *LCD写指令函数 *在RS=0,RW=0模式下*/void LCD_12864_CMD(uchar cmd)                //写命令{    chekbusy12864();     //调用lcd检忙函数,知道不忙的时候结束这个函数,继续执行后面内容    EX0=0;               //关闭外部中断0    LCD_RS_OUT=0;        //指令模式    LCD_RW_OUT=0;        //写模式    LCD_E_OUT=1;         //E使能读写    P0 = cmd;            //将输入参数cmd写入P0口,此时P0口立马将数据写入内部指令存储区    LCD_E_OUT=0;         //E出现下降沿。所存P0口,P0口不在改变    EX0=1;               //开启外部中断0}/* *LCD写数据函数 *在RS=1,RW=0模式下*/void  LCD_12864_DAT(uchar dat){    chekbusy12864();     //调用lcd检忙函数,知道不忙的时候结束这个函数,继续执行后面内容    EX0 = 0;             //关闭外部中断0    LCD_RS_OUT = 1;      //数据模式    LCD_RW_OUT = 0;      //写模式    LCD_E_OUT  = 1;      //E使能读写    P0 = dat;            //将输入参数cmd写入P0口,此时P0口立马将数据写入内部数据存储区    LCD_E_OUT=0;         //E出现下降沿。所存P0口,P0口不在改变    EX0=1;               //开启外部中断0}/* *LCD清屏函数 *调用写数据和写指令函数 *此处和实际LCD12864有区别 **/void CLEAR_12864_SCREEN(void)     //此处分左右屏清屏,左右两屏每一屏都是8页{    uchar page;  //定义页面变量    uchar row;   //定义行变量    for(page = 0xb8; page < 0xc0; page++)    {        LCD_12864_CMD(page);      //1011 1000 - 1100 0000,page0~page7        LCD_12864_CMD(0x40);      //0100 0000 - 0111 1111从每个page的00 0000地址开始到11 1111结束 0~63共64位,每写一位,地址自动移向下一位        for(row=0; row<64; row++) //此处row只实现计数功能,实际地址移动是在向地址写入数据后自动实现的        {            LCD_12864_DAT(0x00);  //对12864所有地址全部写零,此处每写一个,row地址自动加1        }    }}/* *LCD初始化函数 * */void Init_12864_HS(void){    chekbusy12864();              //调用检忙函数    LCD_12864_CMD(0xc0);          //1100 0000 设置显示起始行,此处起始行位后6位,第7位和第6位11表示设置起始行这个功能    LCD_12864_CMD(0x3f);          //0011 1111 设置屏幕显示开关}/* *LCD显示8x16点函数 **//*    指令格式    01-- ---- : 设置列地址    0100 0000:0x40    1011 1--- : 设置行地址    1011 1000: 0xb8    8page 64row*/void Display_8_point(uchar ch, uchar row, uchar page, uchar *adr){    uchar i;                        //定义循环变量i    CHOOSE_12864_SCREEN(ch);        //此处最好选择左右两屏需要显示的屏幕    page = page << 1;               //程序中采用的位移运算代替乘法运算,这样可以大大降低处理器的负担    row = row << 3;                 //此处移位的原因    LCD_12864_CMD(row + 0x40);      //0100 0000 + row    LCD_12864_CMD(page + 0xb8);     //1011 1000 + page    for(i = 0; i < 8; i++)    {        LCD_12864_DAT(*(adr + i));  //adr是数组的首地址    }    LCD_12864_CMD(row + 0x40);    LCD_12864_CMD(page + 0xb9);    for(i = 8; i < 16; i++)         //此处i表示数据数组的下标,8x16显示,数据数组是每16位一组    {        LCD_12864_DAT(*(adr + i));    }}/* *LCD显示16x16点函数 **/void Display_16_point(uchar ch, uchar row, uchar page, uchar *adr){    uchar i;    CHOOSE_12864_SCREEN(ch);    page = page << 1;                                               row = row << 3;                  //此处移位的原因可能是需要保留一部分不显示    LCD_12864_CMD(row + 0x40);       //0100 0000    LCD_12864_CMD(page + 0xb8);      //1011 1000    for(i = 0; i < 16; i++)          //16x16显示,数据数组每32位一组    {        LCD_12864_DAT(*(adr + i));    }    LCD_12864_CMD(row + 0x40);    LCD_12864_CMD(page + 0xb9);    for(i = 16; i < 32; i++)    {        LCD_12864_DAT(*(adr + i));         //adr是数组的首地址    }} /* *LCD数据读取函数 **/uchar DAT_READ_12864(uchar page, uchar arrange)  //page页地址.arrange列地址){    uchar dat;                        //定义变量    chekbusy12864();                  //调用检忙函数    EA = 0;                           //关闭总中断    LCD_12864_CMD(page + 0xb8);       //调用写指令函数写入页地址指令    LCD_12864_CMD(arrange + 0x40);    //调用写指令函数写入列地址指令    EX0 = 0;                          //关闭外部中断0                          P0  = 0xff;                       //初始化数据端口P0    LCD_RW_OUT = 1;                   //设置读模式    LCD_RS_OUT = 1;                   //设置数据模式    LCD_E_OUT  = 1;                       LCD_E_OUT  = 0;                   //12864读数据时第二次读才有效,第一次读取的值不采集    LCD_E_OUT  = 1;                   //二次使能有效    dat = P0;                         //独处P0口的数据读出8位数据    LCD_E_OUT = 0;                    //所存P0口    EX0=1;                            //打开外部中断0    EA = 1;                           //打开总中断    return(dat);                      //将读出的数据作为返回值}/* *LCD反白显示函数 **/void Display_16_point_fb(uchar ch, uchar arrange, uchar page){    uchar i;                         //定义循环变量    uchar xdata dat_fb[32];          //定义反白显示数组    CHOOSE_12864_SCREEN(ch);         //选择屏幕    for(i = 0; i < 16; i++)          //循环进行反白    {        dat_fb = ~(DAT_READ_12864((page << 1), ((arrange << 3) + i)));        dat_fb[i+16]=~(DAT_READ_12864((page << 1) + 1, ((arrange << 3) + i)));    }    Display_16_point(ch, arrange, page, dat_fb);      //调用16x16显示函数,将反白的数据显示}/* *LCD划线函数 **/              //y1比y2小,这里给出画竖线的函数而不用画点的方法  //是为了减少单片机的处理负担void DRAW_TRANSVERSE_Line(uchar y1, uchar y2, uchar x)//y1表示起点,y2表示终点,x表示列地址{    uchar i;    uchar sum = 0;    if(x > 63)                             //如果x比63大,则行地址在右半屏    {        CHOOSE_12864_SCREEN(1);            //选择右半屏        x = x - 64;                        //确定右半屏行地址    }    else    {        CHOOSE_12864_SCREEN(0);            //选择了左半屏显示    }    if((y1 / 8) != (y2 / 8))                   {        for(i = 0; i < (8 - y1 % 8); i++)                 {            sum = sum | ((2 << ((y1 % 8) + i)));        }        LCD_12864_CMD(x + 0x40);        LCD_12864_CMD(y1 / 8 + 0xb8);        LCD_12864_DAT(sum);        sum = 0;        for(i = 0; i < (y2 / 8 - y1 / 8 - 1); i++)        {            LCD_12864_CMD(x + 0x40);            LCD_12864_CMD((y1 / 8) + 0xb9 + i);            LCD_12864_DAT(0xff);        }        for(i = 0; i <= (y2 % 8); i++)        {            sum = sum | (2 << i);        }        LCD_12864_CMD(x + 0x40);        LCD_12864_CMD(y2 / 8 + 0xb8);        LCD_12864_DAT(sum | 1);        sum = 0;            }    else    {        for(i = 0; i <= y2 - y1; i++)        {            sum = sum | (2 << (i + (y1 % 8)));        }        LCD_12864_CMD(0x40 | x);        LCD_12864_CMD(0xb8 | (y1 / 8));        LCD_12864_DAT(sum);    } }/* *LCD划点函数 *x横坐标,y纵坐标左上角为0,0*/  void DRAW_DOT_HS(uchar x, uchar y){    uchar dat;    if(x > 63)    {        CHOOSE_12864_SCREEN(1);        //选右屏        x = x - 64;    }    else    {        CHOOSE_12864_SCREEN(0);        //选左屏    }    dat = DAT_READ_12864(y / 8, x);    //读取lcd的行地址为y/8,列地址为x处的点的内容    LCD_12864_CMD(0x40 | x);           //向指令寄存器写入列指令    LCD_12864_CMD(0xb8 | y / 8);       //此处内部地址标号是0-7     向指令寄存器写入行指令    LCD_12864_DAT((1 << (y % 8)) | dat);}

 

转载地址:http://cyemi.baihongyu.com/

你可能感兴趣的文章
Android异步开发框架之RxJava使用详解
查看>>
收藏学习React Native的几个网址
查看>>
Android网络请求框架之MVP+Retrofit+RxJava搭建
查看>>
android开发,app性能调优工具StrictMode 详解
查看>>
Android绘制优化(一)绘制性能分析
查看>>
Android内存优化(六)LeakCanary使用详解
查看>>
android音频pcm流8位和16位的相互转换
查看>>
用 LeakCanary 检测内存泄漏
查看>>
InputMethodManager内存泄漏引发对View加载的探究
查看>>
Android直播入门实践:动手搭建一套简单的直播系统
查看>>
收藏两个学习android开源网络电话Sipdroid的网址
查看>>
ffmpeg configure参数说明
查看>>
IjkPlayer常用Option设置
查看>>
Android中onTouch与onClick两种监听的完全解析
查看>>
实时Android语音对讲系统架构
查看>>
Linux下动态库查找路径的问题
查看>>
UNIX网络编程 非阻塞connect的实现
查看>>
解决android studio下app依赖第三方包出错的问题
查看>>
android ndk开发crash崩溃定位:
查看>>
Android Notification 通知栏点击不能跳转
查看>>