完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
|
|
|
|
|
|
很给力
|
|
|
|
|
|
|
|
|
|
鸿哥 的液晶程序出的很好,虽然现在的开发板都有12864模块,他们的教程就是最最基础的,其实要用到扩展功能,12864才能显示出牛B的一面,鸿哥开场就讲这方面的,很好。
希望鸿哥在液晶这块多讲讲,很有用,真心谢谢鸿哥无私奉献。 |
|
|
|
|
|
对了,鸿哥,前一段时间玩12864的时候也很头疼, 到底是先写入X坐标还是Y坐标?没有搞懂。希望鸿哥指点。继续学习12864.
|
|
|
|
|
|
本帖最后由 jianhong_wu 于 2013-1-5 17:20 编辑
12864不带字库的液晶屏是先写入Y轴,再写入X轴。谢谢你的提醒,我发布的程序有错,顺序搞反了,现在已经更改过来了。其实我以前写的程序也是先Y轴,然后才是X轴,这次为了符合我们的阅读习惯,我随手就把顺序调换了一下,没想到留下了一个严重的bug。我的程序都是直接截取我之前做的一些项目里面的精华内容,然后直接在word文档上编辑的,没有经过上机编译,如果读者发现bug之后,希望能够指出,让我及时改正,谢谢。 |
|
|
|
|
|
|
|
|
|
|
|
|
|
本帖最后由 jianhong_wu 于 2013-4-1 15:55 编辑
第十二节:液晶屏第一大类定律--纵向显示八个点(KS0107驱动19264屏) (1) 开场白: 在第十一节中介绍了我的新发现“吴坚鸿单色液晶屏三大类定律”,既然是定律就应该是放之四海而皆准,否则就是我吹牛。为了证明它的真理性与普遍性,这节我将以驱动芯片为KS0107的19264屏为例子。 先补一下第十一节的基本知识。想要驱动液晶屏,只要知道怎么样在任何一个地方(X轴与Y轴坐标)显示一个基本单位就够了。在单色液晶屏领域,这个显示的基本单位只有三种类型,也就是我总结的三大类定律。 第一大类定律:纵向显示八个点的类型。 这类液晶屏在纵向上以八个点(一个字节)为基本单位,因此Y坐标数值的最大范围是纵向上的点阵数除以八,然后再减去一(因为从零开始)。而X坐标数值的最大范围就直接是横向上的点阵数减去一(因为从零开始)。 第二大类定律:横向显示八个点的类型。 这类液晶屏在横向上以八个点(一个字节)为基本单位,因此X坐标数值的最大范围是横向上的点阵数除以八,然后再减去一(因为从零开始)。而Y坐标数值的最大范围就直接是纵向上的点阵数减去一(因为从零开始)。 第三大类定律:任意位置显示一个点的类型。 这类液晶屏在任意位置上以一个点为基本单位,因此X坐标数值的最大范围就直接是横向上的点阵数减去一(因为从零开始)。而Y坐标数值的最大范围就直接是纵向上的点阵数减去一(因为从零开始)。 下面,我以驱动芯片为KS0107的19264液晶屏为例子,来介绍一下第一大类定律:纵向显示八个点的类型。19264液晶屏实际上是由左中右三块6464液晶屏合并在一起的,然后通过三根IO口来片选不同的6464液晶屏。因此,我们只要弄懂了一块6464液晶屏的显示方法就够了。6464屏在横向上是64个点,纵向上是64个点,也就是一个正方形的屏。因为它属于第一大类的屏,所以X轴坐标数值的最大范围是64-1=63,而纵向坐标数值的最大范围是(64/8)-1=7.正常的操作思路是这样的,先发送Y轴与X轴的位置数据,确定位置后,就发送一个字节(八个点)的显示数据。这类屏还有一个特征,连续发送显示数据时,在横向(X轴上)的位置数据会自动加一,因此如果在不换行的情况下,只要设定一次位置,就可以从左到右连续发送显示的数据。当换行显示数据时,必须重新设定一下坐标位置。 字节正序与倒序的概念解释:当我们一次在纵向上显示八个点的基本单位时,实际上等于我们发送了一个字节的显示数据,比如0x01,如果是正序的屏,那么从上到下的八个点中,只有第8个点是显示的,其它的是空白,而如果是倒序的屏,则只有第1个点是显示的,其它是空白的。19264这个屏是属于倒序的屏。 取模软件是必须的,读者可以在网上自己下载,资料很多。 (2)功能需求: 在19264屏上的左边,中间,右边分别显示8X16的字符,16X16的汉字,24X24的汉字。 (3)硬件原理: 液晶屏的VEE接20K可调电阻的左边端口,VO接可调电阻的中间端口,可调电阻的右边端口接电源负极,此可调电阻在这里用来调节液晶屏的对比度。模块与背光的电源线接上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 Ccs_dr LATD5 //左6464屏的片选 #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 ();//清空整屏显示内容 Ccs_dr=0;Mcs_dr=1;Scs_dr =1; //左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); Ccs_dr=1;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); Ccs_dr=1;Mcs_dr =1;Scs_dr =0; //右6464屏,正显,24X24的汉字,“鸿哥V5” hz_display2424(0, 0,hz2424_hong,0); hz_display2424(24, 0,hz2424_ge,0); ***_display816(48, 0,***816_v,0); ***_display816(56, 0,***816_5,0); while(1) { CLRWDT(); //喂看门狗,大家不用过度关注此行 } } //------------------时序延时子程序----------------------------- void delay(unsigned int t) { unsigned int i,j; for(i=0;i for(j=0;j<10;j++)CLRWDT(); } //------------------忙闲检查,驱动液晶程序的一部分------------------------------ 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; Ccs_dr=0;Mcs_dr=1;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); //都显示空内容,达到清空的目的 } } Ccs_dr=1;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); //都显示空内容,达到清空的目的 } } Ccs_dr=1;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); //都显示空内容,达到清空的目的 } } } //显示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;Ccs_dr=0; Mcs_dr=1; //左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); Ccs_dr=1; 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); Ccs_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); } (6)小结: 读者要重点弄清楚void screen_clear ,void ***_display816, void hz_display1616, void hz_display2424这四个函数他们之间的联系与规律,即可掌握本节内容的精髓。 根据第一大类定律的原理,在第十一节12864程序的基础上略做修改,就可改成本节19264屏的驱动程序,非常方便。 (未完待续,下节更精彩,不要走开哦) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
只有小组成员才能发言,加入小组>>
求解外围电路实现的是4脚给持续低电平复位并正常工作,高电平不工作的原因
2076 浏览 1 评论
3486 浏览 3 评论
PIC1946程序有一个变量在运行过程中恢复初始值其他变量保持不变
2329 浏览 2 评论
2754 浏览 0 评论
PIC16F1825的RC5引脚,在主程序中操作无效,在中断中可以改变是为什么?
4012 浏览 5 评论
960浏览 0评论
用XC8编译PIC18F25K80时提示下面Error,求怎么解决这个问题
6345浏览 0评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-11-23 20:57 , Processed in 0.840938 second(s), Total 89, Slave 80 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号