LCD(含FSMC)——跟我一起写STM32(第七期)

文章介绍了如何在STM32平台上驱动TFTLCD,包括8080时序的详细解释,LCD驱动的编写,以及如何利用FSMC(FlexibleStaticMemoryController)来模拟8080时序,提高效率。文章还提供了具体的代码示例,展示了如何初始化LCD、设置坐标、清屏以及显示字符等功能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

9 点亮第一块彩屏

9.1 认识TFTLCD

这是市面上显示器优缺点:

这就引出了我们今天的主角(点阵和断码就先不理他们)——LCD。
TFT-LCD 即薄膜晶体管液晶显示器。
它:

  • 低成本
  • 高解析
  • 高对比度
  • 响应速度快

是您的不二选择。

详情请咨询:
LCD原理

这个就是笔者使用的LCD屏幕:

三基色原理
笔者这块LCD屏的颜色是RGB565——意思就是Red有2的5次方=32种,Green有2的6次方=64种,Blue有2的5次方=32种
无法通过其他颜色混合得到的颜色,称之为:基本色
通过三基色(也叫三原色)混合,可以得到自然界中绝大部分颜色!

电脑一般用32位来表示一个颜色(ARGB888):(A表示透明度)

单片机一般用16/24位表示一个颜色(RGB565/RGB888)

RGB565:

RGB888:


那我们驱动LCD其实很驱动OLED(第六期)差不多,就是把写0写1改成写一个个RGB寄存器,往里面写颜色。
驱动LCD的一般过程:

9.2 8080时序

它使用的是8080 的并行接口。8080 并行接口的发明者是 INTEL,该总线也被广泛应用于各类液晶显示器。
时序使用的是8080时序:
LCD屏8080时序

8080时序读写过程
先将读写的数据类型RS设置为高(数据)/低(命令),然后拉低片选,选中对应的设备、然后将读数据还是写数据RD/WR拉低。

读数据:在RD的上升沿、读取数据线上的数据。
写数据:在WR的下降沿、使数据写入设备中。

8080写时序:
数据(RS=1)/命令(RS=0)在WR的上升沿,写入LCD驱动IC,RD保持高电平

8080读时序
数据(RS=1)/命令(RS=0)在RD的上升沿,读取到MCU,WR保持高电平

8080 写数据

void _lcd_wr_data (uint16_t data)
{
    LCD_RS(1);          /* 操作数据 */
    LCD_CS(0);          /* 选中 */
    LCD_DATA_OUT(data);     /* 数据 */
    LCD_WR(0);          /* WR低电平 */
    LCD_WR(1);          /* WR高电平 */
    LCD_CS(1);          /* 释放片选 */
}

8080 读数据

uint16_t  lcd_rd_data(void)
{
	uint16_t ram;  			/* 定义变量 */
 	LCD_RS(1);              		/* 操作数据 */
    LCD_CS(0);			/* 选中 */
   	LCD_RD(0);			/* RD低电平 */
   	ram = LCD_DATA_IN;    	/* 读取数据 */
    LCD_RD(1);			/* RD高电平 */
    LCD_CS(1);			/* 释放片选 */
	return ram;			/* 返回读数 */
}

9.3 LCD驱动

常用LCD的命令:

好多都是我们的老朋友了(第六期OLED实验),在编写驱动的流程也是差不多,往对应显存写入数据即可。
附上代码:
lcd.h

//
// Created by Whisky on 2023/1/6.
//

#ifndef HELLOWORLD_LCD_H
#define HELLOWORLD_LCD_H
#include "main.h"

/* 引脚定义 */
#define LCD_BL_GPIO_PORT                GPIOB
#define LCD_BL_GPIO_PIN                 GPIO_PIN_0
#define LCD_BL_GPIO_CLK_ENABLE()        do{ __HAL_RCC_GPIOB_CLK_ENABLE(); }while(0)

#define LCD_WR_GPIO_PORT                GPIOD
#define LCD_WR_GPIO_PIN                 GPIO_PIN_5
#define LCD_WR_GPIO_CLK_ENABLE()        do{ __HAL_RCC_GPIOD_CLK_ENABLE(); }while(0)

#define LCD_RD_GPIO_PORT                GPIOD
#define LCD_RD_GPIO_PIN                 GPIO_PIN_4
#define LCD_RD_GPIO_CLK_ENABLE()        do{ __HAL_RCC_GPIOD_CLK_ENABLE(); }while(0)

#define LCD_CS_GPIO_PORT                GPIOG
#define LCD_CS_GPIO_PIN                 GPIO_PIN_12
#define LCD_CS_GPIO_CLK_ENABLE()        do{ __HAL_RCC_GPIOG_CLK_ENABLE(); }while(0)

#define LCD_RS_GPIO_PORT                GPIOG
#define LCD_RS_GPIO_PIN                 GPIO_PIN_0
#define LCD_RS_GPIO_CLK_ENABLE()        do{ __HAL_RCC_GPIOG_CLK_ENABLE(); }while(0)


#define LCD_BL(x)       LCD_BL_GPIO_PORT->BSRR = LCD_BL_GPIO_PIN << (16 * (!x))
#define LCD_WR(x)       LCD_WR_GPIO_PORT->BSRR = LCD_WR_GPIO_PIN << (16 * (!x))
#define LCD_RD(x)       LCD_RD_GPIO_PORT->BSRR = LCD_RD_GPIO_PIN << (16 * (!x))
#define LCD_CS(x)       LCD_CS_GPIO_PORT->BSRR = LCD_CS_GPIO_PIN << (16 * (!x))
#define LCD_RS(x)       LCD_RS_GPIO_PORT->BSRR = LCD_RS_GPIO_PIN << (16 * (!x))

#define D0_Pin GPIO_PIN_14
#define D0_GPIO_Port GPIOD
#define D0_len 14

#define D1_Pin GPIO_PIN_15
#define D1_GPIO_Port GPIOD
#define D1_len 15

#define D2_Pin GPIO_PIN_0
#define D2_GPIO_Port GPIOD
#define D2_len 0

#define D3_Pin GPIO_PIN_1
#define D3_GPIO_Port GPIOD
#define D3_len 1

#define D4_Pin GPIO_PIN_7
#define D4_GPIO_Port GPIOE
#define D4_len 7

#define D5_Pin GPIO_PIN_8
#define D5_GPIO_Port GPIOE
#define D5_len 8

#define D6_Pin GPIO_PIN_9
#define D6_GPIO_Port GPIOE
#define D6_len 9

#define D7_Pin GPIO_PIN_10
#define D7_GPIO_Port GPIOE
#define D7_len 10

#define D8_Pin GPIO_PIN_11
#define D8_GPIO_Port GPIOE
#define D8_len 11

#define D9_Pin GPIO_PIN_12
#define D9_GPIO_Port GPIOE
#define D9_len 12

#define D10_Pin GPIO_PIN_13
#define D10_GPIO_Port GPIOE
#define D10_len 13

#define D11_Pin GPIO_PIN_14
#define D11_GPIO_Port GPIOE
#define D11_len 14

#define D12_Pin GPIO_PIN_15
#define D12_GPIO_Port GPIOE
#define D12_len 15

#define D13_Pin GPIO_PIN_8
#define D13_GPIO_Port GPIOD
#define D13_len 8

#define D14_Pin GPIO_PIN_9
#define D14_GPIO_Port GPIOD
#define D14_len 9

#define D15_Pin GPIO_PIN_10
#define D15_GPIO_Port GPIOD
#define D15_len 10

#define GPIO_CLK_Enable() {__HAL_RCC_GPIOE_CLK_ENABLE();__HAL_RCC_GPIOD_CLK_ENABLE();}

#define D0 ((D0_GPIO_Port->IDR & D0_Pin)>>D0_len)
#define D1 ((D1_GPIO_Port->IDR & D1_Pin)>>D1_len)
#define D2 ((D2_GPIO_Port->IDR & D2_Pin)>>D2_len)
#define D3 ((D3_GPIO_Port->IDR & D3_Pin)>>D3_len)
#define D4 ((D4_GPIO_Port->IDR & D4_Pin)>>D4_len)
#define D5 ((D5_GPIO_Port->IDR & D5_Pin)>>D5_len)
#define D6 ((D6_GPIO_Port->IDR & D6_Pin)>>D6_len)
#define D7 ((D7_GPIO_Port->IDR & D7_Pin)>>D7_len)
#define D8 ((D8_GPIO_Port->IDR & D8_Pin)>>D8_len)
#define D9 ((D9_GPIO_Port->IDR & D9_Pin)>>D9_len)
#define D10 ((D10_GPIO_Port->IDR & D10_Pin)>>D10_len)
#define D11 ((D11_GPIO_Port->IDR & D11_Pin)>>D11_len)
#define D12 ((D12_GPIO_Port->IDR & D12_Pin)>>D12_len)
#define D13 ((D13_GPIO_Port->IDR & D13_Pin)>>D13_len)
#define D14 ((D14_GPIO_Port->IDR & D14_Pin)>>D14_len)
#define D15 ((D15_GPIO_Port->IDR & D15_Pin)>>D15_len)

