为了使之前实现的功能更加完善,决定外接一个LCD屏作为显示,后期移植LVGL实现一些界面操作
屏幕具体的型号为如下图:1.83英寸,采用ST7789T3驱动,240*280,接口采用3line 9bit。由于未详细的看说明,此处的3line 9bit 虽然采用spi接口但是发送数据极为不便捷,采用的是官方提供的GPIO模拟的方式。具体的发送数据方式下图,所以购买屏幕一定一定要看清楚接口方式以及数据发送方式等因素,这些都会影响后续的开发进度,以及显示的效果。
此屏幕还有一个问题就是采用FH35C-13s-0.5SHW(50) 接口,此接口对刚刚接触焊接的小伙伴很不友好,尽量选择直接焊接接口的LCD屏幕或者购买LCD模块,这样节省自己焊接和PCB制作过程。
根据官方提供的原理图,自行设计的PCB ,如下:
电路图相对简单,PCB制作完成后,还是采用stm32f103rct6进行通信调试。具体的代码实现,见下面,后续进行LVGL移植说明。
lcd_init.c 代码主要根据提供的demo进行自行更改合适的接口IO
#include "zjy_lcdinit.h"
/*---------------------------
中景园LCD 240(H)RGB x280(V) 1.83寸
TE ---> (PB1) EXIT
MOSI ---> SPI1_MOSI(PA7)
SCLK ---> SPI1_SCK (PA5)
CS ---> (PA4)
D/C ---> (PA6) 0--> 写REG
1--> 写DATA
RESET --> (PC4) 0--> 复位
BLK --> (PC5) 1--> 开启背光 0--> 关闭背光
----------------------------*/
void LCD_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOC_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
// CS MOSI SCLK
GPIO_InitStructure.Pin = SPIx_SCK_PIN | SPIx_MOSI_PIN | SPIx_CS_PIN;
GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP; //推挽输出
GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_HIGH; //速度50MHz
HAL_GPIO_Init(SPIx_SPI_GPIO_PORT, &GPIO_InitStructure);
HAL_GPIO_WritePin(SPIx_SPI_GPIO_PORT,SPIx_SCK_PIN | SPIx_MOSI_PIN | SPIx_CS_PIN, GPIO_PIN_SET);
//RESET
GPIO_InitStructure.Pin = LCD_RESET_PIN;
GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP; //推挽输出
GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_HIGH; //速度50MHz
HAL_GPIO_Init(LCD_RESET_GPIO_PORT, &GPIO_InitStructure);
HAL_GPIO_WritePin(LCD_RESET_GPIO_PORT,LCD_RESET_PIN, GPIO_PIN_SET);
//BLK
GPIO_InitStructure.Pin = LCD_BLK_PIN;
GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP; //推挽输出
GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_HIGH; //速度50MHz
HAL_GPIO_Init(LCD_BLK_GPIO_PORT, &GPIO_InitStructure);
HAL_GPIO_WritePin(LCD_BLK_GPIO_PORT,LCD_BLK_PIN, GPIO_PIN_SET);
//TE
GPIO_InitStructure.Pin = LCD_TE_PIN;
GPIO_InitStructure.Mode = GPIO_MODE_INPUT; //
HAL_GPIO_Init(LCD_TE_GPIO_PORT, &GPIO_InitStructure);
}
/******************************************************************************
函数说明:LCD串行数据写入函数
入口数据:dat 要写入的串行数据
返回值: 无
******************************************************************************/
void LCD_Writ_Bus(uint8_t dat)
{
uint8_t i;
for(i=0;i<8;i++)
{
LCD_SCLK_Clr();
if(dat&0x80)
{
LCD_MOSI_Set();
}
else
{
LCD_MOSI_Clr();
}
LCD_SCLK_Set();
dat<<=1;
}
}
/******************************************************************************
函数说明:LCD写入数据
入口数据:dat 写入的数据
返回值: 无
******************************************************************************/
void LCD_WR_DATA8(uint8_t dat)
{
LCD_CS_Clr();
LCD_SCLK_Clr();
LCD_MOSI_Set();
LCD_SCLK_Set();
LCD_Writ_Bus(dat);
LCD_CS_Set();
}
/******************************************************************************
函数说明:LCD写入数据
入口数据:dat 写入的数据
返回值: 无
******************************************************************************/
void LCD_WR_DATA(uint16_t dat)
{
LCD_WR_DATA8(dat>>8);
LCD_WR_DATA8(dat);
}
/******************************************************************************
函数说明:LCD写入命令
入口数据:dat 写入的命令
返回值: 无
******************************************************************************/
void LCD_WR_REG(uint8_t dat)
{
LCD_CS_Clr();
LCD_SCLK_Clr();
LCD_MOSI_Clr();
LCD_SCLK_Set();
// LCD_DC_Clr();//写命令
LCD_Writ_Bus(dat);
// LCD_DC_Set();//写数据
LCD_CS_Set();
}
/******************************************************************************
函数说明:设置起始和结束地址
入口数据:x1,x2 设置列的起始和结束地址
y1,y2 设置行的起始和结束地址
返回值: 无
******************************************************************************/
void LCD_Address_Set(uint16_t x1,uint16_t y1,uint16_t x2,uint16_t y2)
{
if(USE_HORIZONTAL==0)
{
LCD_WR_REG(0x2a);//列地址设置
LCD_WR_DATA(x1);
LCD_WR_DATA(x2);
LCD_WR_REG(0x2b);//行地址设置
LCD_WR_DATA(y1+20);
LCD_WR_DATA(y2+20);
LCD_WR_REG(0x2c);//储存器写
}
else if(USE_HORIZONTAL==1)
{
LCD_WR_REG(0x2a);//列地址设置
LCD_WR_DATA(x1);
LCD_WR_DATA(x2);
LCD_WR_REG(0x2b);//行地址设置
LCD_WR_DATA(y1+20);
LCD_WR_DATA(y2+20);
LCD_WR_REG(0x2c);//储存器写
}
else if(USE_HORIZONTAL==2)
{
LCD_WR_REG(0x2a);//列地址设置
LCD_WR_DATA(x1+20);
LCD_WR_DATA(x2+20);
LCD_WR_REG(0x2b);//行地址设置
LCD_WR_DATA(y1);
LCD_WR_DATA(y2);
LCD_WR_REG(0x2c);//储存器写
}
else
{
LCD_WR_REG(0x2a);//列地址设置
LCD_WR_DATA(x1+20);
LCD_WR_DATA(x2+20);
LCD_WR_REG(0x2b);//行地址设置
LCD_WR_DATA(y1);
LCD_WR_DATA(y2);
LCD_WR_REG(0x2c);//储存器写
}
}
/*--------------------------
函数:LCD初始化
--------------------------*/
void ZJY_LCD_Init(void)
{
LCD_GPIO_Init();//初始化GPIO
LCD_RES_Set();
HAL_Delay(10);
LCD_RES_Clr();//复位
HAL_Delay(10);
LCD_RES_Set();
HAL_Delay(120);
LCD_BLK_Set();//打开背光
//设置显存扫描模式、数据格式等
LCD_WR_REG(0x36);
if(USE_HORIZONTAL==0)LCD_WR_DATA8(0x00);
else if(USE_HORIZONTAL==1)LCD_WR_DATA8(0xC0);
else if(USE_HORIZONTAL==2)LCD_WR_DATA8(0x70);
else LCD_WR_DATA8(0xA0);
//RGB-5-6-5
LCD_WR_REG( 0x3A);
LCD_WR_DATA8( 0x05);
//porch设置
LCD_WR_REG( 0xB2);
LCD_WR_DATA8( 0x0B);
LCD_WR_DATA8( 0x0B);
LCD_WR_DATA8( 0x00);
LCD_WR_DATA8( 0x33);
LCD_WR_DATA8( 0x33);
//VGH 设置
LCD_WR_REG( 0xB7);
LCD_WR_DATA8( 0x11);
//VCOM设置
LCD_WR_REG( 0xBB);
LCD_WR_DATA8( 0x2F);
//LCM设置
LCD_WR_REG( 0xC0);
LCD_WR_DATA8( 0x2C);
//VDV and VRH 设置
LCD_WR_REG( 0xC2);
LCD_WR_DATA8( 0x01);
//VRH设置
LCD_WR_REG( 0xC3);
LCD_WR_DATA8( 0x0D);
//VDV设置
LCD_WR_REG( 0xC4);
LCD_WR_DATA8( 0x20); //VDV, 0x20:0v
//普通模式下的显存速率设置
LCD_WR_REG( 0xC6);
LCD_WR_DATA8( 0x1D); //0x13:60Hz
//电源控制
// LCD_WR_REG( 0xD0);
// LCD_WR_DATA8( 0xA7);
// LCD_WR_DATA8( 0xA1);
LCD_WR_REG( 0xD0);
LCD_WR_DATA8( 0xA4);
LCD_WR_DATA8( 0xA1);
LCD_WR_REG( 0xD6);
LCD_WR_DATA8( 0xA1); //sleep in后,gate输出为GND
//电压设置
LCD_WR_REG( 0xE0);
LCD_WR_DATA8( 0xF0);
LCD_WR_DATA8( 0x06);
LCD_WR_DATA8( 0x0B);
LCD_WR_DATA8( 0x0A);
LCD_WR_DATA8( 0x09);
LCD_WR_DATA8( 0x26);
LCD_WR_DATA8( 0x29);
LCD_WR_DATA8( 0x33);
LCD_WR_DATA8( 0x41);
LCD_WR_DATA8( 0x18);
LCD_WR_DATA8( 0x16);
LCD_WR_DATA8( 0x15);
LCD_WR_DATA8( 0x29);
LCD_WR_DATA8( 0x2D);
//电压设置
LCD_WR_REG( 0xE1);
LCD_WR_DATA8( 0xF0);
LCD_WR_DATA8( 0x04);
LCD_WR_DATA8( 0x08);
LCD_WR_DATA8( 0x08);
LCD_WR_DATA8( 0x07);
LCD_WR_DATA8( 0x03);
LCD_WR_DATA8( 0x28);
LCD_WR_DATA8( 0x32);
LCD_WR_DATA8( 0x40);
LCD_WR_DATA8( 0x3B);
LCD_WR_DATA8( 0x19);
LCD_WR_DATA8( 0x18);
LCD_WR_DATA8( 0x2A);
LCD_WR_DATA8( 0x2E);
LCD_WR_REG( 0xE4);
LCD_WR_DATA8( 0x25);
LCD_WR_DATA8( 0x00);
LCD_WR_DATA8( 0x00); //当gate没有用完时,bit4(TMG)设为0
LCD_WR_REG( 0x35); //Tearing Effect Line On
LCD_WR_DATA8( 0x00);
LCD_WR_REG( 0x44); //Set Tear Scanline
LCD_WR_DATA8( 0x00);
LCD_WR_DATA8( 0x20);
//显示打开
LCD_WR_REG( 0x21); //Display Inversion On
LCD_WR_REG( 0x11); //Sleep out
HAL_Delay(120);
LCD_WR_REG( 0x29); //Display On
}
lcd_init.h 实现的一些接口IO重命名,方便后续开发。
#ifndef ZJY_LCDINIT_H
#define ZJY_LCDINIT_H
#include "stm32f1xx_hal.h"
#include "stdio.h"
#define SPIx_SPI_GPIO_PORT GPIOA
#define SPIx_SCK_PIN GPIO_PIN_5
#define SPIx_MOSI_PIN GPIO_PIN_7
#define SPIx_CS_PIN GPIO_PIN_4
#define LCD_DC_GPIO_PORT GPIOA
#define LCD_DC_PIN GPIO_PIN_6
#define LCD_RESET_GPIO_PORT GPIOC
#define LCD_RESET_PIN GPIO_PIN_4
#define LCD_BLK_GPIO_PORT GPIOC
#define LCD_BLK_PIN GPIO_PIN_5
#define LCD_TE_GPIO_PORT GPIOB
#define LCD_TE_PIN GPIO_PIN_1
#define LCD_SCLK_Clr() HAL_GPIO_WritePin( SPIx_SPI_GPIO_PORT , SPIx_SCK_PIN, GPIO_PIN_RESET )//SCLK
#define LCD_SCLK_Set() HAL_GPIO_WritePin( SPIx_SPI_GPIO_PORT , SPIx_SCK_PIN, GPIO_PIN_SET )
#define LCD_MOSI_Clr() HAL_GPIO_WritePin( SPIx_SPI_GPIO_PORT , SPIx_MOSI_PIN, GPIO_PIN_RESET )//MOSI
#define LCD_MOSI_Set() HAL_GPIO_WritePin( SPIx_SPI_GPIO_PORT , SPIx_MOSI_PIN, GPIO_PIN_SET )
#define LCD_CS_Clr() HAL_GPIO_WritePin( SPIx_SPI_GPIO_PORT , SPIx_CS_PIN, GPIO_PIN_RESET )//CS
#define LCD_CS_Set() HAL_GPIO_WritePin( SPIx_SPI_GPIO_PORT , SPIx_CS_PIN, GPIO_PIN_SET )
#define LCD_RES_Clr() HAL_GPIO_WritePin( LCD_RESET_GPIO_PORT , LCD_RESET_PIN , GPIO_PIN_RESET )//RES
#define LCD_RES_Set() HAL_GPIO_WritePin( LCD_RESET_GPIO_PORT , LCD_RESET_PIN , GPIO_PIN_SET )
#define LCD_BLK_Clr() HAL_GPIO_WritePin( LCD_BLK_GPIO_PORT , LCD_BLK_PIN , GPIO_PIN_RESET )//BLK
#define LCD_BLK_Set() HAL_GPIO_WritePin( LCD_BLK_GPIO_PORT , LCD_BLK_PIN , GPIO_PIN_SET )
//#define LCD_DC_Clr() HAL_GPIO_WritePin( LCD_DC_GPIO_PORT , LCD_DC_PIN, GPIO_PIN_RESET )//DC
//#define LCD_DC_Set() HAL_GPIO_WritePin( LCD_DC_GPIO_PORT , LCD_DC_PIN, GPIO_PIN_SET )
#define USE_HORIZONTAL 0 //设置横屏或者竖屏显示 0或1 :竖屏 2或3 :横屏
#if USE_HORIZONTAL==0 || USE_HORIZONTAL==1
#define LCD_W 240
#define LCD_H 280
#else
#define LCD_W 280
#define LCD_H 240
#endif
void LCD_GPIO_Init(void); //初始化GPIO
void LCD_Writ_Bus(uint8_t dat); //模拟SPI时序
void LCD_WR_DATA8(uint8_t dat); //写入一个字节
void LCD_WR_DATA(uint16_t dat); //写入两个字节
void LCD_WR_REG(uint8_t dat); //写入一个指令
void LCD_Address_Set(uint16_t x1,uint16_t y1,uint16_t x2,uint16_t y2);//设置坐标函数
void ZJY_LCD_Init(void); //LCD初始化
#endif
以上基本完成了中景园LCD屏的开发和代码实现,后续我们就可以在main.c文件中简单调用提供的划线、显示文字、显示图片功能函数,对屏幕进行简单的测试。这样就给我们后续移植LVGL打下了坚实的基础。