今天给大家分享一下关于proteus的LCD12864的问题,很多小伙伴在做关于LCD12864仿真的时候会遇到一个问题,就是仿真里面的LCD12864没有字库,这个真的让人头大,如果按照我们平常在12864里编写代码的话,结果就是不显示,所以我们需要先自己取字模,一定要注意,取模方式采用倒序、纵向取模:
接着,在proteus软件里打开并新建一个工程,设计一个简单的电路,如图:
编程是重点,LCD12864又把整个显示屏分为左右两边,想要同时显示,必须写代码把左右两边同时打开,即图中的CS1、CS2,根据LCD12864的手册里的时序编写读写代码,网上都可以找到他的中文手册,这里直接把代码放出来:
#define LCDLCDDisp_Off 0x3e #define LCDLCDDisp_On 0x3f #define Page_Add 0xb8//页地址 #define LCDCol_Add 0x40//列地址 #define Start_Line 0xC0//行地址 /*****液晶显示器的端口定义*****/ #define data_ora P0 /*液晶数据总线*/ sbit LCDMcs=P2^4 ; /*片选1*/ sbit LCDScs=P2^3 ; /*片选2*/ sbit RESET=P2^3 ; /*复位信号*/ sbit LCDDi=P2^2 ; /*数据/指令 选择*/ sbit LCDRW=P2^1 ; /*读/写 选择*/ sbit LCDEnable=P2^0 ; /*读/写 使能*/ /**************************************************************************** 函数功能:LCD延时程序 入口参数:t 出口参数: ****************************************************************************/ void LCDdelay(unsigned int t) { unsigned int i,j; for(i=0;i<t;i++); for(j=0;j<10;j++); } /**************************************************************************** 状态检查,LCD是否忙 *****************************************************************************/ void CheckState() { unsigned char dat,DATA;//状态信息(判断是否忙) LCDDi=0; // 数据\指令选择,D/I(RS)="L" ,表示 DB7∽DB0 为显示指令数据 LCDRW=1; //R/W="H" ,E="H"数据被读到DB7∽DB0 do { DATA=0x00; LCDEnable=1; //EN下降源 LCDdelay(2);//延时 dat=DATA; LCDEnable=0; dat=0x80 & dat; //仅当第7位为0时才可操作(判别busy信号) } while(!(dat==0x00)); } /***************************************************************************** 函数功能:写命令到LCD程序,RS(DI)=L,RW=L,EN=H,来一个脉冲写一次 入口参数:cmdcode 出口参数: *****************************************************************************/ void write_com(unsigned char cmdcode) { CheckState();//检测LCD是否处于忙状态 LCDDi=0; LCDRW=0; P1=cmdcode; LCDdelay(2); LCDEnable=1; LCDdelay(2); LCDEnable=0; } /***************************************************************************** 函数功能:LCD初始化函数 *****************************************************************************/ void init_lcd() { LCDdelay(100); LCDMcs=1;//刚开始关闭两屏 LCDScs=1; LCDdelay(100); write_com(LCDLCDDisp_Off); //写初始化命令 write_com(Page_Add+0); write_com(Start_Line+0); write_com(LCDCol_Add+0); write_com(LCDLCDDisp_On); } /***************************************************************************** 函数功能:写数据到LCD程序,RS(DI)=H,RW=L,EN=H,来一个脉冲写一次 入口参数:LCDDispdata *****************************************************************************/ void write_data(unsigned char LCDDispdata) { CheckState();//检测LCD是否处于忙状态 LCDDi=1; LCDRW=0; P1=LCDDispdata; LCDdelay(2); LCDEnable=1; LCDdelay(2); LCDEnable=0; } /***************************************************************************** 函数功能:清除LCD内存程序 入口参数:pag,col,hzk *****************************************************************************/ void Clr_Scr() { unsigned char j,k; LCDMcs=0; //左、右屏均开显示 LCDScs=0; write_com(Page_Add+0); write_com(LCDCol_Add+0); for(k=0;k<8;k++)//控制页数0-7,共8页 { write_com(Page_Add+k); //每页每页进行写 for(j=0;j<64;j++) //每页最多可写32个中文文字或64个ASCII字符 { write_com(LCDCol_Add+j); write_data(0x00);//控制列数0-63,共64列,写点内容,列地址自动加1 } } } /***************************************************************************** 函数功能:指定位置显示汉字16*16程序 入口参数:page,column,hzk *****************************************************************************/ void hz_LCDDisp16(unsigned char page,unsigned char column, unsigned char code *hzk) { unsigned char j=0,i=0; for(j=0;j<2;j++) { write_com(Page_Add+page+j); write_com(LCDCol_Add+column); for(i=0;i<16;i++) write_data(hzk[16*j+i]); } } 最后,只用在主函数里调用就可以,注意,while(1)里面不用调用LCD的函数: void main() { init_lcd(); Clr_Scr(); LCDMcs=0; //左屏开显示 LCDScs=1; hz_LCDDisp16(0,48,Hz_ni);// Hz_Wo为某个汉字的首地址 LCDMcs=1; //右屏开显示 LCDScs=0; hz_LCDDisp16(0,0,Hz_hao); while(1) { } }
显示结果如图所示:
但如果要在同一行显示多个汉字的时候,我们还需要再增加两个函数,左、右屏位置显示,这是因为,之前的代码打开的地址范围小,只是针对数组只有两行的显示,多了也显示不下,这个时候我们就要打开整一行的地址,宽高变为128*16,一共可以显示四行,一行显示8个字,这也是12864的由来,
/***************************************************************************** 函数功能:左屏位置显示 入口参数:page,column,hzk 出口参数: *****************************************************************************/ void Bmp_Left_Disp(unsigned char page,unsigned char column, unsigned char code *Bmp) { unsigned char j=0,i=0; for(j=0;j<2;j++) { write_com(Page_Add+page+j); write_com(LCDCol_Add+column); for(i=0;i<64;i++) write_data(Bmp[128*j+i]); } } /***************************************************************************** 函数功能:右屏位置显示 入口参数:page,column,hzk 出口参数: *****************************************************************************/ void Bmp_Right_Disp(unsigned char page,unsigned char column, unsigned char code *Bmp) { unsigned char j=0,i=0; for(j=0;j<2;j++) { write_com(Page_Add+page+j); write_com(LCDCol_Add+column); for(i=64;i<128;i++) write_data(Bmp[128*j+i]); } }
写完这两个函数后,我们就可以在一行显示多个汉字了,每一个数组都要是16行、16列,如图:
别急,如果你以为剩下的就是直接把取模的数组直接按照顺序放进去就可以了的话,你就又掉进坑了里,因为你会发现他的显示是这样的:
还有这样的:
是不是头很大,看起来很乱,但仔细看的话,你会发现,是有迹可循的,第一个显示错误是因为一个数组里没有达到16行,因为你地址已经打开了,如果不够16行他就会显示乱码,所以即使没有字,也要用0x00补全,那么第二个的原因,仔细观察,每一个字的下半个字跑到了他的后面,因为我们编写的代码的原因,导致把这四行字又分成了八行,每一行执行完才会跑到下一行,所以我们取的模要重新拆开,把下边的一半放到第九行开始显示:
这样的现示结果就正确了:
主函数的代码和刚才一样:
void main() { init_lcd(); Clr_Scr(); LCDMcs=0; //左屏开显示 LCDScs=1; Bmp_Left_Disp(0,0,Bmp1);// Bmp1为某个汉字的首地址 LCDMcs=1; //右屏开显示 LCDScs=0; Bmp_Right_Disp(0,0,Bmp1); while(1) { } }