STM32开发学习笔记之六【LCD显示文字】
- 一、用STM32CubeMX创建STM32工程
- 二、STM32系统C程序设计
-
- (一)用MDK打开工程
- (二)配置LCD驱动BSP程序
-
- [1. 拷贝出厂BSP文件到项目文件夹](#1. 拷贝出厂BSP文件到项目文件夹)
- [2. 出厂BSP文件添加入项目](#2. 出厂BSP文件添加入项目)
- [3. 将BSP文件夹加入文件包含路径](#3. 将BSP文件夹加入文件包含路径)
- (三)编写LCD的显示程序
-
- [1. 在主函数中添加文件包含代码和显示函数](#1. 在主函数中添加文件包含代码和显示函数)
- [2. 编写LCD.C中的显示代码](#2. 编写LCD.C中的显示代码)
- (四)显示结果
- 三、附录
-
- (一)ST7735LCD控制器
- [(二)**核心接口定义** - `ST7735_LCD_Drv_t`](#(二)核心接口定义 -
ST7735_LCD_Drv_t) -
- 1、功能介绍
- [2、**硬件抽象接口** - `ST7735_IO_t`](#2、硬件抽象接口 -
ST7735_IO_t) - [3、**对象结构体** - `ST7735_Object_t`](#3、对象结构体 -
ST7735_Object_t) - [4、**上下文结构体** - `ST7735_Ctx_t`](#4、上下文结构体 -
ST7735_Ctx_t)
- (三)LCD
LCD显示是单片机最常用的功能之一。今天来学学单片机驱动ST7735芯片在160*80的LCD屏幕上显示颜色及文字的过程。
一、用STM32CubeMX创建STM32工程
本文中不涉及STM32CubeMX的下载及安装,需要的请到 https://www.st.com/en/development-tools/stm32cubemx.html#get-software自行下载安装。
(一)新建工程
新建工程文件项目名称设为"lcd_str",本笔记中项目文件存放在"E:\prj\stm32\06_lcd_str"路径下。启动Stm32CubeMX程序在File菜单单击【新建项目】进入CPU选择界面

(二)选择芯片型号
开发板CPU型号为STM32H743VIT6,所以在Commercial Part Number编辑框输入型号。如果事先没有确定型号而是通过项目需求筛选的话可以在左侧导航栏筛选。输入型号后右侧会显示具体封装的列表,双击正确CPU进入系统引脚与配置窗口

如果出现下面窗口选"NO",因为本项目不涉及复杂的内核配置。

进入引脚与配置窗口 后如下图:

(三)LCD接口配置
1、数据接口SPI配置
本案例LCD屏硬件设计连接到了SPI4口作为串行数据接口。所以选择【SPI4】后模式要选【Half-Duplex Master】半双工主控制器,数据传输位宽【Data Size】设为8位,SPI时钟分频系数【Prescaler】设为8,使通信速率为15Mhz 。(红色箭头指向部分应为15Mhz,但是目前时钟树还没有配置所以暂时8Mhz)

SPI4口是可重定向IO口,默认情况下指向PE2和PE6,如下图:

对应引脚图如下:

但是我们开发板上硬件设计时选用了它的重定向引脚PE12和PE14,所以还得把它们改过来。具体操作是先按住Ctrl键,然后鼠标左键单击【引脚图】绿色的PE2引脚并且按住,等到PE12开始闪烁以后,拖放PE2到PE12即可,PE6改成PE14如法砲制。

接口速度选高速

2、控制接口GPIO口配置
屏幕还有两个辅助接口使用了PE11(片选)和PE13(读写使能),所以搜索两个引脚后设置为【输出】,引脚属性也有改动


3、背光接口PWM口配置
LCD还需要一个PWM信号调节背光亮度,本案例选用定时器提供信号输出,打开【TIM1】定时器,设置通道2为;【PWM Generation CH2N】(调背光用)

IO口仍然需要重定向到PE10,如下图:

基础参数如下:

IO引脚的工作模式用默认值就行。以上5个引脚配置好以后LCD的接口就配置完了。
(四)RCC时钟源配置
1、HSE时钟配置
打开高速时钟源 HSE,设为外部驱动。如图所示。

首先打开外部时钟,同时因为本案例使用的SPI要用到外设120Mhz的时钟,需要RCC时钟参数配置这里将工号调整等级设为0(值越大功耗越低)。
2、时钟树配置
进入 【Clock Configuration】 配置栏之后可以看到,界面展现一个完整的 STM32H7 时钟系统框图。从这个时钟树配置图可以看出,配置的主要是外部晶振大小,分频系数,倍频系数以及选择器。在我们配置的工程中,时钟值会动态更新,如果某个时钟值在配置过程中超过允许值,那么相应的选项框会红色提示。

上图时钟配置的关键点是:确定主频、外频和SD卡外设时钟,接下来在时钟树下面进一步配置SPI外设和SD卡外设时钟。

上图中确认SPI4的频率为120Mhz,重新回到SPI的参数配置,确认SPI4的工作频率为15Mhz。

(五)生成工程文件
1、工程设置
接下来我们设置生成一个工程,如下图所示。选择 Project Manager-> Project选项用来配置工程的选项,我们了解一下里面的信息。
Project Name:工程名称,填入工程名称(这里填lcd_str)(半角,不能有中文字符)
Project Location:工程保存路径,点击 Browse 选择保存的位置(这里填E:\prj\stm32\sd)(半角,不能有中文字符)
Toolchain Folder Location:工具链文件夹位置,默认即可。
Application Structure:应用的结构,选择 Advanced,不勾选 Do not generate the main(),因为我们要其生成 main 函数。
Toolchain/IDE:工具链/集成开发环境,我们使用 Keil,因此选择 MDK-ARM,Min Version 选择 V5.27,这里根据 CubeMX 的版本可能会有差异,我们默认使用 V5 以上的版本即可。
Linker Settings 链接器设置:
Minimum Heap Size 最小堆大小,调整到2000。
Minimum Stack Size 最小栈大小,调整到2000。
MCU and Firmware Package 是 MCU 及固件包设置:
MCU Reference:目标 MCU 系列名称。
Firmware Package Name and Version:固件包名称及版本。
勾选 Use Default Firmware Location,文本框里面的路径就是固件包的存储地址,我们使用默认地址即可。(这里因为我有两个版本的固件包,所以它默认使用最新的,这个关系不大,就用新的)。
2、代码生成器设置
打开 Project Manager-> Code Generator 选项,Generated files 生成文件选项,勾选 Generate peripheral initialization as a pair of '.c/.h'files per peripheral,勾选这个选项的话将会将每个外设单独分开成一组.c、.h 文件,使得代码结构更加的清晰,如图所示。

由于 CubeMX 默认勾选了复制所有的库,即工程中不使用到的代码也会复制进来,为了节省 CubeMX 生成工程的空间,我们勾选生成工程时只复制用到的库(这一步是可选操作,大家根据自己的实际选择)
3、保存工程

至此工程最基础配置就已经完成,点击蓝色按钮(SENERATE CODE)就可以生成工程。
4、生成工程
如果我们的 CubeMX 工程放置配置路径中没有中文,生成代码后会弹出类似下图的提示窗口,点击 【Open Project】 就打开 MDK 工程(如果是中文路径则会报错)。

到此为止CubeMX任务就结束了。
二、STM32系统C程序设计
打开生成的C代码项目已经假设在你的计算机安装了MDK,如果MDK还没有安装请自行下载并安装。https://www.keil.com/download/product/
注意!不要下载最新版,有可能存在问题!
(一)用MDK打开工程
在MDK的keil编程界面中打开工程后的主界面如下图:

经过STM32cubeMX的配置,系统主框架代码自动生成且能编译通过了。生成的代码文件如下图:

(二)配置LCD驱动BSP程序
LCD模块在出厂时已经提供了相关的BSP(Board Support Package)底层驱动程序。这些代码文件存放在【BSP\ST7735】文件夹中。
1. 拷贝出厂BSP文件到项目文件夹
本案例由cubeMX自动生成的项目代码保存在了【E:\prj\stm32\06_lcd_str\lcd_str】中了(参见上图),因此我们把【BSP文件夹】及里面的文件拷贝到【Drivers】文件夹里。

其中st7735_reg.c定义了LCD内的全部寄存器,st7735.c定义了访问LCD的最顶层函数,在lcd.c中定义了通过调用st7735.c最底层函数实现的显示功能,font.h为英文字库。
2. 出厂BSP文件添加入项目
文件拷贝完后回到程序界面单击【项目文件结构配置】按钮,在弹出界面的中间窗口中单击【新建】,接下来输入【BSP/ST7735】

新建好【BSP/ST7735】项目文件夹后,右侧单击【添加文件】按钮,将刚才拷贝过来的LCD源文件加入到项目当中。

单击【OK】,源码文件就被导入到项目中了

3. 将BSP文件夹加入文件包含路径
文件加入到项目了但是编译器仍然找不到对应文件,还得将BSP文件夹路径加入编译器识别的头文件路径。

单击【项目配置向导】按钮,出现窗口再单击【...】弹出下面界面

在窗口中单击【新建】按钮,将BSP路径加入到列表中,然后单击【OK】

(三)编写LCD的显示程序
1. 在主函数中添加文件包含代码和显示函数
把main.c文件打开,在文件开头添加代码
c
#include "lcd.h"

我们定义显示文字的功能函数为LCD_Disp(),将其加入到main()函数,如下图:
c
LCD_Disp_Init(); // 初始化显示
LCD_Disp(); // 显示

这样加入的【BSP】代码就可以被调用了。
2. 编写LCD.C中的显示代码
在lcd.c文件中加入图像缓冲区变量和文本缓冲区变量
c
uint8_t LCDIMGBUF[2][80*80*2]; // 图形显存
uint8_t LCDTXTBUF[20]; // 文本显存
该图形缓冲区声明了2页,这里暂时用第0页,目标是在16080的显示分辨率下,在屏幕左侧80 80空间显示一个测试色块,右侧显示测试文本。

在lcd.c文件中加入显示初始化代码,该部分主要设定屏幕显示方向、清屏幕和设置背光
c
void LCD_Disp_Init(void)
{
ST7735Ctx.Orientation = ST7735_ORIENTATION_LANDSCAPE_ROT180; // 横屏原点左下
ST7735Ctx.Panel = HannStar_Panel; // 瀚宇面板
ST7735Ctx.Type = ST7735_0_9_inch_screen; // 0.9寸屏
ST7735_RegisterBusIO(&st7735_pObj,&st7735_pIO);
ST7735_LCD_Driver.Init(&st7735_pObj,ST7735_FORMAT_RBG565,&ST7735Ctx);
ST7735_LCD_Driver.FillRect(&st7735_pObj, 0, 0, ST7735Ctx.Width,ST7735Ctx.Height, BLACK); // 涂黑全屏
LCD_SetBrightness(500); // 设置背光亮度
}
在lcd.c文件中加入显示测试代码,
c
void LCD_Disp(void)
{
uint8_t r,g,b;
uint16_t color565;
r = 0xFF;
g = 0x00;
b = 0xFF;
for(int row=0;row<80;row++)
{
for(int col=0;col<160;col+=2)
{
color565 = r>>3&0x1F;
color565 = color565<<6;
color565 |= g>>2&0x3F;
color565 = color565<<5;
color565 |= b>>3&0x1F;
LCDIMGBUF[0][row*160+col] = color565>>8;
LCDIMGBUF[0][row*160+col+1] = color565;
}
}
ST7735_FillRGBRect(&st7735_pObj, 0, 0, LCDIMGBUF[0], 80, 80); //刷新到LCD
sprintf((char *)&LCDTXTBUF, "Cat");
LCD_ShowString(110, 30, ST7735Ctx.Width, 16, 16, LCDTXTBUF);
}
(四)显示结果

三、附录
(一)ST7735LCD控制器
ST7735 是一款广泛使用的 SPI接口TFT-LCD 显示驱动控制器芯片,常见于小型彩色显示屏(本文操作的显示屏分辨率为160*80),颜色深度 RGB565(16位色)。
本案例采用LCD屏出厂提供了基本的API函数如下所示:
c
int32_t (*Init)(...); // 初始化LCD
int32_t (*DeInit)(...); // 反初始化
int32_t (*ReadID)(...); // 读取芯片ID
int32_t (*DisplayOn)(...); // 开启显示
int32_t (*DisplayOff)(...); // 关闭显示
int32_t (*SetBrightness)(...); // 设置亮度
int32_t (*GetBrightness)(...); // 获取亮度
int32_t (*SetOrientation)(...); // 设置方向
int32_t (*GetOrientation)(...); // 获取方向
int32_t (*SetCursor)(...); // 设置光标位置
int32_t (*DrawBitmap)(...); // 绘制位图
int32_t (*FillRGBRect)(...); // 填充RGB矩形
int32_t (*DrawHLine)(...); // 画水平线
int32_t (*DrawVLine)(...); // 画垂直线
int32_t (*FillRect)(...); // 填充矩形(单色)
int32_t (*GetPixel)(...); // 获取像素颜色
int32_t (*SetPixel)(...); // 设置像素颜色
int32_t (*GetXSize)(...); //获取屏幕宽度
int32_t (*GetYSize)(...); //获取屏幕高度
ST7735屏幕常用的控制函数用法如下:
1、硬件接口层函数
c
static int32_t lcd_init(void);
功能:初始化LCD硬件接口
操作:启动背光PWM定时器
返回值:初始化状态
c
static int32_t lcd_gettick(void);
功能:获取当前系统时间戳
实现:调用HAL_GetTick()
用途:用于延迟和动画计时
c
static int32_t lcd_writereg(uint8_t reg, uint8_t* pdata, uint32_t length);
功能:向LCD寄存器写入数据
参数:
reg:目标寄存器地址
pdata:要写入的数据指针
length:数据长度
c
static int32_t lcd_readreg(uint8_t reg, uint8_t* pdata);
功能:从LCD寄存器读取数据
过程:类似写寄存器,但最后执行SPI接收操作
参数:
reg:源寄存器地址
pdata:接收数据缓冲区指针
c
static int32_t lcd_senddata(uint8_t* pdata, uint32_t length);
功能:发送数据到LCD(用于GRAM写入)
区别:仅发送数据,不包含寄存器选择步骤
static int32_t lcd_recvdata(uint8_t* pdata, uint32_t length);
功能:从LCD接收数据(用于GRAM读取)
2、背光控制函数
c
void LCD_SetBrightness(uint32_t Brightness);
功能:设置LCD背光亮度
实现:通过PWM定时器设置占空比
范围:0-100(百分比)
c
uint32_t LCD_GetBrightness(void);
功能:获取当前背光亮度
实现:读取PWM比较寄存器值
c
void LCD_Light(uint32_t Brightness_Dis, uint32_t time);
功能:背光渐变效果
参数:
Brightness_Dis:目标亮度值
time:渐变时间(毫秒)
3、字符显示函数
c
void LCD_ShowChar(uint16_t x, uint16_t y, uint8_t num, uint8_t size, uint8_t mode);
功能:在指定位置显示单个字符
参数:
x, y:显示起始坐标
num:字符ASCII码(' '到'~')
size:字体大小(12或16)
mode:显示模式(0-覆盖,1-叠加)
支持字体:
12x6点阵(asc2_1206)
16x8点阵(asc2_1608)
内部流程:
根据字体大小选择字库
逐位解析点阵数据
根据模式设置像素颜色
调用ST7735_FillRGBRect批量写入
c
void LCD_ShowString(uint16_t x, uint16_t y, uint16_t width, uint16_t height, uint8_t size, uint8_t *p);
功能:显示字符串(自动换行)
参数:
width, height:显示区域限制
p:字符串指针
特性:
自动换行处理
区域边界检测
连续调用LCD_ShowChar
(二)核心接口定义 - ST7735_LCD_Drv_t
c
// 这是主LCD驱动接口 - 19个标准化的函数指针
typedef struct {
/* Control functions */ // 控制功能(9个)
int32_t (*Init )(ST7735_Object_t*, uint32_t, ST7735_Ctx_t*);
int32_t (*DeInit )(ST7735_Object_t*);
int32_t (*ReadID )(ST7735_Object_t*, uint32_t*);
int32_t (*DisplayOn )(ST7735_Object_t*);
int32_t (*DisplayOff )(ST7735_Object_t*);
int32_t (*SetBrightness )(ST7735_Object_t*, uint32_t);
int32_t (*GetBrightness )(ST7735_Object_t*, uint32_t*);
int32_t (*SetOrientation )(ST7735_Object_t*, ST7735_Ctx_t*);
int32_t (*GetOrientation )(ST7735_Object_t*, uint32_t*);
/* Drawing functions*/ // 绘图功能(10个)
int32_t ( *SetCursor ) (ST7735_Object_t*, uint32_t, uint32_t);
int32_t ( *DrawBitmap ) (ST7735_Object_t*, uint32_t, uint32_t, uint8_t *);
int32_t ( *FillRGBRect ) (ST7735_Object_t*, uint32_t, uint32_t, uint8_t*, uint32_t, uint32_t);
int32_t ( *DrawHLine ) (ST7735_Object_t*, uint32_t, uint32_t, uint32_t, uint32_t);
int32_t ( *DrawVLine ) (ST7735_Object_t*, uint32_t, uint32_t, uint32_t, uint32_t);
int32_t ( *FillRect ) (ST7735_Object_t*, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t);
int32_t ( *GetPixel ) (ST7735_Object_t*, uint32_t, uint32_t, uint32_t*);
int32_t ( *SetPixel ) (ST7735_Object_t*, uint32_t, uint32_t, uint32_t);
int32_t ( *GetXSize ) (ST7735_Object_t*, uint32_t *);
int32_t ( *GetYSize ) (ST7735_Object_t*, uint32_t *);
} ST7735_LCD_Drv_t;
1、功能介绍
c
int32_t (*Init)(...); // 初始化LCD
int32_t (*DeInit)(...); // 反初始化
int32_t (*ReadID)(...); // 读取芯片ID
int32_t (*DisplayOn)(...); // 开启显示
int32_t (*DisplayOff)(...); // 关闭显示
int32_t (*SetBrightness)(...); // 设置亮度
int32_t (*GetBrightness)(...); // 获取亮度
int32_t (*SetOrientation)(...); // 设置方向
int32_t (*GetOrientation)(...); // 获取方向
int32_t (*SetCursor)(...); // 设置光标位置
int32_t (*DrawBitmap)(...); // 绘制位图
int32_t (*FillRGBRect)(...); // 填充RGB矩形
int32_t (*DrawHLine)(...); // 画水平线
int32_t (*DrawVLine)(...); // 画垂直线
int32_t (*FillRect)(...); // 填充矩形(单色)
int32_t (*GetPixel)(...); // 获取像素颜色
int32_t (*SetPixel)(...); // 设置像素颜色
int32_t (*GetXSize)(...); //获取屏幕宽度
int32_t (*GetYSize)(...); //获取屏幕高度
2、硬件抽象接口 - ST7735_IO_t
c
typedef struct {// 这是底层硬件操作接口 - 8个硬件相关的函数指针
ST7735_Init_Func Init; // 初始化硬件
ST7735_DeInit_Func DeInit; // 反初始化硬件
uint16_t Address; // 设备地址(用于I2C等)
ST7735_WriteReg_Func WriteReg; // 写寄存器
ST7735_ReadReg_Func ReadReg; // 读寄存器
ST7735_SendData_Func SendData; // 发送数据
ST7735_RecvData_Func RecvData; // 接收数据
ST7735_GetTick_Func GetTick; // 获取时间戳
} ST7735_IO_t;
3、对象结构体 - ST7735_Object_t
c
typedef struct {// 这是LCD设备对象,包含所有状态信息
ST7735_IO_t IO; // 硬件接口
st7735_ctx_t Ctx; // 内部上下文(寄存器操作)
uint8_t IsInitialized; // 初始化标志
} ST7735_Object_t;
4、上下文结构体 - ST7735_Ctx_t
c
typedef struct {// 这是LCD的显示状态信息
uint32_t Width; // 屏幕宽度
uint32_t Height; // 屏幕高度
uint32_t Orientation; // 显示方向
uint8_t Panel; // 面板类型(HannStar/BOE)
uint8_t Type; // 屏幕类型(0.9寸/1.8寸)
} ST7735_Ctx_t;
(三)LCD
1、硬件接口层函数
static int32_t lcd_init(void);
-
功能:初始化LCD硬件接口
-
操作:启动背光PWM定时器
-
返回值:初始化状态
static int32_t lcd_gettick(void);
-
功能:获取当前系统时间戳
-
实现 :调用
HAL_GetTick() -
用途:用于延迟和动画计时
static int32_t lcd_writereg(uint8_t reg, uint8_t* pdata, uint32_t length);
-
功能:向LCD寄存器写入数据
-
参数 :
-
reg:目标寄存器地址 -
pdata:要写入的数据指针 -
length:数据长度static int32_t lcd_readreg(uint8_t reg, uint8_t* pdata);
-
-
功能:从LCD寄存器读取数据
-
过程:类似写寄存器,但最后执行SPI接收操作
-
参数 :
-
reg:源寄存器地址 -
pdata:接收数据缓冲区指针static int32_t lcd_senddata(uint8_t* pdata, uint32_t length);
-
-
功能:发送数据到LCD(用于GRAM写入)
-
区别:仅发送数据,不包含寄存器选择步骤
static int32_t lcd_recvdata(uint8_t* pdata, uint32_t length);
-
功能:从LCD接收数据(用于GRAM读取)
2、背光控制函数
void LCD_SetBrightness(uint32_t Brightness);
-
功能:设置LCD背光亮度
-
实现:通过PWM定时器设置占空比
-
范围:0-100(百分比)
uint32_t LCD_GetBrightness(void);
-
功能:获取当前背光亮度
-
实现:读取PWM比较寄存器值
void LCD_Light(uint32_t Brightness_Dis, uint32_t time);
-
功能:背光渐变效果
-
参数 :
Brightness_Dis:目标亮度值time:渐变时间(毫秒)
3、字符显示函数
void LCD_ShowChar(uint16_t x, uint16_t y, uint8_t num, uint8_t size, uint8_t mode);
-
功能:在指定位置显示单个字符
-
参数 :
x, y:显示起始坐标num:字符ASCII码(' '到'~')size:字体大小(12或16)mode:显示模式(0-覆盖,1-叠加)
-
支持字体 :
- 12x6点阵(
asc2_1206) - 16x8点阵(
asc2_1608)
- 12x6点阵(
-
内部流程 :
- 根据字体大小选择字库
- 逐位解析点阵数据
- 根据模式设置像素颜色
- 调用
ST7735_FillRGBRect批量写入
void LCD_ShowString(uint16_t x, uint16_t y, uint16_t width, uint16_t height, uint8_t size, uint8_t *p);
-
功能:显示字符串(自动换行)
-
参数 :
width, height:显示区域限制p:字符串指针
-
特性 :
- 自动换行处理
- 区域边界检测
- 连续调用
LCD_ShowChar
有问题欢迎留言,我再学新东西的时候和刚入门的小伙伴一起分享...