#define _D0 (D0_GPIO_Port->ODR)
#define _D1 (D1_GPIO_Port->ODR)
#define _D2 (D2_GPIO_Port->ODR)
#define _D3 (D3_GPIO_Port->ODR)
#define _D4 (D4_GPIO_Port->ODR)
#define _D5 (D5_GPIO_Port->ODR)
#define _D6 (D6_GPIO_Port->ODR)
#define _D7 (D7_GPIO_Port->ODR)
#define _D8 (D8_GPIO_Port->ODR)
#define _D9 (D9_GPIO_Port->ODR)
#define _D10 (D10_GPIO_Port->ODR)
#define _D11 (D11_GPIO_Port->ODR)
#define _D12 (D12_GPIO_Port->ODR)
#define _D13 (D13_GPIO_Port->ODR)
#define _D14 (D14_GPIO_Port->ODR)
#define _D15 (D15_GPIO_Port->ODR)

#define LCD_DATA_IN ( (D0) |(D1 << 1) | (D2 << 2) | (D3 << 3) | (D4 << 4) | (D5 << 5) | (D6 << 6) | \
                        (D7 << 7) |(D8 << 8) | (D9 << 9) | (D10 << 10) | (D11 << 11) | (D12 << 12) | \
                         (D13 << 13) | (D14 << 14) | (D15 << 15) )

#define get_bit(x, Pin, bit, len, o) {(x & Pin) >> bit;if((((x & Pin) >> bit) & 0x01) == 1) o |= (1 << len);  else o &= ~(1 << len);}

#define LCD_DATA_OUT(x) {       \
                            get_bit(x, GPIO_PIN_0, 0, D0_len, _D0) \
                            get_bit(x, GPIO_PIN_1, 1, D1_len, _D1) \
                            get_bit(x, GPIO_PIN_2, 2, D2_len, _D2) \
                            get_bit(x, GPIO_PIN_3, 3, D3_len, _D3) \
                            get_bit(x, GPIO_PIN_4, 4, D4_len, _D4) \
                            get_bit(x, GPIO_PIN_5, 5, D5_len, _D5) \
                            get_bit(x, GPIO_PIN_6, 6, D6_len, _D6) \
                            get_bit(x, GPIO_PIN_7, 7, D7_len, _D7) \
                            get_bit(x, GPIO_PIN_8, 8, D8_len, _D8) \
                            get_bit(x, GPIO_PIN_9, 9, D9_len, _D9) \
                            get_bit(x, GPIO_PIN_10, 10, D10_len, _D10) \
                            get_bit(x, GPIO_PIN_11, 11, D11_len, _D11) \
                            get_bit(x, GPIO_PIN_12, 12, D12_len, _D12) \
                            get_bit(x, GPIO_PIN_13, 13, D13_len, _D13) \
                            get_bit(x, GPIO_PIN_14, 14, D14_len, _D14) \
                            get_bit(x, GPIO_PIN_15, 15, D15_len, _D15) \
                        }

#define LCD_FONTSIZE_1206 12
#define LCD_FONTSIZE_1608 16
#define LCD_FONTSIZE_2412 24
#define LCD_FONTSIZE_3216 32
typedef struct
{
    uint16_t width;
    uint16_t height;
    uint16_t id;
    uint8_t dir;        /* 方向   0-竖屏 1-横屏*/
    uint16_t wramcmd;   /* gram指令 w */
    uint16_t rramcmd;   /* gram指令 r */
    uint16_t setxcmd;   /* 设置x坐标 */
    uint16_t setycmd;   /* 设置y坐标 */
} _lcd_dev;

#define WHITE           0xFFFF      /* 白色 */
#define BLACK           0x0000      /* 黑色 */
#define RED             0xF800      /* 红色 */
#define GREEN           0x07E0      /* 绿色 */
#define BLUE            0x001F      /* 蓝色 */
#define MAGENTA         0XF81F      /* 品红色/紫红色 = BLUE + RED */
#define YELLOW          0XFFE0      /* 黄色 = GREEN + RED */
#define CYAN            0X07FF      /* 青色 = GREEN + BLUE */
#define BROWN           0XBC40      /* 棕色 */
#define BRRED           0XFC07      /* 棕红色 */
#define GRAY            0X8430      /* 灰色 */
#define DARKBLUE        0X01CF      /* 深蓝色 */
#define LIGHTBLUE       0X7D7C      /* 浅蓝色 */
#define GRAYBLUE        0X5458      /* 灰蓝色 */
#define LIGHTGREEN      0X841F      /* 浅绿色 */
#define LGRAY           0XC618      /* 浅灰色(PANNEL),窗体背景色 */
#define LGRAYBLUE       0XA651      /* 浅灰蓝色(中间层颜色) */
#define LBBLUE          0X2B12      /* 浅棕蓝色(选择条目的反色) */

typedef struct {
    uint16_t color;
    uint8_t size;
    uint8_t mode;
} f_args;

void lcd_init(void);
#define lcd_clear(...)                  _lcd_clear((f_args){__VA_ARGS__})
#define lcd_show_char(x, y, chr,...)    _lcd_show_char(x, y, chr, (f_args){__VA_ARGS__})

void _lcd_clear(f_args in);
void _lcd_show_char(uint16_t x, uint16_t y, char chr, f_args in);

void _lcd_wr_data (uint16_t data);
void _lcd_wr_regno(uint16_t reg);
void _lcd_ex_ili9341_reginit(void);
void _lcd_ex_st7789_reginit(void);

#endif //HELLOWORLD_LCD_H

这里的LCD_DATA_IN是读这些引脚的电平,之后合并成一个16位数据。
LCD_DATA_OUT(x)同理,就是把x写给这16个引脚
这里笔者的方法非常的笨拙,见谅。(其实比较好的方法是把这些引脚归为一个GPIOx里面,直接对GPIOx_PIN_ALL进行操作)

然后编写的驱动上使用了默认函数来方便调用。实现原理可以参考(第六期)
lcd.c

//
// Created by Whisky on 2023/1/6.
//
#include "lcd.h"
#include "retarget.h"
#include "lcdfont.h"

uint32_t g_point_color = RED;
uint32_t g_back_color = WHITE;

_lcd_dev lcddev;

/* 8080 写数据 */
void _lcd_wr_data (uint16_t data)
{
    LCD_RS(1);          /* 操作数据 */
    LCD_CS(0);          /* 选中 */
    LCD_DATA_OUT(data);     /* 数据 */
    LCD_WR(0);          /* WR低电平 */
    LCD_WR(1);          /* WR高电平 */
    LCD_CS(1);          /* 释放片选 */
}

/* 8080 写命令 */
void _lcd_wr_regno(uint16_t reg)
{
    LCD_RS(0);          /* RS=0,表示写寄存器 */
    LCD_CS(0);          /* 选中 */
    LCD_DATA_OUT(reg);    /* 命令 */
    LCD_WR(0);          /* WR低电平 */
    LCD_WR(1);          /* WR高电平 */
    LCD_CS(1);          /* 释放片选 */
}

/* 往寄存器写值 */
void lcd_write_reg(uint16_t reg, uint16_t data)
{
    _lcd_wr_regno(reg);    /* 写入要写的寄存器序号 */
    _lcd_wr_data(data);      /* 写入数据 */
}

static void lcd_opt_delay(uint32_t i)
{
    while(i--);
}

