文章目录
- 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=0…25): 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的驱动
不过,笔者没有像正点原子那样做很多完备的功能函数(像什么画圆,画线就没移植过来了),朋友们可以自行查阅。