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

news/2024/11/7 18:04:59/

文章目录

  • 9 点亮第一块彩屏
    • 9.1 认识TFTLCD
    • 9.2 8080时序
    • 9.3 LCD驱动
    • 9.4 用FSMC爆改8080时序
    • 9.5 新版LCD驱动

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();  //dummyR = 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 0typedef struct {uint16_t color;uint8_t size;uint8_t mode;uint16_t width;     //stringuint16_t height;    //stringuint16_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();  //dummyR = 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 ConfigurationPG0   ------> FSMC_A10PE7   ------> FSMC_D4PE8   ------> FSMC_D5PE9   ------> FSMC_D6PE10   ------> FSMC_D7PE11   ------> FSMC_D8PE12   ------> FSMC_D9PE13   ------> FSMC_D10PE14   ------> FSMC_D11PE15   ------> FSMC_D12PD8   ------> FSMC_D13PD9   ------> FSMC_D14PD10   ------> FSMC_D15PD14   ------> FSMC_D0PD15   ------> FSMC_D1PD0   ------> FSMC_D2PD1   ------> FSMC_D3PD4   ------> FSMC_NOEPD5   ------> FSMC_NWEPG12   ------> 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();     //开始写入GRAMfor (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的驱动
不过,笔者没有像正点原子那样做很多完备的功能函数(像什么画圆,画线就没移植过来了),朋友们可以自行查阅。


http://www.ppmy.cn/news/934413.html

相关文章

UEFI实战--------HII之uni文件

uni文件 HII的实现涉及到多种不同类型的文件&#xff0c;uni文件是其中最简单的一种&#xff0c;它用来存放各种语言的字符串以实现本地化。本节主要参考自《edk-ii-uni-specification.pdf》&#xff0c;后面简称为参考文档。 关于uni文件的作用&#xff0c;在参考文档中做了如…

python中的data解释_python中的data解释_python unicodedata用法

UCD是Unicode字符数据库(Unicode Character DataBase)的缩写。 UCD由一些描述Unicode字符属性和内部关系的纯文本或html文件组成。 UCD中的文本文件大都是适合于程序分析的Unicode相关数据。其中的html文件解释了数据库的组织,数据的格式和含义。 UCD中最庞大的文件无疑就是描…

如何在 C++ 中调用 python 解析器来执行 python 代码(六)?

今天轮到讨论安全问题了。 python 代码中包含有害内容该怎么办&#xff1f;常用技术是沙箱&#xff08;Sandboxing&#xff09;。本文从一些基础设施讲起。 目录 如何在 C 中调用 python 解析器来执行 python 代码&#xff08;一&#xff09;&#xff1f;如何在 C 中调用 pyt…

Spring的那些开发小技巧(中)

BeanPostProcessor BeanPostProcessor&#xff0c;中文名 Bean的后置处理器&#xff0c;在Bean创建的过程中起作用。 BeanPostProcessor是Bean在创建过程中一个非常重要的扩展点&#xff0c;因为每个Bean在创建的各个阶段&#xff0c;都会回调BeanPostProcessor及其子接口的方法…

前端 js通过汉字实时识别出全拼

根据汉字识别出拼音 通过汉字实时识别出全拼效果直接上代码 通过汉字实时识别出全拼 在一次移动端项目中&#xff0c;因为是和银行对接项目&#xff0c;所以有了这一个需求&#xff0c;实时带出汉字全拼。用的库是VUX。 效果 直接上代码 <x-inputtitle"中文姓名&quo…

csapp实验bomb lab(反汇编技术与gdb调试)

一.实验目的 该实验要在linux环境下做&#xff0c;因为我的虚拟机VMware不能与宿主机之间传输文件、复制黏贴&#xff08;弄了好长时间也没弄成&#xff09;&#xff0c;所以实验包我是在虚拟机中登录官网下载的&#xff0c;下载地址为&#xff1a; CS:APP3e, Bryant and OHal…

常见行业缩略语

缩略语全称释义MMPMinimal Marketable Product最小適銷產品。保有最小量而適切的功能需求產品&#xff0c;可以滿足用戶基本需求和體驗。MVPMinimum Viable Product最小可行性產品。是可以讓目標用戶使用的前期產品&#xff0c;幫助開發團隊蒐集回饋&#xff0c;並從中學習。在…

dns服务器系统架构,详解 DNS 与 CoreDNS 的实现原理

原文链接:https://draveness.me/dns-coredns 【编者的话】域名系统(Domain Name System)是整个互联网的电话簿,它能够将可被人理解的域名翻译成可被机器理解 IP 地址,使得互联网的使用者不再需要直接接触很难阅读和理解的 IP 地址。 我们在这篇文章中的第一部分会介绍 DNS 的…