/* LCD读数据 */
uint16_t lcd_rd_data(void)
{
    volatile uint16_t ram;  /* 防止被优化 */

    GPIO_InitTypeDef GPIO_InitStruct;
    /* LCD_DATA 引脚模式设置, 上拉输入, 准备接收数据 */
    GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
    GPIO_InitStruct.Pull = GPIO_PULLUP;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
    GPIO_InitStruct.Pin = D0_Pin;
    HAL_GPIO_Init(D0_GPIO_Port, &GPIO_InitStruct);
    GPIO_InitStruct.Pin = D1_Pin;
    HAL_GPIO_Init(D1_GPIO_Port, &GPIO_InitStruct);
    GPIO_InitStruct.Pin = D2_Pin;
    HAL_GPIO_Init(D2_GPIO_Port, &GPIO_InitStruct);;
    GPIO_InitStruct.Pin = D3_Pin;
    HAL_GPIO_Init(D3_GPIO_Port, &GPIO_InitStruct);
    GPIO_InitStruct.Pin = D4_Pin;
    HAL_GPIO_Init(D4_GPIO_Port, &GPIO_InitStruct);
    GPIO_InitStruct.Pin = D5_Pin;
    HAL_GPIO_Init(D5_GPIO_Port, &GPIO_InitStruct);
    GPIO_InitStruct.Pin = D6_Pin;
    HAL_GPIO_Init(D6_GPIO_Port, &GPIO_InitStruct);
    GPIO_InitStruct.Pin = D7_Pin;
    HAL_GPIO_Init(D7_GPIO_Port, &GPIO_InitStruct);
    GPIO_InitStruct.Pin = D8_Pin;
    HAL_GPIO_Init(D8_GPIO_Port, &GPIO_InitStruct);
    GPIO_InitStruct.Pin = D9_Pin;
    HAL_GPIO_Init(D9_GPIO_Port, &GPIO_InitStruct);
    GPIO_InitStruct.Pin = D10_Pin;
    HAL_GPIO_Init(D10_GPIO_Port, &GPIO_InitStruct);
    GPIO_InitStruct.Pin = D11_Pin;
    HAL_GPIO_Init(D11_GPIO_Port, &GPIO_InitStruct);
    GPIO_InitStruct.Pin = D12_Pin;
    HAL_GPIO_Init(D12_GPIO_Port, &GPIO_InitStruct);
    GPIO_InitStruct.Pin = D13_Pin;
    HAL_GPIO_Init(D13_GPIO_Port, &GPIO_InitStruct);
    GPIO_InitStruct.Pin = D14_Pin;
    HAL_GPIO_Init(D14_GPIO_Port, &GPIO_InitStruct);
    GPIO_InitStruct.Pin = D15_Pin;
    HAL_GPIO_Init(D15_GPIO_Port, &GPIO_InitStruct);

    LCD_RS(1);              /* RS=1,表示操作数据 */
    LCD_CS(0);
    LCD_RD(0);
    lcd_opt_delay(2);
    ram = LCD_DATA_IN;      /* 读取数据 */
    LCD_RD(1);
    LCD_CS(1);

    /* LCD_DATA 引脚模式设置, 推挽输出, 恢复输出状态 */
    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
    GPIO_InitStruct.Pull = GPIO_PULLUP;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
    GPIO_InitStruct.Pin = D0_Pin;
    HAL_GPIO_Init(D0_GPIO_Port, &GPIO_InitStruct);
    GPIO_InitStruct.Pin = D1_Pin;
    HAL_GPIO_Init(D1_GPIO_Port, &GPIO_InitStruct);
    GPIO_InitStruct.Pin = D2_Pin;
    HAL_GPIO_Init(D2_GPIO_Port, &GPIO_InitStruct);
    GPIO_InitStruct.Pin = D3_Pin;
    HAL_GPIO_Init(D3_GPIO_Port, &GPIO_InitStruct);
    GPIO_InitStruct.Pin = D4_Pin;
    HAL_GPIO_Init(D4_GPIO_Port, &GPIO_InitStruct);
    GPIO_InitStruct.Pin = D5_Pin;
    HAL_GPIO_Init(D5_GPIO_Port, &GPIO_InitStruct);
    GPIO_InitStruct.Pin = D6_Pin;
    HAL_GPIO_Init(D6_GPIO_Port, &GPIO_InitStruct);
    GPIO_InitStruct.Pin = D7_Pin;
    HAL_GPIO_Init(D7_GPIO_Port, &GPIO_InitStruct);
    GPIO_InitStruct.Pin = D8_Pin;
    HAL_GPIO_Init(D8_GPIO_Port, &GPIO_InitStruct);
    GPIO_InitStruct.Pin = D9_Pin;
    HAL_GPIO_Init(D9_GPIO_Port, &GPIO_InitStruct);
    GPIO_InitStruct.Pin = D10_Pin;
    HAL_GPIO_Init(D10_GPIO_Port, &GPIO_InitStruct);
    GPIO_InitStruct.Pin = D11_Pin;
    HAL_GPIO_Init(D11_GPIO_Port, &GPIO_InitStruct);
    GPIO_InitStruct.Pin = D12_Pin;
    HAL_GPIO_Init(D12_GPIO_Port, &GPIO_InitStruct);
    GPIO_InitStruct.Pin = D13_Pin;
    HAL_GPIO_Init(D13_GPIO_Port, &GPIO_InitStruct);
    GPIO_InitStruct.Pin = D14_Pin;
    HAL_GPIO_Init(D14_GPIO_Port, &GPIO_InitStruct);
    GPIO_InitStruct.Pin = D15_Pin;
    HAL_GPIO_Init(D15_GPIO_Port, &GPIO_InitStruct);
    return ram;
}

/* 准备写GRAM */
void lcd_write_ram_prepare(void)
{
    _lcd_wr_regno(lcddev.wramcmd);
}

/* 准备读GRAM */
void lcd_read_ram_prepare(void)
{
    _lcd_wr_regno(lcddev.rramcmd);
}

/* 设置坐标 */
void lcd_set_cursor(uint16_t x, uint16_t y)
{
    _lcd_wr_regno(lcddev.setxcmd);
    _lcd_wr_data(x >> 8);
    _lcd_wr_data(x & 0xFF);
    _lcd_wr_regno(lcddev.setycmd);
    _lcd_wr_data(y >> 8);
    _lcd_wr_data(y & 0xFF);
}

void lcd_draw_point(uint16_t x, uint16_t y, uint16_t color)
{
    lcd_set_cursor(x, y);
    lcd_write_ram_prepare();
    _lcd_wr_data(color);
}

uint16_t lcd_read_point(uint16_t x, uint16_t y)
{
    uint16_t R = 0, G = 0, B = 0;
    lcd_set_cursor(x, y);
    lcd_read_ram_prepare();
   // _lcd_wr_regno(0x2E);
    R = lcd_rd_data();  //dummy
    R = lcd_rd_data();
    B = lcd_rd_data();
    G = R & 0xFF;
    return (((R >> 11) << 11) | ((G >> 2) << 5) | (B >> 11));
}

void lcd_init(void)
{
    GPIO_InitTypeDef GPIO_InitStruct = {0};
    GPIO_CLK_Enable();
    LCD_BL_GPIO_CLK_ENABLE();
    LCD_CS_GPIO_CLK_ENABLE();
    LCD_WR_GPIO_CLK_ENABLE();
    LCD_RD_GPIO_CLK_ENABLE();
    LCD_RS_GPIO_CLK_ENABLE();

    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;

    GPIO_InitStruct.Pin = D0_Pin;
    HAL_GPIO_Init(D0_GPIO_Port, &GPIO_InitStruct);
    GPIO_InitStruct.Pin = D1_Pin;
    HAL_GPIO_Init(D1_GPIO_Port, &GPIO_InitStruct);
    GPIO_InitStruct.Pin = D2_Pin;
    HAL_GPIO_Init(D2_GPIO_Port, &GPIO_InitStruct);
    GPIO_InitStruct.Pin = D3_Pin;
    HAL_GPIO_Init(D3_GPIO_Port, &GPIO_InitStruct);
    GPIO_InitStruct.Pin = D4_Pin;
    HAL_GPIO_Init(D4_GPIO_Port, &GPIO_InitStruct);
    GPIO_InitStruct.Pin = D5_Pin;
    HAL_GPIO_Init(D5_GPIO_Port, &GPIO_InitStruct);
    GPIO_InitStruct.Pin = D6_Pin;
    HAL_GPIO_Init(D6_GPIO_Port, &GPIO_InitStruct);
    GPIO_InitStruct.Pin = D7_Pin;
    HAL_GPIO_Init(D7_GPIO_Port, &GPIO_InitStruct);
    GPIO_InitStruct.Pin = D8_Pin;
    HAL_GPIO_Init(D8_GPIO_Port, &GPIO_InitStruct);
    GPIO_InitStruct.Pin = D9_Pin;
    HAL_GPIO_Init(D9_GPIO_Port, &GPIO_InitStruct);
    GPIO_InitStruct.Pin = D10_Pin;
    HAL_GPIO_Init(D10_GPIO_Port, &GPIO_InitStruct);
    GPIO_InitStruct.Pin = D11_Pin;
    HAL_GPIO_Init(D11_GPIO_Port, &GPIO_InitStruct);
    GPIO_InitStruct.Pin = D12_Pin;
    HAL_GPIO_Init(D12_GPIO_Port, &GPIO_InitStruct);
    GPIO_InitStruct.Pin = D13_Pin;
    HAL_GPIO_Init(D13_GPIO_Port, &GPIO_InitStruct);
    GPIO_InitStruct.Pin = D14_Pin;
    HAL_GPIO_Init(D14_GPIO_Port, &GPIO_InitStruct);
    GPIO_InitStruct.Pin = D15_Pin;
    HAL_GPIO_Init(D15_GPIO_Port, &GPIO_InitStruct);

    GPIO_InitStruct.Pin = LCD_BL_GPIO_PIN;
    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
    GPIO_InitStruct.Pull = GPIO_PULLUP;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(LCD_BL_GPIO_PORT, &GPIO_InitStruct);

    GPIO_InitStruct.Pin = LCD_CS_GPIO_PIN;
    HAL_GPIO_Init(LCD_CS_GPIO_PORT, &GPIO_InitStruct);

    GPIO_InitStruct.Pin = LCD_WR_GPIO_PIN;
    HAL_GPIO_Init(LCD_WR_GPIO_PORT, &GPIO_InitStruct);

    GPIO_InitStruct.Pin = LCD_RD_GPIO_PIN;
    HAL_GPIO_Init(LCD_RD_GPIO_PORT, &GPIO_InitStruct);

    GPIO_InitStruct.Pin = LCD_RS_GPIO_PIN;
    HAL_GPIO_Init(LCD_RS_GPIO_PORT, &GPIO_InitStruct);

    LCD_WR(1);                  /* WR 默认拉高 */
    LCD_RD(1);                  /* RD 默认拉高 */
    LCD_CS(1);                  /* CS 默认拉高 */
    LCD_RS(1);                  /* RS 默认拉高 */
    LCD_DATA_OUT(0xffff);       /* D0-15 默认拉高 */

    _lcd_wr_regno(0xD3);
    lcddev.id = lcd_rd_data();  /* dummy */
    lcddev.id = lcd_rd_data();  /* 00 */
    lcddev.id = lcd_rd_data();  /* 93 */
    lcddev.id <<= 8;
    lcddev.id |= lcd_rd_data();  /* 41 */

    printf("id:%#x \r\n", lcddev.id);
    /* 完成初始化数组序列 */
    if (lcddev.id == 0x9341)
    {
        _lcd_ex_ili9341_reginit();
    }
    else{
        _lcd_ex_st7789_reginit();
    }

    /* 初始化LCD结构体 */
    lcddev.width = 240;
    lcddev.height = 320;
    lcddev.setxcmd = 0x2A;
    lcddev.setycmd = 0x2B;
    lcddev.wramcmd = 0x2C;
    lcddev.rramcmd = 0x2E;

    /* 设置终末位置 */
    _lcd_wr_regno(lcddev.setxcmd);
    _lcd_wr_data(0);
    _lcd_wr_data(0);
    _lcd_wr_data((lcddev.width - 1) >> 8);
    _lcd_wr_data((lcddev.width - 1) & 0XFF);
    _lcd_wr_regno(lcddev.setycmd);
    _lcd_wr_data(0);
    _lcd_wr_data(0);
    _lcd_wr_data((lcddev.height - 1) >> 8);
    _lcd_wr_data((lcddev.height - 1) & 0XFF);

    /* 设置扫描方向 */
    lcd_write_reg(0x36, 1 << 3);

    /* 点亮背光 */
    LCD_BL(1);

    lcd_clear();

}

