Mz_MenuGUI的菜单系统当中,在Menu_Resource.c文件里完成了所有的菜单字符资源以及菜单项的定义,主要定义有三类如下,可以从源码中看出:


代码前面的地方,定义了单条菜单项的内容,代码的注释里已有格式的简单说明了,用户需要参考前介绍通用LCD驱动程序当中关于字库的介绍,因为这里的定义是与驱动函数中字库的定义相关的。比如,菜单项Menu_String01的定义里,采用的是西文字库(ASCII码)与自定义汉字库相结合的类型,第一个量的意义为该菜单项中有几个字符,第二个量之后的数据为字符在字库中的序号,如果字符是ASCII码西文字符的话,可以直接用’A’之类的定义即可,编译时会取该字符的ASCII码值,如果是自定义汉字库的话则要在其序号的基础之上加上0x80(即128),上面的代码中就是例子。(在下面,会介绍这里的菜单GUI代码所应用的LCD驱动程序中的字库定义情况)
而菜单项要定义多少个就由用户自行选择了,在上面的代码当中,共定义了6个菜单项,跟显示效果里的一样,分别是:
“菜单选项一”、“菜单选项二”、“菜单选项三”、“菜单选项四”、“菜单选项五”、“菜单选项六”。
菜单中使用的汉字字符在驱动里都有了定义,它们在字库里的序号分别是:
“菜”——0
“单”——1
“选”——2
“项”——3
“一”——4
“二”——5
“三”——6
“四”——7
“五”——8
“六”——9
在GB_Table.c文件当中定义了它们的字库,如下:

定义好字库后同时也要在LCD_Dis.c当中为新的字库修改一定的代码,在该文件的前面加上对这个字库的外部声明,如下:

然后在FontSet函数中加上该字库所对应的配置代码,如下:

接下来定义了一个数组,用于一组菜单的配置,也就是说可以在程序里面定义多组菜单,然后每一组菜单都可以拥有自己的特性配置。下面看看这个配置的数组里具体定义的含意。
Menu_List01_Config[]={6,3,1,16,10}其中定义了5个数据,第一个数据为声明该组菜单当中共有多少个菜单项;比如在前面的介绍中得知,这个菜单Demo里面共定义了六个菜单项,所以在这里将该数值设置为6;接下来的一个数据代表混合字符菜单项里的汉字字库序号,能过前面介绍的代码可知,定义为3;接着便是ASCII码西文字库的序号,为1。
而其中的数据16表示该组菜单当中每一个菜单项所占用的Y轴的点数,通常取该组菜单当中的字符里Y轴方向最大的值;这里可知前面定义的菜单项里面采用的是16*16点阵的字符,所以该值定义为了16。最后的一个数据表示菜单项显示时,对X轴原点的固定偏移数;可以从下图中看出在数组中定义的10的意义:

如上图所示意的,如果想要菜单项的字符从X轴为0的地方开始显示的话,只需要将菜单配置数组中的最后一个数设为0即可。
最后一个定义是对一组菜单的定义了,用户在菜单响应的控制程序里会使用到该数组,一组菜单定义对应一个菜单界面,如果有多级菜单的话,可以定义多组菜单,格式参考代码里的定义即可。这组数据当中,第一个量为该组菜单的配置,接下去的数据为该组菜单当中的每个菜单项的定义。
注意:可能不同的编译器对指针的编译会有所不同,包括常量的定义,这点请用户注意,要移植该代码时可能要作出适当的修改。
4.2.2. Menu_GUI_Config.h菜单GUI配置头文件
菜单GUI的配置头文件里面定义了菜单的字符色等,下面是源码:

最前面用了两个typedef对两个数据类型进行了重定义;然后用define定义了两种颜色(该菜单GUI代码是可以适应彩色的LCD的,所以这里的定义为将来的扩展打基础),再跟着下来是重定义了几个常量,它们在代码中都有注释,这些颜色的重定义将会在Menu_GUI.c的代码当中使用。
最后的一个定义在代码当中屏蔽掉了,它是针对于带字库的LCD模块而作的扩展定义。
4.2.3. Menu_GUI.c菜单接口函数
先看看两个跟配置有关的函数:

