最近项目使用STM32F407驱动tft彩屏,屏幕的内部驱动芯片为ST7789V,这个IC网上资料不多。
废话不说,直接上完整驱动代码。
/*
*************************************************************************************************
*
* 模块名称 : TFT液晶显示器驱动模块
* 文件名称 : lcd_drv.h
* 版 本 : V1.0
* 说 明 : 头文件.
* lcddrv_FillColorPre函数设置LCD显示区域,之后把数据赋予LCDDRV_RAM即可绘图此区域.
* 其他绘图函数都可通过lcddrv_FillColorPre和予LCDDRV_RAM构成
***************************************************************************************************
*/
#ifndef __LCD_DRV_H
#define __LCD_DRV_H
#include "stm32f4xx.h"
#define ST7789_REG *(__IO u16 *)(((uint32_t)0x60000000))
#define ST7789_RAM *(__IO u16 *)(((uint32_t)0x60000000) + (1 << 17)) //A16 右移1位
/*******************供bsp调用的接口API**********************/
//背景光控制
#define BRIGHT_MAX 100
#define BRIGHT_MIN 0
#define BRIGHT_DEFAULT BRIGHT_MAX
#define LCD_WIDTH 320 // 显示屏宽度像素
#define LCD_HEIGHT 240 // 显示屏高度像素
#define LCDDRV_RAM ST7789_RAM //显示GRAM接收地址
extern void lcddrv_Init(void); //初始化函数
extern void lcddrv_FillColorPre(u16 X, u16 Y, u16 Width , u16 Height);
extern void lcddrv_SetBackLight(u8 bright);//设置背光
extern void lcddrv_DispOn(void); //打开显示
extern void lcddrv_DispOff(void); //关闭显示
#endif
/***************************** (END OF FILE) *********************************/
#include "stm32f4xx.h"
#include
#include
#include "delay.h"
#include "lcd_drv.h"
#define lcddelay_ms(n) delay_ms(n)
#define LCD_RST_PIN PFout(15)
//ST7789寄存器定义
#define ST7789_CMD_RDDID 0x04 //读取ID
#define ST7789_CMD_SLPIN 0x10
#define ST7789_CMD_SLPOUT 0x11
#define ST7789_CMD_DISPOFF 0x28
#define ST7789_CMD_DISPON 0x29
#define ST7789_CMD_CASETF 0x2A
#define ST7789_CMD_RASETF 0x2B
#define ST7789_CMD_RAMWR 0x2C //开始写内存数据
#define ST7789_CMD_PTLON 0x12 //部分显示模式
#define ST7789_CMD_NORON 0x13 //普通模式
#define ST7789_CMD_INVOFF 0x20 //关闭反显示
#define ST7789_CMD_INVON 0x21 //打开反显示
#define ST7789_CMD_PTLAR 0x30 //部分显示区域 由上下两个水平坐标
//写寄存器(CMD)
//regval:寄存器值
static void ST7789_WR_REG(vu16 regval)
{
regval = regval;
ST7789_REG = regval;
}
//写数据
//data:要写入的值
static void ST7789_WR_DATA(vu16 data)
{
data = data;
ST7789_RAM = data;
}
//读LCD数据
//返回值:读到的值
static u16 ST7789_RD_DATA(void)
{
vu16 ram;
ram = ST7789_RAM;
return ram;
}
/*
*********************************************************************************************
* 函 数 名: ST7789_ReadID
* 功能说明: 读取LCD驱动芯片ID
* 形 参: 无
* 返 回 值: 无
*********************************************************************************************
*/
u16 ST7789_ReadID(void)
{
u16 id = 0;
ST7789_WR_REG(ST7789_CMD_RDDID);
ST7789_RD_DATA();
id = ST7789_RD_DATA();
ST7789_RD_DATA();
ST7789_RD_DATA() ;
return id ;
}
/*
****************************************************************************************
* 函 数 名: ST7789_REG_Init
* 功能说明: 初始化LCD驱动芯片寄存器
* 形 参: 无
* 返 回 值: 无
****************************************************************************************
*/
static void ST7789_REG_Init(void)
{
/* 初始化LCD,写LCD寄存器进行配置 */
lcddelay_ms(10);
ST7789_WR_REG(ST7789_CMD_SLPOUT); //exit SLEEP mode
lcddelay_ms(150);
ST7789_WR_REG(0x00D0); //PWCTRL1: Power Control 1
ST7789_WR_DATA(0x00A4);
ST7789_WR_DATA(0x81);
ST7789_WR_REG(0x00BB);
ST7789_WR_DATA(0x0030); //VCOMS: VCOM setting
ST7789_WR_REG(0x0036); //memory data access control
ST7789_WR_DATA(0x60); //MADCTL: 方向等等
ST7789_WR_REG(0x003A); // Interface Pixel Format
ST7789_WR_DATA(0x05); //COLMOD: 16bit
ST7789_WR_REG(0x00E0);
ST7789_WR_DATA(0x00D0);
ST7789_WR_DATA(0x0000);
ST7789_WR_DATA(0x0002);
ST7789_WR_DATA(0x0007);
ST7789_WR_DATA(0x000B);
ST7789_WR_DATA(0x001A);
ST7789_WR_DATA(0x0031);
ST7789_WR_DATA(0x0054);
ST7789_WR_DATA(0x0040);
ST7789_WR_DATA(0x0029);
ST7789_WR_DATA(0x0012);
ST7789_WR_DATA(0x0012);
ST7789_WR_DATA(0x0012);
ST7789_WR_DATA(0x0017);
ST7789_WR_REG(0x00E1);
ST7789_WR_DATA(0x00D0);
ST7789_WR_DATA(0x0000);
ST7789_WR_DATA(0x0002);
ST7789_WR_DATA(0x0007);
ST7789_WR_DATA(0x0005);
ST7789_WR_DATA(0x0025);
ST7789_WR_DATA(0x002D);
ST7789_WR_DATA(0x0044);
ST7789_WR_DATA(0x0045);
ST7789_WR_DATA(0x001C);
ST7789_WR_DATA(0x0018);
ST7789_WR_DATA(0x0016);
ST7789_WR_DATA(0x001C);
ST7789_WR_DATA(0x001D);
ST7789_WR_REG(0x21);//取反
ST7789_WR_REG(ST7789_CMD_DISPON);// DISPALY ON
lcddelay_ms(100);
}
/*
***************************************************************************************************
* 函 数 名: ST7789_Init
* 功能说明: 初始化LCD
* 形 参: 无
* 返 回 值: 0/1:失败/正确
***************************************************************************************************
*/
u8 ST7789_Init(void)
{
//硬重启
LCD_RST_PIN = 1 ;
lcddelay_ms(3);
LCD_RST_PIN = 0 ;
lcddelay_ms(6);
LCD_RST_PIN = 1 ;
lcddelay_ms(150);
if ( ST7789_ReadID() == 0x85 )
{
ST7789_REG_Init(); /* 初始化屏IC寄存器 */
return 1;
}
return 0;
}
/*
*************************************************************************************************
* 函 数 名: STM_GPIOConfig
* 功能说明: 配置LCD控制口线,FSMC管脚设置
* 形 参: 无
* 返 回 值: 无
*************************************************************************************************
*/
/*
LCD的 RS(DC) 引脚 接 PD11/FSMC_A16 (0命令,1数据)
LCD的 CS 片选引脚 接 PD7/FSMC_NE1
其他的 NWE, NOE, D15~D0 接对应的FSMC口线即可。
接线如下:
PD0/FSMC_D2
PD1/FSMC_D3
PD4/FSMC_NOE --- 读控制信号,OE , N 即低有效 (读)
PD5/FSMC_NWE --- 写控制信号,WE ,N 即低有效 (写)
PD7/FSMC_NE1 --- 块选择,片选信号CS
PD8/FSMC_D13
PD9/FSMC_D14
PD10/FSMC_D15
PD11/FSMC_A16 --- 地址 RS DC (命令OR数据选择)
PD14/FSMC_D0
PD15/FSMC_D1
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
---- TFT LCD接口其他信号 (非FSMC所使用)----
PC13/LCD_PWM --- LCD背光PWM控制 由背光控制函数进行配置初始化
PF15/LCD_RST --- 硬复位LCD
*/
static void STM_GPIOConfig(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
/* 使能 GPIO时钟 */
RCC_AHB1PeriphClockCmd( RCC_AHB1Periph_GPIOD | RCC_AHB1Periph_GPIOE |
RCC_AHB1Periph_GPIOG | RCC_AHB1Periph_GPIOF, ENABLE);
/* 使能FSMC时钟 */
RCC_AHB3PeriphClockCmd(RCC_AHB3Periph_FSMC, ENABLE);
/* 设置 PD.00(D2), PD.01(D3), PD.04(NOE), PD.05(NWE), PD.08(D13), PD.09(D14),
PD.10(D15), PD.14(D0), PD.15(D1) 为复用推挽输出 + PD11(DC), PD7(CS) */
GPIO_InitStructure.GPIO_Pin =
GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_7 |
GPIO_Pin_8 | GPIO_Pin_9 | GPIO_Pin_10 | GPIO_Pin_11 | GPIO_Pin_14 | GPIO_Pin_15;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(GPIOD, &GPIO_InitStructure);
/* 设置 PE.07(D4), PE.08(D5), PE.09(D6), PE.10(D7), PE.11(D8), PE.12(D9), PE.13(D10),
PE.14(D11), PE.15(D12) 为复用推挽输出 */
GPIO_InitStructure.GPIO_Pin =
GPIO_Pin_4 | GPIO_Pin_5 | 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_Init(GPIOE, &GPIO_InitStructure);
/*复用映射功能*/
GPIO_PinAFConfig(GPIOD, GPIO_PinSource0, GPIO_AF_FSMC);
GPIO_PinAFConfig(GPIOD, GPIO_PinSource1, GPIO_AF_FSMC);
GPIO_PinAFConfig(GPIOD, GPIO_PinSource4, GPIO_AF_FSMC);
GPIO_PinAFConfig(GPIOD, GPIO_PinSource5, GPIO_AF_FSMC);
GPIO_PinAFConfig(GPIOD, GPIO_PinSource7, GPIO_AF_FSMC);
GPIO_PinAFConfig(GPIOD, GPIO_PinSource8, GPIO_AF_FSMC);
GPIO_PinAFConfig(GPIOD, GPIO_PinSource9, GPIO_AF_FSMC);
GPIO_PinAFConfig(GPIOD, GPIO_PinSource10, GPIO_AF_FSMC);
GPIO_PinAFConfig(GPIOD, GPIO_PinSource11, GPIO_AF_FSMC);
GPIO_PinAFConfig(GPIOD, GPIO_PinSource14, GPIO_AF_FSMC);
GPIO_PinAFConfig(GPIOD, GPIO_PinSource15, GPIO_AF_FSMC);
GPIO_PinAFConfig(GPIOE, GPIO_PinSource4 , GPIO_AF_FSMC);
GPIO_PinAFConfig(GPIOE, GPIO_PinSource5 , GPIO_AF_FSMC);
GPIO_PinAFConfig(GPIOE, GPIO_PinSource7 , GPIO_AF_FSMC);
GPIO_PinAFConfig(GPIOE, GPIO_PinSource8 , GPIO_AF_FSMC);
GPIO_PinAFConfig(GPIOE, GPIO_PinSource9 , GPIO_AF_FSMC);
GPIO_PinAFConfig(GPIOE, GPIO_PinSource10 , GPIO_AF_FSMC);
GPIO_PinAFConfig(GPIOE, GPIO_PinSource11 , GPIO_AF_FSMC);
GPIO_PinAFConfig(GPIOE, GPIO_PinSource12 , GPIO_AF_FSMC);
GPIO_PinAFConfig(GPIOE, GPIO_PinSource13 , GPIO_AF_FSMC);
GPIO_PinAFConfig(GPIOE, GPIO_PinSource14 , GPIO_AF_FSMC);
GPIO_PinAFConfig(GPIOE, GPIO_PinSource15 , GPIO_AF_FSMC);
/* 设置 PF15 (LCD/RST)) 为推挽输出 */
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_15;
GPIO_Init(GPIOF, &GPIO_InitStructure);
GPIO_SetBits(GPIOF, GPIO_Pin_15);
/* 设置 PF14 (LCD/EN)) 为推挽输出 */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14;
GPIO_Init(GPIOF, &GPIO_InitStructure);
GPIO_SetBits(GPIOF, GPIO_Pin_14 );
/* 设置 背光 为PWM */
//在背光函数
}
/*
*************************************************************************************************
* 函 数 名: STM_FSMCConfig
* 功能说明: 配置FSMC并口访问时序
* 形 参: 无
* 返 回 值: 无
*************************************************************************************************
*/
static void STM_FSMCConfig(void)
{
FSMC_NORSRAMInitTypeDef fsmcinit;
FSMC_NORSRAMTimingInitTypeDef timingWrite;
FSMC_NORSRAMTimingInitTypeDef timingRead;
// 写 时间配置 主要是 AddressSetupTime 与 DataSetupTime ,可修改更小。写就是LCD显示。
timingWrite.FSMC_AddressSetupTime = 15;
timingWrite.FSMC_AddressHoldTime = 0;
timingWrite.FSMC_DataSetupTime = 20;
timingWrite.FSMC_BusTurnAroundDuration = 0;
timingWrite.FSMC_CLKDivision = 0;
timingWrite.FSMC_DataLatency = 0;
timingWrite.FSMC_AccessMode = FSMC_AccessMode_A;
// 读 时间配置 主要是 AddressSetupTime 与 DataSetupTime
timingRead.FSMC_AddressSetupTime = 8;
timingRead.FSMC_AddressHoldTime = 0;
timingRead.FSMC_DataSetupTime = 9;
timingRead.FSMC_BusTurnAroundDuration = 0;
timingRead.FSMC_CLKDivision = 0;
timingRead.FSMC_DataLatency = 0;
timingRead.FSMC_AccessMode = FSMC_AccessMode_A;
fsmcinit.FSMC_Bank = FSMC_Bank1_NORSRAM1;
fsmcinit.FSMC_DataAddressMux = FSMC_DataAddressMux_Disable;
fsmcinit.FSMC_MemoryType = FSMC_MemoryType_SRAM;
fsmcinit.FSMC_MemoryDataWidth = FSMC_MemoryDataWidth_16b;
fsmcinit.FSMC_BurstAccessMode = FSMC_BurstAccessMode_Disable;
fsmcinit.FSMC_AsynchronousWait = FSMC_AsynchronousWait_Disable;
fsmcinit.FSMC_WaitSignalPolarity = FSMC_WaitSignalPolarity_Low;
fsmcinit.FSMC_WrapMode = FSMC_WrapMode_Disable;
fsmcinit.FSMC_WaitSignalActive = FSMC_WaitSignalActive_BeforeWaitState;
fsmcinit.FSMC_WriteOperation = FSMC_WriteOperation_Enable;
fsmcinit.FSMC_WaitSignal = FSMC_WaitSignal_Disable;
fsmcinit.FSMC_ExtendedMode = FSMC_ExtendedMode_Enable; //读写不一致
fsmcinit.FSMC_WriteBurst = FSMC_WriteBurst_Disable;
fsmcinit.FSMC_ReadWriteTimingStruct = &timingRead;
fsmcinit.FSMC_WriteTimingStruct = &timingWrite;
FSMC_NORSRAMInit(&fsmcinit);
/* - BANK 1 (of NOR/SRAM Bank 1~4) is enabled */
FSMC_NORSRAMCmd(FSMC_Bank1_NORSRAM1, ENABLE);
}
static void MCU_GpioFsmcInit( void )
{
STM_GPIOConfig();
STM_FSMCConfig();
}
/****************************供外部使用的函数****************************/
/*
*************************************************************************************************
* 函 数 名: lcddrv_SetBackLight
* 功能说明: 初始化控制LCD背光,配置为PWM模式。
* 形 参: _bright 亮度,0是灭,BRIGHT_MAX最亮
* 返 回 值: 无
**************************************************************************************************
*/
void lcddrv_SetBackLight(uint8_t _bright)
{
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
//打开GPIOD的时钟
RCC_AHB1PeriphClockCmd( RCC_AHB1Periph_GPIOD , ENABLE);
if (_bright == 0)
{
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_25MHz;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(GPIOD, &GPIO_InitStructure);
GPIO_ResetBits(GPIOD, GPIO_Pin_13);
//关闭TIM4
TIM_Cmd(TIM4, DISABLE);
return;
}
else if (_bright == BRIGHT_MAX) //最大亮度,直接推挽输出
{
// 配置背光GPIO为推挽输出模式
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_25MHz;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(GPIOD, &GPIO_InitStructure);
GPIO_SetBits(GPIOD, GPIO_Pin_13);
//关闭TIM4
TIM_Cmd(TIM4, DISABLE);
return;
}
//配置背光GPIO为复用推挽输出模式
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(GPIOD, &GPIO_InitStructure);
GPIO_PinAFConfig(GPIOD, GPIO_PinSource13, GPIO_AF_TIM4); //GPIO复用
//使能TIM4的时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);
//TIM 配置: 产生1路PWM信号;
//分频10K,再 /BRIGHT_MAX
TIM_TimeBaseStructure.TIM_Period = BRIGHT_MAX - 1; /* TIM_Period is ARR Register */
TIM_TimeBaseStructure.TIM_Prescaler = 7200 - 1; //10k
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure);
// PWM Channel2
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_Pulse = ((TIM_TimeBaseStructure.TIM_Period + 1) * _bright) / (BRIGHT_MAX);
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OC2Init(TIM4, &TIM_OCInitStructure);
TIM_OC2PreloadConfig(TIM4, TIM_OCPreload_Enable);
TIM_ARRPreloadConfig(TIM4, ENABLE);
//使能 TIM4 定时器
TIM_Cmd(TIM4, ENABLE);
}
/*
*************************************************************************************************
* 函 数 名: lcddrv_DispOn
* 功能说明: 打开显示
* 形 参: 无
* 返 回 值: 无
*************************************************************************************************
*/
void lcddrv_DispOn(void)
{
ST7789_WR_REG(ST7789_CMD_DISPON);
}
/*
*************************************************************************************************
* 函 数 名: lcddrv_DispOff
* 功能说明: 关闭显示
* 形 参: 无
* 返 回 值: 无
*************************************************************************************************
*/
void lcddrv_DispOff(void)
{
ST7789_WR_REG(ST7789_CMD_DISPOFF);
}
/*
*************************************************************************************************
* 函 数 名: lcddrv_FillColorPre
* 功能说明: 设置将要显示的区域。使用此函数后,只需要顺序赋值给LCDDRV_RAM即可绘图此区域.
* 形 参: 无
* 返 回 值: 无
*************************************************************************************************
*/
void lcddrv_FillColorPre(u16 _usX, u16 _usY, u16 _usWidth , u16 _usHeight)
{
//显示区域限制
ST7789_WR_REG(ST7789_CMD_CASETF);
ST7789_WR_DATA(_usX >> 8);
ST7789_WR_DATA(_usX );
ST7789_WR_DATA( (_usX + _usWidth - 1) >> 8);
ST7789_WR_DATA( (_usX + _usWidth - 1) );
ST7789_WR_REG(ST7789_CMD_RASETF );
ST7789_WR_DATA(_usY >> 8);
ST7789_WR_DATA(_usY );
ST7789_WR_DATA( (_usY + _usHeight - 1) >> 8 );
ST7789_WR_DATA( (_usY + _usHeight - 1) );
//准备接收数据
ST7789_REG = ST7789_CMD_RAMWR;
}
/*
*************************************************************************************************
* 函 数 名: lcddrv_Init
* 功能说明: LCD初始化函数
* 形 参: _
* 返 回 值: 无
**************************************************************************************************
*/
void lcddrv_Init(void)
{
MCU_GpioFsmcInit();
lcddrv_SetBackLight(0);
// FSMC重置后必须延迟
if ( ST7789_Init() )
{
printf("LCD ok r\n");
}else
{
printf("LCD err \r\n");
}
}
/****************************(END OF FILE)*****************************/
这里说说调试过程遇到的坑。
1.一开始发现连背光都不能打开。示波器测电压发现一直低电平。排除软件问题,交给硬件工程师,发现是连锡了。
2.解决背光后,发现LCD还是黑乎乎的,不能显示;甚至连ST7789的ID都不能读取。折腾一阵子软件后,打开电路图,最后发现电源脚没有连接电源,只是对地加电容而已。交给硬件工程师,飞线测试,终于亮了。
3.LCD虽然亮了,但是显示花屏,只能在边缘显示,无法正常显示。以为是初始化寄存器没有搞对,一番折腾,还是不行;最后定下心来慢慢看代码,发现一个函数参数对调搞混了。修改完,OK。
使用FSMC扫屏还是挺快的,我使用的LCD为16位色深,320*240像素。全屏单色扫频看不出绘制过程。使用SD卡放置无压缩的RGB565图片(150KB),使用FATFS文件系统,全屏图片显示,需要100~180毫秒(SD类型不同读取速度有区别)。如果是8位色深的图片,体积少一半,可流畅显示。