void _lcd_clear(f_args in)
{
    uint16_t color = in.color?in.color:g_back_color;
    uint32_t index = 0;
    uint32_t totalpoint = lcddev.width;

    totalpoint *= lcddev.height;                /* 得到总点数 */
    lcd_set_cursor(0, 0);                   /* 设置光标位置 */
    lcd_write_ram_prepare();                    /* 开始写入GRAM */

    LCD_RS(1);                               /* RS=1,表示写数据 */
    LCD_CS(0);

    for (index = 0; index < totalpoint; index++)
    {
        LCD_DATA_OUT(color);                /* 写入要写的数据 */
        LCD_WR(0);
        LCD_WR(1);
    }

    LCD_CS(1);
}

void _lcd_show_char(uint16_t x, uint16_t y, char chr, f_args in)
{
    /* 设置默认参数 */
    uint8_t size = in.size ? in.size : LCD_FONTSIZE_1608;
    uint8_t mode = in.mode ? in.mode : 1;
    uint16_t color = in.color ? in.color : g_point_color;

    uint8_t *pfont = NULL;
    uint8_t chr_cnt = (size/8+((size%8)?1:0))*(size/2) ;  /* 总字节数 */
    uint16_t y0 = y;
    chr = chr - ' ';    //字库是从' '开始存的

    switch(size)
    {
        case LCD_FONTSIZE_1206:
            pfont = (uint8_t *)asc2_1206[chr];  /* 调用1206字体 */
            break;

        case LCD_FONTSIZE_1608:
            pfont = (uint8_t *)asc2_1608[chr];  /* 调用1608字体 */
            break;

        case LCD_FONTSIZE_2412:
            pfont = (uint8_t *)asc2_2412[chr];  /* 调用2412字体 */
            break;

        case LCD_FONTSIZE_3216:
            pfont = (uint8_t *)asc2_3216[chr];  /* 调用3216字体 */
            break;

        default:
            return ;
    }
    for(uint8_t t = 0; t < chr_cnt; t++)
    {
        uint8_t temp = pfont[t];
        for(uint8_t t1 = 0; t1 < 8; t1++)   //处理8位数据
        {
            if(temp & 0x80){                //首位有效
                lcd_draw_point(x, y , color);
            }
            else if(mode == 0)
            {
                lcd_draw_point(x, y , g_back_color);
            }

            temp <<= 1;
            y++;
            if((y - y0) == size)
            {
                y = y0;
                x++;
                break;
            }
        }
    }
}

lcd_ex.c (用于存放厂家推荐设置配置的初始化命令)

#include "lcd.h"


/**
 * @brief       ST7789 寄存器初始化代码
 * @param       无
 * @retval      无
 */
void _lcd_ex_st7789_reginit(void)
{
    _lcd_wr_regno(0x11);

    delay_ms(120);

    _lcd_wr_regno(0x36);
    _lcd_wr_data(0x00);

    _lcd_wr_regno(0x3A);
    _lcd_wr_data(0X05);

    _lcd_wr_regno(0xB2);
    _lcd_wr_data(0x0C);
    _lcd_wr_data(0x0C);
    _lcd_wr_data(0x00);
    _lcd_wr_data(0x33);
    _lcd_wr_data(0x33);

    _lcd_wr_regno(0xB7);
    _lcd_wr_data(0x35);

    _lcd_wr_regno(0xBB); /* vcom */
    _lcd_wr_data(0x32);  /* 30 */

    _lcd_wr_regno(0xC0);
    _lcd_wr_data(0x0C);

    _lcd_wr_regno(0xC2);
    _lcd_wr_data(0x01);

    _lcd_wr_regno(0xC3); /* vrh */
    _lcd_wr_data(0x10);  /* 17 0D */

    _lcd_wr_regno(0xC4); /* vdv */
    _lcd_wr_data(0x20);  /* 20 */

    _lcd_wr_regno(0xC6);
    _lcd_wr_data(0x0f);

    _lcd_wr_regno(0xD0);
    _lcd_wr_data(0xA4);
    _lcd_wr_data(0xA1);

    _lcd_wr_regno(0xE0); /* Set Gamma  */
    _lcd_wr_data(0xd0);
    _lcd_wr_data(0x00);
    _lcd_wr_data(0x02);
    _lcd_wr_data(0x07);
    _lcd_wr_data(0x0a);
    _lcd_wr_data(0x28);
    _lcd_wr_data(0x32);
    _lcd_wr_data(0X44);
    _lcd_wr_data(0x42);
    _lcd_wr_data(0x06);
    _lcd_wr_data(0x0e);
    _lcd_wr_data(0x12);
    _lcd_wr_data(0x14);
    _lcd_wr_data(0x17);


    _lcd_wr_regno(0XE1);  /* Set Gamma */
    _lcd_wr_data(0xd0);
    _lcd_wr_data(0x00);
    _lcd_wr_data(0x02);
    _lcd_wr_data(0x07);
    _lcd_wr_data(0x0a);
    _lcd_wr_data(0x28);
    _lcd_wr_data(0x31);
    _lcd_wr_data(0x54);
    _lcd_wr_data(0x47);
    _lcd_wr_data(0x0e);
    _lcd_wr_data(0x1c);
    _lcd_wr_data(0x17);
    _lcd_wr_data(0x1b);
    _lcd_wr_data(0x1e);


    _lcd_wr_regno(0x2A);
    _lcd_wr_data(0x00);
    _lcd_wr_data(0x00);
    _lcd_wr_data(0x00);
    _lcd_wr_data(0xef);

    _lcd_wr_regno(0x2B);
    _lcd_wr_data(0x00);
    _lcd_wr_data(0x00);
    _lcd_wr_data(0x01);
    _lcd_wr_data(0x3f);

    _lcd_wr_regno(0x29); /* display on */
}

/**
 * @brief       ILI9341寄存器初始化代码
 * @param       无
 * @retval      无
 */
