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

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

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


1.模块介绍

基于stm32的多功能时钟5——LCD12864库函数的建立_第1张图片
LCD12864液晶显示屏

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

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

基于stm32的多功能时钟5——LCD12864库函数的建立_第2张图片
LCD12864液晶屏引脚

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

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

基于stm32的多功能时钟5——LCD12864库函数的建立_第3张图片
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

        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库函数的建立)