第七十八节:在多个窗口里通过移动光标来设置不同参数的液晶屏菜单程序。
	
开场白:
	上一节讲了1个窗口下如何设置参数的菜单程序,这一节多增加1个窗口变成2个窗口,看看它们两个窗口之间是如何通过按键程序进行切换的。继续巩固上一节教给大家的两个知识点:
   第一个知识点:我在前面讲数码管显示的时候就提出了一个 “一二级菜单显示理论”:凡是人机界面显示,不管是数码管还是液晶屏,都可以把显示的内容分成不同的窗口来显示,每个显示的窗口中又可以分成不同的局部显示。其中窗口就是一级菜单,用ucWd变量表示。局部就是二级菜单,用ucPart来表示。不同的窗口,会有不同的更新显示变量ucWdXUpdate来对应,表示整屏全部更新显示。不同的局部,也会有不同的更新显示变量ucWdXPartYUpdate来对应,表示局部更新显示。把每一个窗口的内容分为两种类型,一种类型是那些不用经常刷新显示的内容,只有在切换窗口的时候才需要更新的,这种内容放在整屏更新显示的括号里,比如清屏操作等内容。另外一种是那些经常需要刷新显示的内容,这种内容放在局部更新显示的括号里。
    第二个知识点:按键如何跟液晶屏显示有机的结合起来?只要遵循鸿哥总结出来的一个规律“在不同的窗口下,根据不同的局部变量来操作不同的参数”,这样再复杂的人机交互程序都会显得很简单清晰。
具体内容,请看源代码讲解。
(1)硬件平台:基于朱兆祺51单片机学习板。加按键对应S1键,减按键对应S5键,切换“光标”移动按键对应S9键,设置参数按键对应S13键。
(2)实现功能:
     通过按键设置8个不同的参数。
    有2个窗口。每个窗口显示4个参数。每个参数的范围是从0到99。
    有4个按键:
(a) 一个是设置参数S13按键,按下此按键,液晶屏的第1个窗口第一行会出现反显的光标,表示进入设置参数模式,再次按下此按键,反显光标会消失,并且强行切换到第1个窗口,表示退出设置参数模式。
(b) 一个是移动光标S9按键,在进入设置参数的模式下,依次按下此按键,液晶屏上的光标会从上往下移动,表示选中不同的参数。当移动到每个窗口最下边那一行时,再按下此按键会进行切换窗口的操作。
(c) 一个是减数S5按键,在设置参数模式下,依次按下此按键,被选中的参数会逐渐减小。
(d) 一个是加数S1按键,在设置参数模式下,依次按下此按键,被选中的参数会逐渐加大。
(3)源代码讲解如下:
#include "REG52.H"
/* 注释一:
 * 本程序用到的变量比较多,所以在keil编译模式里要设置一下编译模式memory model,
 * 否则编译会出错.右键单击Target选择“Options for Target'Target1'”就会出来一个框
 * 在memory model中选择compact:variables in pdata 就可以了。
 */