void _lcd_ex_ili9341_reginit(void)
{
    _lcd_wr_regno(0xCF);
    _lcd_wr_data(0x00);
    _lcd_wr_data(0xC1);
    _lcd_wr_data(0X30);
    _lcd_wr_regno(0xED);
    _lcd_wr_data(0x64);
    _lcd_wr_data(0x03);
    _lcd_wr_data(0X12);
    _lcd_wr_data(0X81);
    _lcd_wr_regno(0xE8);
    _lcd_wr_data(0x85);
    _lcd_wr_data(0x10);
    _lcd_wr_data(0x7A);
    _lcd_wr_regno(0xCB);
    _lcd_wr_data(0x39);
    _lcd_wr_data(0x2C);
    _lcd_wr_data(0x00);
    _lcd_wr_data(0x34);
    _lcd_wr_data(0x02);
    _lcd_wr_regno(0xF7);
    _lcd_wr_data(0x20);
    _lcd_wr_regno(0xEA);
    _lcd_wr_data(0x00);
    _lcd_wr_data(0x00);
    _lcd_wr_regno(0xC0); /* Power control */
    _lcd_wr_data(0x1B);  /* VRH[5:0] */
    _lcd_wr_regno(0xC1); /* Power control */
    _lcd_wr_data(0x01);  /* SAP[2:0];BT[3:0] */
    _lcd_wr_regno(0xC5); /* VCM control */
    _lcd_wr_data(0x30);  /* 3F */
    _lcd_wr_data(0x30);  /* 3C */
    _lcd_wr_regno(0xC7); /* VCM control2 */
    _lcd_wr_data(0XB7);
    _lcd_wr_regno(0x36); /*  Memory Access Control */
    _lcd_wr_data(0x48);
    _lcd_wr_regno(0x3A);
    _lcd_wr_data(0x55);
    _lcd_wr_regno(0xB1);
    _lcd_wr_data(0x00);
    _lcd_wr_data(0x1A);
    _lcd_wr_regno(0xB6); /*  Display Function Control */
    _lcd_wr_data(0x0A);
    _lcd_wr_data(0xA2);
    _lcd_wr_regno(0xF2); /*  3Gamma Function Disable */
    _lcd_wr_data(0x00);
    _lcd_wr_regno(0x26); /* Gamma curve selected */
    _lcd_wr_data(0x01);
    _lcd_wr_regno(0xE0); /* Set Gamma */
    _lcd_wr_data(0x0F);
    _lcd_wr_data(0x2A);
    _lcd_wr_data(0x28);
    _lcd_wr_data(0x08);
    _lcd_wr_data(0x0E);
    _lcd_wr_data(0x08);
    _lcd_wr_data(0x54);
    _lcd_wr_data(0XA9);
    _lcd_wr_data(0x43);
    _lcd_wr_data(0x0A);
    _lcd_wr_data(0x0F);
    _lcd_wr_data(0x00);
    _lcd_wr_data(0x00);
    _lcd_wr_data(0x00);
    _lcd_wr_data(0x00);
    _lcd_wr_regno(0XE1);    /* Set Gamma */
    _lcd_wr_data(0x00);
    _lcd_wr_data(0x15);
    _lcd_wr_data(0x17);
    _lcd_wr_data(0x07);
    _lcd_wr_data(0x11);
    _lcd_wr_data(0x06);
    _lcd_wr_data(0x2B);
    _lcd_wr_data(0x56);
    _lcd_wr_data(0x3C);
    _lcd_wr_data(0x05);
    _lcd_wr_data(0x10);
    _lcd_wr_data(0x0F);
    _lcd_wr_data(0x3F);
    _lcd_wr_data(0x3F);
    _lcd_wr_data(0x0F);
    _lcd_wr_regno(0x2B);
    _lcd_wr_data(0x00);
    _lcd_wr_data(0x00);
    _lcd_wr_data(0x01);
    _lcd_wr_data(0x3f);
    _lcd_wr_regno(0x2A);
    _lcd_wr_data(0x00);
    _lcd_wr_data(0x00);
    _lcd_wr_data(0x00);
    _lcd_wr_data(0xef);
    _lcd_wr_regno(0x11); /* Exit Sleep */
    delay_ms(120);
    _lcd_wr_regno(0x29); /* display on */
 }

最后还有字库(和第六期OLED实验类似,这里碍于篇幅问题放不出来)

