第二十节:按键操作液晶显示屏菜单的基本程序框架(1)开场白:大部分带液晶显示屏的机器,都要求可以跟按键进行配合以便达到可以设置机器内部参数的目的,菜鸟们可能会觉得很复杂,可是鸿哥觉得那都是小菜一碟,因为鸿哥在做那么多项目中早已经发现了它们的规律,现在提取出来跟大家分享。大家不要感激我那么晚了还熬夜写技术贴,要感谢就感谢电子发烧友论坛的管理员zhihuizhou,他花了那么多时间帮我把这个帖子整理出一套目录链接来,我今天看到后非常感动,再苦再忙也不能有负于他。在编写液晶显示程序中,有两个最核心的要素。它们分别是窗口变量与更新变量。(a)窗口变量:在编写整个程序中,必须要有一条主线,然后按键等所有操作都围绕着这条主线展开,这样无论程序有多复杂也不会乱。这条主线就是窗口(变量名为wd_sec),在窗口的主线下,再分出一个局部窗口(变量名为part_sec)。(b)更新变量:液晶屏是属于静态控制的屏,因为涉及到整屏更新(wd_update)和局部更新(part_X_Y_update)。把不用经常更新显示的内容放到整屏更新里,把要经常更新显示的内容放到局部更新里。大家只要仔细阅读本程序,领悟到了这条窗口变量与更新变量的精髓,以后做再复杂的人机对话界面都难不倒你。(2)功能需求:结合第二节的独立按键与第十一节的12864显示屏驱动程序,要求可以显示两个窗口,每一屏液晶显示窗口可以显示4行8X16字符的内容,每一行显示一个数字,按“上”“下”键时,每一行中的数字可以上下依次负显,相当于光标上下移动,按“加”“减”键时,被选择中的某一行中带负显的数字会依次增加或减小(最大范围是0至99)。当负显光标停留在第一屏第4行的数字时,如果继续按“下”键,则负显光标会切换到第二屏的第1行数字。负显光标停留在第二屏的第4行的数字时,继续按“下”键,则又会切换返回到第一屏的第1行数字。(3)硬件原理:请参考第二节的独立按键与第十一节的12864显示屏的硬件电路。(4)源码适合的单片机:PIC18F4520,晶振为11.0592MHz。(5)源代码讲解如下:请下载附件源代码讲解。(6)小结:曾经有很多老板咨询我,单色液晶屏品种那么多,什么时候该选段码屏,什么时候该选COB的点阵液晶模块,什么时候应该选COG的点阵液晶模块.鸿哥的建议是:(a)段码屏的优点:单价最便宜,显示效果最漂亮。段码屏的缺点:因为是定制的,非标准的,前期要一两千的开模费用。因为不是点阵的,不能在一个位置任意显示一个字体,仅适用于界面要求不高的场合。(b)COB点阵液晶模块的优点:可以显示字符或汉字,标准化,普及化,小批量采购不用愁,不用收开模费,厂家众多,而且还相互兼容。COB点阵液晶模块的缺点:单价高,体积大。(C)COG点阵液晶模块的优点:可以显示字符或汉字,单价比COB液晶模块便宜,体积小。COG点阵液晶模块的缺点:也是属于非标准屏,开模费最高。有些不用开模费的屏,是因为某个厂家给他的大客户已经开模做了,顺便给你做的时候当然不用收开模费,但是,如果你的产品量不大,就意味着你以后有可能会缺货,而且这种屏不是标准屏,这家公司有的品种,其他家的公司往往没有相同的,或者能够兼容的。(7)下集预告:从下一节开始进入一个新的领域,我会花很多章节来讲常用的几种数码管驱动方式以及基本编程框架。(未完待续,下节更精彩,不要走开哦)#includepic18.h//包含芯片相关头文件//补充说明:吴坚鸿程序风格是这样的,凡是输出IO后缀都是_dr,凡是输入的//IO后缀都//是_sr#definebeep_drLATA1//蜂鸣器输出#definekey_sr1RC1//独立按键输入#definekey_sr2RC2//独立按键输入#definekey_sr3RC3//独立按键输入#definekey_sr4RC4//独立按键输入#defineLcd_busPORTB//液晶的数据总线#defineBf_srRB7//芯片忙检查#defineMcs_drLATD6//左右6464屏的片选#defineScs_drLATD7//左右6464屏的片选#defineEnable_drLATD5//类似时钟的数据线#defineDi_drRC7//数据与指令的选择线#defineRW_drRD4//读与写的选择线//补充说明:吴坚鸿程序风格是这样的,凡是做延时计数阀值的常量//前缀都用cnt_表示。凡是延时常量,都应该根据上机实际情况来调整出最佳的数值#definecnt_delay_cnt125//按键去抖动延时阀值#definecnt_voice_time60//蜂鸣器响的声音长短的延时阀值#defineDisp_On0x3f//液晶模块的寄存器初始化的参数,抄网上的,我也从来没有#defineDisp_Off0x3e//研究过它是什么含义,因为根本没必要知道,直接用就行#defineCol_Add0x40#definePage_Add0xb8#defineStart_Line0xc0voiddelay(unsignedintt);//时序延时函数声明voidchk_busy();//忙检测,液晶驱动时序的一部分voidwrite_com(unsignedcharcmdcode);//往液晶模块写入指令voidwrite_data(unsignedcharDi_drspdata);//往液晶模块写入数据voidinit_lcd();voidscreen_clear();//清空屏的内容voidzf_display816(unsignedcharcol,unsignedcharpag,constunsignedchar*zk,unsignedcharopposite_flag);//显示8X16的字符驱动函数voidhz_display1616(unsignedcharcol,unsignedcharpag,constunsignedchar*zk,unsignedcharopposite_flag);//显示16X16的汉字函数voidnumber_display(unsignedcharcol,unsignedcharpag,unsignedcharzf_sec,unsignedcharopposite_flag);//在某个位置直接显示数字voiddisplay();//总显示函数,本节最核心的内容voidwd1();//显示窗口1函数,本节最核心的内容voidwd2();//显示窗口2函数,本节最核心的内容voidconvert_fuxian();//转换到合适的相关负显标志位//补充说明:吴坚鸿程序风格是这样的,凡是按键扫描函数都放在定时中//断里(当然也可以放在main函数循环里),凡是按键服务程序都是放在main函数循环里。有人说不应该把子程序//放在中断里,别听他们,信鸿哥无坎坷。voidkey_scan();//按键扫描函数,放在定时中断里voidkey_service();//按键服务函数,放在main函数循环里//补充说明:吴坚鸿程序风格是这样的,凡是switch()语句括号里面的变量名//后缀都用_step表示。unsignedcharkey_step=1;//按键扫描步骤变量,在switch()语句的括号里//补充说明:吴坚鸿程序风格是这样的,凡是按键或者感应输入的自锁变量名//后缀都用_lock表示。unsignedcharkey_lock1=0;//按键自锁标志unsignedcharkey_lock2=0;//按键自锁标志unsignedcharkey_lock3=0;//按键自锁标志unsignedcharkey_lock4=0;//按键自锁标志//补充说明:吴坚鸿程序风格是这样的,凡是计数器延时的变量//后缀都用_cnt表示。unsignedintdelay_cnt1=0;//延时计数器的变量unsignedintdelay_cnt2=0;//延时计数器的变量unsignedintdelay_cnt3=0;//延时计数器的变量unsignedintdelay_cnt4=0;//延时计数器的变量unsignedintvoice_time_cnt;//蜂鸣器响的声音长短的计数延时//补充说明:吴坚鸿程序风格是这样的,凡是做类型的变量的分类//后缀都用_sec表示。unsignedcharkey_sec=0;//触发的某号键unsignedcharwd_sec=1;//代表当前显示的是哪个窗口,本节最核心的变量unsignedcharpart_sec=1;//代表当前窗口中显示哪个局部区域,本节最核心的变量unsignedcharwd_update=1;//窗口整屏更新显示标志,本节最核心的变量unsignedcharpart_1_1_update=1;//局部窗口更新显示标志,本节最核心的变量.part_X_Y_update,其中X表示第几屏,Y表示第几unsignedcharpart_1_2_update=1;//局部窗口更新显示标志,本节最核心的变量.part_X_Y_update,其中X表示第几屏,Y表示第几unsignedcharpart_1_3_update=1;//局部窗口更新显示标志,本节最核心的变量.part_X_Y_update,其中X表示第几屏,Y表示第几unsignedcharpart_1_4_update=1;//局部窗口更新显示标志,本节最核心的变量.part_X_Y_update,其中X表示第几屏,Y表示第几unsignedcharpart_2_1_update=1;//局部窗口更新显示标志,本节最核心的变量.part_X_Y_update,其中X表示第几屏,Y表示第几unsignedcharpart_2_2_update=1;//局部窗口更新显示标志,本节最核心的变量.part_X_Y_update,其中X表示第几屏,Y表示第几unsignedcharpart_2_3_update=1;//局部窗口更新显示标志,本节最核心的变量.part_X_Y_update,其中X表示第几屏,Y表示第几unsignedcharpart_2_4_update=1;//局部窗口更新显示标志,本节最核心的变量.part_X_Y_update,其中X表示第几屏,Y表示第几unsignedcharnumber1=1;//第一屏第1行的显示内容变量,本节最核心的变量unsignedcharnumber2=1;//第一屏第2行的显示内容变量,本节最核心的变量unsignedcharnumber3=1;//第一屏第3行的显示内容变量,本节最核心的变量unsignedcharnumber4=1;//第一屏第4行的显示内容变量,本节最核心的变量unsignedcharnumber5=1;//第二屏第1行的显示内容变量,本节最核心的变量unsignedcharnumber6=1;//第二屏第2行的显示内容变量,本节最核心的变量unsignedcharnumber7=1;//第二屏第3行的显示内容变量,本节最核心的变量unsignedcharnumber8=1;//第二屏第4行的显示内容变量,本节最核心的变量unsignedcharnumber1_flag=0;//是否负显的标志unsignedcharnumber2_flag=0;//是否负显的标志unsignedcharnumber3_flag=0;//是否负显的标志unsignedcharnumber4_flag=0;//是否负显的标志unsignedcharnumber5_flag=0;//是否负显的标志unsignedcharnumber6_flag=0;//是否负显的标志unsignedcharnumber7_flag=0;//是否负显的标志unsignedcharnumber8_flag=0;//是否负显的标志unsignedcharnumber_temp10;//临时中间变量,用来分解十位显示un