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

发布者:智慧启迪最新更新时间:2025-01-14 来源: jianshu关键字:stm32  多功能时钟  LCD12864  库函数 手机看文章 扫描二维码
随时随地手机看文章

        在上一章中,小编实现了多功能时钟的测距功能。在这一章中,小编本来打算介绍人机交互界面的设计。但是,后来还是想了一下,先将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        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!='')

    {

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  多功能时钟  LCD12864  库函数 引用地址:基于stm32的多功能时钟5——LCD12864库函数的建立

上一篇:stm32中主频的超频实验
下一篇:基于stm32的多功能时钟7——报警模块设计

推荐阅读最新更新时间:2026-03-25 11:25

基于stm32多功能时钟10——数据采集与蓝牙控制
嘿,我的小可爱们! 在《蓝牙串口通信》这一章中,小编带着大家编写了蓝牙串口通信程序,测试了蓝牙通信正常。由于我后来又找到了《蓝牙调试器》软件,功能强大,可自定义控件,所以下面将编写通信协议,通过这款软件,来实现数据采集和蓝牙控制。而我们在上一章中,完成了蓝牙监控界面的设计,所以这一章中,我们开始讲解程序的编写。 首先,我们要将数据包设置成结构体的形式,便于后面的操作和管理,同时定义接收数据堆栈和发送数据堆栈。之所以这样做,是因为通信协议规定,数据包必须包括起始字节、数据字节、校验字节和结束字节,这样做的目的就是确保数据传输的正确性和稳定性。关于通信方面的知识,我以后用到的话,还会介绍的,例如ESP8266等
[单片机]
基于stm32多功能时钟2——DHT11测量温湿度
1.模块介绍 DHT11温湿度传感器 DHT11温湿度传感器是数字式的,包括1个电阻式感湿元件和1个NTC测温元件,内部自带AD转换功能,采用单总线,具有响应快、抗干扰能力强、性价比高等特点。该模块总共4个引脚,其中两个是电源引脚VCC和GND,一个是数据引脚,还有一个为空引脚。 DHT11外部引脚 目前流行的数据传输总线有II2C总线,SPI总线,单总线等,而DHT11则采用单总线传输数据。单总线,顾名思义,就是采用单根信号线,即可传输时钟,又能传输数据,而且数据传输是双向的,从而有主机和从机之别。在这里,stm32作为核心控制器,所以是主机,而DHT11为从机。 采用单总线进行数据传输,我们需
[单片机]
基于stm32多功能时钟7——报警模块设计
自上一章《UI界面设计》后,我们的开发工作已经完成了60%左右了。这一章呢,小编将介绍报警模块的设计。之前所做的工作,只是完成对环境参量的测量和显示。现在要对所获取的参量进行分析,即根据实际情况设置相关的阈值,判断是否异常,若出现异常,则采取报警,同时提醒工作人员,并且系统也会采取一定的措施来解决问题。 在本制作中,只是象征性的做一下报警设计。举个例子,假如湿度出现异常(过高),超出上限,报警,同时需要启动抽湿机,当环境湿度达到正常值后,停止抽湿机,报警随之停止。这才是一个真正的反馈控制系统。但本制作,由于材料有限,只有一个电机,虽然能够有一定效果,但功能还是受限,所以就省略了。 因此,在报警模块,我们这样
[单片机]
基于stm32多功能时钟3——MQ135检测空气质量
在上一章中,小编主要讲了如何通过DHT11测量温湿度,由于有单总线通信,需要编写时序函数,所以难度有点大。那么在这一章中,小编打算用MQ135模块来检测空气质量,仍然是对环境参量的获取。不像DHT11模块,在MQ135内部并没有集成AD转换器,当然,我们也不需要在外围搭建AD转换电路,而是利用stm32的内部ADC资源,完成对获取到的模拟量的转换。 1.模块介绍 MQ135传感器主要检测空气中的一些有害气体,比如硫化物、氨气等,还可以对烟雾等进行检测,总之,就是检测空气中污染物的一款传感器。下面,就是MQ135模块的实物图。 MQ135模块 由图可知:该模块有4个引脚,分别是两个电源VCC和GND,一
[单片机]
stm32库函数FSMC_NORSRAMInit()解析
这是一段对nor存储器的时序进行编程的函数,函数形式为void FSMC_NORSRAMInit(FSMC_NORSRAMInitTypeDef* FSMC_NORSRAMInitStruct),里面只有一个参数,这个参数为指针类型,指向一段数据结构,这个数据结构就保存着对时序进行配置的的各个参数,这个结构的详细内容为 typedef struct { uint32_t FSMC_Bank;//nor被分为四块,其中这个参数是说明对那个块编程 uint32_t FSMC_DataAddressMux;//地址数据是否复用 uint32_t FSMC_MemoryType;//存储器类型 uint3
[单片机]
CH32和stm32库函数一样吗?
STM32是ST公司推出的一款32位微控制器,其拥有强大的性能及丰富的外设功能,实现了嵌入式应用的功能。而在STM32的开发中,库函数是不可或缺的,因为它简化了开发者的编程难度,使得开发工作更加高效和易于实现。而目前市面上也出现了CH32,那么问题来了,这两个库函数是否一样呢? 虽然CH32和STM32都是由不同的生产厂家推出的,但两者在产品的架构上有许多相似之处,因此它们的库函数也存在一些相似之处。比如,它们都有GPIO口、UART、IIC、SPI等外设的库函数。这些库函数的命名和功能都是十分类似的,使用方法也基本相同。在这些方面,两者的库函数可以说是非常相似的。 但是,CH32和STM32库函数也存在着一些不同之处。其中一个主
[单片机]
解析STM32库函数-1
一、引言 我们在学习32的过程中,固件库是个必不可少的东西,因为程序开发不再是从寄存器层次起始,而要首先去熟悉 STM32 所提供的固件库。那是否一定要使用固件库呢?当然不是。但 STM32 微控制器的寄存器规模可不 是常见的 8 位单片机可以比拟,若自己细细琢磨各个寄存器的意义,必然会消耗相当的时间, 并且对于程序后续的维护,升级来说也会增加资源的消耗。 二、正题 以最常用的 GPIO 设备的初始化函数为例,如下程序段一: GPIO_InitTypeDef GPIO_InitStructure; ○1 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4; ○2 GPIO_InitS
[单片机]
keil5配置stm32库函数开发
在将模板文件添加到工程中后,      1、点击魔术棒,选择C/C++,添加头文件的路径;    2、C/C++里面的define内填入:STM32F10X_MD,USE_STDPERIPH_DRIVER; 3、Output-- select。。选择Output文件夹(为了好看) 根目录:   
[单片机]
keil5配置<font color='red'>stm32</font><font color='red'>库函数</font>开发
小广播
最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

厂商技术中心

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

 
机器人开发圈

电子工程世界版权所有 京ICP证060456号 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2026 EEWORLD.com.cn, Inc. All rights reserved