#ifndef __LCDFONT_H
#define __LCDFONT_H
/* 12*12 ASCII字符集点阵 */
const unsigned char asc2_1206[95][12]={
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*" ",0*/
...//省略代码
#endif

9.4 用FSMC爆改8080时序

不过,刚刚我们编写的LCD驱动还是太过暴力,这么多引脚,执行起来也有很多语句,相应执行效率也不算太高,那有没有可以统一管理这些多引脚的方法的办法呢?
有,FSMC。
FSMC是沟通CPU与外部存储器之间的桥梁。用于驱动SRAM,NOR FLASH,NAND FLASH及PC卡类型的存储器。
配置好FSMC,定义一个指向这些地址的指针,通过对指针操作就可以直接修改存储单元的内容,FSMC自动完成读写命令和数据访问操作,不需要程序去实现时序,FSMC外设配置好就可以模拟出时序
LCD类似于SRAM的存储,8080时序也类似FSMC的A模式时序。
这是FSMC的引脚:

红色框起来的,确实能为我们所用。
于是

就可以变成

接着选模式:

访问模式上选A模式
因为我们OE读时序需要翻转,且LCD使用的是类似异步、地址与数据线独立的SRAM控制方式,故选A模式。

那RS怎么办?单独给它个引脚来软件控制吗?
No,no,no,太麻烦了,虽然LCD没有地址引脚,但是我们可以利用FSMC地址线的这个概念来控制RS引脚——把RS接到Ax地址线上去。
RS引脚接在地址线Ax上去了呀,我们怎么控制它呢?
使用FSMC外接存储器,其存储单元是映射到STM32的内部寻址空间的。
从FSMC角度看,可以把外部存储器划分为固定大小为256M字节的四个存储块。
FSMC存储块1被分为4个区,每个区管理64M字节空间:

我们访问FSMC地址,就能使对应地址线置位(地址线对应高低电平等于我们访问的地址),不得不说,FSMC真的好智能。
而当FSMC_A10为高电平时(即RS为高电平),FSMC_D[15:0]被理解为数据。
当FSMC_A10为低电平时(即RS为低电平),FSMC_D[15:0]被理解为命令。
于是,我们就可以靠访问不同的地址达到传输数据和命令的效果。
比如,我们选择第一块的区域4作为我们的LCD
于是NE4的基地址就是

公式:0x6000 0000 + (0x400 0000 * (x - 1))
当x=4时,NE4=0x6C000000

A10作为RS引脚:

公式:FSMC_Ay(y=025)2^y * 2 
当y=10时,A10= 2^10*2=0x800

所得:

代表LCD命令的地址:0x6C00 0000
代表LCD数据的地址:0x6C00 0800

详情也可以参考:
STM32 ----小谈FSMC RS选择

9.5 新版LCD驱动

原理了于心,码字如神助。
直接附上代码:
lcd.h

//
// Created by Whisky on 2023/1/7.
//

#ifndef HELLOWORLD_LCD_H
#define HELLOWORLD_LCD_H
#include "main.h"

/* 写引脚 */
#define LCD_WR_GPIO_PORT                    GPIOD
#define LCD_WR_GPIO_PIN                     GPIO_PIN_5
#define LCD_WR_GPIO_CLK_ENABLE()            do{ __HAL_RCC_GPIOD_CLK_ENABLE(); }while(0)

/* 读引脚 */
#define LCD_RD_GPIO_PORT                    GPIOD
#define LCD_RD_GPIO_PIN                     GPIO_PIN_4
#define LCD_RD_GPIO_CLK_ENABLE()            do{ __HAL_RCC_GPIOD_CLK_ENABLE(); }while(0)

/* LCD_BL背光引脚 */
#define LCD_BL_GPIO_PORT                    GPIOB
#define LCD_BL_GPIO_PIN                     GPIO_PIN_0
#define LCD_BL_GPIO_CLK_ENABLE()            do{ __HAL_RCC_GPIOB_CLK_ENABLE(); }while(0)

/*  LCD_CS(需要根据LCD_FSMC_NEX设置正确的IO口) 和 LCD_RS(需要根据LCD_FSMC_AX设置正确的IO口) 引脚 定义 */
/* CS片选引脚 */
#define LCD_CS_GPIO_PORT                    GPIOG
#define LCD_CS_GPIO_PIN                     GPIO_PIN_12
#define LCD_CS_GPIO_CLK_ENABLE()            do{ __HAL_RCC_GPIOG_CLK_ENABLE(); }while(0)     /* PG口时钟使能 */

/* RS数据类型引脚 */
#define LCD_RS_GPIO_PORT                    GPIOG
#define LCD_RS_GPIO_PIN                     GPIO_PIN_0
#define LCD_RS_GPIO_CLK_ENABLE()            do{ __HAL_RCC_GPIOG_CLK_ENABLE(); }while(0)     /* PG口时钟使能 */

#define GPIO_CLK_Enable() {__HAL_RCC_GPIOE_CLK_ENABLE();__HAL_RCC_GPIOD_CLK_ENABLE();__HAL_RCC_GPIOG_CLK_ENABLE();}


/* LCD重要参数集 */
typedef struct
{
    uint16_t width;
    uint16_t height;
    uint16_t id;
    uint8_t dir;        /* 方向   0-竖屏 1-横屏*/
    uint16_t wramcmd;   /* gram指令 w */
    uint16_t rramcmd;   /* gram指令 r */
    uint16_t setxcmd;   /* 设置x坐标 */
    uint16_t setycmd;   /* 设置y坐标 */
} _lcd_dev;
extern _lcd_dev _lcddev;

/* LCD背光控制 */
#define LCD_BL(x)       LCD_BL_GPIO_PORT->BSRR = LCD_BL_GPIO_PIN << (16 * (!x))

/* FSMC 硬件选择 */
#define LCD_FSMC_NEX         4              /* 使用FSMC_NE4接LCD_CS,取值范围只能是: 1~4 */
#define LCD_FSMC_AX          10             /* 使用FSMC_A10接LCD_RS,取值范围是: 0 ~ 25 */
/* LCD_BASE = (0X6000 0000 + (0X400 0000 * (x - 1))) | (2^y * 2 - 2) */
#define FSMC_ADDR_DATA          ((uint32_t) 0X6C000800) //A10置为1    CS = 1
#define FSMC_ADDR_CMD           ((uint32_t) 0X6C000000) //A10置为0    CS = 0

#define LCD_CHINESE_FONT_12 12
#define LCD_CHINESE_FONT_16 16
#define LCD_CHINESE_FONT_24 24

#define LCD_FONTSIZE_1206 12
#define LCD_FONTSIZE_1608 16
#define LCD_FONTSIZE_2412 24
#define LCD_FONTSIZE_3216 32

#define WHITE           0xFFFF      /* 白色 */
#define BLACK           0x0000      /* 黑色 */
#define RED             0xF800      /* 红色 */
#define GREEN           0x07E0      /* 绿色 */
#define BLUE            0x001F      /* 蓝色 */
#define MAGENTA         0XF81F      /* 品红色/紫红色 = BLUE + RED */
#define YELLOW          0XFFE0      /* 黄色 = GREEN + RED */
#define CYAN            0X07FF      /* 青色 = GREEN + BLUE */
#define BROWN           0XBC40      /* 棕色 */
#define BRRED           0XFC07      /* 棕红色 */
#define GRAY            0X8430      /* 灰色 */
#define DARKBLUE        0X01CF      /* 深蓝色 */
#define LIGHTBLUE       0X7D7C      /* 浅蓝色 */
#define GRAYBLUE        0X5458      /* 灰蓝色 */
#define LIGHTGREEN      0X841F      /* 浅绿色 */
#define LGRAY           0XC618      /* 浅灰色(PANNEL),窗体背景色 */
#define LGRAYBLUE       0XA651      /* 浅灰蓝色(中间层颜色) */
#define LBBLUE          0X2B12      /* 浅棕蓝色(选择条目的反色) */

#define LCD_MODE_BACKFILLED 1
#define LCD_MODE_NO_BACKFILLED 0

typedef struct {
    uint16_t color;
    uint8_t size;
    uint8_t mode;
    uint16_t width;     //string
    uint16_t height;    //string
    uint16_t back_color;
    uint16_t chinese_size;
} f_args;
extern f_args painter;

//To User:
void lcd_init(void);
void lcd_draw_point(uint16_t x, uint16_t y, uint16_t color);
uint16_t lcd_read_point(uint16_t x, uint16_t y);

#define lcd_clear(...)                  _lcd_clear((f_args){__VA_ARGS__})
#define lcd_show_char(x, y, chr,...)    _lcd_show_char(x, y, chr, (f_args){__VA_ARGS__})
#define lcd_show_num(x, y, num, len,...)    _lcd_show_num(x, y, num, len, (f_args){__VA_ARGS__})
void lcd_show_string(uint16_t x, uint16_t y, uint8_t *p);

void lcd_fill(uint16_t sx,uint16_t sy,uint16_t ex,uint16_t ey,uint16_t color);

//To Me:
void _lcd_clear(f_args in);
void _lcd_show_char(uint16_t x, uint16_t y, char chr, f_args in);
void _lcd_show_num(uint16_t x, uint16_t y, uint32_t num, uint8_t len,  f_args in);

void _lcd_wr_data (uint16_t data);
void _lcd_wr_regno(uint16_t reg);
void _lcd_ex_ili9341_reginit(void);
void _lcd_ex_st7789_reginit(void);

#endif //HELLOWORLD_LCD_H

lcd.c

//
// Created by Whisky on 2023/1/7.
//
#include "lcd.h"
#include "retarget.h"

SRAM_HandleTypeDef hsram4;

/* 管理LCD重要参数 */
_lcd_dev _lcddev;

/* 写命令 */
void _lcd_wr_regno(volatile uint16_t cmd)
{
    cmd = cmd;
    *(uint16_t *)(FSMC_ADDR_CMD) = cmd;
}

/* 写数据 */
void _lcd_wr_data(volatile uint16_t data)
{
    data = data;
    *(uint16_t *)(FSMC_ADDR_DATA) = data;
}

/* 写寄存器 */
void lcd_write_reg(uint16_t regno, uint16_t data)
{
    _lcd_wr_regno(regno);
    _lcd_wr_data(data);
}

/* 读数据 */
uint16_t lcd_rd_data(void)
{
    volatile uint16_t ram;
    ram = *(uint16_t *)(FSMC_ADDR_DATA);
    return ram;
}

/* 准备写GRAM */
void lcd_write_ram_prepare(void)
{
    _lcd_wr_regno(_lcddev.wramcmd);
}

/* 准备读GRAM */
void lcd_read_ram_prepare(void)
{
    _lcd_wr_regno(_lcddev.rramcmd);
}

/* 设置坐标 */
void lcd_set_cursor(uint16_t x, uint16_t y)
{
    _lcd_wr_regno(_lcddev.setxcmd);
    _lcd_wr_data(x >> 8);
    _lcd_wr_data(x & 0xFF);
    _lcd_wr_regno(_lcddev.setycmd);
    _lcd_wr_data(y >> 8);
    _lcd_wr_data(y & 0xFF);
}

void lcd_draw_point(uint16_t x, uint16_t y, uint16_t color)
{
    lcd_set_cursor(x, y);
    lcd_write_ram_prepare();
    _lcd_wr_data(color);
}

uint16_t lcd_read_point(uint16_t x, uint16_t y)
{
    uint16_t R = 0, G = 0, B = 0;
    lcd_set_cursor(x, y);
    lcd_read_ram_prepare();
    R = lcd_rd_data();  //dummy
    R = lcd_rd_data();
    B = lcd_rd_data();
    G = R & 0xFF;
    return (((R >> 11) << 11) | ((G >> 2) << 5) | (B >> 11));
}

void lcd_MspInit(void);

void lcd_init(void)
{
    FSMC_NORSRAM_TimingTypeDef Timing = {0};
    FSMC_NORSRAM_TimingTypeDef ExtTiming = {0};
    /** Perform the SRAM4 memory initialization sequence
    */
    hsram4.Instance = FSMC_NORSRAM_DEVICE;
    hsram4.Extended = FSMC_NORSRAM_EXTENDED_DEVICE;
    /* hsram4.Init */
    hsram4.Init.NSBank = FSMC_NORSRAM_BANK4;
    hsram4.Init.DataAddressMux = FSMC_DATA_ADDRESS_MUX_DISABLE;
    hsram4.Init.MemoryType = FSMC_MEMORY_TYPE_SRAM;
    hsram4.Init.MemoryDataWidth = FSMC_NORSRAM_MEM_BUS_WIDTH_16;
    hsram4.Init.BurstAccessMode = FSMC_BURST_ACCESS_MODE_DISABLE;
    hsram4.Init.WaitSignalPolarity = FSMC_WAIT_SIGNAL_POLARITY_LOW;
    hsram4.Init.WrapMode = FSMC_WRAP_MODE_DISABLE;
    hsram4.Init.WaitSignalActive = FSMC_WAIT_TIMING_BEFORE_WS;
    hsram4.Init.WriteOperation = FSMC_WRITE_OPERATION_ENABLE;
    hsram4.Init.WaitSignal = FSMC_WAIT_SIGNAL_DISABLE;
    hsram4.Init.ExtendedMode = FSMC_EXTENDED_MODE_ENABLE;
    hsram4.Init.AsynchronousWait = FSMC_ASYNCHRONOUS_WAIT_DISABLE;
    hsram4.Init.WriteBurst = FSMC_WRITE_BURST_DISABLE;
    /* Timing */                /* 读时序 */
    Timing.AddressSetupTime = 1;
    Timing.AddressHoldTime = 15;
    Timing.DataSetupTime = 15;
    Timing.BusTurnAroundDuration = 0;
    Timing.CLKDivision = 16;
    Timing.DataLatency = 17;
    Timing.AccessMode = FSMC_ACCESS_MODE_A;
    /* ExtTiming */             /* 写时序 */
    ExtTiming.AddressSetupTime = 1;
    ExtTiming.AddressHoldTime = 15;
    ExtTiming.DataSetupTime = 3;
    ExtTiming.BusTurnAroundDuration = 0;
    ExtTiming.CLKDivision = 16;
    ExtTiming.DataLatency = 17;
    ExtTiming.AccessMode = FSMC_ACCESS_MODE_A;

    lcd_MspInit();
    HAL_SRAM_Init(&hsram4, &Timing, &ExtTiming);

    delay_ms(50);

    _lcd_wr_regno(0xD3);
    _lcddev.id = lcd_rd_data();  /* dummy */
    _lcddev.id = lcd_rd_data();  /* 00 */
    _lcddev.id = lcd_rd_data();  /* 93 */
    _lcddev.id <<= 8;
    _lcddev.id |= lcd_rd_data();  /* 41 */

    printf("LCD id:%#x \r\n", _lcddev.id);
    /* 完成初始化数组序列 */
    if (_lcddev.id == 0x9341)
    {
        _lcd_ex_ili9341_reginit();
    }
    else{
        _lcd_ex_st7789_reginit();
    }

    /* 初始化LCD结构体 */
    _lcddev.width = 240;
    _lcddev.height = 320;
    _lcddev.setxcmd = 0x2A;
    _lcddev.setycmd = 0x2B;
    _lcddev.wramcmd = 0x2C;
    _lcddev.rramcmd = 0x2E;

    /* 设置终末位置 */
    _lcd_wr_regno(_lcddev.setxcmd);
    _lcd_wr_data(0);
    _lcd_wr_data(0);
    _lcd_wr_data((_lcddev.width - 1) >> 8);
    _lcd_wr_data((_lcddev.width - 1) & 0XFF);
    _lcd_wr_regno(_lcddev.setycmd);
    _lcd_wr_data(0);
    _lcd_wr_data(0);
    _lcd_wr_data((_lcddev.height - 1) >> 8);
    _lcd_wr_data((_lcddev.height - 1) & 0XFF);

    /* 设置扫描方向 */
    lcd_write_reg(0x36, 1 << 3);

    painter.color = RED;
    painter.back_color = WHITE;
    painter.size = LCD_FONTSIZE_1608;
    painter.mode = LCD_MODE_BACKFILLED;
    painter.chinese_size = LCD_CHINESE_FONT_16;

    /* 点亮背光 */
    LCD_BL(1);

    lcd_clear();
}

static uint32_t FSMC_Initialized = 0;

void lcd_MspInit(void){
    GPIO_InitTypeDef GPIO_InitStruct = {0};
    if (FSMC_Initialized) {
        return;
    }
    FSMC_Initialized = 1;
    /* Peripheral clock enable */
    __HAL_RCC_FSMC_CLK_ENABLE();

    GPIO_CLK_Enable();
    LCD_BL_GPIO_CLK_ENABLE();
    LCD_CS_GPIO_CLK_ENABLE();
    LCD_WR_GPIO_CLK_ENABLE();
    LCD_RD_GPIO_CLK_ENABLE();
    LCD_RS_GPIO_CLK_ENABLE();

    GPIO_InitStruct.Pin = LCD_BL_GPIO_PIN;
    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
    GPIO_InitStruct.Pull = GPIO_PULLUP;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(LCD_BL_GPIO_PORT, &GPIO_InitStruct);

//    GPIO_InitStruct.Pin = LCD_CS_GPIO_PIN;
//    HAL_GPIO_Init(LCD_CS_GPIO_PORT, &GPIO_InitStruct);
//
//    GPIO_InitStruct.Pin = LCD_WR_GPIO_PIN;
//    HAL_GPIO_Init(LCD_WR_GPIO_PORT, &GPIO_InitStruct);
//
//    GPIO_InitStruct.Pin = LCD_RD_GPIO_PIN;
//    HAL_GPIO_Init(LCD_RD_GPIO_PORT, &GPIO_InitStruct);
//
//    GPIO_InitStruct.Pin = LCD_RS_GPIO_PIN;
//    HAL_GPIO_Init(LCD_RS_GPIO_PORT, &GPIO_InitStruct);
    /** FSMC GPIO Configuration
    PG0   ------> FSMC_A10
    PE7   ------> FSMC_D4
    PE8   ------> FSMC_D5
    PE9   ------> FSMC_D6
    PE10   ------> FSMC_D7
    PE11   ------> FSMC_D8
    PE12   ------> FSMC_D9
    PE13   ------> FSMC_D10
    PE14   ------> FSMC_D11
    PE15   ------> FSMC_D12
    PD8   ------> FSMC_D13
    PD9   ------> FSMC_D14
    PD10   ------> FSMC_D15
    PD14   ------> FSMC_D0
    PD15   ------> FSMC_D1
    PD0   ------> FSMC_D2
    PD1   ------> FSMC_D3
    PD4   ------> FSMC_NOE
    PD5   ------> FSMC_NWE
    PG12   ------> FSMC_NE4
    */
    /* GPIO_InitStruct */
    GPIO_InitStruct.Pin = GPIO_PIN_0|GPIO_PIN_12;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(GPIOG, &GPIO_InitStruct);
    /* GPIO_InitStruct */
    GPIO_InitStruct.Pin = GPIO_PIN_7|GPIO_PIN_8|GPIO_PIN_9|GPIO_PIN_10
                          |GPIO_PIN_11|GPIO_PIN_12|GPIO_PIN_13|GPIO_PIN_14
                          |GPIO_PIN_15;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(GPIOE, &GPIO_InitStruct);
    /* GPIO_InitStruct */
    GPIO_InitStruct.Pin = GPIO_PIN_8|GPIO_PIN_9|GPIO_PIN_10|GPIO_PIN_14
                          |GPIO_PIN_15|GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_4
                          |GPIO_PIN_5;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);

}

