3 Star 15 Fork 4

建伟F4nniu / FN1895E-MCU101

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

#第八十二节:如何通过调用液晶屏内部字库把一个任意数值的变量显示出来。

开场白:

本来这一节打算开始讲调用液晶屏内部字库时的反显程序,但是我担心跳跃太大,恐怕很多初学者跟不上,所以多插入这一节讲讲后面菜单程序中经常用到的基本功能,在调用内部字库的情况下,如何把一个任意数值的变量显示在液晶屏上。这一节的功能需求跟前面第76节是一模一样的,只不过前面的不是用自带字库,现在的是用自带字库而已。我们还是需要做一个变量转换成ASCII码的函数,以后只要调用这个转换函数就可以了。这一节就要把这个转换函数和框架思路教给大家。

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

  • (1)硬件平台:
  • 基于朱兆祺51单片机学习板。
  • (2)实现功能:我们定义一个char型的全局变量,把它默认初始化为218,开机上电后,能看到正中间恰好显示这个全局变量的数值218。大家也可以试着更改它的默认初始值,只要不超过char型最大数值255范围,我们就会看到它上电后显示的就是这个初始值。

  • (3)源代码讲解如下:

#include "REG52.H"

sbit  LCDCS_dr  = P1 ^ 6; //片选线
sbit  LCDSID_dr = P1 ^ 7; //串行数据线
sbit  LCDCLK_dr = P3 ^ 2; //串行时钟线
sbit  LCDRST_dr = P3 ^ 4; //复位线

sbit beep_dr = P2 ^ 7; //蜂鸣器的驱动IO口

void initial_myself(void);
void initial_peripheral(void);
void delay_long(unsigned int uiDelaylong);

unsigned char *number_to_ASCII(unsigned char  ucBitNumber);
void display_service(void); //显示服务程序,在main函数里


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(void); // 清屏。4行8列的坐标点全部显示2个空字符相当于清屏了。
void display_double_code(unsigned int x, unsigned int y, const unsigned char ucArray1, const unsigned char  ucArray2); //在一个坐标点显示1个汉字或者2个字符的函数
void delay_short(unsigned int uiDelayshort); //延时


code unsigned char  ucAddrTable[] = //调用内部字库时,液晶屏的坐标体系,位置编码,是驱动内容,读者可以不用深究它的含义。
{
    0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
    0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
    0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
    0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
};

code unsigned char ASCII816_0[] = "0"; //0  对于数组内的字符,编译会自动翻译成 ASCII码(1字节)
code unsigned char ASCII816_1[] = "1"; //1
code unsigned char ASCII816_2[] = "2"; //2
code unsigned char ASCII816_3[] = "3"; //3
code unsigned char ASCII816_4[] = "4"; //4
code unsigned char ASCII816_5[] = "5"; //5
code unsigned char ASCII816_6[] = "6"; //6
code unsigned char ASCII816_7[] = "7"; //7
code unsigned char ASCII816_8[] = "8"; //8
code unsigned char ASCII816_9[] = "9"; //9
code unsigned char ASCII816_nc[] = " "; //空格

/* 注释一:
* 以下变量就是本程序的任意变量,网友可以自己更改它的大小来测试本程序,不要超过255.
*/
unsigned char ucAnyNumber = 218; //任意变量默认初始化为218。
unsigned char ucWd1Part1Update = 1; //窗口1的第1个局部更新显示变量  1代表更新显示,响应函数内部会清零


void main()
{
    initial_myself();
    delay_long(100);
    initial_peripheral();

    while(1)
    {
        display_service(); //显示服务程序
    }

}



/* 注释二:在一个坐标点显示1个汉字或者2个字符的函数
* 第1,2个参数x,y是坐标体系。x的范围是0至8,y的范围是0至3.
* 第3个参数ucArray1是第1个汉字机内码或者ASCII码。
* 第4个参数ucArray2是第2个汉字机内码或者ASCII码。
*/
void display_double_code(unsigned int x, unsigned int y, const unsigned char ucArray1, const unsigned char  ucArray2)
{
    WriteCommand(0x30);   //基本指令集
    WriteCommand(ucAddrTable[8 * y + x]);    //起始位置
    LCDWriteData(ucArray1);
    LCDWriteData(ucArray2);
}


void display_clear(void) // 清屏。4行8列的坐标点全部显示2个空字符相当于清屏了。
{

    unsigned int i, j;
    for(i = 0; i < 4; i++)
    {
        for(j = 0; j < 8; j++)
        {
            display_double_code(j, i, 0x20, 0x20); //0x20是空格的ASCII码
        }
    }


}

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;
}



