3 Star 15 Fork 4

建伟F4nniu / FN1895E-MCU101

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
该仓库未声明开源许可证文件(LICENSE),使用请关注具体项目描述及其代码上游依赖。
克隆/下载
MCU085.md 50.61 KB
一键复制 编辑 原始数据 按行查看 历史
建伟F4nniu 提交于 2016-12-24 10:44 . 整理完 70-90节。

#第八十五节:实时同步把加减按键输入的数值转换成BCD码数组的液晶屏显示程序。

开场白:

把运算处理完的数值转换成BCD码数组才可以更好方便显示和数字按键的输入编辑。这一节主要跟大家讲这方面的算法程序。本节的核心转换函数是void data_to_buffer(…)。

具体内容,请看源代码讲解。

  • (1)硬件平台:
  • 基于朱兆祺51单片机学习板。数字1键对应S1键,数字2键对应S2键,数字3键对应S3键…. 数字9键对应S9键, 数字0键对应S10键。小数键对应S11,S13按键是加按键,S14按键是减按键,清零键对应S16,其它按键不用。
  • (2)实现功能:
  • 通过S13,S14这两个加减按键更改第2行显示的数值,此数值会同步更新显示在第1行的BCD码数组上。 MCU085
  • (3)源代码讲解如下:
#include "REG52.H"

#define const_voice_short  40   //蜂鸣器短叫的持续时间

#define const_key_time  10    //按键去抖动延时的时间

sbit key_sr1 = P0 ^ 0; //第一行输入
sbit key_sr2 = P0 ^ 1; //第二行输入
sbit key_sr3 = P0 ^ 2; //第三行输入
sbit key_sr4 = P0 ^ 3; //第四行输入

sbit key_dr1 = P0 ^ 4; //第一列输出
sbit key_dr2 = P0 ^ 5; //第二列输出
sbit key_dr3 = P0 ^ 6; //第三列输出
sbit key_dr4 = P0 ^ 7; //第四列输出

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 key_number_input(unsigned char ucKeyNumber); //输入数字按键
void set_data(unsigned char ucKeyNumberTemp, //设置参数
              unsigned char ucDotBitMax,
              unsigned char ucDataCntMax,
              unsigned char *p_ucDotCnt,
              unsigned char *p_ucDotBitS,
              unsigned char *p_ucWdPartCnt,
              unsigned char *p_ucSetDataBuffer,
              unsigned char ucIntCntMax,
              unsigned char *p_ucIntCnt);

void data_to_buffer(unsigned long ulWillConverData,  //把数值转换成数组
                    unsigned char ucConverDotCnt,
                    unsigned char ucConverDataSize,
                    unsigned char *p_ucDotCnt,
                    unsigned char *p_ucDotBitS,
                    unsigned char *p_ucWdPartCnt,
                    unsigned char *p_ucConverBuffer);
unsigned long buffer_to_data(unsigned char ucConverDataSize, unsigned char ucConverDotCnt, unsigned char *p_ucConverBuffer); //把带小数点的BCD数组转换成long类型的数值。

void key_delete_input(void); //删除按键

void T0_time(); //定时中断函数
void key_service();
void key_scan(); //按键扫描函数 放在定时中断里

void initial_myself();
void initial_peripheral();


