最近入手一款正点原子的TFTLCD屏幕.打算开发一下.
该模块为ST7789驱动显示屏显示,像素为240*320原理图如下:
LCD_CS为片选信号 LCD_RD为读信号 LCD_WR为写信号 LCD_RS为选择数据还是命令(0:命令 1:数据) LCD_BL LCD_D[0~15]要读写的数据.
此开发板RESET引脚接入的是按键复位引脚,所以直接使用硬件复位.
ST7789命令数据
详细请参考https://blog.csdn.net/qq_28576837/article/details/136279283,介绍的比较全.
初始化配置
GPIO等外设配置
#define LCD_RD(x) GPIO_WriteBit(GPIOC,GPIO_Pin_6,(BitAction)(x))
#define LCD_WR(x) GPIO_WriteBit(GPIOC,GPIO_Pin_7,(BitAction)(x))
#define LCD_RS(x) GPIO_WriteBit(GPIOC,GPIO_Pin_8,(BitAction)(x))
#define LCD_CS(x) GPIO_WriteBit(GPIOC,GPIO_Pin_9,(BitAction)(x))
#define LCD_BL(x) GPIO_WriteBit(GPIOC,GPIO_Pin_10,(BitAction)(x))
#define LCD_DATA_OUT(x) GPIOB->ODR = (x) //LCD_D[0~15]
#define LCD_DATA_IN GPIOB->IDR
#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 */
void LCD_Config(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable,ENABLE);//关闭JTAG
GPIO_InitTypeDef lcdgpio = {
.GPIO_Mode = GPIO_Mode_Out_PP,
.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7 | GPIO_Pin_8 | GPIO_Pin_9 | GPIO_Pin_10,
.GPIO_Speed = GPIO_Speed_50MHz,
};
GPIO_Init(GPIOC,&lcdgpio);
GPIO_InitTypeDef lcddata = {
.GPIO_Mode = GPIO_Mode_Out_PP,
.GPIO_Pin = GPIO_Pin_All,
.GPIO_Speed = GPIO_Speed_50MHz,
};
GPIO_Init(GPIOB,&lcddata);
LCD_BL(0);//关闭背光
}
LCD命令读写
写操作
图中1的位置为,写命令操作,2为写数据操作
void LCD_Write_Cmd(u16 cmd) //写命令
{
LCD_CS(0);
LCD_RS(0);
LCD_DATA_OUT(cmd);
LCD_WR(0);
LCD_WR(1);
LCD_CS(1);
}
//当然如果使用函数调用的话,LCD帧率会刷新很低建议用下面的方式
void LCD_Write_Data(u16 data) //写寄存器数据
{
LCD_CS(0);
LCD_RS(1);
LCD_DATA_OUT(data);
LCD_WR(0);
LCD_WR(1);
LCD_CS(1);
}
//最好将下面的写法写到.h文件里
#define LCD_W_Data(data)\
{\
LCD_CS(0);\
LCD_RS(1);\
LCD_DATA_OUT(data);\
LCD_WR(0);\
LCD_WR(1);\
LCD_CS(1);\
}
读操作
uint16_t LCD_Read_Reg(void)
{
//在进行读操作前需要将 LCD_D[0~15]端口配置为输入模式
uint16_t data;
GPIOB->CRL = 0x88888888;
GPIOB->CRH = 0x88888888;
LCD_RS(1);
LCD_CS(0);
LCD_RD(0);
LCD_Delay(2);
data = LCD_DATA_IN;
LCD_RD(1);
LCD_CS(1);
//在进行读操作结束后需要将 LCD_D[0~15]端口配置为输出模式
GPIOB->CRL = 0x33333333;
GPIOB->CRH = 0x33333333;
return data;
}
LCD初始化命令
初始化的命令在数据手册里由大家可以直接复制
void LCD_Init(void)
{
LCD_Config();
LCD_CS(1);
LCD_RD(1);
LCD_RS(1);
LCD_WR(1);
LCD_DATA_OUT(0xffff);
LCD_Write_Cmd(0x11);
LCD_Delay(120);
LCD_Write_Cmd(0x36);
LCD_W_Data(0x00);
LCD_Write_Cmd(0x3A);
LCD_W_Data(0X05);
LCD_Write_Cmd(0xB2);
LCD_W_Data(0x0C);
LCD_W_Data(0x0C);
LCD_W_Data(0x00);
LCD_W_Data(0x33);
LCD_W_Data(0x33);
LCD_Write_Cmd(0xB7);
LCD_W_Data(0x35);
LCD_Write_Cmd(0xBB); /* vcom */
LCD_W_Data(0x32); /* 30 */
LCD_Write_Cmd(0xC0);
LCD_W_Data(0x0C);
LCD_Write_Cmd(0xC2);
LCD_W_Data(0x01);
LCD_Write_Cmd(0xC3); /* vrh */
LCD_W_Data(0x10); /* 17 0D */
LCD_Write_Cmd(0xC4); /* vdv */
LCD_W_Data(0x20); /* 20 */
LCD_Write_Cmd(0xC6);
LCD_W_Data(0x0f);
LCD_Write_Cmd(0xD0);
LCD_W_Data(0xA4);
LCD_W_Data(0xA1);
LCD_Write_Cmd(0xE0); /* Set Gamma */
LCD_W_Data(0xd0);
LCD_W_Data(0x00);
LCD_W_Data(0x02);
LCD_W_Data(0x07);
LCD_W_Data(0x0a);
LCD_W_Data(0x28);
LCD_W_Data(0x32);
LCD_W_Data(0X44);
LCD_W_Data(0x42);
LCD_W_Data(0x06);
LCD_W_Data(0x0e);
LCD_W_Data(0x12);
LCD_W_Data(0x14);
LCD_W_Data(0x17);
LCD_Write_Cmd(0XE1); /* Set Gamma */
LCD_W_Data(0xd0);
LCD_W_Data(0x00);
LCD_W_Data(0x02);
LCD_W_Data(0x07);
LCD_W_Data(0x0a);
LCD_W_Data(0x28);
LCD_W_Data(0x31);
LCD_W_Data(0x54);
LCD_W_Data(0x47);
LCD_W_Data(0x0e);
LCD_W_Data(0x1c);
LCD_W_Data(0x17);
LCD_W_Data(0x1b);
LCD_W_Data(0x1e);
LCD_Write_Cmd(0x2A);
LCD_W_Data(0x00);
LCD_W_Data(0x00);
LCD_W_Data(0x00);
LCD_W_Data(0xef);
LCD_Write_Cmd(0x2B);
LCD_W_Data(0x00);
LCD_W_Data(0x00);
LCD_W_Data(0x01);
LCD_W_Data(0x3f);
LCD_Write_Cmd(0x29); /* display on */
Set_LCD_Scan(0); //设置扫描方式 0 = 从左到右,从上至下
LCD_BL(1); //开启背光
LCD_Clear(WHITE); //清屏--> 0xffff
}
LCD的清屏,设置坐标,画点,设置扫描方式
清屏
void LCD_Clear(u16 color)
{
u32 i;
LCD_RS(1); /* RS=1,表示写数据 */
LCD_CS(0);
for (i = 0; i < 240*320; i++)
{
LCD_DATA_OUT(color); /* 写入要写的数据 */
LCD_WR(0);
LCD_WR(1);
}
LCD_CS(1);
}
设置坐标
void Set_LCD_XY(u16 x,u16 y)
{
LCD_Write_Cmd(0x2a);
LCD_Write_Data(x >> 8);
LCD_Write_Data(x & 0xff);
LCD_Write_Cmd(0x2b);
LCD_Write_Data(y >> 8);
LCD_Write_Data(y & 0xff);
}
画点
在画点之前,需要对GRAM写入值,所以我们封装一层函数
/*
* regno : 寄存器编号
* data : 要写入的数据
*/
void LCD_Write_RegData(u16 regno,u16 data)
{
LCD_Write_Cmd(regno);
LCD_Write_Data(data);
}
/*
* 0x2c 为写GRAM指令
*/
void LCD_Write_GRAM(void)
{
LCD_Write_RegNo(0x2C);
}
/*
* x : 设置x位置
* y : 设置y位置
* color : 设置颜色值
*/
void LCD_draw_point(uint16_t x, uint16_t y, uint32_t color)
{
Set_LCD_XY(x,y);
LCD_Write_GRAM();
LCD_Write_Data(color);
}
设置扫描方式
总共8种扫描方式分别为
可以通过设置 MY MX MV三位来改变,这里默认 全为0,也就是上图模式6
void Set_LCD_Scan(u8 dir)
{
LCD_Write_Cmd( 0x36 ); //设置GRAM自增方向指令
LCD_Write_Data ( 0x08 | dir);//根据ucOption的值设置LCD参数,共0-7种模式
LCD_Write_Cmd( 0x2a ); //这是列地址设置指令,在从左到右,从上到下的扫描方式(默认)
下面,该指令用于设置横坐标(x 坐标)
LCD_Write_Data ( 0x00 ); /* x 起始坐标高8位 */
LCD_Write_Data ( 0x00 ); /* x 起始坐标低8位 */
LCD_Write_Data ( ((240-1)>>8)&0xFF );/* x 结束坐标高8位 */
LCD_Write_Data ( (240-1)&0xFF ); /* x 结束坐标低8位 */
LCD_Write_Cmd ( 0x2b ); 是页地址设置指令,在从左到右,从上到下的扫描方
式(默认)下面,该指令用于设置纵坐标(y 坐标)
LCD_Write_Data ( 0x00 ); /* y 起始坐标高8位 */
LCD_Write_Data ( 0x00 ); /* y 起始坐标低8位 */
LCD_Write_Data ( ((320-1)>>8)&0xFF );/*y 结束坐标高8位 */
LCD_Write_Data ( (320-1)&0xFF ); /*y 结束坐标低8位 */
/* write gram start */
LCD_Write_Cmd( 0x2c );
}
主函数
int main(void)
{
LCD_Init();
while (1)
{
for(u8 i=0;i<100;i++)
{
for(u8 j=0;j<240;j++)
LCD_draw_point(j,109+i,BLUE);
}
for(u8 i=0;i<110;i++)
{
for(u8 j=0;j<240;j++)
LCD_draw_point(j,210+i,RED);
}
}
}
测试结果
源码已上传CSDN