/* 注释三:
* 本程序的核心转换函数。
* 是可以把一位任意数字变量的函数转换成对应的ASCII码,由于ASCII码放在数组里,所以返回的是指针,代表数组的首地址。
*/
unsigned char *number_to_ASCII(unsigned char  ucBitNumber)
{
    unsigned char *p_ucAnyNumber;  //此指针根据ucBitNumber数值的大小,分别调用不同的ASCII码。

    switch(ucBitNumber)  //根据ucBitNumber数值的大小,分别调用不同的ASCII码。
    {
    case 0:
        p_ucAnyNumber = ASCII816_0;
        break;
    case 1:
        p_ucAnyNumber = ASCII816_1;
        break;
    case 2:
        p_ucAnyNumber = ASCII816_2;
        break;
    case 3:
        p_ucAnyNumber = ASCII816_3;
        break;
    case 4:
        p_ucAnyNumber = ASCII816_4;
        break;
    case 5:
        p_ucAnyNumber = ASCII816_5;
        break;
    case 6:
        p_ucAnyNumber = ASCII816_6;
        break;
    case 7:
        p_ucAnyNumber = ASCII816_7;
        break;
    case 8:
        p_ucAnyNumber = ASCII816_8;
        break;
    case 9:
        p_ucAnyNumber = ASCII816_9;
        break;
    case 10:
        p_ucAnyNumber = ASCII816_nc;
        break;
    default:   //如果上面的条件都不符合,那么默认指向空格ASCII码
        p_ucAnyNumber = ASCII816_nc;
        break;
    }

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


void display_service(void) //显示服务程序,在main函数里
{
    /* 注释四:
    * 这里的局部变量用static关键词修饰,是因为这个函数一直在主函数while(1)里循环扫描,我不希望它每次进来这个函数
    * 都多花几条指令去初始化这些局部变量,这样会多耗掉几个指令,所以我就用static关键字避免了这种情况,让这些局部变量
    * 只在上电那一刻就初始化了,以后每次进来这个函数不用再初始化这些变量。
    */
    static unsigned char ucAnyNumber_1; //分解变量的个位
    static unsigned char ucAnyNumber_10; //分解变量的十位
    static unsigned char ucAnyNumber_100; //分解变量的百位

    static unsigned char *p_ucAnyNumber_1; //经过数字转换成字模后,分解变量的个位字模首地址
    static unsigned char *p_ucAnyNumber_10; //经过数字转换成字模后,分解变量的十位字模首地址
    static unsigned char *p_ucAnyNumber_100; //经过数字转换成字模后,分解变量的百位字模首地址


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

        if(ucAnyNumber >= 100) //有3位数以上
        {
            ucAnyNumber_100 = ucAnyNumber / 100; //百位
        }
        else //否则显示空
        {
            ucAnyNumber_100 = 10; //在下面的转换函数中,代码10表示空字模
        }

        if(ucAnyNumber >= 10) //有2位数以上
        {
            ucAnyNumber_10 = ucAnyNumber % 100 / 10; //十位
        }
        else //否则显示空
        {
            ucAnyNumber_10 = 10; //在下面的转换函数中,代码10表示空字模
        }

        ucAnyNumber_1 = ucAnyNumber % 10 / 1; //个位

        p_ucAnyNumber_100 = number_to_ASCII(ucAnyNumber_100); //把数字转换成字符ASCII码
        p_ucAnyNumber_10 = number_to_ASCII(ucAnyNumber_10); //把数字转换成字符ASCII码
        p_ucAnyNumber_1 = number_to_ASCII(ucAnyNumber_1);   //把数字转换成字符ASCII码

        display_double_code(2, 1, ASCII816_nc[0], p_ucAnyNumber_100[0]); //液晶屏的显示驱动函数  这里的ASCII816_nc[0]代表填充显示一个空格字符
        display_double_code(3, 1, p_ucAnyNumber_10[0], p_ucAnyNumber_1[0]); //液晶屏的显示驱动函数

    }
}


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++) //内嵌循环的空指令数量
        {
            ; //一个分号相当于执行一条空语句
        }
    }
}


void initial_myself(void)  //第一区 初始化单片机
{

    beep_dr = 1; //用PNP三极管控制蜂鸣器,输出高电平时不叫。



}

void initial_peripheral(void) //第二区 初始化外围
{

    LCDInit(); //初始化12864 内部包含液晶模块的复位
    WriteCommand(0x0C); //命令字0x0c表示用内部字库模式。命令字0x36表示用自构字库模式。
    display_clear(); // 清屏。4行8列的坐标点全部显示2个空字符相当于清屏了。

}

总结陈词:

在液晶屏程序里,经常要用到反显的功能来表示选中某一项菜单。在调用内部字库时,这样的驱动程序又该怎么写?欲知详情,请听下回分解-----如何在调用液晶屏内部字库时让某行内容反显。

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

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

搜索帮助