void lcd_display_service(void); //应用层面的液晶屏显示程序
void clear_all_canvas(void);  //把画布全部清零

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_dot[] = //小数点
{
    /*--  文字:  .  --*/
    /*--  宋体12;  此字体下对应的点阵为:宽x高=8x16   --*/
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x60, 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_xiang[] =
{
    /*--  文字:  项  --*/
    /*--  宋体12;  此字体下对应的点阵为:宽x高=16x16   --*/
    0x00, 0x00, 0x03, 0xFE, 0xFC, 0x20, 0x10, 0x40, 0x11, 0xFC, 0x11, 0x04, 0x11, 0x24, 0x11, 0x24,
    0x11, 0x24, 0x11, 0x24, 0x1D, 0x24, 0xE1, 0x34, 0x00, 0x48, 0x01, 0x86, 0x06, 0x02, 0x00, 0x00,
};

code unsigned char Hz1616_shu[] =
{
    /*--  文字:  数  --*/
    /*--  宋体12;  此字体下对应的点阵为:宽x高=16x16   --*/
    0x08, 0x20, 0x49, 0x30, 0x2A, 0x20, 0x1C, 0x20, 0xFF, 0x7E, 0x1C, 0x44, 0x2B, 0x44, 0x48, 0xC4,
    0x08, 0x28, 0xFF, 0x28, 0x12, 0x10, 0x34, 0x10, 0x0C, 0x28, 0x32, 0x4E, 0xC0, 0x84, 0x00, 0x00,
};

code unsigned char Hz1616_zhu[] =
{
    /*--  文字:  组  --*/
    /*--  宋体12;  此字体下对应的点阵为:宽x高=16x16   --*/
    0x10, 0x00, 0x19, 0xF8, 0x11, 0x08, 0x25, 0x08, 0x25, 0x08, 0x79, 0xF8, 0x09, 0x08, 0x11, 0x08,
    0x21, 0x08, 0x7D, 0xF8, 0x01, 0x08, 0x01, 0x08, 0x0D, 0x08, 0x73, 0xFE, 0x00, 0x00, 0x00, 0x00,
};

code unsigned char Hz1616_zhi[] =
{
    /*--  文字:  值  --*/
    /*--  宋体12;  此字体下对应的点阵为:宽x高=16x16   --*/
    0x10, 0x40, 0x18, 0x60, 0x17, 0xFC, 0x10, 0x40, 0x20, 0x80, 0x33, 0xF8, 0x62, 0x08, 0xA3, 0xF8,
    0x22, 0x08, 0x23, 0xF8, 0x22, 0x08, 0x23, 0xF8, 0x22, 0x08, 0x22, 0x08, 0x2F, 0xFE, 0x20, 0x00,
};

/* 注释一:
* 以下是画布显示数组。横向是6个字节,纵向16行,可以显示3个16x16的汉字.
*  注意,这节内容的画布跟前面79章节的画布大小不一样,79节前面的横向是4个字节,这节的横向是6个字节。
*/
unsigned char ucCanvasBuffer[] =
{
    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, 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,
};


/* 注释二:
* 以下5个变量记录一个参数的5种信息,包括小数点的数量,小数点个数,数据的位置,数组具体值,整数个数
*/
unsigned char ucDotCnt_1 = 0; //记录当前输入的小数点数量,如果小数点的数量不为0,说明当前数组已包含小数点,此时再按小数点按键则无效
unsigned char ucDotBitS_1 = 0; //记录当前输入的小数点个数,如果小数点的个数如果超过规定ucDotBitMax位,此时再按任何输入按键则无效
unsigned char ucWdPartCnt_1 = 0; //记录当前输入的数据在数组中的位置。
unsigned char ucDataBuffer_1[6] = {0, 10, 10, 10, 10, 10}; //一项的BCD码数组缓冲
unsigned char ucIntCnt_1 = 0; //记录当前输入的整数个数,如果整数的个数如果超过规定ucIntCntMax位,此时再按任何输入按键则无效

unsigned long ulData_1 = 0; //用一个long变量表示BCD码的具体数值。


unsigned char ucKeyStep = 1; //按键扫描步骤变量

unsigned char ucKeySec = 0; //被触发的按键编号
unsigned int  uiKeyTimeCnt = 0; //按键去抖动延时计数器
unsigned char ucKeyLock = 0; //按键触发后自锁的变量标志

unsigned char ucRowRecord = 1; //记录当前扫描到第几列了


unsigned int  uiVoiceCnt = 0; //蜂鸣器鸣叫的持续时间计数器


unsigned char ucWd = 1; //窗口变量
unsigned char ucPart = 2; //局部变量 0代表没有选中任何一行,其它数值1到4代表选中某一行


unsigned char ucWd1Update = 1; //窗口1的整屏更新显示变量      1代表更新显示,响应函数内部会清零
unsigned char ucWd1Part1Update = 0; //窗口1的第1行局部更新显示变量  1代表更新显示,响应函数内部会自动把它清零
unsigned char ucWd1Part2Update = 0; //窗口1的第2行局部更新显示变量  1代表更新显示,响应函数内部会自动把它清零

void main()
{
    initial_myself();      //第一区,上电后马上初始化
    delay_long(100);       //一线,延时线。延时一段时间
    initial_peripheral();  //第二区,上电后延时一段时间再初始化

    while(1)   //第三区
    {
        key_service(); //按键服务程序
        lcd_display_service(); //应用层面的液晶屏显示程序
    }

}


void initial_myself()  //第一区 上电后马上初始化
{

    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()//按键扫描函数 放在定时中断里
{

    switch(ucKeyStep)
    {
    case 1:   //按键扫描输出第ucRowRecord列低电平
        if(ucRowRecord == 1) //第一列输出低电平
        {
            key_dr1 = 0;
            key_dr2 = 1;
            key_dr3 = 1;
            key_dr4 = 1;
        }
        else if(ucRowRecord == 2) //第二列输出低电平
        {
            key_dr1 = 1;
            key_dr2 = 0;
            key_dr3 = 1;
            key_dr4 = 1;
        }
        else if(ucRowRecord == 3) //第三列输出低电平
        {
            key_dr1 = 1;
            key_dr2 = 1;
            key_dr3 = 0;
            key_dr4 = 1;
        }
        else   //第四列输出低电平
        {
            key_dr1 = 1;
            key_dr2 = 1;
            key_dr3 = 1;
            key_dr4 = 0;
        }

        uiKeyTimeCnt = 0; //延时计数器清零
        ucKeyStep++;     //切换到下一个运行步骤
        break;

    case 2:     //此处的小延时用来等待刚才列输出信号稳定,再判断输入信号。不是去抖动延时。
        uiKeyTimeCnt++;
        if(uiKeyTimeCnt > 1)
        {
            uiKeyTimeCnt = 0;
            ucKeyStep++;     //切换到下一个运行步骤
        }
        break;

    case 3:
        if(key_sr1 == 1 && key_sr2 == 1 && key_sr3 == 1 && key_sr4 == 1)
        {
            ucKeyStep = 1; //如果没有按键按下,返回到第一个运行步骤重新开始扫描
            ucKeyLock = 0; //按键自锁标志清零
            uiKeyTimeCnt = 0; //按键去抖动延时计数器清零,此行非常巧妙

            ucRowRecord++;  //输出下一列
            if(ucRowRecord > 4)
            {
                ucRowRecord = 1; //依次输出完四列之后,继续从第一列开始输出低电平
            }

        }
        else if(ucKeyLock == 0) //有按键按下,且是第一次触发
        {
            if(key_sr1 == 0 && key_sr2 == 1 && key_sr3 == 1 && key_sr4 == 1)
            {
                uiKeyTimeCnt++;  //去抖动延时计数器
                if(uiKeyTimeCnt > const_key_time)
                {
                    uiKeyTimeCnt = 0;
                    ucKeyLock = 1; //自锁按键置位,避免一直触发,只有松开按键,此标志位才会被清零

                    if(ucRowRecord == 1) //第一列输出低电平
                    {
                        ucKeySec = 1; //触发1号键 对应朱兆祺学习板的S1键
                    }
                    else if(ucRowRecord == 2) //第二列输出低电平
                    {
                        ucKeySec = 2; //触发2号键 对应朱兆祺学习板的S2键
                    }
                    else if(ucRowRecord == 3) //第三列输出低电平
                    {
                        ucKeySec = 3; //触发3号键 对应朱兆祺学习板的S3键
                    }
                    else   //第四列输出低电平
                    {
                        ucKeySec = 4; //触发4号键 对应朱兆祺学习板的S4键
                    }

                }

            }
            else if(key_sr1 == 1 && key_sr2 == 0 && key_sr3 == 1 && key_sr4 == 1)
            {
                uiKeyTimeCnt++;  //去抖动延时计数器
                if(uiKeyTimeCnt > const_key_time)
                {
                    uiKeyTimeCnt = 0;
                    ucKeyLock = 1; //自锁按键置位,避免一直触发,只有松开按键,此标志位才会被清零
                    if(ucRowRecord == 1) //第一列输出低电平
                    {
                        ucKeySec = 5; //触发5号键 对应朱兆祺学习板的S5键
                    }
                    else if(ucRowRecord == 2) //第二列输出低电平
                    {
                        ucKeySec = 6; //触发6号键 对应朱兆祺学习板的S6键
                    }
                    else if(ucRowRecord == 3) //第三列输出低电平
                    {
                        ucKeySec = 7; //触发7号键 对应朱兆祺学习板的S7键
                    }
                    else   //第四列输出低电平
                    {
                        ucKeySec = 8; //触发8号键 对应朱兆祺学习板的S8键
                    }
                }

            }
            else if(key_sr1 == 1 && key_sr2 == 1 && key_sr3 == 0 && key_sr4 == 1)
            {
                uiKeyTimeCnt++;  //去抖动延时计数器
                if(uiKeyTimeCnt > const_key_time)
                {
                    uiKeyTimeCnt = 0;
                    ucKeyLock = 1; //自锁按键置位,避免一直触发,只有松开按键,此标志位才会被清零
                    if(ucRowRecord == 1) //第一列输出低电平
                    {
                        ucKeySec = 9; //触发9号键 对应朱兆祺学习板的S9键
                    }
                    else if(ucRowRecord == 2) //第二列输出低电平
                    {
                        ucKeySec = 10; //触发10号键 对应朱兆祺学习板的S10键
                    }
                    else if(ucRowRecord == 3) //第三列输出低电平
                    {
                        ucKeySec = 11; //触发11号键 对应朱兆祺学习板的S11键
                    }
                    else   //第四列输出低电平
                    {
                        ucKeySec = 12; //触发12号键 对应朱兆祺学习板的S12键
                    }
                }

            }
            else if(key_sr1 == 1 && key_sr2 == 1 && key_sr3 == 1 && key_sr4 == 0)
            {
                uiKeyTimeCnt++;  //去抖动延时计数器
                if(uiKeyTimeCnt > const_key_time)
                {
                    uiKeyTimeCnt = 0;
                    ucKeyLock = 1; //自锁按键置位,避免一直触发,只有松开按键,此标志位才会被清零
                    if(ucRowRecord == 1) //第一列输出低电平
                    {
                        ucKeySec = 13; //触发13号键 对应朱兆祺学习板的S13键
                    }
                    else if(ucRowRecord == 2) //第二列输出低电平
                    {
                        ucKeySec = 14; //触发14号键 对应朱兆祺学习板的S14键
                    }
                    else if(ucRowRecord == 3) //第三列输出低电平
                    {
                        ucKeySec = 15; //触发15号键 对应朱兆祺学习板的S15键
                    }
                    else   //第四列输出低电平
                    {
                        ucKeySec = 16; //触发16号键 对应朱兆祺学习板的S16键
                    }
                }

            }

        }
        break;

    }


}


void key_service() //按键服务的应用程序
{
    switch(ucKeySec) //按键服务状态切换
    {
    case 1:// 数字1 对应朱兆祺学习板的S1键
        key_number_input(1); //输入数字按键
        uiVoiceCnt = const_voice_short; //按键声音触发,滴一声就停。
        ucKeySec = 0; //响应按键服务处理程序后,按键编号清零,避免一致触发
        break;
    case 2:// 数字2 对应朱兆祺学习板的S2键
        key_number_input(2); //输入数字按键
        uiVoiceCnt = const_voice_short; //按键声音触发,滴一声就停。
        ucKeySec = 0; //响应按键服务处理程序后,按键编号清零,避免一致触发
        break;
    case 3:// 数字3 对应朱兆祺学习板的S3键
        key_number_input(3); //输入数字按键
        uiVoiceCnt = const_voice_short; //按键声音触发,滴一声就停。
        ucKeySec = 0; //响应按键服务处理程序后,按键编号清零,避免一致触发
        break;
    case 4:// 数字4 对应朱兆祺学习板的S4键
        key_number_input(4); //输入数字按键
        uiVoiceCnt = const_voice_short; //按键声音触发,滴一声就停。
        ucKeySec = 0; //响应按键服务处理程序后,按键编号清零,避免一致触发
        break;
    case 5:// 数字5 对应朱兆祺学习板的S5键
        key_number_input(5); //输入数字按键
        uiVoiceCnt = const_voice_short; //按键声音触发,滴一声就停。
        ucKeySec = 0; //响应按键服务处理程序后,按键编号清零,避免一致触发
        break;
    case 6:// 数字6 对应朱兆祺学习板的S6键
        key_number_input(6); //输入数字按键
        uiVoiceCnt = const_voice_short; //按键声音触发,滴一声就停。
        ucKeySec = 0; //响应按键服务处理程序后,按键编号清零,避免一致触发
        break;
    case 7:// 数字7 对应朱兆祺学习板的S7键
        key_number_input(7); //输入数字按键
        uiVoiceCnt = const_voice_short; //按键声音触发,滴一声就停。
        ucKeySec = 0; //响应按键服务处理程序后,按键编号清零,避免一致触发
        break;
    case 8: //数字8 对应朱兆祺学习板的S8键
        key_number_input(8); //输入数字按键
        uiVoiceCnt = const_voice_short; //按键声音触发,滴一声就停。
        ucKeySec = 0; //响应按键服务处理程序后,按键编号清零,避免一致触发
        break;
    case 9:// 数字9 对应朱兆祺学习板的S9键
        key_number_input(9); //输入数字按键
        uiVoiceCnt = const_voice_short; //按键声音触发,滴一声就停。
        ucKeySec = 0; //响应按键服务处理程序后,按键编号清零,避免一致触发
        break;
    case 10:// 数字0  对应朱兆祺学习板的S10键
        key_number_input(0); //输入数字按键
        uiVoiceCnt = const_voice_short; //按键声音触发,滴一声就停。
        ucKeySec = 0; //响应按键服务处理程序后,按键编号清零,避免一致触发
        break;
    case 11:// 小数点按键 对应朱兆祺学习板的S11键
        key_number_input(11); //输入数字按键  11代表小数点
        uiVoiceCnt = const_voice_short; //按键声音触发,滴一声就停。
        ucKeySec = 0; //响应按键服务处理程序后,按键编号清零,避免一致触发
        break;
    case 12:// 本节暂时不用 对应朱兆祺学习板的S12键

        uiVoiceCnt = const_voice_short; //按键声音触发,滴一声就停。
        ucKeySec = 0; //响应按键服务处理程序后,按键编号清零,避免一致触发
        break;
    case 13:// 加按键 对应朱兆祺学习板的S13键
        ulData_1++;
        if(ulData_1 > 99999)
        {
            ulData_1 = 99999;
        }

        data_to_buffer(ulData_1,  //把数值转换成数组,这是本节核心函数,请好好研究此函数的具体功能。
                       2,  //小数点最大个数
                       6,  //数组缓冲最大个数
                       &ucDotCnt_1,
                       &ucDotBitS_1,
                       &ucWdPartCnt_1,
                       ucDataBuffer_1);  //被转换成的数组

        ucWd1Part1Update = 1; //实时更新显示数组
        ucWd1Part2Update = 1; //实时更新显示数值

        uiVoiceCnt = const_voice_short; //按键声音触发,滴一声就停。
        ucKeySec = 0; //响应按键服务处理程序后,按键编号清零,避免一致触发
        break;
    case 14:// 减按键  对应朱兆祺学习板的S14键
        ulData_1--;
        if(ulData_1 > 99999) //unsigned long类型的变量0减去1会变成0xffffffff
        {
            ulData_1 = 0;
        }

        data_to_buffer(ulData_1,  //把数值转换成数组,这是本节核心函数,请好好研究此函数的具体功能。
                       2,  //小数点最大个数
                       6,  //数组缓冲最大个数
                       &ucDotCnt_1,
                       &ucDotBitS_1,
                       &ucWdPartCnt_1,
                       ucDataBuffer_1);  //被转换成的数组

        ucWd1Part1Update = 1; //实时更新显示数组
        ucWd1Part2Update = 1; //实时更新显示数值

        uiVoiceCnt = const_voice_short; //按键声音触发,滴一声就停。
        ucKeySec = 0; //响应按键服务处理程序后,按键编号清零,避免一致触发
        break;
    case 15:// 本节暂时不用 对应朱兆祺学习板的S15键

        uiVoiceCnt = const_voice_short; //按键声音触发,滴一声就停。
        ucKeySec = 0; //响应按键服务处理程序后,按键编号清零,避免一致触发
        break;
    case 16:// 清除按键 对应朱兆祺学习板的S16键
        key_delete_input(); //删除按键
        uiVoiceCnt = const_voice_short; //按键声音触发,滴一声就停。
        ucKeySec = 0; //响应按键服务处理程序后,按键编号清零,避免一致触发
        break;
    }
}



void key_number_input(unsigned char ucKeyNumber) //输入数字按键
{

    switch(ucWd)
    {
    case 1:   //第1窗口。本节程序只有1个窗口
        switch(ucPart)
        {

        case 1:  //1窗口第1项
            set_data(ucKeyNumber,
                     2,  //小数点最大个数
                     6,  //数组缓冲最大个数
                     &ucDotCnt_1,
                     &ucDotBitS_1,
                     &ucWdPartCnt_1,
                     ucDataBuffer_1,
                     3, //整数部分的最大个数
                     &ucIntCnt_1);

            ulData_1 = buffer_to_data(6, 2, ucDataBuffer_1); //把带小数点的BCD码数组转换成long数值。
            ucWd1Part1Update = 1; //第一行局部更新显示
            ucWd1Part2Update = 1; //第二行局部更新显示
            break;
        }

        break;
    }

}


/* 注释三:
* 涉及到参数的4种信息,包括小数点的数量,小数点的个数,数据的位置,数组具体值,整数的数量,整数的个数,以及它们之间的相互作用关系。
* 以下参数,指针类型的参数是让代入的全局变量在退出函数后维持它当前最新更改的数值不变。
* 第1个参数ucKeyNumberTemp是当前按键输入的数值。
* 第2个参数ucDotBitMax是限定被设置参数的小数点最大位数。
* 第3个参数ucDataCntMax是限定被设置参数的最大数组个数。
* 第4个参数*p_ucDotCnt是记录当前输入的小数点数量,如果小数点的数量不为0,说明当前数组已包含小数点,此时再按小数点按键则无效。
* 第5个参数*p_ucDotBitS是记录当前输入的小数点个数,如果小数点的个数如果超过规定ucDotBitMax位,此时再按任何输入按键则无效
* 第6个参数*p_ucWdPartCnt是记录当前输入的数据在数组中的位置,方便锁定每次按键输入的数字显示位置。
* 第7个参数*p_ucSetDataBuffer是BCD码数组缓冲的具体数字内容。
* 第8个参数ucIntCntMax是限定被设置参数的整数部分的最大位数。
* 第9个参数*p_ucIntCnt是记录当前输入的整数部分个数,如果整数部分的个数如果超过规定ucIntCntMax位,此时再按任何输入按键则无效
*/
void set_data(unsigned char ucKeyNumberTemp,
              unsigned char ucDotBitMax,
              unsigned char ucDataCntMax,
              unsigned char *p_ucDotCnt,
              unsigned char *p_ucDotBitS,
              unsigned char *p_ucWdPartCnt,
              unsigned char *p_ucSetDataBuffer,
              unsigned char ucIntCntMax,
              unsigned char *p_ucIntCnt)
{
    unsigned int i;

    if(ucKeyNumberTemp == 11) //等于小数点
    {
        if(ucDotBitMax == 0) //如果限定的小数点最大数是0,就意味着此数据不允许带小数点,必须是整数。
        {
            return; //直接返回退出
        }
        else if(*p_ucDotCnt > 0) //小数点个数大于0,意味着当前数组已经包含了小数点,此时再输入小数点则无效。
        {
            return; //直接返回退出
        }
        else  //否则有效,记录当前已经包含一个小数点的信息。
        {
            *p_ucDotCnt = 1; //只能包含一个小数点
        }
    }
    else  //如果输入的不是小数点
    {
        if(*p_ucDotCnt == 1) //如果之前已经输入了一个小数点,那么此时输入的数字就是小数点后的数据
        {
            if(*p_ucDotBitS < ucDotBitMax) //如果小数点位数还没超过最大限制位数,则继续加1记录当前小数点位数。
            {
                *p_ucDotBitS = (*p_ucDotBitS) + 1;
            }
            else //如果小数点位数已经超过允许的范围,则输入的按键无效,直接退出。
            {
                return; //直接返回退出
            }
        }
        else if(*p_ucIntCnt < ucIntCntMax) //如果之前没有输入小数点,那么输入的就是整数个数超,整数个数没有超过极限
        {
            *p_ucIntCnt = (*p_ucIntCnt) + 1;
        }
        else //整数个数超过极限
        {
            return; //直接返回退出
        }
    }



    if(*p_ucWdPartCnt == 0 && p_ucSetDataBuffer[0] == 0 && ucKeyNumberTemp != 11) //如果当前默认位置是第0个位置,并且默认第0个数据是0,并且当前的按键输入不是小数点,则不用移位
    {
        ;
    }
    else  //否则,移位
    {
        for(i = 0; i < (ucDataCntMax - 1); i++) //移位
        {
            p_ucSetDataBuffer[ucDataCntMax - 1 - i] = p_ucSetDataBuffer[ucDataCntMax - 2 - i];
        }
        *p_ucWdPartCnt = (*p_ucWdPartCnt) + 1;
    }
    p_ucSetDataBuffer[0] = ucKeyNumberTemp; //当前输入的数字或者小数点永远在第右边第0个位置。



}


/* 注释四:
* 功能:把一个带小数点的BCD码数组转换成一个long类型的数值。
* 第1个参数ucConverDataSize是这个数组的最大有效个数。
* 第2个参数ucConverDotCnt是这个数组要转换成的long数值带几个小数点
* 第3个参数*p_ucConverBuffer是具体此数组的数据
* 函数最后返回被转换的long数值。
*/
unsigned long buffer_to_data(unsigned char ucConverDataSize, unsigned char ucConverDotCnt, unsigned char *p_ucConverBuffer)
{
    unsigned long ulConverResult = 0;
    unsigned long ulConverResultTemp = 0;
    unsigned char ucConverResultBuffer[6]; //因为本节内容的ucConverDataSize是6,所以取6.
    unsigned char i;
    unsigned char j;
    unsigned char ucConverFlag;

    for(i = 0; i < ucConverDataSize; i++)
    {
        ucConverResultBuffer[i] = 0; //先把临时缓冲区清零
    }

    j = 0;
    ucConverFlag = 0;
    for(i = 0; i < ucConverDataSize; i++)
    {
        if(p_ucConverBuffer[i] == 11) //小数点
        {
            ucConverFlag = i; //记录小数点的位置
        }
        else if(p_ucConverBuffer[i] < 10)
        {
            ucConverResultBuffer[j] = p_ucConverBuffer[i]; //提取数组中的有效数字
            j++;
        }


    }


    for(i = 0; i < ucConverDataSize; i++) //通过处理每一位从而合成一个long类型的数值
    {
        ulConverResultTemp = 0;
        ulConverResultTemp = ucConverResultBuffer[i];
        for(j = 0; j < i; j++)
        {
            ulConverResultTemp = ulConverResultTemp * 10; //把每一位对应的进位扩大到对应的倍数
        }
        ulConverResult = ulConverResult + ulConverResultTemp;
    }


    for(i = ucConverFlag; i < ucConverDotCnt; i++) //根据数组小数点的位置和实际要转换成的小数点个数,来扩大到对应的倍数。
    {
        ulConverResult = ulConverResult * 10;
    }

    return ulConverResult;
}


/* 注释五:
* 本节的核心函数,值得好好研究!
* 功能:把一个long类型的数值转换成一个带小数点的BCD码数组
* 第1个参数ulWillConverData是即将被转换的unsigned long类型数值。
* 第2个参数ucConverDotCnt是这个数值带几个小数点
* 第3个参数ucConverDataSize是这个数组的最大有效个数。
* 第4个参数*p_ucDotCnt是记录当前输入的小数点数量,如果小数点的数量不为0,说明当前数组已包含小数点,此时再按小数点按键则无效。
* 第5个参数*p_ucDotBitS是记录当前输入的小数点个数,如果小数点的个数如果超过规定ucDotBitMax位,此时再按任何输入按键则无效
* 第6个参数*p_ucWdPartCnt是记录当前输入的数据在数组中的位置,方便锁定每次按键输入的数字显示位置。
* 第7个参数*p_ucConverBuffer是具体此数组的数据。
*/

void data_to_buffer(unsigned long ulWillConverData,
                    unsigned char ucConverDotCnt,
                    unsigned char ucConverDataSize,
                    unsigned char *p_ucDotCnt,
                    unsigned char *p_ucDotBitS,
                    unsigned char *p_ucWdPartCnt,
                    unsigned char *p_ucConverBuffer)
{

    unsigned char ucConverResultBuffer[6]; //因为本节内容的ucConverDataSize是6,所以取6.
    unsigned char i;
    unsigned char ucValidaDotCnt = 0;

    if(ucConverDotCnt == 0) //没有小数点
    {
        *p_ucDotCnt = 0; //当前没有输入小数点的标志
        *p_ucDotBitS = 0; //当前输入的小数点个数是0

        ucConverResultBuffer[5] = 10; //没有小数点的时候,第5位必然是显示空格

        //以下是具体把数值转换成数组,不需要显示的高位填入10表示显示空格
        if(ulWillConverData >= 10000)
        {
            ucConverResultBuffer[4] = ulWillConverData % 100000 / 10000;
        }
        else
        {
            ucConverResultBuffer[4] = 10;
        }

        if(ulWillConverData >= 1000)
        {
            ucConverResultBuffer[3] = ulWillConverData % 10000 / 1000;
        }
        else
        {
            ucConverResultBuffer[3] = 10;
        }

        if(ulWillConverData >= 100)
        {
            ucConverResultBuffer[2] = ulWillConverData % 1000 / 100;
        }
        else
        {
            ucConverResultBuffer[2] = 10;
        }

        if(ulWillConverData >= 10)
        {
            ucConverResultBuffer[1] = ulWillConverData % 100 / 10;
        }
        else
        {
            ucConverResultBuffer[1] = 10;
        }


        ucConverResultBuffer[0] = ulWillConverData % 10;


    }
    else if(ucConverDotCnt == 1) //1位小数点
    {
        *p_ucDotCnt = 1; //当前已经有输入小数点的标志
        *p_ucDotBitS = 1; //当前输入的小数点个数是1
        ucConverResultBuffer[1] = 11; //第1位填入小数点11

        //以下是具体把数值转换成数组,不需要显示的高位填入10表示显示空格
        if(ulWillConverData >= 10000)
        {
            ucConverResultBuffer[5] = ulWillConverData % 100000 / 10000;
        }
        else
        {
            ucConverResultBuffer[5] = 10;
        }

        if(ulWillConverData >= 1000)
        {
            ucConverResultBuffer[4] = ulWillConverData % 10000 / 1000;
        }
        else
        {
            ucConverResultBuffer[4] = 10;
        }

        if(ulWillConverData >= 100)
        {
            ucConverResultBuffer[3] = ulWillConverData % 1000 / 100;
        }
        else
        {
            ucConverResultBuffer[3] = 10;
        }


        ucConverResultBuffer[2] = ulWillConverData % 100 / 10;
        ucConverResultBuffer[0] = ulWillConverData % 10;


    }
    else if(ucConverDotCnt == 2) //2位小数点
    {
        *p_ucDotCnt = 1; //当前已经有输入小数点的标志
        *p_ucDotBitS = 2; //当前输入的小数点个数是2

        ucConverResultBuffer[2] = 11; //第2位填入小数点11

        //以下是具体把数值转换成数组,不需要显示的高位填入10表示显示空格
        if(ulWillConverData >= 10000)
        {
            ucConverResultBuffer[5] = ulWillConverData % 100000 / 10000;
        }
        else
        {
            ucConverResultBuffer[5] = 10;
        }

        if(ulWillConverData >= 1000)
        {
            ucConverResultBuffer[4] = ulWillConverData % 10000 / 1000;
        }
        else
        {
            ucConverResultBuffer[4] = 10;
        }


        ucConverResultBuffer[3] = ulWillConverData % 1000 / 100;
        ucConverResultBuffer[1] = ulWillConverData % 100 / 10;
        ucConverResultBuffer[0] = ulWillConverData % 10;
    }


    ucValidaDotCnt = 0;
    for(i = 0; i < ucConverDataSize; i++)
    {
        if(ucConverResultBuffer[i] != 10) //统计数组有效的BCD码位数
        {
            ucValidaDotCnt++;
        }
        p_ucConverBuffer[i] = ucConverResultBuffer[i]; //把转换的结果传输给实际的数组用来外部显示

    }

    *p_ucWdPartCnt = ucValidaDotCnt - 1; //当前显示的实际位置
}

void key_delete_input(void) //删除按键
{
    static unsigned int i;

    switch(ucWd)
    {
    case 1:   //第1窗口。本节程序只有1个窗口
        switch(ucPart)
        {
        case 1:  //1窗口第1行
            //清零
            ulData_1 = 0; //long数值清零
            ucIntCnt_1 = 0;
            ucDotBitS_1 = 0;
            ucDotCnt_1 = 0;
            ucWdPartCnt_1 = 0;
            for(i = 0; i < 6; i++)
            {
                ucDataBuffer_1[i] = 10;
            }
            ucDataBuffer_1[0] = 0; //第0个位置填入0

            ucWd1Part1Update = 1; //第一行局部更新显示
            ucWd1Part2Update = 1; //第二行局部更新显示
            break;
        case 2:  //1窗口第2行
            //清零
            ulData_1 = 0; //long数值清零
            ucIntCnt_1 = 0;
            ucDotBitS_1 = 0;
            ucDotCnt_1 = 0;
            ucWdPartCnt_1 = 0;
            for(i = 0; i < 6; i++)
            {
                ucDataBuffer_1[i] = 10;
            }
            ucDataBuffer_1[0] = 0; //第0个位置填入0

            ucWd1Part1Update = 1; //第一行局部更新显示
            ucWd1Part2Update = 1; //第二行局部更新显示
            break;
        }

        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;
    case 11:   //小数点
        p_ucAnyNumber = Zf816_dot;
        break;
    default:   //如果上面的条件都不符合,那么默认指向空字模
        p_ucAnyNumber = Zf816_nc;
        break;
    }

    return p_ucAnyNumber;  //返回转换结束后的指针
}



void lcd_display_service(void) //应用层面的液晶屏显示程序
{


    static unsigned char *p_ucAnyNumber; //经过数字转换成字模后,分解变量的某位字模首地址
    static unsigned char ucCursorFlag;  //光标标志,也就是反显的标志,它是根据局部变量ucPart来定的
    static unsigned int i;
    static unsigned char ucDataBuffer_temp[6]; //分解一个10进制的long类型数据的每一位

    switch(ucWd)  //本程序的核心变量,窗口显示变量。类似于一级菜单的变量。代表显示不同的窗口。
    {
    case 1:   //显示窗口1的数据
        if(ucWd1Update == 1) //窗口1整屏更新,里面只放那些不用经常刷新显示的内容
        {
            ucWd1Update = 0; //及时清零,避免一直更新

            ucWd1Part1Update = 1; //激活窗口1的第1行局部更新显示变量,这里在前面数码管显示框架上有所改进
            ucWd1Part2Update = 1; //激活窗口1的第2行局部更新显示变量,这里在前面数码管显示框架上有所改进

            display_clear(0x00); // 清屏操作, 全部显示空填充0x00,全部显示点阵用0xff。
            clear_all_canvas();  //把画布全部清零

            display_lattice(0, 0, Hz1616_yi, 0, 2, 16, 0); //一项数组
            display_lattice(1, 0, Hz1616_xiang, 0, 2, 16, 0);
            display_lattice(2, 0, Hz1616_shu, 0, 2, 16, 0);
            display_lattice(3, 0, Hz1616_zhu, 0, 2, 16, 0);
            display_lattice(4, 0, Zf816_mao_hao, 0, 1, 16, 0); //冒号

            display_lattice(0, 16, Hz1616_yi, 0, 2, 16, 0); //一项数值
            display_lattice(1, 16, Hz1616_xiang, 0, 2, 16, 0);
            display_lattice(2, 16, Hz1616_shu, 0, 2, 16, 0);
            display_lattice(3, 16, Hz1616_zhi, 0, 2, 16, 0);
            display_lattice(4, 16, Zf816_mao_hao, 0, 1, 16, 0); //冒号

        }

        if(ucWd1Part1Update == 1) //窗口1的第1行局部更新显示变量,里面放一些经常需要刷新显示的内容
        {
            ucWd1Part1Update = 0; //及时清零,避免一直更新

            if(ucPart == 1) //被选中
            {
                ucCursorFlag = 1; //反显 显示
            }
            else //没被选中
            {
                ucCursorFlag = 0; //正常 显示
            }


            for(i = 0; i < 6; i++) //把每个数组缓冲的字模依次插入画布
            {
                p_ucAnyNumber = number_to_matrix(ucDataBuffer_1[5 - i]);
                insert_buffer_to_canvas(i, 0, p_ucAnyNumber, 0, 1, 16); //这里的i是画布的横向地址,一共可以显示6个字符,因此取值范围是0到5
            }

            display_lattice(5, 0, ucCanvasBuffer, ucCursorFlag, 6, 16, 0); //显示整屏的画布,最后的参数0是偏移量
        }

        if(ucWd1Part2Update == 1) //窗口1的第2行局部更新显示变量,里面放一些经常需要刷新显示的内容
        {
            ucWd1Part2Update = 0; //及时清零,避免一直更新

            if(ucPart == 2) //被选中
            {
                ucCursorFlag = 1; //反显 显示
            }
            else //没被选中
            {
                ucCursorFlag = 0; //正常 显示
            }

            if(ulData_1 >= 10000)
            {
                ucDataBuffer_temp[5] = ulData_1 % 100000 / 10000;
            }
            else
            {
                ucDataBuffer_temp[5] = 10; //空格
            }

            if(ulData_1 >= 1000)
            {
                ucDataBuffer_temp[4] = ulData_1 % 10000 / 1000;
            }
            else
            {
                ucDataBuffer_temp[4] = 10; //空格
            }

            ucDataBuffer_temp[3] = ulData_1 % 1000 / 100;
            ucDataBuffer_temp[2] = 11; //11代表小数点
            ucDataBuffer_temp[1] = ulData_1 % 100 / 10;
            ucDataBuffer_temp[0] = ulData_1 % 10 / 1;

            for(i = 0; i < 6; i++) //把每个数组缓冲的字模依次插入画布
            {
                p_ucAnyNumber = number_to_matrix(ucDataBuffer_temp[5 - i]);
                insert_buffer_to_canvas(i, 0, p_ucAnyNumber, 0, 1, 16); //这里的i是画布的横向地址,一共可以显示6个字符,因此取值范围是0到5
            }

            display_lattice(5, 16, ucCanvasBuffer, ucCursorFlag, 6, 16, 0); //显示整屏的画布,最后的参数0是偏移量
        }


        break;
        //本程序只有1个窗口,所以只有一个case 1,如果要增加窗口,就直接增加 case 2, case 3...
    }

}



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); //开显示缓冲指令

}

