使用 STM32 控制 LCD12864 显示屏:驱动与文本显示
STM32 LCD12864 AIP31020 基础指令集 调试显示字符
求点赞谢谢啦ovo 关注就太感激啦! 歇歇支持啦
下一章写关于LCD12864实现绘制图像功能。
引言
在嵌入式开发中,LCD 显示屏广泛用于图形界面和文字显示。今天,我们来介绍如何用 STM32 微控制器驱动一个常见的 128×64 分辨率的 LCD 显示屏(LCD12864)。LCD12864 采用串行接口,并使用基本的命令和数据通信方式。本文将详细解析如何在 STM32 环境中驱动 LCD12864,包括引脚配置、命令发送、以及显示数据的具体实现。
LCD12864 模块简介
LCD12864 是一种字符型和图形型显示屏,支持 128×64 的显示分辨率。它使用 3 根主要控制引脚:
1. LCD 显示模式
LCD12864 支持基本指令集和扩展指令集,可以进行文字显示,也可以通过扩展指令绘制图形。通过串行通信传送的每个字节需要分为 3 个数据包进行传输,且有特定的时序要求。
具体参考说明书,设置在h文件中有说明
2. STM32 GPIO 初始化
在 STM32 微控制器中,我们需要对控制 LCD 的 GPIO 引脚进行初始化。以 STM32F1xx 为例,本次我以
//PA5 CS
//PA6 SID
//PA7 SCLK
宏定义如下
#define LCD_CS_GPIO_PIN GPIO_PIN_5 // PA5 - Chip Select
#define LCD_SID_GPIO_PIN GPIO_PIN_6 // PA6 - Serial Data (MOSI)
#define LCD_CLK_GPIO_PIN GPIO_PIN_7 // PA7 - Serial Clock
#define LCD_GPIO_PORT GPIOA
/* 12864使用的引脚定义,CS:PB13 CLK:PB15, SID:PB14 */
#define LCD_CS(a) HAL_GPIO_WritePin(LCD_GPIO_PORT, LCD_CS_GPIO_PIN, (a) ? GPIO_PIN_SET : GPIO_PIN_RESET)
#define LCD_CLK(a) HAL_GPIO_WritePin(LCD_GPIO_PORT, LCD_CLK_GPIO_PIN, (a) ? GPIO_PIN_SET : GPIO_PIN_RESET)
#define LCD_SID(a) HAL_GPIO_WritePin(LCD_GPIO_PORT, LCD_SID_GPIO_PIN, (a) ? GPIO_PIN_SET : GPIO_PIN_RESET)
/* 功能设置指令 */
#define LCD_FUNCTION_SET_BASIC 0x30 // 8位接口,基本指令集 (DL=1, RE=0) 0011 0000
#define LCD_FUNCTION_SET_EXTEND 0x34 // 8位接口,扩展指令集 (DL=1, RE=1) 0011 0100
#define LCD_FUNCTION_SET_DRAWPIC 0x36 // 拓展指令集下打开绘画显示
/* 显示控制指令 */
#define LCD_DISPLAY_OFF 0x08 // 关闭整体显示 (D=0, C=0, B=0) 0000 1000
#define LCD_DISPLAY_ON 0x0C // 打开整体显示,不显示游标 (D=1, C=0, B=0) 0000 1100
#define LCD_DISPLAY_CURSOR_ON 0x0E // 显示游标 (D=1, C=1, B=0) 0000 1110
#define LCD_DISPLAY_CURSOR_BLINK 0x0F // 显示游标 + 反白闪烁 (D=1, C=1, B=1) 0000 1111
/* 清除显示 */
#define LCD_DISPLAY_CLEAR 0x01 // 清除显示 0000 0001
只有一块LCD ,故CS 我直接保持高,
我们需要启用 GPIOA 时钟,并配置适当的引脚模式为推挽输出模式。以下是相关的 GPIO 初始化代码:
void LCD_GPIO_Init(void) {
// 启用 GPIO 端口时钟
__HAL_RCC_GPIOA_CLK_ENABLE();
GPIO_InitTypeDef GPIO_InitStruct = {0};
// 配置 CS、SID(MOSI)、SCLK 为推挽输出模式
GPIO_InitStruct.Pin = LCD_CS_GPIO_PIN | LCD_SID_GPIO_PIN | LCD_CLK_GPIO_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; // 推挽输出
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; // 高速输出
HAL_GPIO_Init(LCD_GPIO_PORT, &GPIO_InitStruct);
// 默认拉高 CS,使 LCD 处于选中状态
HAL_GPIO_WritePin(LCD_GPIO_PORT, LCD_CS_GPIO_PIN, GPIO_PIN_SET);
// 默认 SCLK 置低,SID 数据线默认置低
HAL_GPIO_WritePin(LCD_GPIO_PORT, LCD_SID_GPIO_PIN, GPIO_PIN_RESET);
HAL_GPIO_WritePin(LCD_GPIO_PORT, LCD_CLK_GPIO_PIN, GPIO_PIN_RESET);
}
3.延迟函数说明
LCD 通信要求精确的时序,因此需要延时函数来控制时钟和数据传输的速度。但是延迟函数有很多种不同的显示形式,根据不同的显示形式,会有不同的设定,故最后留一个函数指针结构,需要在LCD 初始化时候对他进行指向。
typedef void(*delayus)(uint32_t us); //在h文件
extern delayus Sys_Delayus; //在h文件
//以下为c文件
delayus Sys_Delayus =NULL;
void LCD_DelayUs(uint32_t us)
{
if (Sys_Delayus != NULL) {
Sys_Delayus(us); // 调用外部的延时函数
}
}
void LCD_DelayMs(uint32_t ms)
{
while (ms--) {
LCD_DelayUs(1000); // 1 毫秒 = 1000 微秒
}
}
void LCD_Init(delayus sys_delay){
LCD_GPIO_Init();
// 确保 Sys_Delayus 指向外部的延迟函数
Sys_Delayus = sys_delay; // 将 Sys_Delayus 指向 }
}
//在main中实现
LCD_Init(My_DelayUs);
//需要根据自己的delay设定,注意是uint32 ,
//因为uint8 只支持到256,而uint16 66535 us级如果直接1ms的话直接超过了 我就用uint32 了
4. LCD 命令和数据发送
发送字节
LCD12864 通过串行接口接收命令和数据。每次传输需要 3 个字节的传输,其中包含命令的高 4 位和低 4 位。为了发送这些字节,我们需要定义一个函数来串行地将字节通过 SID 和 CLK 引脚发送出去。以下是发送字节的代码:
简单的通信 SCLK为低时变化SID,SID变化好了 将SCLK拉高
LCD_SID() 是宏定义,也可以用HAL 函数实现一样。
static void lcd_send_byte(uint8_t byte)
{
unsigned char i;
for(i = 0; i < 8; i++)
{
LCD_CLK(0); // 拉低时钟
uint8_t bit = (byte >> (7 - i)) & 0x01; // 获取每一位数据
LCD_SID(bit); // 按位发送数据
LCD_DelayUs(5); // 延迟
LCD_CLK(1); // 拉高时钟
}
}
发送命令
LCD12864 有两类基本命令:功能设置和显示控制。我们通过串行接口发送命令来进行功能的切换和控制显示状态。
参考说明书中时序实现
以下是设置和发送命令的几个常见函数:
/**
* @brief 发送 LCD12864 指令(带 RS 和 R/W 控制)
*
* @param RS 寄存器选择位 (0 = 命令, 1 = 数据)
* @param R_W 读写控制位 (0 = 写, 1 = 读)
* @param data 要发送的指令数据(8位)
*
* @note LCD 采用 3 字节格式进行串行通信:
* - 第一个字节: 11111 R/W RS 0
* - 第二个字节: D7 D6 D5 D4 0 0 0 0
* - 第三个字节: D3 D2 D1 D0 0 0 0 0
*
* @note LCD12864 需要连续发送三个字节完成一次指令传输。
*/
void LCD_SendCommand(uint8_t RS, uint8_t R_W, uint8_t data)
{
uint8_t byte1, byte2, byte3;
// 第一个字节: 11111 R/W RS 0
byte1 = (0xf8) | (R_W << 2) | (RS << 1); // 设置 RS 和 R/W 位
// 第二个字节: D7 D6 D5 D4 0 0 0 0
byte2 = (data & 0xF0); // 提取 data 的高 4 位,低 4 位自动填 0
// 第三个字节: D3 D2 D1 D0 0 0 0 0
byte3 = (data << 4) & 0xF0; // 提取 data 的低 4 位,并左移 4 位放入高 4 位
// 发送三个字节
lcd_send_byte(byte1); // 发送第一个字节
lcd_send_byte(byte2); // 发送第二个字节
lcd_send_byte(byte3); // 发送第三个字节
}
发送指令与发送数据
/**
* @brief 发送 LCD12864 指令(固定为写入命令)
*
* @param cmd 要发送的命令(8位)
*
* @note 由于 R/W=0 RS=0,代表写入命令,此函数专门用于发送 LCD 命令。
*/
void LCD_WriteCommand(uint8_t cmd)
{
LCD_SendCommand(0, 0, cmd); // RS=0 (命令), RW=0 (写)
}
void LCD_WriteDisplayData(uint8_t data)
{
LCD_SendCommand(1, 0, data);
}
显示字符串函数说明
在 LCD12864 中显示文本是通过设置显示位置后逐个写入字符的方式实现的。每个字符占用 16×16 像素,显示的文本通常是从特定的地址(行列)开始。
/* LCD12864内部字库显示的DDRAM地址,每个地址占16*16个像素点 */
static unsigned char lcd_str_addr[4][8] = {
{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}
};
void LCD_Draw_Str(uint8_t column, uint8_t row, uint8_t *str)
{
// 设置起始位置
LCD_WriteCommand(lcd_str_addr[column][row]);
while (*str)
{
LCD_WriteDisplayData(*str++); // 逐个字符显示
}
}
LCD初始化
初始化 LCD12864 涉及多个步骤:首先配置引脚,然后初始化显示模式,最后执行一些基本的 LCD 设置。例如,选择功能集、开启显示、清屏等
void LCD_Init(delayus sys_delay)
{
LCD_GPIO_Init();
Sys_Delayus = sys_delay; // 将延迟函数传入
LCD_DelayMs(50); // 延时 50ms 等待 LCD 准备
LCD_WriteCommand(LCD_FUNCTION_SET_BASIC); // 选择基本指令集
LCD_WriteCommand(LCD_DISPLAY_ON); // 打开显示
LCD_WriteCommand(LCD_DISPLAY_CLEAR); // 清除屏幕
LCD_DelayMs(2); // 延时,确保屏幕清除完成
}
文本显示
初始化完成后,我们可以通过 LCD_Draw_Str 函数在屏幕上显示文本。例如:
LCD_Draw_Str(0, 0, "Hello, World!"); // 在第一行显示 "Hello, World!"
LCD_Draw_Str(1, 0, "STM32 LCD Test"); // 在第二行显示 "STM32 LCD Test"
总结
通过 STM32 微控制器控制 LCD12864 显示屏,可以实现多种图形和文字显示功能。本文详细介绍了从引脚配置、命令发送到显示文本的完整实现流程。通过了解 LCD12864 的工作原理和 STM32 的控制方法,开发者可以根据项目需求灵活配置显示效果,满足不同应用的需求。
希望本文能够帮助大家快速上手 STM32 控制 LCD12864 显示屏的开发。如果有任何问题或建议,欢迎留言交流!
求点赞求关注谢谢啦ovo
下一章写关于LCD12864实现绘制图像功能。
作者:Hi_sl