void _lcd_clear(f_args in)
{
    uint16_t color = in.color? in.color: painter.back_color;
    uint32_t totalpoint = _lcddev.width;
    totalpoint *= _lcddev.height;                /* 得到总点数 */
    lcd_set_cursor(0, 0);                   /* 设置光标位置 */
    lcd_write_ram_prepare();                    /* 开始写入GRAM */
    for (uint32_t index; index < totalpoint; index++)
    {
        _lcd_wr_data(color);
    }
}

//在指定区域内填充单个颜色
//(sx,sy),(ex,ey):填充矩形对角坐标
//区域大小为:(ex-sx+1)*(ey-sy+1)
//color:要填充的颜色
void lcd_fill(uint16_t sx,uint16_t sy,uint16_t ex,uint16_t ey,uint16_t color)
{
    uint16_t i, j;
    uint16_t xlen = 0;

    xlen = ex - sx + 1;

    for (i = sy; i <= ey; i++)
    {
        lcd_set_cursor(sx, i);       //设置光标位置
        lcd_write_ram_prepare();     //开始写入GRAM

        for (j = 0; j < xlen; j++)
        {
            *(uint16_t *)FSMC_ADDR_DATA = color;   //设置光标位置
        }
    }
}


lcd_ex.c

#include "lcd.h"


/**
 * @brief       ST7789 寄存器初始化代码
 * @param       无
 * @retval      无
 */
void _lcd_ex_st7789_reginit(void)
{
    _lcd_wr_regno(0x11);

    delay_ms(120);

    _lcd_wr_regno(0x36);
    _lcd_wr_data(0x00);

    _lcd_wr_regno(0x3A);
    _lcd_wr_data(0X05);

    _lcd_wr_regno(0xB2);
    _lcd_wr_data(0x0C);
    _lcd_wr_data(0x0C);
    _lcd_wr_data(0x00);
    _lcd_wr_data(0x33);
    _lcd_wr_data(0x33);

    _lcd_wr_regno(0xB7);
    _lcd_wr_data(0x35);

    _lcd_wr_regno(0xBB); /* vcom */
    _lcd_wr_data(0x32);  /* 30 */

    _lcd_wr_regno(0xC0);
    _lcd_wr_data(0x0C);

    _lcd_wr_regno(0xC2);
    _lcd_wr_data(0x01);

    _lcd_wr_regno(0xC3); /* vrh */
    _lcd_wr_data(0x10);  /* 17 0D */

    _lcd_wr_regno(0xC4); /* vdv */
    _lcd_wr_data(0x20);  /* 20 */

    _lcd_wr_regno(0xC6);
    _lcd_wr_data(0x0f);

    _lcd_wr_regno(0xD0);
    _lcd_wr_data(0xA4);
    _lcd_wr_data(0xA1);

    _lcd_wr_regno(0xE0); /* Set Gamma  */
    _lcd_wr_data(0xd0);
    _lcd_wr_data(0x00);
    _lcd_wr_data(0x02);
    _lcd_wr_data(0x07);
    _lcd_wr_data(0x0a);
    _lcd_wr_data(0x28);
    _lcd_wr_data(0x32);
    _lcd_wr_data(0X44);
    _lcd_wr_data(0x42);
    _lcd_wr_data(0x06);
    _lcd_wr_data(0x0e);
    _lcd_wr_data(0x12);
    _lcd_wr_data(0x14);
    _lcd_wr_data(0x17);


    _lcd_wr_regno(0XE1);  /* Set Gamma */
    _lcd_wr_data(0xd0);
    _lcd_wr_data(0x00);
    _lcd_wr_data(0x02);
    _lcd_wr_data(0x07);
    _lcd_wr_data(0x0a);
    _lcd_wr_data(0x28);
    _lcd_wr_data(0x31);
    _lcd_wr_data(0x54);
    _lcd_wr_data(0x47);
    _lcd_wr_data(0x0e);
    _lcd_wr_data(0x1c);
    _lcd_wr_data(0x17);
    _lcd_wr_data(0x1b);
    _lcd_wr_data(0x1e);


    _lcd_wr_regno(0x2A);
    _lcd_wr_data(0x00);
    _lcd_wr_data(0x00);
    _lcd_wr_data(0x00);
    _lcd_wr_data(0xef);

    _lcd_wr_regno(0x2B);
    _lcd_wr_data(0x00);
    _lcd_wr_data(0x00);
    _lcd_wr_data(0x01);
    _lcd_wr_data(0x3f);

    _lcd_wr_regno(0x29); /* display on */
}