#define const_voice_short  40   //蜂鸣器短叫的持续时间
#define const_key_time1  20    //按键去抖动延时的时间
#define const_key_time2  20    //按键去抖动延时的时间
#define const_key_time3  20    //按键去抖动延时的时间
#define const_key_time4  20    //按键去抖动延时的时间
sbit key_sr1=P0^0; //对应朱兆祺学习板的S1键
sbit key_sr2=P0^1; //对应朱兆祺学习板的S5键
sbit key_sr3=P0^2; //对应朱兆祺学习板的S9键
sbit key_sr4=P0^3; //对应朱兆祺学习板的S13键
sbit key_gnd_dr=P0^4; //模拟独立按键的地GND,因此必须一直输出低电平
sbit beep_dr=P2^7; //蜂鸣器的驱动IO口
sbit  LCDCS_dr  = P1^6;  //片选线
sbit  LCDSID_dr = P1^7;  //串行数据线
sbit  LCDCLK_dr = P3^2;  //串行时钟线
sbit  LCDRST_dr = P3^4;  //复位线
void SendByteToLcd(unsigned char ucData);  //发送一个字节数据到液晶模块
void SPIWrite(unsigned char ucWData, unsigned char ucWRS); //模拟SPI发送一个字节的命令或者数据给液晶模块的底层驱动
void WriteCommand(unsigned char ucCommand); //发送一个字节的命令给液晶模块
void LCDWriteData(unsigned char ucData);   //发送一个字节的数据给液晶模块
void LCDInit(void);  //初始化  函数内部包括液晶模块的复位
void display_clear(unsigned char ucFillDate); // 清屏 全部显示空填充0x00   全部显示点阵用0xff
void insert_buffer_to_canvas(unsigned int x,unsigned int y,const unsigned char  *ucArray,unsigned char ucFbFlag,unsigned int x_amount,unsigned int y_amount);//把字模插入画布.
void display_lattice(unsigned int x,unsigned int y,const unsigned char  *ucArray,unsigned char ucFbFlag,unsigned int x_amount,unsigned int y_amount,unsigned int uiOffSetAddr); //显示任意点阵函数
unsigned char *number_to_matrix(unsigned char  ucBitNumber); //把一位数字转换成字模首地址的函数
void delay_short(unsigned int uiDelayshort); //延时
void delay_long(unsigned int uiDelayLong);
void T0_time(); //定时中断函数
void key_service(void); //按键服务的应用程序
void key_scan(void);//按键扫描函数 放在定时中断里
void initial_myself();    
void initial_peripheral();
void lcd_display_service(void); //应用层面的液晶屏显示程序
void clear_all_canvas(void);  //把画布全部清零
void wd1(void);//窗口1显示的内容
void wd2(void);//窗口2显示的内容
code unsigned char Zf816_0[]= 
{
/*--  文字:  0  --*/
/*--  宋体12;  此字体下对应的点阵为:宽x高=8x16   --*/
0x00,0x00,0x00,0x18,0x24,0x42,0x42,0x42,0x42,0x42,0x42,0x42,0x24,0x18,0x00,0x00,
};
code unsigned char Zf816_1[]= 
{
/*--  文字:  1  --*/
/*--  宋体12;  此字体下对应的点阵为:宽x高=8x16   --*/
0x00,0x00,0x00,0x10,0x70,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x7C,0x00,0x00,
};
code unsigned char Zf816_2[]= 
{
/*--  文字:  2  --*/
/*--  宋体12;  此字体下对应的点阵为:宽x高=8x16   --*/
0x00,0x00,0x00,0x3C,0x42,0x42,0x42,0x04,0x04,0x08,0x10,0x20,0x42,0x7E,0x00,0x00,
};
code unsigned char Zf816_3[]= 
{
/*--  文字:  3  --*/
/*--  宋体12;  此字体下对应的点阵为:宽x高=8x16   --*/
0x00,0x00,0x00,0x3C,0x42,0x42,0x04,0x18,0x04,0x02,0x02,0x42,0x44,0x38,0x00,0x00,
};
code unsigned char Zf816_4[]= 
{
/*--  文字:  4  --*/
/*--  宋体12;  此字体下对应的点阵为:宽x高=8x16   --*/
0x00,0x00,0x00,0x04,0x0C,0x14,0x24,0x24,0x44,0x44,0x7E,0x04,0x04,0x1E,0x00,0x00,
};
code unsigned char Zf816_5[]= 
{
/*--  文字:  5  --*/
/*--  宋体12;  此字体下对应的点阵为:宽x高=8x16   --*/
0x00,0x00,0x00,0x7E,0x40,0x40,0x40,0x58,0x64,0x02,0x02,0x42,0x44,0x38,0x00,0x00,
};
code unsigned char Zf816_6[]= 
{
/*--  文字:  6  --*/
/*--  宋体12;  此字体下对应的点阵为:宽x高=8x16   --*/
0x00,0x00,0x00,0x1C,0x24,0x40,0x40,0x58,0x64,0x42,0x42,0x42,0x24,0x18,0x00,0x00,
};
code unsigned char Zf816_7[]= 
{
/*--  文字:  7  --*/
/*--  宋体12;  此字体下对应的点阵为:宽x高=8x16   --*/
0x00,0x00,0x00,0x7E,0x44,0x44,0x08,0x08,0x10,0x10,0x10,0x10,0x10,0x10,0x00,0x00,
};
code unsigned char Zf816_8[]= 
{
/*--  文字:  8  --*/
/*--  宋体12;  此字体下对应的点阵为:宽x高=8x16   --*/
0x00,0x00,0x00,0x3C,0x42,0x42,0x42,0x24,0x18,0x24,0x42,0x42,0x42,0x3C,0x00,0x00,
};
code unsigned char Zf816_9[]= 
{
/*--  文字:  9  --*/
/*--  宋体12;  此字体下对应的点阵为:宽x高=8x16   --*/
0x00,0x00,0x00,0x18,0x24,0x42,0x42,0x42,0x26,0x1A,0x02,0x02,0x24,0x38,0x00,0x00,
};
code unsigned char Zf816_nc[]=  //空字模
{
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
};
code unsigned char Zf816_mao_hao[]=  //冒号
{
/*--  文字:  :  --*/
/*--  宋体12;  此字体下对应的点阵为:宽x高=8x16   --*/
0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x00,0x00,0x18,0x18,0x00,0x00,
};
code unsigned char Hz1616_yi[]= 
{
/*--  文字:  一  --*/
/*--  宋体12;  此字体下对应的点阵为:宽x高=16x16   --*/
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x7F,0xFE,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
};
code unsigned char Hz1616_er[]= 
{
/*--  文字:  二  --*/
/*--  宋体12;  此字体下对应的点阵为:宽x高=16x16   --*/
0x00,0x00,0x00,0x10,0x3F,0xF8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x7F,0xFE,0x00,0x00,0x00,0x00,0x00,0x00,
};
code unsigned char Hz1616_san[]= 
{
/*--  文字:  三  --*/
/*--  宋体12;  此字体下对应的点阵为:宽x高=16x16   --*/
0x00,0x00,0x00,0x00,0x7F,0xFC,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3F,0xF8,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7F,0xFE,0x00,0x00,0x00,0x00,
};
code unsigned char Hz1616_si[]= 
{
/*--  文字:  四  --*/
/*--  宋体12;  此字体下对应的点阵为:宽x高=16x16   --*/
0x00,0x00,0x7F,0xFC,0x44,0x84,0x44,0x84,0x44,0x84,0x44,0x84,0x44,0x84,0x44,0x84,
0x48,0x84,0x48,0x7C,0x50,0x04,0x60,0x04,0x40,0x04,0x7F,0xFC,0x40,0x04,0x00,0x00,
};
code unsigned char Hz1616_chuang[]= 
{
/*--  文字:  窗  --*/
/*--  宋体12;  此字体下对应的点阵为:宽x高=16x16   --*/
0x01,0x00,0x00,0x80,0x7F,0xFE,0x40,0x22,0x09,0x18,0x12,0x06,0x7F,0xF8,0x11,0x08,
0x13,0xE8,0x14,0x48,0x1A,0x88,0x11,0x08,0x12,0x88,0x14,0x08,0x1F,0xF8,0x10,0x08,
};
code unsigned char Hz1616_kou[]= 
{
/*--  文字:  口  --*/
/*--  宋体12;  此字体下对应的点阵为:宽x高=16x16   --*/
0x00,0x00,0x00,0x00,0x3F,0xF8,0x20,0x08,0x20,0x08,0x20,0x08,0x20,0x08,0x20,0x08,
0x20,0x08,0x20,0x08,0x20,0x08,0x3F,0xF8,0x20,0x08,0x20,0x08,0x00,0x00,0x00,0x00,
};
code unsigned char Hz1616_hang[]= 
{
/*--  文字:  行  --*/
/*--  宋体12;  此字体下对应的点阵为:宽x高=16x16   --*/
0x08,0x00,0x1C,0x00,0x31,0xFC,0x40,0x00,0x88,0x00,0x0C,0x00,0x1B,0xFE,0x30,0x20,
0x50,0x20,0x90,0x20,0x10,0x20,0x10,0x20,0x10,0x20,0x10,0x20,0x10,0xA0,0x10,0x40,
};
unsigned char ucCanvasBuffer[]= //画布显示数组。注意,这里没有code关键字,是全局变量。初始化全部填充0x00
{
0x00,0x00,0x00,0x00,  //上半屏
0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00, 
//------------上半屏和下半屏的分割线-----------
0x00,0x00,0x00,0x00,  //下半屏
0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,
};
unsigned char ucKeySec=0;   //被触发的按键编号
unsigned int  uiVoiceCnt=0;  //蜂鸣器鸣叫的持续时间计数器
unsigned char ucWd=1; //窗口变量
unsigned char ucPart=0; //局部变量 0代表没有选中任何一行,其它数值1到4代表选中某一行
unsigned char ucWd1Update=1; //窗口1的整屏更新显示变量      1代表更新显示,响应函数内部会清零
unsigned char ucWd1Part1Update=0; //窗口1的第1行局部更新显示变量  1代表更新显示,响应函数内部会清零
unsigned char ucWd1Part2Update=0; //窗口1的第2行局部更新显示变量  1代表更新显示,响应函数内部会清零
unsigned char ucWd1Part3Update=0; //窗口1的第3行局部更新显示变量  1代表更新显示,响应函数内部会清零
unsigned char ucWd1Part4Update=0; //窗口1的第4行局部更新显示变量  1代表更新显示,响应函数内部会清零
unsigned char ucWd2Update=0; //窗口2的整屏更新显示变量      1代表更新显示,响应函数内部会清零
unsigned char ucWd2Part1Update=0; //窗口2的第1行局部更新显示变量  1代表更新显示,响应函数内部会清零
unsigned char ucWd2Part2Update=0; //窗口2的第2行局部更新显示变量  1代表更新显示,响应函数内部会清零
unsigned char ucWd2Part3Update=0; //窗口2的第3行局部更新显示变量  1代表更新显示,响应函数内部会清零
unsigned char ucWd2Part4Update=0; //窗口2的第4行局部更新显示变量  1代表更新显示,响应函数内部会清零
unsigned char ucData_1_1=8;  //第1个窗口第1行的被设置数据
unsigned char ucData_1_2=9;  //第1个窗口第2行的被设置数据
unsigned char ucData_1_3=10;  //第1个窗口第3行的被设置数据
unsigned char ucData_1_4=11;  //第1个窗口第4行的被设置数据
unsigned char ucData_2_1=12;  //第2个窗口第1行的被设置数据
unsigned char ucData_2_2=13;  //第2个窗口第2行的被设置数据
unsigned char ucData_2_3=14;  //第2个窗口第3行的被设置数据
unsigned char ucData_2_4=15;  //第2个窗口第4行的被设置数据
void main() 
  {
        initial_myself();      //第一区,上电后马上初始化
        delay_long(100);       //一线,延时线。延时一段时间
        initial_peripheral();  //第二区,上电后延时一段时间再初始化
        while(1)   //第三区
        { 
                    key_service(); //按键服务的应用程序
            lcd_display_service(); //应用层面的液晶屏显示程序
        }
}
void initial_myself()  //第一区 上电后马上初始化
{
/* 注释二:
* 矩阵键盘也可以做独立按键,前提是把某一根公共输出线输出低电平,
* 模拟独立按键的触发地,本程序中,把key_gnd_dr输出低电平。
* 朱兆祺51学习板的S1和S5两个按键就是本程序中用到的两个独立按键。
*/
   key_gnd_dr=0; //模拟独立按键的地GND,因此必须一直输出低电平
   beep_dr=1; //用PNP三极管控制蜂鸣器,输出高电平时不叫。
   TMOD=0x01;  //设置定时器0为工作方式1
   TH0=0xf8;   //重装初始值(65535-2000)=63535=0xf82f
   TL0=0x2f;
}
void initial_peripheral() //第二区 上电后延时一段时间再初始化
{
    LCDInit(); //初始化12864 内部包含液晶模块的复位
    EA=1;     //开总中断
    ET0=1;    //允许定时中断
    TR0=1;    //启动定时中断
}
void T0_time() interrupt 1
{
  TF0=0;  //清除中断标志
  TR0=0; //关中断
  key_scan(); //按键扫描函数
  if(uiVoiceCnt!=0)
  {
     uiVoiceCnt--; //每次进入定时中断都自减1,直到等于零为止。才停止鸣叫
         beep_dr=0;  //蜂鸣器是PNP三极管控制,低电平就开始鸣叫。
  }
  else
  {
     ; //此处多加一个空指令,想维持跟if括号语句的数量对称,都是两条指令。不加也可以。
           beep_dr=1;  //蜂鸣器是PNP三极管控制,高电平就停止鸣叫。
  }
  TH0=0xf8;   //重装初始值(65535-2000)=63535=0xf82f
  TL0=0x2f;
  TR0=1;  //开中断
}
void key_scan(void)//按键扫描函数 放在定时中断里
{  
  static unsigned int  uiKeyTimeCnt1=0; //按键去抖动延时计数器
  static unsigned char ucKeyLock1=0; //按键触发后自锁的变量标志
  static unsigned int  uiKeyTimeCnt2=0; //按键去抖动延时计数器
  static unsigned char ucKeyLock2=0; //按键触发后自锁的变量标志
  static unsigned int  uiKeyTimeCnt3=0; //按键去抖动延时计数器
  static unsigned char ucKeyLock3=0; //按键触发后自锁的变量标志
  static unsigned int  uiKeyTimeCnt4=0; //按键去抖动延时计数器
  static unsigned char ucKeyLock4=0; //按键触发后自锁的变量标志
  if(key_sr1==1)//IO是高电平,说明按键没有被按下,这时要及时清零一些标志位
  {
     ucKeyLock1=0; //按键自锁标志清零
     uiKeyTimeCnt1=0;//按键去抖动延时计数器清零,此行非常巧妙,是我实战中摸索出来的。      
  }
  else if(ucKeyLock1==0)//有按键按下,且是第一次被按下
  {
     uiKeyTimeCnt1++; //累加定时中断次数
     if(uiKeyTimeCnt1>const_key_time1)
     {
        uiKeyTimeCnt1=0; 
        ucKeyLock1=1;  //自锁按键置位,避免一直触发
        ucKeySec=1;    //触发1号键
     }
  }
  if(key_sr2==1)//IO是高电平,说明按键没有被按下,这时要及时清零一些标志位
  {
     ucKeyLock2=0; //按键自锁标志清零
     uiKeyTimeCnt2=0;//按键去抖动延时计数器清零,此行非常巧妙,是我实战中摸索出来的。      
  }
  else if(ucKeyLock2==0)//有按键按下,且是第一次被按下
  {
     uiKeyTimeCnt2++; //累加定时中断次数
     if(uiKeyTimeCnt2>const_key_time2)
     {
        uiKeyTimeCnt2=0; 
        ucKeyLock2=1;  //自锁按键置位,避免一直触发
        ucKeySec=2;    //触发2号键
     }
  }
  if(key_sr3==1)//IO是高电平,说明按键没有被按下,这时要及时清零一些标志位
  {
     ucKeyLock3=0; //按键自锁标志清零
     uiKeyTimeCnt3=0;//按键去抖动延时计数器清零,此行非常巧妙,是我实战中摸索出来的。      
  }
  else if(ucKeyLock3==0)//有按键按下,且是第一次被按下
  {
     uiKeyTimeCnt3++; //累加定时中断次数
     if(uiKeyTimeCnt3>const_key_time3)
     {
        uiKeyTimeCnt3=0; 
        ucKeyLock3=1;  //自锁按键置位,避免一直触发
        ucKeySec=3;    //触发3号键
     }
  }
  if(key_sr4==1)//IO是高电平,说明按键没有被按下,这时要及时清零一些标志位
  {
     ucKeyLock4=0; //按键自锁标志清零
     uiKeyTimeCnt4=0;//按键去抖动延时计数器清零,此行非常巧妙,是我实战中摸索出来的。      
  }
  else if(ucKeyLock4==0)//有按键按下,且是第一次被按下
  {
     uiKeyTimeCnt4++; //累加定时中断次数
     if(uiKeyTimeCnt4>const_key_time4)
     {
        uiKeyTimeCnt4=0; 
        ucKeyLock4=1;  //自锁按键置位,避免一直触发
        ucKeySec=4;    //触发4号键
     }
  }
}
void key_service(void) //按键服务的应用程序
{
  switch(ucKeySec) //按键服务状态切换
  {
    case 1:// 加按键 对应朱兆祺学习板的S1键 
          switch(ucWd)  //在不同的窗口下,设置不同的参数
          {
              case 1:  //窗口1
                   switch(ucPart)  //在窗口1下,根据不同的局部变量来设置不同的参数
                   {
                          case 0:   //无光标显示的状态 此处的case 0可以省略
                                break;
                          case 1:   //设置第1行参数 
                                ucData_1_1++;
                                if(ucData_1_1>99)
                                {
                                   ucData_1_1=99;
                                }
                                ucWd1Part1Update=1; //1代表更新显示,响应函数内部会清零
                                break;
                          case 2:   //设置第2行参数 
                                ucData_1_2++;
                                if(ucData_1_2>99)
                                {
                                   ucData_1_2=99;
                                }
                                ucWd1Part2Update=1; //1代表更新显示,响应函数内部会清零
                                break;
                          case 3:   //设置第3行参数 
                                ucData_1_3++;
                                if(ucData_1_3>99)
                                {
                                   ucData_1_3=99;
                                }
                                ucWd1Part3Update=1; //1代表更新显示,响应函数内部会清零
                                break;
                          case 4:   //设置第4行参数 
                                ucData_1_4++;
                                if(ucData_1_4>99)
                                {
                                   ucData_1_4=99;
                                }
                                ucWd1Part4Update=1; //1代表更新显示,响应函数内部会清零
                                break;
                   }
                   break;
              case 2:  //窗口2
                   switch(ucPart)  //在窗口2下,根据不同的局部变量来设置不同的参数
                   {
                          case 0:   //无光标显示的状态 此处的case 0可以省略
                                break;
                          case 1:   //设置第1行参数 
                                ucData_2_1++;
                                if(ucData_2_1>99)
                                {
                                   ucData_2_1=99;
                                }
                                ucWd2Part1Update=1; //1代表更新显示,响应函数内部会清零
                                break;
                          case 2:   //设置第2行参数 
                                ucData_2_2++;
                                if(ucData_2_2>99)
                                {
                                   ucData_2_2=99;
                                }
                                ucWd2Part2Update=1; //1代表更新显示,响应函数内部会清零
                                break;
                          case 3:   //设置第3行参数 
                                ucData_2_3++;
                                if(ucData_2_3>99)
                                {
                                   ucData_2_3=99;
                                }
                                ucWd2Part3Update=1; //1代表更新显示,响应函数内部会清零
                                break;
                          case 4:   //设置第4行参数 
                                ucData_2_4++;
                                if(ucData_2_4>99)
                                {
                                   ucData_2_4=99;
                                }
                                ucWd2Part4Update=1; //1代表更新显示,响应函数内部会清零
                                break;
                   }
                   break;         
          }     
          uiVoiceCnt=const_voice_short; //按键声音触发,滴一声就停。
          ucKeySec=0;  //响应按键服务处理程序后,按键编号清零,避免一致触发
          break;    
    
    case 2:// 减按键 对应朱兆祺学习板的S5键 
          switch(ucWd)  //在不同的窗口下,设置不同的参数
          {
              case 1:  //窗口1
                   switch(ucPart)  //在窗口1下,根据不同的局部变量来设置不同的参数
                   {
                          case 0:   //无光标显示的状态 此处的case 0可以省略
                                break;
                          case 1:   //设置第1行参数 
                                ucData_1_1--;
                                if(ucData_1_1>99) //一直减到最后,单片机C语言编译器有一个特征,0减去1会溢出变成255(0xff)
                                {
                                   ucData_1_1=0;
                                }
                                ucWd1Part1Update=1; //1代表更新显示,响应函数内部会清零
                                break;
                          case 2:   //设置第2行参数 
                                ucData_1_2--;
                                if(ucData_1_2>99) //一直减到最后,单片机C语言编译器有一个特征,0减去1会溢出变成255(0xff)
                                {
                                   ucData_1_2=0;
                                }
                                ucWd1Part2Update=1; //1代表更新显示,响应函数内部会清零
                                break;
                          case 3:   //设置第3行参数 
                                ucData_1_3--;
                                if(ucData_1_3>99) //一直减到最后,单片机C语言编译器有一个特征,0减去1会溢出变成255(0xff)
                                {
                                   ucData_1_3=0;
                                }
                                ucWd1Part3Update=1; //1代表更新显示,响应函数内部会清零
                                break;
                          case 4:   //设置第4行参数 
                                ucData_1_4--;
                                if(ucData_1_4>99) //一直减到最后,单片机C语言编译器有一个特征,0减去1会溢出变成255(0xff)
                                {
                                   ucData_1_4=0;
                                }
                                ucWd1Part4Update=1; //1代表更新显示,响应函数内部会清零
                                break;
                   }
                   break;
              case 2:  //窗口2
                   switch(ucPart)  //在窗口2下,根据不同的局部变量来设置不同的参数
                   {
                          case 0:   //无光标显示的状态 此处的case 0可以省略
                                break;
                          case 1:   //设置第1行参数 
                                ucData_2_1--;
                                if(ucData_2_1>99) //一直减到最后,单片机C语言编译器有一个特征,0减去1会溢出变成255(0xff)
                                {
                                   ucData_2_1=0;
                                }
                                ucWd2Part1Update=1; //1代表更新显示,响应函数内部会清零
                                break;
                          case 2:   //设置第2行参数 
                                ucData_2_2--;
                                if(ucData_2_2>99) //一直减到最后,单片机C语言编译器有一个特征,0减去1会溢出变成255(0xff)
                                {
                                   ucData_2_2=0;
                                }
                                ucWd2Part2Update=1; //1代表更新显示,响应函数内部会清零
                                break;
                          case 3:   //设置第3行参数 
                                ucData_2_3--;
                                if(ucData_2_3>99) //一直减到最后,单片机C语言编译器有一个特征,0减去1会溢出变成255(0xff)
                                {
                                   ucData_2_3=0;
                                }
                                ucWd2Part3Update=1; //1代表更新显示,响应函数内部会清零
                                break;
                          case 4:   //设置第4行参数 
                                ucData_2_4--;
                                if(ucData_2_4>99) //一直减到最后,单片机C语言编译器有一个特征,0减去1会溢出变成255(0xff)
                                {
                                   ucData_2_4=0;
                                }
                                ucWd2Part4Update=1; //1代表更新显示,响应函数内部会清零
                                break;
                   }
                   break;         
          }     
          uiVoiceCnt=const_voice_short; //按键声音触发,滴一声就停。
          ucKeySec=0;  //响应按键服务处理程序后,按键编号清零,避免一致触发
          break;  
    case 3:// 切换"光标"移动按键 对应朱兆祺学习板的S9键
          switch(ucWd)  //在不同的窗口下,设置不同的参数
          {
              case 1: //窗口1
                   switch(ucPart)  //在窗口1下,根据不同的局部变量来设置不同的参数
                   {
                          case 0:   //无光标显示的状态 此处的case 0可以省略
                                break;
                          case 1:   //设置第1行参数 
                                ucPart=2; //光标切换到下一行
                                ucWd1Part1Update=1; //更新显示原来那一行,目的是更新反显光标的状态
                                ucWd1Part2Update=1; //更新显示下一行,    目的是更新反显光标的状态
                                break;
                          case 2:   //设置第2行参数 
                                ucPart=3; //光标切换到下一行
                                ucWd1Part2Update=1; //更新显示原来那一行,目的是更新反显光标的状态
                                ucWd1Part3Update=1; //更新显示下一行,    目的是更新反显光标的状态
                                break;
                          case 3:   //设置第3行参数 
                                ucPart=4; //光标切换到下一行
                                ucWd1Part3Update=1; //更新显示原来那一行,目的是更新反显光标的状态
                                ucWd1Part4Update=1; //更新显示下一行,    目的是更新反显光标的状态
                                break;
                          case 4:   //设置第4行参数 
						        ucWd=2;  //切换到第2个窗口
                                ucPart=1; //光标返回到最上面第一行
								ucWd2Update=1; //窗口2整屏更新
                                break;
                   }
                   break;
              case 2: //窗口2
                   switch(ucPart)  //在窗口2下,根据不同的局部变量来设置不同的参数
                   {
                          case 0:   //无光标显示的状态 此处的case 0可以省略
                                break;
                          case 1:   //设置第1行参数 
                                ucPart=2; //光标切换到下一行
                                ucWd2Part1Update=1; //更新显示原来那一行,目的是更新反显光标的状态
                                ucWd2Part2Update=1; //更新显示下一行,    目的是更新反显光标的状态
                                break;
                          case 2:   //设置第2行参数 
                                ucPart=3; //光标切换到下一行
                                ucWd2Part2Update=1; //更新显示原来那一行,目的是更新反显光标的状态
                                ucWd2Part3Update=1; //更新显示下一行,    目的是更新反显光标的状态
                                break;
                          case 3:   //设置第3行参数 
                                ucPart=4; //光标切换到下一行
                                ucWd2Part3Update=1; //更新显示原来那一行,目的是更新反显光标的状态
                                ucWd2Part4Update=1; //更新显示下一行,    目的是更新反显光标的状态
                                break;
                          case 4:   //设置第4行参数 
						        ucWd=1;  //切换到第1个窗口
                                ucPart=1; //光标返回到最上面第一行
								ucWd1Update=1; //窗口1整屏更新
                                break;
                   }
                   break;         
          }          
        
          uiVoiceCnt=const_voice_short; //按键声音触发,滴一声就停。
          ucKeySec=0;  //响应按键服务处理程序后,按键编号清零,避免一致触发
          break;      
   
    case 4: // 设置按键  对应朱兆祺学习板的S13键,按一次进入设置状态,出现反显光标。再按一次推出设置状态,消除反显光标,并且强行切换到第1个窗口
          switch(ucWd)  //在不同的窗口下,设置不同的参数
          {
              case 1:  //窗口1
                   switch(ucPart)  //在窗口1下,根据不同的局部变量来设置不同的参数
                   {
                          case 0:   //无光标显示的状态
                                ucPart=1; //光标显示第一行,进入设置模式
                                ucWd1Part1Update=1; //更新显示
                                break;
                          case 1:   //设置第1行参数 
                                ucPart=0; //无光标显示,退出设置模式
                                ucWd1Part1Update=1; //更新显示
                                break;
                          case 2:   //设置第2行参数 
                                ucPart=0; //无光标显示,退出设置模式
                                ucWd1Part2Update=1; //更新显示
                                break;
                          case 3:   //设置第3行参数 
                                ucPart=0; //无光标显示,退出设置模式
                                ucWd1Part3Update=1; //更新显示
                                break;
                          case 4:   //设置第4行参数 
                                ucPart=0; //无光标显示,退出设置模式
                                ucWd1Part4Update=1; //更新显示
                                break;
                   }
                   break;
              case 2:  //窗口2
                   switch(ucPart)  //在窗口2下,根据不同的局部变量来设置不同的参数
                   {
                          case 0:   //无光标显示的状态
						        ucWd=1; //强行切换到第1个窗口
                                ucPart=1; //光标显示第一行,进入设置模式
								ucWd1Update=1; //窗口1整屏更新
                                break;
                          case 1:   //设置第1行参数 
						  		ucWd=1; //强行切换到第1个窗口
                                ucPart=0; //无光标显示,退出设置模式
								ucWd1Update=1; //窗口1整屏更新
                                break;
                          case 2:   //设置第2行参数 
						  		ucWd=1; //强行切换到第1个窗口
                                ucPart=0; //无光标显示,退出设置模式
								ucWd1Update=1; //窗口1整屏更新
                                break;
                          case 3:   //设置第3行参数 
						  	    ucWd=1; //强行切换到第1个窗口
                                ucPart=0; //无光标显示,退出设置模式
								ucWd1Update=1; //窗口1整屏更新
                                break;
                          case 4:   //设置第4行参数 
						  		ucWd=1; //强行切换到第1个窗口
                                ucPart=0; //无光标显示,退出设置模式
								ucWd1Update=1; //窗口1整屏更新
                                break;
                   }
                   break;         
          }    
          uiVoiceCnt=const_voice_short; //按键声音触发,滴一声就停。
          ucKeySec=0;  //响应按键服务处理程序后,按键编号清零,避免一致触发
          break;          
  }                
}
unsigned char *number_to_matrix(unsigned char  ucBitNumber)
{
    unsigned char *p_ucAnyNumber;  //此指针根据ucBitNumber数值的大小,分别调用不同的字库。
        switch(ucBitNumber)  //根据ucBitNumber数值的大小,分别调用不同的字库。
        { 
            case 0:
             p_ucAnyNumber=Zf816_0;
                     break;
            case 1:
             p_ucAnyNumber=Zf816_1;
                     break;
            case 2:
             p_ucAnyNumber=Zf816_2;
                     break;
            case 3:
             p_ucAnyNumber=Zf816_3;
                     break;
            case 4:
             p_ucAnyNumber=Zf816_4;
                     break;
            case 5:
             p_ucAnyNumber=Zf816_5;
                     break;
            case 6:
             p_ucAnyNumber=Zf816_6;
                     break;
            case 7:
             p_ucAnyNumber=Zf816_7;
                     break;
            case 8:
             p_ucAnyNumber=Zf816_8;
                     break;
            case 9:
             p_ucAnyNumber=Zf816_9;
                     break;
            case 10:
             p_ucAnyNumber=Zf816_nc;
                     break;
                default:   //如果上面的条件都不符合,那么默认指向空字模
             p_ucAnyNumber=Zf816_nc;
                     break;
        }
    return p_ucAnyNumber;  //返回转换结束后的指针
}
void lcd_display_service(void) //应用层面的液晶屏显示程序
{
    switch(ucWd)  //本程序的核心变量,窗口显示变量。类似于一级菜单的变量。代表显示不同的窗口。
    {
        case 1:  
              wd1();  //窗口1显示的内容
              break;
        case 2:  
              wd2();  //窗口2显示的内容
              break;
        //本程序只有2个窗口,所以只有一个case 1,如果要增加窗口,就直接增加 case 2, case 3...        
    }
}
void wd1(void)  //窗口1显示的内容
{
    unsigned char ucAnyNumber_1; //分解变量的个位
    unsigned char ucAnyNumber_10; //分解变量的十位
    unsigned char *p_ucAnyNumber_1; //经过数字转换成字模后,分解变量的个位字模首地址
    unsigned char *p_ucAnyNumber_10; //经过数字转换成字模后,分解变量的十位字模首地址
    unsigned char ucCursorFlag;  //光标标志,也就是反显的标志,它是根据局部变量ucPart来定的
/* 注释三:
 * 把每一个窗口的内容分为两种类型,一种类型是那些不用经常刷新显示的内容,只有在切换窗口的时候
 * 才需要更新,这种内容放在整屏更新显示的括号里,比如清屏操作等内容。另外一种是那些经常需要
 * 刷新显示的内容,这种内容放在局部更新显示的括号里。
 */
    if(ucWd1Update==1)  //窗口1整屏更新,里面只放那些不用经常刷新显示的内容
    {
        ucWd1Update=0;  //及时清零,避免一直更新
        ucWd1Part1Update=1; //激活窗口1的第1行局部更新显示变量,这里在前面数码管显示框架上有所改进
        ucWd1Part2Update=1; //激活窗口1的第2行局部更新显示变量,这里在前面数码管显示框架上有所改进
        ucWd1Part3Update=1; //激活窗口1的第3行局部更新显示变量,这里在前面数码管显示框架上有所改进
        ucWd1Part4Update=1; //激活窗口1的第4行局部更新显示变量,这里在前面数码管显示框架上有所改进
        display_clear(0x00); // 清屏操作, 全部显示空填充0x00,全部显示点阵用0xff。
        clear_all_canvas();  //把画布全部清零
        insert_buffer_to_canvas(0,0,Zf816_mao_hao,0,1,16);//把冒号的字模插入画布
        display_lattice(0,0,Hz1616_yi,0,2,16,0);    //一窗口一行,这些内容不用经常更新,只有在切换窗口的时候才更新显示
        display_lattice(1,0,Hz1616_chuang,0,2,16,0);   
        display_lattice(2,0,Hz1616_kou,0,2,16,0);   
        display_lattice(3,0,Hz1616_yi,0,2,16,0); 
        display_lattice(4,0,Hz1616_hang,0,2,16,0); 
        display_lattice(0,16,Hz1616_yi,0,2,16,0);    //一窗口二行
        display_lattice(1,16,Hz1616_chuang,0,2,16,0);   
        display_lattice(2,16,Hz1616_kou,0,2,16,0);   
        display_lattice(3,16,Hz1616_er,0,2,16,0); 
        display_lattice(4,16,Hz1616_hang,0,2,16,0); 
        display_lattice(8,0,Hz1616_yi,0,2,16,0);    //一窗口三行
        display_lattice(9,0,Hz1616_chuang,0,2,16,0);   
        display_lattice(10,0,Hz1616_kou,0,2,16,0);   
        display_lattice(11,0,Hz1616_san,0,2,16,0); 
        display_lattice(12,0,Hz1616_hang,0,2,16,0); 
        display_lattice(8,16,Hz1616_yi,0,2,16,0);    //一窗口四行
        display_lattice(9,16,Hz1616_chuang,0,2,16,0);   
        display_lattice(10,16,Hz1616_kou,0,2,16,0);   
        display_lattice(11,16,Hz1616_si,0,2,16,0); 
        display_lattice(12,16,Hz1616_hang,0,2,16,0); 
    }
/* 注释四:
 * 注意!我前面讲数码管显示的时候有一句话讲错了,我那时说<局部更新应该写在整屏更新之前>,这是不对的。
 * 按照现在的显示程序框架<即整屏显示更新括号里包含了所有局部变量的激活>,应该是<整屏更新应该写在局部更新之前>
 * 这样才对。
 */
    if(ucWd1Part1Update==1) //窗口1的第1行局部更新显示变量,里面放一些经常需要刷新显示的内容
    {
        ucWd1Part1Update=0; //及时清零,避免一直更新
        if(ucPart==1) //被选中
        {
             ucCursorFlag=1; //反显 显示
        }
        else //没被选中
        {
             ucCursorFlag=0; //正常 显示
        }
        if(ucData_1_1>=10) //有2位数以上
        {
             ucAnyNumber_10=ucData_1_1/10;  //十位
        }
        else //否则显示空
        {
             ucAnyNumber_10=10;  //在下面的转换函数中,代码10表示空字模
        }
        ucAnyNumber_1=ucData_1_1%10/1;  //个位
    
        p_ucAnyNumber_10=number_to_matrix(ucAnyNumber_10); //把数字转换成字模首地址
        p_ucAnyNumber_1=number_to_matrix(ucAnyNumber_1); //把数字转换成字模首地址
        insert_buffer_to_canvas(2,0,p_ucAnyNumber_10,ucCursorFlag,1,16);//把十的字模插入画布
        insert_buffer_to_canvas(3,0,p_ucAnyNumber_1,ucCursorFlag,1,16);//把个的字模插入画布
        display_lattice(5,0,ucCanvasBuffer,0,4,16,0);   //显示整屏的画布,最后的参数0是偏移量
                          
    }
    if(ucWd1Part2Update==1) //窗口1的第2行局部更新显示变量,里面放一些经常需要刷新显示的内容
    {
         ucWd1Part2Update=0; //及时清零,避免一直更新
         if(ucPart==2) //被选中
         {
             ucCursorFlag=1; //反显 显示
         }
         else //没被选中
         {
             ucCursorFlag=0; //正常 显示
         }
         if(ucData_1_2>=10) //有2位数以上
         {
             ucAnyNumber_10=ucData_1_2/10;  //十位
         }
         else //否则显示空
         {
             ucAnyNumber_10=10;  //在下面的转换函数中,代码10表示空字模
         }
         ucAnyNumber_1=ucData_1_2%10/1;  //个位
    
         p_ucAnyNumber_10=number_to_matrix(ucAnyNumber_10); //把数字转换成字模首地址
         p_ucAnyNumber_1=number_to_matrix(ucAnyNumber_1); //把数字转换成字模首地址
         insert_buffer_to_canvas(2,0,p_ucAnyNumber_10,ucCursorFlag,1,16);//把十的字模插入画布
         insert_buffer_to_canvas(3,0,p_ucAnyNumber_1,ucCursorFlag,1,16);//把个的字模插入画布
         display_lattice(5,16,ucCanvasBuffer,0,4,16,0);   //显示整屏的画布,最后的参数0是偏移量
                          
     }
     if(ucWd1Part3Update==1) //窗口1的第3行局部更新显示变量,里面放一些经常需要刷新显示的内容
     {
         ucWd1Part3Update=0; //及时清零,避免一直更新
         if(ucPart==3) //被选中
         {
             ucCursorFlag=1; //反显 显示
         }
         else //没被选中
         {
             ucCursorFlag=0; //正常 显示
         }
         if(ucData_1_3>=10) //有2位数以上
         {
             ucAnyNumber_10=ucData_1_3/10;  //十位
         }
         else //否则显示空
         {
             ucAnyNumber_10=10;  //在下面的转换函数中,代码10表示空字模
         }
         ucAnyNumber_1=ucData_1_3%10/1;  //个位
    
         p_ucAnyNumber_10=number_to_matrix(ucAnyNumber_10); //把数字转换成字模首地址
         p_ucAnyNumber_1=number_to_matrix(ucAnyNumber_1); //把数字转换成字模首地址
         insert_buffer_to_canvas(2,0,p_ucAnyNumber_10,ucCursorFlag,1,16);//把十的字模插入画布
         insert_buffer_to_canvas(3,0,p_ucAnyNumber_1,ucCursorFlag,1,16);//把个的字模插入画布
         display_lattice(13,0,ucCanvasBuffer,0,4,16,0);   //显示整屏的画布,最后的参数0是偏移量
                          
     }
     if(ucWd1Part4Update==1) //窗口1的第4行局部更新显示变量,里面放一些经常需要刷新显示的内容
     {
         ucWd1Part4Update=0; //及时清零,避免一直更新
         if(ucPart==4) //被选中
         {
             ucCursorFlag=1; //反显 显示
         }
         else //没被选中
         {
             ucCursorFlag=0; //正常 显示
         }
         if(ucData_1_4>=10) //有2位数以上
         {
             ucAnyNumber_10=ucData_1_4/10;  //十位
         }
         else //否则显示空
         {
             ucAnyNumber_10=10;  //在下面的转换函数中,代码10表示空字模
         }
         ucAnyNumber_1=ucData_1_4%10/1;  //个位
    
         p_ucAnyNumber_10=number_to_matrix(ucAnyNumber_10); //把数字转换成字模首地址
         p_ucAnyNumber_1=number_to_matrix(ucAnyNumber_1); //把数字转换成字模首地址
         insert_buffer_to_canvas(2,0,p_ucAnyNumber_10,ucCursorFlag,1,16);//把十的字模插入画布
         insert_buffer_to_canvas(3,0,p_ucAnyNumber_1,ucCursorFlag,1,16);//把个的字模插入画布
         display_lattice(13,16,ucCanvasBuffer,0,4,16,0);   //显示整屏的画布,最后的参数0是偏移量                          
     }
}
void wd2(void)  //窗口2显示的内容
{
    unsigned char ucAnyNumber_1; //分解变量的个位
    unsigned char ucAnyNumber_10; //分解变量的十位
    unsigned char *p_ucAnyNumber_1; //经过数字转换成字模后,分解变量的个位字模首地址
    unsigned char *p_ucAnyNumber_10; //经过数字转换成字模后,分解变量的十位字模首地址
    unsigned char ucCursorFlag;  //光标标志,也就是反显的标志,它是根据局部变量ucPart来定的
    if(ucWd2Update==1)  //窗口2整屏更新,里面只放那些不用经常刷新显示的内容
    {
        ucWd2Update=0;  //及时清零,避免一直更新
        ucWd2Part1Update=1; //激活窗口2的第1行局部更新显示变量,这里在前面数码管显示框架上有所改进
        ucWd2Part2Update=1; //激活窗口2的第2行局部更新显示变量,这里在前面数码管显示框架上有所改进
        ucWd2Part3Update=1; //激活窗口2的第3行局部更新显示变量,这里在前面数码管显示框架上有所改进
        ucWd2Part4Update=1; //激活窗口2的第4行局部更新显示变量,这里在前面数码管显示框架上有所改进
        display_clear(0x00); // 清屏操作, 全部显示空填充0x00,全部显示点阵用0xff。
        clear_all_canvas();  //把画布全部清零
        insert_buffer_to_canvas(0,0,Zf816_mao_hao,0,1,16);//把冒号的字模插入画布
        display_lattice(0,0,Hz1616_er,0,2,16,0);    //二窗口一行,这些内容不用经常更新,只有在切换窗口的时候才更新显示
        display_lattice(1,0,Hz1616_chuang,0,2,16,0);   
        display_lattice(2,0,Hz1616_kou,0,2,16,0);   
        display_lattice(3,0,Hz1616_yi,0,2,16,0); 
        display_lattice(4,0,Hz1616_hang,0,2,16,0); 
        display_lattice(0,16,Hz1616_er,0,2,16,0);    //二窗口二行
        display_lattice(1,16,Hz1616_chuang,0,2,16,0);   
        display_lattice(2,16,Hz1616_kou,0,2,16,0);   
        display_lattice(3,16,Hz1616_er,0,2,16,0); 
        display_lattice(4,16,Hz1616_hang,0,2,16,0); 
        display_lattice(8,0,Hz1616_er,0,2,16,0);    //二窗口三行
        display_lattice(9,0,Hz1616_chuang,0,2,16,0);   
        display_lattice(10,0,Hz1616_kou,0,2,16,0);   
        display_lattice(11,0,Hz1616_san,0,2,16,0); 
        display_lattice(12,0,Hz1616_hang,0,2,16,0); 
        display_lattice(8,16,Hz1616_er,0,2,16,0);    //二窗口四行
        display_lattice(9,16,Hz1616_chuang,0,2,16,0);   
        display_lattice(10,16,Hz1616_kou,0,2,16,0);   
        display_lattice(11,16,Hz1616_si,0,2,16,0); 
        display_lattice(12,16,Hz1616_hang,0,2,16,0); 
    }
    if(ucWd2Part1Update==1) //窗口2的第1行局部更新显示变量,里面放一些经常需要刷新显示的内容
    {
        ucWd2Part1Update=0; //及时清零,避免一直更新
        if(ucPart==1) //被选中
        {
             ucCursorFlag=1; //反显 显示
        }
        else //没被选中
        {
             ucCursorFlag=0; //正常 显示
        }
        if(ucData_2_1>=10) //有2位数以上
        {
             ucAnyNumber_10=ucData_2_1/10;  //十位
        }
        else //否则显示空
        {
             ucAnyNumber_10=10;  //在下面的转换函数中,代码10表示空字模
        }
        ucAnyNumber_1=ucData_2_1%10/1;  //个位
    
        p_ucAnyNumber_10=number_to_matrix(ucAnyNumber_10); //把数字转换成字模首地址
        p_ucAnyNumber_1=number_to_matrix(ucAnyNumber_1); //把数字转换成字模首地址
        insert_buffer_to_canvas(2,0,p_ucAnyNumber_10,ucCursorFlag,1,16);//把十的字模插入画布
        insert_buffer_to_canvas(3,0,p_ucAnyNumber_1,ucCursorFlag,1,16);//把个的字模插入画布
        display_lattice(5,0,ucCanvasBuffer,0,4,16,0);   //显示整屏的画布,最后的参数0是偏移量
                          
    }
    if(ucWd2Part2Update==1) //窗口2的第2行局部更新显示变量,里面放一些经常需要刷新显示的内容
    {
         ucWd2Part2Update=0; //及时清零,避免一直更新
         if(ucPart==2) //被选中
         {
             ucCursorFlag=1; //反显 显示
         }
         else //没被选中
         {
             ucCursorFlag=0; //正常 显示
         }
         if(ucData_2_2>=10) //有2位数以上
         {
             ucAnyNumber_10=ucData_2_2/10;  //十位
         }
         else //否则显示空
         {
             ucAnyNumber_10=10;  //在下面的转换函数中,代码10表示空字模
         }
         ucAnyNumber_1=ucData_2_2%10/1;  //个位
    
         p_ucAnyNumber_10=number_to_matrix(ucAnyNumber_10); //把数字转换成字模首地址
         p_ucAnyNumber_1=number_to_matrix(ucAnyNumber_1); //把数字转换成字模首地址
         insert_buffer_to_canvas(2,0,p_ucAnyNumber_10,ucCursorFlag,1,16);//把十的字模插入画布
         insert_buffer_to_canvas(3,0,p_ucAnyNumber_1,ucCursorFlag,1,16);//把个的字模插入画布
         display_lattice(5,16,ucCanvasBuffer,0,4,16,0);   //显示整屏的画布,最后的参数0是偏移量
                          
     }
     if(ucWd2Part3Update==1) //窗口2的第3行局部更新显示变量,里面放一些经常需要刷新显示的内容
     {
         ucWd2Part3Update=0; //及时清零,避免一直更新
         if(ucPart==3) //被选中
         {
             ucCursorFlag=1; //反显 显示
         }
         else //没被选中
         {
             ucCursorFlag=0; //正常 显示
         }
         if(ucData_2_3>=10) //有2位数以上
         {
             ucAnyNumber_10=ucData_2_3/10;  //十位
         }
         else //否则显示空
         {
             ucAnyNumber_10=10;  //在下面的转换函数中,代码10表示空字模
         }
         ucAnyNumber_1=ucData_2_3%10/1;  //个位
    
         p_ucAnyNumber_10=number_to_matrix(ucAnyNumber_10); //把数字转换成字模首地址
         p_ucAnyNumber_1=number_to_matrix(ucAnyNumber_1); //把数字转换成字模首地址
         insert_buffer_to_canvas(2,0,p_ucAnyNumber_10,ucCursorFlag,1,16);//把十的字模插入画布
         insert_buffer_to_canvas(3,0,p_ucAnyNumber_1,ucCursorFlag,1,16);//把个的字模插入画布
         display_lattice(13,0,ucCanvasBuffer,0,4,16,0);   //显示整屏的画布,最后的参数0是偏移量
                          
     }
     if(ucWd2Part4Update==1) //窗口2的第4行局部更新显示变量,里面放一些经常需要刷新显示的内容
     {
         ucWd2Part4Update=0; //及时清零,避免一直更新
         if(ucPart==4) //被选中
         {
             ucCursorFlag=1; //反显 显示
         }
         else //没被选中
         {
             ucCursorFlag=0; //正常 显示
         }
         if(ucData_2_4>=10) //有2位数以上
         {
             ucAnyNumber_10=ucData_2_4/10;  //十位
         }
         else //否则显示空
         {
             ucAnyNumber_10=10;  //在下面的转换函数中,代码10表示空字模
         }
         ucAnyNumber_1=ucData_2_4%10/1;  //个位
    
         p_ucAnyNumber_10=number_to_matrix(ucAnyNumber_10); //把数字转换成字模首地址
         p_ucAnyNumber_1=number_to_matrix(ucAnyNumber_1); //把数字转换成字模首地址
         insert_buffer_to_canvas(2,0,p_ucAnyNumber_10,ucCursorFlag,1,16);//把十的字模插入画布
         insert_buffer_to_canvas(3,0,p_ucAnyNumber_1,ucCursorFlag,1,16);//把个的字模插入画布
         display_lattice(13,16,ucCanvasBuffer,0,4,16,0);   //显示整屏的画布,最后的参数0是偏移量                          
     }
}
void clear_all_canvas(void)  //把画布全部清零
{
   unsigned int j=0;
   unsigned int i=0;
   for(j=0;j<16;j++)  //这里的16表示画布有16行
   { 
      for(i=0;i<4;i++) //这里的4表示画布每行有4个字节
      {
                  ucCanvasBuffer[j*4+i]=0x00;
      }
   }         
}
void display_clear(unsigned char ucFillDate) // 清屏  全部显示空填充0x00   全部显示点阵用0xff
{    
    unsigned char x,y;
    WriteCommand(0x34);  //关显示缓冲指令            
    WriteCommand(0x34);  //关显示缓冲指令  故意写2次,怕1次关不了 这个是因为我参考到某厂家的驱动程序也是这样写的
    y=0;
    while(y<32)  //y轴的范围0至31
    {
         WriteCommand(y+0x80);        //垂直地址
         WriteCommand(0x80);          //水平地址
         for(x=0;x<32;x++)  //256个横向点,有32个字节
         {  
            LCDWriteData(ucFillDate);
         }
         y++;
    }
    WriteCommand(0x36); //开显示缓冲指令
}
/* 注释五:
 * 把字模插入画布的函数.
 * 这是本节的核心函数,读者尤其要搞懂x_amount和y_amount对应的显示关系。
 * 第1,2个参数x,y是在画布中的坐标体系。
 * x的范围是0至3,因为画布的横向只要4个字节。y的范围是0至15,因为画布的纵向只有16行。
 * 第3个参数*ucArray是字模的数组。
 * 第4个参数ucFbFlag是反白显示标志。0代表正常显示,1代表反白显示。
 * 第5,6个参数x_amount,y_amount分别代表字模数组的横向有多少个字节,纵向有几横。
 */
