美文网首页
基于stm32的多功能时钟5——LCD12864库函数的建立

基于stm32的多功能时钟5——LCD12864库函数的建立

作者: 世缘 | 来源:发表于2020-02-05 16:53 被阅读0次

            好久不见,我的小可爱们~

            在上一章中,小编实现了多功能时钟的测距功能。在这一章中,小编本来打算介绍人机交互界面的设计。但是,后来还是想了一下,先将LCD库函数建立起来,因为真正的技术重点在这里。至于UI的设计,咱们将在下一章介绍。但至于UI设计的美观程度,我只能尽力而为,毕竟咱们的LCD12864液晶分别率只有128*64,而且是单色的,先天不足。


    1.模块介绍

    LCD12864液晶显示屏

            液晶显示器种类很多,本人目前用过LCD1602、LCD12864、Nokia5110等,还有些比如TFT彩屏,OLED都没有用过。而此块LCD12864是一款基于ST7567的128*64的1.7英寸的图形点阵式液晶。而LCD12864液晶有的可以并口传输数据,有的可以串口传输数据,而我们这块LCD12864是以串行口的方式传输数据,并且采用的是SPI协议,后面我会重点讲一下这个协议。

            这里,介绍一下LCD12864库函数的编写。当然,这里很多都需要查阅数据手册的(注:数据手册真的很重要,不懂就查,这个就相当于课本,前提是你基础也要扎实一些,这样看起手册不那么费劲)。

    LCD12864液晶屏引脚

            首先,介绍一下液晶的主要控制引脚:BL为背光源,打开之后,液晶就变得更亮了;RESET为复位,即液晶恢复到初始状态;A0为数据或命令选择,若为1,写数据,若为0,写命令;CSB:片选信号,低电平时允许写操作;SCL为时钟线,SDA为数据线。这样的控制就是基于SPI协议进行数据的同步串行传输,在移位脉冲下,数据按位传输,高位在前,低位在后,为全双工通信,数据传输速度总体来说比I2C总线要快。

           其次,要写LCD12864的驱动函数,要查看芯片的时序图,如下图。

    SPI时序图

            每次在传输数据前,需要将CS端拉低,设置A0的电平,指定操作是写指令还是写数据,然后SDA一次取字节数据的高位到低位进行发送,每一次发送需要一个SCL的上升沿,在发送完1字节数据后,CS端需要拉高,这样1字节的数据就发送完毕了。

            这里,LCD12864液晶的指令表这里不再阐述,自己上网查询(小编直接就拿现成的指令用的)。

            最后,关于LCD12864液晶的扫描方式,这里重点说一下。LCD12864从上到下分为8页,也就是第1行~第8行为第0页,往下类推,到第57行~第64行为第7页。而LCD12864从左到右分为128列,即第0列到第127列。我在写LCD的时候,需要先写页地址,然后在写列地址,最后写数据。而且这里的列地址要分两次写,先将列地址高4位与0x10进行或运算写入,再将列地址低4位写入。

            关于写入字符和汉字等,都需要实现建立字模数据,这里,大家可以用"字模提取V2.2"软件,如果不会的,可以上网查阅相关的资料。至此,就可以编写相关驱动和控制代码了。

    2.软件编程

    (1)编写相关控制引脚的GPIO

    /*lcd的GPIO配置*/

    void lcd_gpio_init(void)

    {

        GPIO_InitTypeDef GPIO_InitStructure;

        RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);

        GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5|GPIO_Pin_7;

        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;

        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

        GPIO_Init(GPIOA, &GPIO_InitStructure);

        RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);

        GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2|GPIO_Pin_3|GPIO_Pin_4|GPIO_Pin_5;

        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;

        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

        GPIO_Init(GPIOB, &GPIO_InitStructure);

        RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);

        GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE);

    }

            将LCD12864控制引脚设置成推挽输出模式,开启相关的RCC时钟。这里,有一点别忘了,由于stm32的PB3,PB4的初始默认功能为JTAG的相关引脚,所以需要开启复用功能,才能作为普通的I/O口使用。

    (2)编写 写操作函数:包括写数据函数和写指令函数

    /*lcd写数据函数*/

    void lcd_write_data(u8 data)

    {

        u8 i;

        LCD_CS_H;

        LCD_SCL_H;

        LCD_A0_H;

        LCD_CS_L;

        for(i=0; i<8; i++)

        {

            LCD_SCL_L;

            if(data&0x80)

            {

                LCD_SDA_H;

            }

            else

            {

                LCD_SDA_L;

            }

            LCD_SCL_H;

            data <<= 1;

        }

        LCD_CS_H;

    }

    /*lcd写指令函数*/

    void lcd_write_cmd(u8 cmd)

    {

        u8 i;

        LCD_CS_H;

        LCD_SCL_H;

        LCD_A0_L;

        LCD_CS_L;

        for(i=0; i<8; i++)

        {

            LCD_SCL_L;

            if(cmd&0x80)

            {

                LCD_SDA_H;

            }

            else

            {

                LCD_SDA_L;

            }

            LCD_SCL_H;

            cmd <<= 1;

        }

        LCD_CS_H;

    }

    写数据函数和写指令函数就根据时序图进行编写,并且注意如何写字节数据的某位以及移位运算符的使用方法,这里不需要做任何的延时。

    (3)编写LCD初始化函数

    /*lcd复位函数*/

    void HDReset(void)

    {

        LCD_RST_L;

        lcd_delay(2);

        LCD_RST_H;

        lcd_delay(4);

    }

    /*lcd延时函数*/

    void lcd_delay(u16 value)

    {

        u16 i,j;

        for(i=0;i<value;i++)

            for(j=0;j<500;j++);

    }

    /*lcd初始化函数*/

    void lcd_Init(void)

    {

        lcd_gpio_init();

        lcd_delay(10);

        HDReset();

        lcd_delay(100);

        lcd_write_cmd(0xe2);

        lcd_write_cmd(0xa2);

        lcd_write_cmd(0xa0);

        lcd_write_cmd(0xc8);

        lcd_write_cmd(0xa4);

        lcd_write_cmd(0xa6);

        lcd_write_cmd(0x25);

        lcd_write_cmd(0x81);

        lcd_write_cmd(0x1a);

        lcd_write_cmd(0x2f);

        lcd_write_cmd(0x40);

        lcd_write_cmd(0xaf);

        LCD_BK_ON;

    }

    (4)编写清屏函数

    void lcd_clearscreen(void)

    {

        u8 i,j;

        for(i=0; i<8; i++)

        {

            lcd_write_cmd(0xb0+i);                      //写页地址

            for(j=0; j<128; j++)

            {

                lcd_write_cmd(0x10+((j&0xf0)>>4));      //写列地址

                lcd_write_cmd(0x00+(j&0x0f));

                lcd_write_data(0x00);                  //写数据

            }

        }

    }

    (5)建立字模数据库

            这里,我们先建立font.h头文件,然后通过字模软件取模,将数据存放在里面。这里,重点讲一下汉字如何存放。首先,我们先定义一个方便对汉字进行查找的结构体。

    /*定义新的数据结构,用以方便地对汉字进行索引*/

    typedef struct

    {

        u8 index[2];//定义汉字索引

        u8 charmode[32];//定于汉字字模

    }CHAR;

    然后,定义一个存放汉字字模数据的数组。

    CHAR const str[] = {

        /*--  文字:  时  --*/

        /*--  宋体12;  此字体下对应的点阵为:宽x高=16x16  --*/

        {"时",0x00,0xFC,0x84,0x84,0x84,0xFC,0x00,0x10,0x10,0x10,0x10,0x10,0xFF,0x10,0x10,0x00,

        0x00,0x3F,0x10,0x10,0x10,0x3F,0x00,0x00,0x01,0x06,0x40,0x80,0x7F,0x00,0x00,0x00},

    //后面省略

            编写写字符串函数思路:首先,取字符串当前字符,并判断是不是结束字符,如果不是,通过此字符与字符串数组里的汉字索引进行对比,即查找字库。如果找到相等的,则在LCD刷新该汉字索引后的字模数据,否则不刷新。最后,取下一个字符,依次类推。

    (6)编写写数字函数、写字符函数、写汉字字符串函数

    /*lcd显示数字函数*/

    void lcd_display_num_m(u8 page, u8 column, u8 num)

    {

        u8 i,j,column_H,column_L;

        for(i=0; i<2; i++)

        {

            lcd_write_cmd(0xb0+page+i);

            for(j=0; j<8; j++)

            {

                column_H = 0x10|((column+j)>>4)&0x0f;

                column_L = (column+j)&0x0f;

                lcd_write_cmd(column_H);

                lcd_write_cmd(column_L);

                lcd_write_data(num_m[num][j+i*8]);

            }

        }

    }

    /*lcd显示字符函数*/

    void lcd_display_letter_m(u8 page, u8 column, u8 letter)

    {

        u8 i,j,column_H,column_L;

        for(i=0; i<2; i++)

        {

            lcd_write_cmd(0xb0+page+i);

            for(j=0; j<8; j++)

            {

                column_H = 0x10|((column+j)>>4)&0x0f;

                column_L = (column+j)&0x0f;

                lcd_write_cmd(column_H);

                lcd_write_cmd(column_L);

                lcd_write_data(letter_m[letter-65][j+i*8]);

            }

        }

    }

    /*lcd显示字符串函数*/

    void lcd_display_string(u8 page, u8 column, u8 *string)

    {

        u8 i,j,wordnum,column_H,column_L;

        while(*string!='\0')

        {

    for(wordnum=0; wordnum<80; wordnum++)

    {

                if(*string==str[wordnum].index[0]&&*(string+1)==str[wordnum].index[1])

                {

                    for(i=0; i<2; i++)

                    {

                        lcd_write_cmd(page+0xb0+i);

                        for(j=0; j<16; j++)

                        {

                            column_H = 0x10|((j+column)>>4)&0x0f;

                            column_L = (j+column)&0x0f;

                            lcd_write_cmd(column_H);

                            lcd_write_cmd(column_L);

                            lcd_write_data(str[wordnum].charmode[j+i*16]);

                        }

                    }

    column += 16;

                    break;//如果找到,则跳出

                }

    }

    string += 2;

        }

    }


            至此,我们的LCD12864库函数就搭建结束了。之后的UI界面设计就靠这些函数了。在这一章中,我并没有急着去介绍多功能时钟的UI界面介绍,原因在于你会LCD12864的库函数,那么就可以自己调用进行设计了,所以真正的干货在这一章节。在下一章中,我将真正开始UI界面的介绍,并且配合按键的控制,将会有一个不一样的世界呈现在你的面前。

    相关文章

      网友评论

          本文标题:基于stm32的多功能时钟5——LCD12864库函数的建立

          本文链接:https://www.haomeiwen.com/subject/kjhmthtx.html