/**
 * @brief       ILI9341寄存器初始化代码
 * @param       无
 * @retval      无
 */
void _lcd_ex_ili9341_reginit(void)
{
    _lcd_wr_regno(0xCF);
    _lcd_wr_data(0x00);
    _lcd_wr_data(0xC1);
    _lcd_wr_data(0X30);
    _lcd_wr_regno(0xED);
    _lcd_wr_data(0x64);
    _lcd_wr_data(0x03);
    _lcd_wr_data(0X12);
    _lcd_wr_data(0X81);
    _lcd_wr_regno(0xE8);
    _lcd_wr_data(0x85);
    _lcd_wr_data(0x10);
    _lcd_wr_data(0x7A);
    _lcd_wr_regno(0xCB);
    _lcd_wr_data(0x39);
    _lcd_wr_data(0x2C);
    _lcd_wr_data(0x00);
    _lcd_wr_data(0x34);
    _lcd_wr_data(0x02);
    _lcd_wr_regno(0xF7);
    _lcd_wr_data(0x20);
    _lcd_wr_regno(0xEA);
    _lcd_wr_data(0x00);
    _lcd_wr_data(0x00);
    _lcd_wr_regno(0xC0); /* Power control */
    _lcd_wr_data(0x1B);  /* VRH[5:0] */
    _lcd_wr_regno(0xC1); /* Power control */
    _lcd_wr_data(0x01);  /* SAP[2:0];BT[3:0] */
    _lcd_wr_regno(0xC5); /* VCM control */
    _lcd_wr_data(0x30);  /* 3F */
    _lcd_wr_data(0x30);  /* 3C */
    _lcd_wr_regno(0xC7); /* VCM control2 */
    _lcd_wr_data(0XB7);
    _lcd_wr_regno(0x36); /*  Memory Access Control */
    _lcd_wr_data(0x48);
    _lcd_wr_regno(0x3A);
    _lcd_wr_data(0x55);
    _lcd_wr_regno(0xB1);
    _lcd_wr_data(0x00);
    _lcd_wr_data(0x1A);
    _lcd_wr_regno(0xB6); /*  Display Function Control */
    _lcd_wr_data(0x0A);
    _lcd_wr_data(0xA2);
    _lcd_wr_regno(0xF2); /*  3Gamma Function Disable */
    _lcd_wr_data(0x00);
    _lcd_wr_regno(0x26); /* Gamma curve selected */
    _lcd_wr_data(0x01);
    _lcd_wr_regno(0xE0); /* Set Gamma */
    _lcd_wr_data(0x0F);
    _lcd_wr_data(0x2A);
    _lcd_wr_data(0x28);
    _lcd_wr_data(0x08);
    _lcd_wr_data(0x0E);
    _lcd_wr_data(0x08);
    _lcd_wr_data(0x54);
    _lcd_wr_data(0XA9);
    _lcd_wr_data(0x43);
    _lcd_wr_data(0x0A);
    _lcd_wr_data(0x0F);
    _lcd_wr_data(0x00);
    _lcd_wr_data(0x00);
    _lcd_wr_data(0x00);
    _lcd_wr_data(0x00);
    _lcd_wr_regno(0XE1);    /* Set Gamma */
    _lcd_wr_data(0x00);
    _lcd_wr_data(0x15);
    _lcd_wr_data(0x17);
    _lcd_wr_data(0x07);
    _lcd_wr_data(0x11);
    _lcd_wr_data(0x06);
    _lcd_wr_data(0x2B);
    _lcd_wr_data(0x56);
    _lcd_wr_data(0x3C);
    _lcd_wr_data(0x05);
    _lcd_wr_data(0x10);
    _lcd_wr_data(0x0F);
    _lcd_wr_data(0x3F);
    _lcd_wr_data(0x3F);
    _lcd_wr_data(0x0F);
    _lcd_wr_regno(0x2B);
    _lcd_wr_data(0x00);
    _lcd_wr_data(0x00);
    _lcd_wr_data(0x01);
    _lcd_wr_data(0x3f);
    _lcd_wr_regno(0x2A);
    _lcd_wr_data(0x00);
    _lcd_wr_data(0x00);
    _lcd_wr_data(0x00);
    _lcd_wr_data(0xef);
    _lcd_wr_regno(0x11); /* Exit Sleep */
    delay_ms(120);
    _lcd_wr_regno(0x29); /* display on */
 }
 

lcdlib.c

//
// Created by Whisky on 2023/1/7.
//

#include "lcd.h"
#include "lcdfont.h"

f_args painter;

void _lcd_show_char(uint16_t x, uint16_t y, char chr, f_args in)
{
    /* 设置默认参数 */
    uint8_t size = in.size ? in.size : painter.size;
    uint16_t color = in.color ? in.color : painter.color;
    uint8_t mode = in.mode ? in.mode : painter.mode;

    uint8_t *pfont = NULL;
    uint8_t chr_cnt = (size/8+((size%8)?1:0))*(size/2) ;  /* 总字节数 */
    uint16_t y0 = y;
    chr = chr - ' ';    //字库是从' '开始存的

    switch(size)
    {
        case LCD_FONTSIZE_1206:
            pfont = (uint8_t *)asc2_1206[chr];  /* 调用1206字体 */
            break;

        case LCD_FONTSIZE_1608:
            pfont = (uint8_t *)asc2_1608[chr];  /* 调用1608字体 */
            break;

        case LCD_FONTSIZE_2412:
            pfont = (uint8_t *)asc2_2412[chr];  /* 调用2412字体 */
            break;

        case LCD_FONTSIZE_3216:
            pfont = (uint8_t *)asc2_3216[chr];  /* 调用3216字体 */
            break;

        default:
            return ;
    }
    for(uint8_t t = 0; t < chr_cnt; t++)
    {
        uint8_t temp = pfont[t];
        for(uint8_t t1 = 0; t1 < 8; t1++)   //处理8位数据
        {
            if(temp & 0x80){                //首位有效
                lcd_draw_point(x, y , color);
            }
            else if(mode == LCD_MODE_BACKFILLED)
            {
                lcd_draw_point(x, y , painter.back_color);
            }

            temp <<= 1;
            y++;
            if((y - y0) == size)
            {
                y = y0;
                x++;
                break;
            }
        }
    }
}
void lcd_show_string(uint16_t x, uint16_t y, uint8_t *p)
{
    //默认参数
    uint16_t width = painter.width ? painter.width : (_lcddev.width - x);
    uint16_t height = painter.height ? painter.height : (_lcddev.height - y);


    uint8_t x0 = x;
    width += x;
    height += y;

    while ((*p <= '~') && (*p >= ' '))   //判断是不是非法字符!
    {
        if (x >= width)
        {
            x = x0;
            y += painter.size;
        }

        if (y >= height)break; //退出

        lcd_show_char(x, y, *p, .size = painter.size, .color = painter.color, .mode = painter.mode);
        x += painter.size / 2;
        p++;
    }
}

uint32_t _lcd_pow(uint8_t m, uint8_t n)
{
    uint32_t result = 1;

    while (n--)result *= m;

    return result;
}

void _lcd_show_num(uint16_t x, uint16_t y, uint32_t num, uint8_t len,  f_args in)
{
    //默认参数
    uint8_t size = in.size ? in.size : painter.size;
    uint16_t color = in.color ? in.color : painter.color;
    uint8_t mode = in.mode ? in.mode : painter.mode;

    uint8_t t, temp;
    uint8_t enshow = 0;

    for (t = 0; t < len; t++)
    {
        temp = (num / _lcd_pow(10, len - t - 1)) % 10;

        if (enshow == 0 && t < (len - 1))
        {
            if (temp == 0)
            {
                lcd_show_char(x + (size / 2)*t, y, ' ', .size=size, .mode = mode, .color = color);
                continue;
            }
            else enshow = 1;

        }

        lcd_show_char(x + (size / 2)*t, y, temp + '0', .size=size, .mode = mode, .color = color);
    }
}

lcdfont.h (太大了,附不了了)

#ifndef __LCDFONT_H
#define __LCDFONT_H

/* 12*12 ASCII字符集点阵 */
const unsigned char asc2_1206[95][12]={
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*" ",0*/
{0x00,0x00,0x00,0x00,0x3F,0x40,0x00,0x00,0x00,0x00,0x00,0x00},/*"!",1*/
...//省略代码

至此,我们也完成了一个LCD的驱动
不过,笔者没有像正点原子那样做很多完备的功能函数(像什么画圆,画线就没移植过来了),朋友们可以自行查阅。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值