本文共 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/