void insert_buffer_to_canvas(unsigned int x,unsigned int y,const unsigned char  *ucArray,unsigned char ucFbFlag,unsigned int x_amount,unsigned int y_amount)
{
   unsigned int j=0;
   unsigned int i=0;
   unsigned char ucTemp;
   for(j=0;j<y_amount;j++)
   {
      for(i=0;i<x_amount;i++)
      {
                   ucTemp=ucArray[j*x_amount+i];
                   if(ucFbFlag==0)
                   {
              ucCanvasBuffer[(y+j)*4+x+i]=ucTemp; //这里的4代表画布每一行只有4个字节
                   }
                   else
                   {
              ucCanvasBuffer[(y+j)*4+x+i]=~ucTemp; //这里的4代表画布每一行只有4个字节
                   }
      }
   }         
}
/* 注释六:
* 显示任意点阵函数.
* 注意,本函数在前几节的基础上多增加了第7个参数uiOffSetAddr,它是偏移地址。
* 对于这个函数,读者尤其要搞懂x_amount和y_amount对应的显示关系。
* 第1,2个参数x,y是坐标体系。x的范围是0至15,y的范围是0至31.
* 第3个参数*ucArray是字模的数组。
* 第4个参数ucFbFlag是反白显示标志。0代表正常显示,1代表反白显示。
* 第5,6个参数x_amount,y_amount分别代表字模数组的横向有多少个字节,纵向有几横。
* 第7个参数uiOffSetAddr是偏移地址,代表字模数组的从第几个数据开始显示。
*/
void display_lattice(unsigned int x,unsigned int y,const unsigned char  *ucArray,unsigned char ucFbFlag,unsigned int x_amount,unsigned int y_amount,unsigned int uiOffSetAddr)
{
   unsigned int j=0;
   unsigned int i=0;
   unsigned char ucTemp;
 //注意,要把以下两行指令屏蔽,否则屏幕在更新显示时会整屏闪动
 //  WriteCommand(0x34);  //关显示缓冲指令            
 //  WriteCommand(0x34);  //关显示缓冲指令  故意写2次,怕1次关不了 这个是因为我参考到某厂家的驱动程序也是这样写的
   for(j=0;j<y_amount;j++) //y_amount代表y轴有多少横
   {
       WriteCommand(y+j+0x80);        //垂直地址
       WriteCommand(x+0x80);          //水平地址
       for(i=0;i<x_amount;i++) //x_amount代表x轴有多少列
       {
           ucTemp=ucArray[j*x_amount+i+uiOffSetAddr]; //uiOffSetAddr是字模数组的偏移地址
           if(ucFbFlag==1)  //反白显示
           {
               ucTemp=~ucTemp;
           }
           LCDWriteData(ucTemp);
          //         delay_short(30000);  //把上一节这个延时函数去掉,加快刷屏速度
      }
   }
   WriteCommand(0x36); //开显示缓冲指令
}
void SendByteToLcd(unsigned char ucData)  //发送一个字节数据到液晶模块
{
        unsigned char i;
        for ( i = 0; i < 8; i++ )
        {
                if ( (ucData << i) & 0x80 )
                {
                        LCDSID_dr = 1;
                }
                else
                {
                        LCDSID_dr = 0;
                }
                LCDCLK_dr = 0;
                LCDCLK_dr = 1;
        }
}
void SPIWrite(unsigned char ucWData, unsigned char ucWRS) //模拟SPI发送一个字节的命令或者数据给液晶模块的底层驱动
{
        SendByteToLcd( 0xf8 + (ucWRS << 1) );
        SendByteToLcd( ucWData & 0xf0 );
        SendByteToLcd( (ucWData << 4) & 0xf0);
}
void WriteCommand(unsigned char ucCommand) //发送一个字节的命令给液晶模块
{
        LCDCS_dr = 0;
        LCDCS_dr = 1;
        SPIWrite(ucCommand, 0);
        delay_short(90); 
}
void LCDWriteData(unsigned char ucData)  //发送一个字节的数据给液晶模块
{
        LCDCS_dr = 0;
        LCDCS_dr = 1;
        SPIWrite(ucData, 1);
}
void LCDInit(void) //初始化  函数内部包括液晶模块的复位
{
        LCDRST_dr = 1;  //复位
        LCDRST_dr = 0;
        LCDRST_dr = 1;
}
void delay_short(unsigned int uiDelayShort) //延时函数
{
   unsigned int i;  
   for(i=0;i<uiDelayShort;i++)
   {
     ;  
   }
}
void delay_long(unsigned int uiDelayLong)
{
   unsigned int i;
   unsigned int j;
   for(i=0;i<uiDelayLong;i++)
   {
      for(j=0;j<500;j++)  //内嵌循环的空指令数量
          {
             ; //一个分号相当于执行一条空语句
          }
   }
}
	
总结陈词:
	这一节讲了在多个窗口里设置不同的参数。还有一种常见的项目,要求把第1窗口是用来作为主菜单,主菜单里面有3个子菜单,可以通过移动光标进入不同的子菜单窗口进行参数设置,这类项目该如何编程?欲知详情,请听下回分解-----通过主菜单移动光标来进入子菜单窗口的液晶屏程序。
(未完待续,下节更精彩,不要走开哦)
			
			
			
						
			
 我要赚赏金