如注释所述,UCHAR GetMLNum(UCHAR ** Menu_List)函数是获取一组菜单资源里面的菜单项个数的,传递进该函数的参数就是上一小节当中所说的一组菜单的定义数组,返回的是该组菜单资源当中的菜单项个数;不过要注意的是跟前面介绍的定义有点不一样,在前面介绍时,如果菜单项个数为6个的话,在定义里面就直接将菜单资源配置数组里的第一个量置为6了,这里的返回值也是读取了该数据,但减去了1,因为菜单项的序号从0开始。
UCHAR GetMLiNum_Page(UCHAR** Menu_List)函数就与LCD驱动程序当中的配置有关了,它的功能是获取一屏LCD显示当中共能显示菜单项数量,代码中使用了Dis_Y_MAX的配置,并除以菜单项的Y轴点数,得到的是一屏LCD显示能容下的菜单项数。
在菜单显示的代码里面,定义了几个全局的变量,用于更新显示时使用,如下:

Y_WIDTH_MENU存放有当前菜单组的一个菜单项占用Y轴点数,与菜单组配置数组当中的定义是一样的意义;只不过可能有多个菜单组资源存在于用户的应用程序当中,而这个变量保存的是当前选择的,也就是当前显示在LCD当中的菜单组的。
X_SPACE_FRONT存放着当前菜单组在显示菜单项时,菜单项字符偏移X轴原点的点数。
Dis_Menu_Num则保存着当前的菜单组能在一屏LCD当中显示的个数。
Font_GB中为当前的菜单组当中选用的自定义汉字库序号,Font_String则是ASCII码西文字库的序号。
First_Index_old变量保存的是菜单当前显示在LCD屏上的首行菜单项的序号;由于菜单组可能有多个菜单项,而菜单项个数大于一屏能显示的菜单项个数时,首行的菜单项就有可能会在用户操作菜单时发生变化;此变量将在菜单的显示刷新当中使用。
y_Index_old则保存当前菜单项当中,处于活动的那项序号,也就是选择了哪一项菜单。
First_Index_old和y_Inded_old变量初始化它们的值为0xff,而在代码中也利用了该数值(0xff)在下面的代码当中会有介绍。
接下来,有两个控制函数,功能差不多,只是有细微的区别,在用户编写菜单响应控制函数时有用:

Redraw_Menu和Initial_Menu函数的功能相似,可以从代码中看出来,在函数里面都要对前面介绍的全局变量进行设置,并调用UpData_Menu函数进行重绘菜单。
而在Initial_Menu函数当中,将First_Index_old和y_Index_old重置为0xff,则是要全部重新的绘制菜单,使当前显示屏中第一列菜单项为该菜单组中的第一项,而当前选择的菜单项初始化为该菜单组中的第一项;函数中传递进来的Menu_List参数为要选用的菜单资源链表(也就是二维数组)。
Redraw_Menu函数有三个参数,分别在注释中已有说明;在该函数里与Inialt_Menu的区别也就是对y_Index_old的初始化,其实也就是Redraw_Menu用于重绘菜单,但菜单组中显示在LCD屏上的第一项菜单项并不一定是菜单组中的首项,而是由Fist_Index指定;而当前处于选定状态的菜单项由参数Menu_Index指定。此函数一般使用在菜单响应后,用户程序进入了另外的显示界面的,当返回时重绘菜单使用。
菜单显示控制函数主要只有两个,无论用户定义了多少组菜单资源,都是这两个函数来完成显示。下面分别看一下它们的代码:
UpDate_Menu菜单更新函数:


