使用 STM32 控制 LCD12864 显示屏:驱动与文本显示

STM32 LCD12864 AIP31020 基础指令集 调试显示字符

求点赞谢谢啦ovo 关注就太感激啦! 歇歇支持啦
下一章写关于LCD12864实现绘制图像功能。

引言

在嵌入式开发中,LCD 显示屏广泛用于图形界面和文字显示。今天,我们来介绍如何用 STM32 微控制器驱动一个常见的 128×64 分辨率的 LCD 显示屏(LCD12864)。LCD12864 采用串行接口,并使用基本的命令和数据通信方式。本文将详细解析如何在 STM32 环境中驱动 LCD12864,包括引脚配置、命令发送、以及显示数据的具体实现。

LCD12864 模块简介

LCD12864 是一种字符型和图形型显示屏,支持 128×64 的显示分辨率。它使用 3 根主要控制引脚:

  • CS (Chip Select):选择 LCD 模块。
  • SID (Serial Data):串行数据输入线,负责传送数据。
  • CLK (Serial Clock):串行时钟线,用于同步数据传输。
  • 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

    物联沃分享整理
    物联沃-IOTWORD物联网 » 使用 STM32 控制 LCD12864 显示屏:驱动与文本显示

    发表回复