/* 注释六:
* 注意,这节内容的画布跟第79节前面的画布大小不一样,第79节前面的横向是4个字节,这节的横向是6个字节。
* 把字模插入画布的函数.
* 这是本节的核心函数,读者尤其要搞懂x_amount和y_amount对应的显示关系。
* 第1,2个参数x,y是在画布中的坐标体系。
* x的范围是0至5,因为画布的横向只要6个字节。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) * 6 + x + i] = ucTemp; //这里的6代表画布每一行只有6个字节。前面章节的横向是4个字节,要稍微注意的。
            }
            else
            {
                ucCanvasBuffer[(y + j) * 6 + x + i] = ~ucTemp; //这里的6代表画布每一行只有6个字节。前面章节的横向是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++) //内嵌循环的空指令数量
        {
            ; //一个分号相当于执行一条空语句
        }
    }
}

总结陈词:

前面两节都讲了数组和数值的相互转换函数,结合前面的基础,下一节讲数字键盘与液晶菜单的综合程序,欲知详情,请听下回分解----数字键盘与液晶菜单的综合程序。

(未完待续,下节更精彩,不要走开哦)

C
1
https://gitee.com/F4NNIU/FN1895E-MCU101.git
git@gitee.com:F4NNIU/FN1895E-MCU101.git
F4NNIU
FN1895E-MCU101
FN1895E-MCU101
Development

搜索帮助