菜单更新函数较为复杂,下在分段分析代码。
函数多次调用到的子函数ShowMenu_Item是菜单项绘制函数,将在接下去的代码中介绍,该函数有两个参数,一个是要绘制的菜单项,一个是菜单项的颜色。
①:
判断传递进来的参数Fisrt_Index是否超出了选用的菜单组的菜单项个数,如果超出了则处理一下;接着对List_Num进行处理,该变量的意义为一屏LCD能显示菜单项的个数,如果当前选用的菜单组的菜单项个数大于一屏LCD能显示的菜单个数,则取后者的值;如果等于或小于,则取当前菜单组的菜单项个数。
②:
判断First_Index_old是否等于传递进来的参数First_Index,如果不相等则表示当前LCD屏显示当中的第一列菜单项发生较之前发生了变化,需要重绘整个菜单。
要重绘全部菜单时,首先要将屏幕全部清屏。
③:
此处判断y_Index_old是否为0xff,如果是0xff,则认定为初始化菜单时的重绘菜单,程序会将当前屏的LCD显示的第一列菜单项选择为菜单组中的第一项菜单项,而处于选择状态的菜单项为菜单组资源当中的第一项菜单项。
重绘菜单时,先绘制处于选择状态的反色条,与前面图样里屏幕上与背景的白色反色的黑色条一样,当然对于彩色的LCD是可以选择很多的不同颜色的。然后在同样的位置上绘制出该条处于选择状态的菜单项,也就是菜单组资源中的第一项菜单项了。随后绘制其它的不处于选择状态的菜单项。
④:
跟随着前面的判断,如果y_Index_old不为0xff的话,则是正常的重绘菜单,也就是通过Redraw_Menu函数调用进来的,或者是LCD屏上首行显示的菜单项发生了变化。
⑤:
这时,也是首先绘制处于选择状态的反色条,它的位置由Menu_Index-First_Index计算出来,Menu_Index是传递进来的参数,代表当前菜单当中处于选择状态的菜单项是菜单组资源当中的第几项,而First_Index则是当前菜单项当中处于LCD屏显示的首行菜单项是菜单组资源当中的第几项,两者相减便得出当前处于选择状态的菜单项应该在LCD屏当中的具体位置了;这里使用y_Index_old变量暂时保存一下位置数据。
然后,根据计算出来的当前处于选择状态的菜单项的位置绘制反色条,最后依次绘制一屏LCD可以显示的多个菜单项。
⑥:
当然在绘制菜单项时会判断是否为处于选择状态的,如是则选用配置的选择菜单项字符色来绘制。
⑦:
否则就使用正常的菜单项字符色绘制菜单项字符。
⑧:
这里将更新菜单前的LCD屏显示首行菜单项序号更新,以便在下次调用显示时使用。
⑨:
最后,如果不必要对全屏进行菜单刷新的话,就会进入该分支进行对菜单的刷新了,也就是y_Index_old和First_Index_old都没有被初始化为0xff,而且First_Index_old与传递进来的参数First_Index是相同的,也即说明LCD屏上的首行菜单项没有发生变化。
然后,计算出当前LCD屏上显示的处于选择状态的菜单项位置,将该位置的显示清零,重绘上该位置的菜单项字符;再计算刷新后处于选择状态的菜单项的位置,在该位置绘制反色条,最后再绘制处于选择状态的菜单项。
ShowMenu_Item菜单项绘制函数前面介绍的LCD驱动程序当中的字库定义以及菜单项的定义有很紧密的关系,下面是它的代码:


局部变量x首先会置为X_SPACE_FRONT的值,而X_SPACE_FRONT也就是在菜单组资源当中定义的配置数组中的菜单项字符偏移X轴原点的点数。
获取要绘制的菜单项的第一个节字,存放于Char_Nmb变量当中。
这里作一下简单的说明,在国标的二级汉字库中,每个汉字的GB码值的高八位和低八位值都是大于0xA1的;所以在此会判断Char_Nmb值是否大于0xA1,如小于则表明当前的菜单GUI使用的是自定义汉字库和LCD驱动中自带的ASCII码西文字库的字符。
绘制菜单项当中的自定义汉字库或者ASCII码字符时,Char_Nmb的值代表该菜单项的字符个数,并以该数值作一个for循环,依次将该菜单项的字符绘制完毕。在前面已介绍过,在定义菜单项时,如使用自定义的汉字库字符,则在定义时在该字符的基础上加上128;所以在代码当中可看到从菜单项的定义数组中读出字符序号数据后会判断是否大于128,如果大于则表示该字符为自定义的汉字库当中的字符,调用FontSet函数选择该字库作为当前字符类型,并调整字符序号值;如小于则选用LCD驱运中的ASCII码西文字库作为当前字符类型。最后调用PutChar函数绘制字符,并对X轴的坐标作出调整,以便显示下一个字符。
在前面介绍Menu_GUI_Config.h文件时,已经介绍过Hz_Lib_II的宏定义表示所使用的LCD是否为自带汉字库的LCD,如果是的话,则Hz_Lib_II有定义,则“#def Hz_Lib_II”和“#endif”之间的代码将会被编译,作为选用自带汉字库的LCD时显示菜单中的汉字的显控代码。
注意:Mzdesign的自带汉字库的LCD在驱动程序里提供有FontSet_cn和PutString_cn这两个函数,而本书所介绍的LCD通用驱动程序当中是没有这两个函数的,这点请读者参考Mzdesign的代码。


