完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
我很佩服你的执着的精神。。但是我不得不告诉你做技术的都是很自信的,认为自己搞懂了很多东西,认为自己有多了不起。。其实哪个做技术的不是靠自己钻研,自学出来的。实话跟你讲的这个我在上学的时候都会用了,毕业的时候就搞清楚原理了(我们是没有开C的)。 不过我还是要肯定你的奉献,这对初学者是有用的,虽然对我没有用。(本人90的)
|
|
|
|
|
|
|
|
本帖最后由 jianhong_wu 于 2013-4-1 15:51 编辑
第十一节:鸿哥单色液晶屏三大类定律(KS0107驱动12864屏) (1) 开场白: 我曾经做过一年的液晶屏工程师,“出家”做自由职业者之后我又在很多项目上应用液晶屏。深圳很多液晶模块公司都是用我帮他们做的测试程序。不管什么品种的单色屏我都能玩得转。谦虚一点说就是,在单色液晶驱动程序这方面,吴坚鸿在全国绝对排名在前三名之内(如果不谦虚地说我就号称第一了,呵呵)。 接下来我要介绍一下我的新发现,单色屏驱动程序的三大类定律。市面上有各种各样的单色屏,什么点阵,段码等等,但是都逃不出我总结的三大类定律。读者只要学会了鸿哥的三大类定律,以后任何单色屏对于你们来说都是浮云。 想要驱动液晶屏,只要知道怎么样在任何一个地方(X轴与Y轴坐标)显示一个基本单位就够了。在单色液晶屏领域,这个显示的基本单位只有三种类型,也就是我总结的三大类定律。 第一大类定律:纵向显示八个点的类型。 这类液晶屏在纵向上以八个点(一个字节)为基本单位,因此Y坐标数值的最大范围是纵向上的点阵数除以八,然后再减去一(因为从零开始)。而X坐标数值的最大范围就直接是横向上的点阵数减去一(因为从零开始)。 第二大类定律:横向显示八个点的类型。 这类液晶屏在横向上以八个点(一个字节)为基本单位,因此X坐标数值的最大范围是横向上的点阵数除以八,然后再减去一(因为从零开始)。而Y坐标数值的最大范围就直接是纵向上的点阵数减去一(因为从零开始)。 第三大类定律:任意位置显示一个点的类型。 这类液晶屏在任意位置上以一个点为基本单位,因此X坐标数值的最大范围就直接是横向上的点阵数减去一(因为从零开始)。而Y坐标数值的最大范围就直接是纵向上的点阵数减去一(因为从零开始)。 下面,我以驱动芯片为KS0107的12864液晶屏为例子,来介绍一下第一大类定律:纵向显示八个点的类型。12864液晶屏实际上是由左右两块6464液晶屏合并在一起的,然后通过两根IO口来片选不同的6464液晶屏。因此,我们只要弄懂了一块6464液晶屏的显示方法就够了。6464屏在横向上是64个点,纵向上是64个点,也就是一个正方形的屏。因为它属于第一大类的屏,所以X轴坐标数值的最大范围是64-1=63,而纵向坐标数值的最大范围是(64/8)-1=7.正常的操作思路是这样的,先发送X轴与Y轴的位置数据,确定位置后,就发送一个字节(八个点)的显示数据。这类屏还有一个特征,连续发送显示数据时,在横向(X轴上)的位置数据会自动加一,因此如果在不换行的情况下,只要设定一次位置,就可以从左到右连续发送显示的数据。当换行显示数据时,必须重新设定一下坐标位置。 字节正序与倒序的概念解释:当我们一次在纵向上显示八个点的基本单位时,实际上等于我们发送了一个字节的显示数据,比如0x01,如果是正序的屏,那么从上到下的八个点中,只有第8个点是显示的,其它的是空白,而如果是倒序的屏,则只有第1个点是显示的,其它是空白的。12864这个屏是属于倒序的屏。 取模软件是必须的,读者可以在网上自己下载,资料很多。 (2)功能需求: 在12864屏上显示8X16的字符,16X16的汉字,24X24的汉字。 (3)硬件原理: 液晶屏的VEE接20K可调电阻的左边端口,VO接可调电阻的中间端口,VSS接可调电阻的右边端口,此可调电阻在这里用来调节液晶屏的对比度。模块与背光的电源线接上5V,其它数据线跟单片机的IO口连接上。这个大家都懂。 (4)源码适合的单片机:PIC18f4520,晶振为11.0592MHz。 (5)源代码讲解如下: #include #define Disp_On 0x3f //液晶模块的寄存器初始化的参数,抄网上的,我也从来没有 #define Disp_Off 0x3e //研究过它是什么含义,因为根本没必要知道,直接用就行 #define Col_Add 0x40 #define Page_Add 0xb8 #define Start_Line 0xc0 //补充说明:吴坚鸿程序风格是这样的,凡是输出IO后缀都是_dr,凡是输入的//IO后缀都//是_sr,凡是数据总线后缀都是_bus #define Lcd_bus PORTB //液晶的数据总线 #define Bf_sr RB7 //芯片忙检查 #define Mcs_dr LATD6 //左右6464屏的片选 #define Scs_dr LATD7 //左右6464屏的片选 #define Enable_dr LATD5 //类似时钟的数据线 #define Di_dr RC7 //数据与指令的选择线 #define RW_dr RD4 //读与写的选择线 //本程序的复位脚rest_dr直接用一个电阻和一个电容实现硬件复位,省一个IO void delay(unsigned int t); //时序延时函数声明 void chk_busy () ; //忙检测,液晶驱动时序的一部分 void write_com(unsigned char cmdcode); //往液晶模块写入指令 void write_data(unsigned char Di_drspdata); //往液晶模块写入数据 void screen_clear(); //清空屏的内容 void ***_display816(unsigned char col, unsigned char pag,const unsigned char * zk,unsigned char opposite_flag); //显示8X16的字符函数,本节的核心内容 void hz_display1616(unsigned char col, unsigned char pag,const unsigned char * zk,unsigned char opposite_flag); //显示16X16的汉字函数,本节的核心内容 void hz_display2424(unsigned char col, unsigned char pag,const unsigned char * zk,unsigned char opposite_flag); //显示24X24的汉字函数,本节的核心内容 void init_lcd(); //初始化液晶模块 //补充说明:吴坚鸿程序风格是这样的,凡是字库内容,如果是字符,则前缀用***,然后紧//跟着点阵数,接着下划线,最后紧跟显示的字符。如果有重复的,则多加一个序列号标////识。如果是汉字,则前缀用hz,其它的一样。 const unsigned char ***816_v[]=//从取模软件中复制的字库,纵向取模,字节倒序 { /*-- 文字: V --*/ /*-- 宋体12; 此字体下对应的点阵为:宽x高=8x16 --*/ 0x08,0x78,0x88,0x00,0x00,0xC8,0x38,0x08,0x00,0x00,0x07,0x38,0x0E,0x01,0x00,0x00, }; const unsigned char ***816_5[]= { /*-- 文字: 5 --*/ /*-- 宋体12; 此字体下对应的点阵为:宽x高=8x16 --*/ 0x00,0xF8,0x08,0x88,0x88,0x08,0x08,0x00,0x00,0x19,0x21,0x20,0x20,0x11,0x0E,0x00, }; const unsigned char hz1616_hong[]= { /*-- 文字: 鸿 --*/ /*-- 宋体12; 此字体下对应的点阵为:宽x高=16x16 --*/ 0x10,0x61,0x86,0x60,0x08,0xF8,0x08,0x00,0xFC,0x0E,0x35,0x04,0x44,0x7C,0x00,0x00, 0x04,0x7C,0x03,0x04,0x04,0x03,0x0A,0x08,0x09,0x09,0x09,0x09,0x49,0x81,0x7F,0x00, }; const unsigned char hz1616_ge[]= { /*-- 文字: 哥 --*/ /*-- 宋体12; 此字体下对应的点阵为:宽x高=16x16 --*/ 0x00,0x02,0x02,0x7A,0x2A,0x2A,0x2A,0x2A,0x7A,0x02,0x02,0x7E,0x02,0x82,0x00,0x00, 0x01,0x01,0x01,0x3D,0x15,0x15,0x15,0x15,0x3D,0x41,0x81,0x7F,0x01,0x01,0x01,0x00, }; const unsigned char hz2424_hong[]= { /*-- 文字: 鸿 --*/ /*-- 宋体18; 此字体下对应的点阵为:宽x高=24x24 --*/ 0x00,0x80,0x00,0x04,0x18,0x90,0x40,0x40,0x40,0xC0,0x40,0x60,0x40,0xE0,0x20,0x30, 0xAC,0x24,0x20,0xE0,0xE0,0x00,0x00,0x00,0x00,0x00,0x03,0x06,0xF8,0x07,0x00,0x00, 0x80,0xFF,0x80,0x40,0x00,0x7F,0x40,0x40,0x41,0x49,0x58,0x4F,0xC0,0xE0,0x00,0x00, 0x00,0x00,0x01,0x7F,0x60,0x00,0x01,0x01,0x00,0x00,0x04,0x04,0x04,0x04,0x04,0x04, 0x24,0x22,0x62,0x64,0x3F,0x03,0x00,0x00, }; const unsigned char hz2424_ge[]= { /*-- 文字: 哥 --*/ /*-- 宋体18; 此字体下对应的点阵为:宽x高=24x24 --*/ 0x00,0x00,0x00,0x08,0x08,0x08,0xE8,0x48,0x48,0x48,0x48,0x48,0xE8,0x08,0x08,0x08, 0x08,0xF8,0x08,0x0C,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x10,0xD0,0xD3,0x52, 0x52,0x52,0x52,0x52,0xD3,0x10,0x10,0x10,0x10,0xF7,0x10,0x10,0x08,0x08,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x0F,0x0F,0x04,0x04,0x04,0x04,0x04,0x0F,0x00,0x20,0x20, 0x60,0x7F,0x00,0x00,0x00,0x00,0x00,0x00, }; //主程序 main() { ADCON0=0x00; ADCON1=0x0f; ADCON2=0x00; TRISA=0x02; TRISE=0x00; TRISC=0x7f; TRISD=0x0f; TRISB=0x00; RBPU=0; SSPEN=0; //决定RA5作为IO TRISE2=1; //补充说明,以上的内容为寄存器配置,每种不同的单片机会有点差异, //大家不用过度关注以上寄存器的配置,只要知道有这么一回事即可 init_lcd(); //初始化液晶屏 screen_clear ();//清空整屏显示内容 Mcs_dr=1;Scs_dr =0; //左6464屏,正显,16X16的汉字,“鸿哥V5” hz_display1616(0, 0,hz1616_hong,0); hz_display1616(16, 0,hz1616_ge,0); ***_display816(32, 0,***816_v,0); ***_display816(40, 0,***816_5,0); Mcs_dr =0;Scs_dr =1; //右6464屏,反显,24X24的汉字,“鸿哥V5” hz_display2424(0, 0,hz2424_hong,1); hz_display2424(24, 0,hz2424_ge,1); ***_display816(48, 0,***816_v,1); ***_display816(56, 0,***816_5,1); while(1) { CLRWDT(); //喂看门狗,大家不用过度关注此行 } } //------------------时序延时子程序----------------------------- void delay(unsigned int t) { unsigned int i,j; for(i=0;i } //------------------忙闲检查,驱动液晶程序的一部分------------------------------ void chk_busy () { TRISB=0xff; Lcd_bus=0xff; Di_dr=0; RW_dr=1; delay(0); Enable_dr=1; while(Bf_sr==1); Enable_dr=0; TRISB=0x00; } //------------------写命令到LCD,,驱动液晶程序的一部分------------------------------ void write_com(unsigned char cmdcode) { chk_busy (); Di_dr=0; RW_dr=0; Lcd_bus=cmdcode; delay(0); Enable_dr=1; delay(0); Enable_dr=0; } //-------------------写数据到LCD,,驱动液晶程序的一部分---------------------------- void write_data(unsigned char Di_drspdata) { chk_busy (); Di_dr=1; RW_dr=0; Lcd_bus=Di_drspdata; delay(0); Enable_dr=1; delay(0); Enable_dr=0; } //*------------------清空屏幕的内容,本节的核心内容 ---------------*/ void screen_clear () { unsigned char j,i; Mcs_dr=1;Scs_dr=0; //选择左屏6464 for(j =0; j <8; j ++) //此处的j代表Y轴的坐标,范围是(0到7),(64/8)-1=7. { write_com(Page_Add+ j); //设定Y轴的起始位置,切换到下一行 write_com(Col_Add+0); //设定X轴的起始位置,每次换行都从0开始 for(i=0; i <64; i ++) //此处i代表X轴,每发送一个显示数据,X轴内部的位置会自动//加一,范围是(0到63),64-1=63 { write_data(0x00); //都显示空内容,达到清空的目的 } } Mcs_dr=0;Scs_dr=1; //选择右屏6464 for(j =0; j <8; j ++) //此处的j代表Y轴的坐标,范围是(0到7),(64/8)-1=7. { write_com(Page_Add+ j); //设定Y轴的起始位置,切换到下一行 write_com(Col_Add+0); //设定X轴的起始位置,每次换行都从0开始 for(i=0; i <64; i ++) //此处i代表X轴,每发送一个显示数据,X轴内部的位置会自动//加一,范围是(0到63),64-1=63 { write_data(0x00); //都显示空内容,达到清空的目的 } } } //显示8X16的字符函数,本节的核心内容。col代表X轴,pag代表Y轴,zk代表显示相对//应的字库,opposite_flag代表是否反显,0表示正常显示,1表示反显 void ***_display816(unsigned char col, unsigned char pag,const unsigned char *zk,unsigned char opposite_flag) { unsigned char j=0,i=0; for(j=0;j<2;j++) //此处j代表Y轴的数据,也就是显示第几行了,一个8X16字符只占用//两行,每行8个点,两行的高度就是16个点, { write_com(Page_Add+pag+j);//换下一行Y轴的坐标 write_com(Col_Add+col); //每次换行都重新设定X轴的坐标, for(i=0;i<8;i++) //此处i代表X轴的数据,每发送一个显示数据,X轴位置会自动加/////一,因为一个字符的宽度是8个点,因此范围是(0到7),8-1=7. { if(opposite_flag ==1) //反显 { write_data(~zk [8*j+i]); //发送显示的字节数据,纵向显示八个点,一行8个///点,因此8*j } else //正显 { write_data(zk [8*j+i]); //发送显示的字节数据,纵向显示八个点 ,一行8个点,//因此8*j } } } } //显示16X16的汉字函数,本节的核心内容。col代表X轴,pag代表Y轴,zk代表显示相//对应的字库,opposite_flag代表是否反显,0表示正常显示,1表示反显 void hz_display1616(unsigned char col, unsigned char pag,const unsigned char *zk,unsigned char opposite_flag) { unsigned char j=0,i=0; for(j=0;j<2;j++) //此处j代表Y轴的数据,也就是显示第几行了,一个16X16汉字只占//用两行,每行8个点,两行的高度就是16个点, { write_com(Page_Add+pag+j);//换下一行Y轴的坐标 write_com(Col_Add+col); //每次换行都重新设定X轴的坐标, for(i=0;i<16;i++) //此处i代表X轴的数据,每发送一个显示数据,X轴位置会自动加/////一,因为一个16X16汉字的宽度是16个点,因此范围是(0到15),16-1=15. { if(opposite_flag ==1) //反显 { write_data(~zk [16*j+i]); //发送显示的字节数据,纵向显示八个点,一行16个///点,因此16*j } else //正显 { write_data(zk [16*j+i]); //发送显示的字节数据,纵向显示八个点 ,一行16个点,//因此16*j } } } } //显示24X24的汉字函数,本节的核心内容。col代表X轴,pag代表Y轴,zk代表显示相//对应的字库,opposite_flag代表是否反显,0表示正常显示,1表示反显 void hz_display2424(unsigned char col, unsigned char pag,const unsigned char *zk,unsigned char opposite_flag) { unsigned char j=0,i=0; for(j=0;j<3;j++) //此处j代表Y轴的数据,也就是显示第几行了,一个24X24汉字只占//用三行,每行8个点,三行的高度就是24个点, { write_com(Page_Add+pag+j);//换下一行Y轴的坐标 write_com(Col_Add+col); //每次换行都重新设定X轴的坐标, for(i=0;i<24;i++) //此处i代表X轴的数据,每发送一个显示数据,X轴位置会自动加/////一,因为一个24X24汉字的宽度是24个点,因此范围是(0到23),24-1=23. { if(opposite_flag ==1) //反显 { write_data(~zk [24*j+i]); //发送显示的字节数据,纵向显示八个点,一行24///个点,因此24*j } else //正显 { write_data(zk [24*j+i]); //发送显示的字节数据,纵向显示八个点 ,一行24个/////点,因此24*j } } } } //*------------------初始化LCD屏--------------------------*/ void init_lcd() { //如果是用IO口复位的,请在这里添加代码,本程序的复位脚直接用一个电阻和一个电容实现硬件复位 //rest_dr=0; //delay(500); //rest_dr=1; Mcs_dr=1; //左6464屏初始化 Scs_dr=0; delay(100); write_com(Disp_Off); write_com(Page_Add+0); write_com(Start_Line+0); write_com(Col_Add+0); write_com(Disp_On); Mcs_dr=0; //右6464屏初始化 Scs_dr=1; delay(100); write_com(Disp_Off); write_com(Page_Add+0); write_com(Start_Line+0); write_com(Col_Add+0); write_com(Disp_On); } (6)小结: 读者要重点弄清楚void screen_clear ,void ***_display816, void hz_display1616, void hz_display2424这四个函数他们之间的联系与规律,即可掌握本节内容的精髓。 12864是由左右两个屏合并起来的,如果想一个汉字左边显示在左屏,右边显示在右屏,也就是跨屏显示的功能,或者如果想显示一副128X64的图像,根据本节的精髓,读者只要在程序上做一些改动处理,应该不难的。 这类液晶屏在纵向上只适合显示点阵数是8倍数的字体,如果不是8倍数的点阵字体,则需要在程序上做特殊处理,这种不是8倍数的字体不实用,同时,我认为跨屏显示的功能也不实用,读者也没必要逞强去尝试。 (未完待续,下节更精彩,不要走开哦) |
|
|
|
|
|
|
|
|
|
|
|
只有小组成员才能发言,加入小组>>
求解外围电路实现的是4脚给持续低电平复位并正常工作,高电平不工作的原因
2077 浏览 1 评论
3501 浏览 3 评论
PIC1946程序有一个变量在运行过程中恢复初始值其他变量保持不变
2333 浏览 2 评论
2760 浏览 0 评论
PIC16F1825的RC5引脚,在主程序中操作无效,在中断中可以改变是为什么?
4015 浏览 5 评论
965浏览 0评论
用XC8编译PIC18F25K80时提示下面Error,求怎么解决这个问题
6349浏览 0评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-11-27 12:20 , Processed in 1.064093 second(s), Total 104, Slave 89 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号