lijinlei 发表于 2024-11-14 03:17:35

AI8051U 驱动 TFT-LCD

本帖最后由 lijinlei 于 2024-11-14 03:31 编辑

AI8051U 驱动 TFT 显示屏本文主要介绍了 TFT 屏驱动原理,以及 AI8051U 驱动 TFT 显示图片。TFT-LCD 原理
TFT (Thin Film Transistor) -LCD (Liquid Crystal Display),薄膜晶体管型液晶显示屏,像素点由薄膜晶体管驱动,具有响应速度快,色彩控制精确,成像更逼真等优点。
器件结构
TFT 屏的主要结构组成包括:背光源、导光板、扩散膜、棱镜膜、配向膜、液晶材料、薄膜晶体管等。
背光源
LCD 屏幕的光就是来自于屏幕最下面的光源。LCD 光源可以是长条荧光灯 (CCFL), 也可以是发光二极管(LED), 通常用 LED 使屏幕更薄。
光源放置方式
1.将 LED 平铺在最下面的反光板上;2.将LED放在屏幕的侧边。
导光板
LED放在屏幕的侧边时,会出现亮度不均匀的情况,因此需要一层导光板,其作用就是将点光源转换为面光源,使光束传播更均匀。
扩散膜
扩散膜是为了令光扩散的面积更大,且更加柔和,提高舒适度。
棱镜膜
棱镜膜的作用是将分散的光线集中到特定角度。只有符合该角度范围的光线才能从棱镜膜射出,其他角度的光线则被反射到反光板,再反射回棱镜膜,以期再次利用。使用棱镜膜是因为光线是四处分散的,需要尽可能让光线方向聚集,也可提高亮度。
偏光片
偏光片的作用是过滤。只允许特定方向的光透过。具有两层相互垂直的偏光片用来控制屏幕的亮灭显示。
液晶
液晶层位于两层偏光片之间,液晶层的分子无外场条件下是规则排列的,当施加定向电场便会发生形变。液晶是一种介于固体和液体之间的特殊物质,是一种有机化合物,常态下呈液态,但是它的分子排列却和固体晶体一样非常规则,因此取名液晶。如果给液晶施加电场,会改变它的分子排列,从而改变光线的传播方向,配合偏振光片,它就具有控制光线透过率的作用,再配合彩色滤光片,改变加给液晶电压大小,就能改变某一颜色透光量的多少。
配向膜
配向膜将液晶层夹在中间,液晶沿着配向膜表面的细槽定向排列,这样透过偏光片的光线便会沿着液晶层分子排布方向发生变化,从而透过外层偏光片。
电极层
电极层产生定向电场,从而驱动液晶分子排列发生扭转。
彩色滤光膜
令不同波长的光选择性透过。
薄膜晶体管
电极层上的玻璃基板上覆盖着薄膜晶体管。
显示原理
薄膜晶体管通过控制电压可实现像素点的亮度调节,该像素点范围内的液晶分子也会受到电压产生的电场影响发生分子排列扭曲,再经过偏光片即可调整亮度,子像素由 RGB 三色组合实现各种色彩输出。
详见:TFT_LCD液晶屏 .
驱动芯片
利用 ST7735S 驱动芯片和 SPI 通信协议,实现 TFT-LCD驱动显示。
AI8051U 驱动 1.8 英寸 TFT 显示屏
使用 AI8051U 驱动 TFT-LCD 显示和刷新图片。
代码

/*
AI8051U
0.96inch TFT_ST7735 screen
IRC rate 24MHz
*/
#include <AI8051U.h>
#include <absacc.h>
#include <intrins.h>
#include <string.h>
#include "BMP.h"

typedef unsigned char u8;
typedef unsigned int u16;
typedef unsigned long u32;

#define MAIN_Fosc 24000000UL // main frequency

//--------------------------- TFT 液晶屏接线说明 -------------------------------------//
sbit bl      =P2^2;//接模块BL引脚,背光可以采用IO控制或者PWM控制,也可以直接接到高电平常亮
sbit scl       =P2^7;//接模块CLK引脚,接裸屏Pin9_SCL
sbit sda       =P2^6;//接模块DIN/MOSI引脚,接裸屏Pin8_SDA
sbit rs      =P2^5;//接模块D/C引脚,接裸屏Pin7_A0
sbit cs      =P2^4;//接模块CE引脚,接裸屏Pin12_CS
sbit reset   =P2^1;//接模块RST引脚,接裸屏Pin6_RES
//--------------------------- End of 液晶屏接线 ---------------------------------//

//定义常用颜色
#define RED                0xf800
#define GREEN                0x07e0
#define BLUE                 0x001f
#define WHITE                0xffff
#define BLACK                0x0000
#define YELLOW0xFFE0
#define GRAY0   0xEF7D   
#define GRAY1   0x8410             
#define GRAY2   0x4208   

code unsigned char gImage_AIC_LOGO_RGB[];
/**********************************
----------Image2LCD取模选项设置---------
水平扫描
16位
80*160
不包含图像头数据
自左至右
自顶至底
低位在前
**********************************/
void GPIO_Init();
void delay_ms(u16 time);
void lcd_initial();
void showimage(const unsigned char *p);
void showimageFull(const unsigned char *p);
void main(void)
{
        GPIO_Init();
        //P3M1 = 0x00;P3M0 = 0x00;
        lcd_initial();
        bl=1;
    showimageFull(gImage_AIC_LOGO_RGB);
while(1)
{
                showimageFull(gImage_AIC_LOGO_RGB);
                delay_ms(200);
}
}

/*-------------- GPIO initialize ------------------*/
void GPIO_Init()
{
        P0M1 = 0x00;   P0M0 = 0x00;
P1M1 = 0x00;   P1M0 = 0x00;
P2M1 = 0x00;   P2M0 = 0x00;
P3M1 = 0x00;   P3M0 = 0x00;
P4M1 = 0x00;   P4M0 = 0x00;
P5M1 = 0x00;   P5M0 = 0x00;
        P6M1 = 0x00;   P6M0 = 0x00;
P7M1 = 0x00;   P7M0 = 0x00;
}

void delay_ms(u16 time)
{
u16 i;
        do
        {
                i = MAIN_Fosc / 6000;
                while(--i);
        }
        while(--time);
}


//向SPI总线传输一个8位数据
voidSPI_WriteData(u8 Data)
{
        unsigned char i=0;
        for(i=8;i>0;i--)
        {
                if(Data&0x80)       
                sda=1; //输出数据
                else sda=0;
                scl=0;
                scl=1;
                Data<<=1;
        }
}
//向液晶屏写一个8位指令
voidLcd_WriteIndex(u8 Data)
{
               
                cs=0;
                rs=0;
                SPI_WriteData(Data);                
                cs=1;
}
//向液晶屏写一个8位数据
voidLcd_WriteData(u8 Data)
{       
                unsigned char i=0;
                cs=0;
                rs=1;
                SPI_WriteData(Data);        
                cs=1;
}
//向液晶屏写一个16位数据
voidLCD_WriteData_16Bit(unsigned int Data)
{
        unsigned char i=0;
        cs=0;
        rs=1;
        SPI_WriteData(Data>>8);         //写入高8位数据
        SPI_WriteData(Data);                         //写入低8位数据
        cs=1;

}
void Reset()
{
    reset=0;
    delay_ms(100);
    reset=1;
    delay_ms(100);
}
//液晶屏初始化 for S6D02A1
void lcd_initial()
{        Reset();//Reset before LCD Init.
               
        //LCD Init For 1.44Inch LCD Panel with ST7735R.
        Lcd_WriteIndex(0x11);//Sleep exit
        delay_ms (120);
       
        Lcd_WriteIndex(0x21); // normal : 0x20;inverse color: 0x21;

Lcd_WriteIndex(0xB1);
Lcd_WriteData(0x05);
Lcd_WriteData(0x3A);
Lcd_WriteData(0x3A);

Lcd_WriteIndex(0xB2);
Lcd_WriteData(0x05);
Lcd_WriteData(0x3A);
Lcd_WriteData(0x3A);

Lcd_WriteIndex(0xB3);
Lcd_WriteData(0x05);
Lcd_WriteData(0x3A);
Lcd_WriteData(0x3A);
Lcd_WriteData(0x05);
Lcd_WriteData(0x3A);
Lcd_WriteData(0x3A);

Lcd_WriteIndex(0xB4);
Lcd_WriteData(0x03);

Lcd_WriteIndex(0xC0);
Lcd_WriteData(0x62);
Lcd_WriteData(0x02);
Lcd_WriteData(0x04);

Lcd_WriteIndex(0xC1);
Lcd_WriteData(0xC0);

Lcd_WriteIndex(0xC2);
Lcd_WriteData(0x0D);
Lcd_WriteData(0x00);

Lcd_WriteIndex(0xC3);
Lcd_WriteData(0x8D);
Lcd_WriteData(0x6A);   

Lcd_WriteIndex(0xC4);
Lcd_WriteData(0x8D);
Lcd_WriteData(0xEE);

Lcd_WriteIndex(0xC5);/*VCOM*/
Lcd_WriteData(0x0E);   

Lcd_WriteIndex(0xE0);
Lcd_WriteData(0x10);
Lcd_WriteData(0x0E);
Lcd_WriteData(0x02);
Lcd_WriteData(0x03);
Lcd_WriteData(0x0E);
Lcd_WriteData(0x07);
Lcd_WriteData(0x02);
Lcd_WriteData(0x07);
Lcd_WriteData(0x0A);
Lcd_WriteData(0x12);
Lcd_WriteData(0x27);
Lcd_WriteData(0x37);
Lcd_WriteData(0x00);
Lcd_WriteData(0x0D);
Lcd_WriteData(0x0E);
Lcd_WriteData(0x10);

Lcd_WriteIndex(0xE1);
Lcd_WriteData(0x10);
Lcd_WriteData(0x0E);
Lcd_WriteData(0x03);
Lcd_WriteData(0x03);
Lcd_WriteData(0x0F);
Lcd_WriteData(0x06);
Lcd_WriteData(0x02);
Lcd_WriteData(0x08);
Lcd_WriteData(0x0A);
Lcd_WriteData(0x13);
Lcd_WriteData(0x26);
Lcd_WriteData(0x36);
Lcd_WriteData(0x00);
Lcd_WriteData(0x0D);
Lcd_WriteData(0x0E);
Lcd_WriteData(0x10);

Lcd_WriteIndex(0x3A);
Lcd_WriteData(0x05);

Lcd_WriteIndex(0x36);
Lcd_WriteData(0xC8);

Lcd_WriteIndex(0x29);
}
/*************************************************
函数名:LCD_Set_Region
功能:设置lcd显示区域,在此区域写点数据自动换行
入口参数:xy起点和终点
返回值:无
*************************************************/
void Lcd_SetRegion(unsigned int x_start,unsigned int y_start,unsigned int x_end,unsigned int y_end)
{       

        Lcd_WriteIndex(0x2a);
        Lcd_WriteData(0x00);
        Lcd_WriteData(x_start+0x1A);
        Lcd_WriteData(0x00);
        Lcd_WriteData(x_end+0x1A);

        Lcd_WriteIndex(0x2b);
        Lcd_WriteData(0x00);
        Lcd_WriteData(y_start+1);
        Lcd_WriteData(0x00);
        Lcd_WriteData(y_end+1);       
        Lcd_WriteIndex(0x2c);
}


void dsp_single_colour(int color)
{
        u8 i,j;
        Lcd_SetRegion(0,0,80-1,160-1);
        for (i=0;i<160;i++)
            for (j=0;j<80;j++)
              LCD_WriteData_16Bit(color);
}

//取模方式 水平扫描 从左到右 低位在前
void showimageFull(const unsigned char *p) //显示128*64
{
        int i,j,k;
        unsigned char picH,picL;
        dsp_single_colour(WHITE); //清屏
       
        for(k=0;k<1;k++)//纵坐标图片数量
        {
                   for(j=0;j<1;j++)//横坐标图片数量
                {       
                        Lcd_SetRegion(80*j,160*k,80*j+79,160*k+159); //坐标设置
                  for(i=0;i<80*160;i++)
                       {       
                               picL=*(p+i*2);        //数据低位在前
                                picH=*(p+i*2+1);                               
                                LCD_WriteData_16Bit(picH<<8|picL);                                               
                       }       
               }
        }               
}AI8051U 驱动 0.96 英寸 TFT 显示屏
Image2LCD
Image2LCD 软件设置
打开目标图片 - 选择输出类型 - 扫描方式 - 输出灰度 - 宽度和高度 - 保存
代码

AI8051U 驱动 80*160 像素图片的显示

#include <AI8051U.h>
#include <intrins.h>
#include <sys.h>
#include <lcd.h>
#include <font.h>
void showimageF()
{
        int i,j,k;
        for(k=0;k<1;k++)
        {
                   for(j=0;j<3;j++)
                {       
                        Address_set(128*j,160*k,128*j+127,160*k+159);
                  for(i=0;i<20480;i++)
                       {                               
                                 LCD_WR_DATA8(image_fire);
                               LCD_WR_DATA8(image_fire);                                       
                       }       
               }
        }
               
}
void main()
{
        GPIO_Init();
        Lcd_Init();
        LCD_Clear(WHITE);
        BACK_COLOR=BLACK;;POINT_COLOR=WHITE;
        while(1)
        {
                LCD_Clear(WHITE);
                BACK_COLOR=BLACK;;POINT_COLOR=WHITE;
                showimageF();
                delayms(200);
}
}

显示效果局部刷新
根据前面的测试结果,发现循环延迟刷新画面会导致画面抖动失帧,因此对于多数情况仅需要进行局部数据的修改即可,可极大减少主控负担和代码体积。
目标
让炉子上方的火苗动起来
方案
使用 PhotoShop 软件,打开原始图片,选中目标火苗区域,复制到多个新建图层,调整设置扭曲滤镜(波纹)的幅度值,控制火苗的形变,幅值最好是连续等间隔,最后裁剪火苗并存储为 BMP 图片。
代码
0.96 英寸 TFT 显示屏代码
void main(void)
{
        GPIO_Init();
        lcd_initial();
        bl=1;
        showimageFull(gImage_bg);
while(1)
{
                showimagePart(gImage_fire_0);
                delay_ms(50);
                showimagePart(gImage_fire_N100);
                delay_ms(50);
                showimagePart(gImage_fire_N200);
                delay_ms(50);
                showimagePart(gImage_fire_N100);
                delay_ms(50);
                showimagePart(gImage_fire_0);
                delay_ms(50);
                showimagePart(gImage_fire_P100);
                delay_ms(50);
                showimagePart(gImage_fire_P200);
                delay_ms(50);
                showimagePart(gImage_fire_P100);
                delay_ms(50);
}

对于 1.8 inch TFT 使用代码如下

#include <AI8051U.h>
#include <intrins.h>
#include "sys.h"
#include "LCD.h"
#include "font.h"
#include "bmp.h"

void main()
{
        GPIO_Init();
        Lcd_Init();
        LCD_Clear(WHITE);
        BACK_COLOR=BLACK;;POINT_COLOR=WHITE;
        showimageFull();//显示整张图片
        while(1)
        {//开始局部刷新循环
                showimagePart(57,52,56,41,gImage_fire_0);
                delayms(50);
                //showimagePart(57,52,56,41,gImage_fire_N100);
                //delayms(50);
                showimagePart(57,52,56,41,gImage_fire_N200);
                delayms(50);
                //showimagePart(57,52,56,41,gImage_fire_N100);
                //delayms(50);
                showimagePart(57,52,56,41,gImage_fire_0);
                delayms(50);
                //showimagePart(57,52,56,41,gImage_fire_P100);
                //delayms(50);
                showimagePart(57,52,56,41,gImage_fire_P200);
                delayms(50);
                //showimagePart(57,52,56,41,gImage_fire_P100);
                //delayms(50);
}
}

这里为了减小数组体积,避免报错,注释了中间部分过渡帧画面;加上过渡部分效果更连贯,动画更细腻。
显示效果
0.96 英寸 TFT-LCD 显示效果
1.8 英寸 TFT-LCD 显示效果
使用的开发板为基于 AI8051U 芯片的学习板,PCB工程见:Ai8051U多功能学习板 - 立创开源硬件平台
具体工程见附件(包含代码和显示BMP图片)。

















邮箱 发表于 2024-11-14 11:26:19

{:4_250:}

jovewang 发表于 2024-11-14 14:48:47

有驱动3.5寸TFT屏的代码吗?

ercircle 发表于 2024-11-14 15:20:51

{:5_332:}

ercircle 发表于 2024-11-14 22:40:39

来交作业{:5_351:}
296

VCC 发表于 2024-11-15 00:14:05

TFT液晶面板的原理,和TFT驱动芯片的应用方法基本没有关系

哈哈哈开头的铺垫太像我们的教科书了

lijinlei 发表于 2024-11-15 00:56:30

VCC 发表于 2024-11-15 00:14
TFT液晶面板的原理,和TFT驱动芯片的应用方法基本没有关系

哈哈哈开头的铺垫太像我们的教科书了 ...

{:4_167:}我是光学专业的,所以更专注于显示原理部分,电路设计、驱动芯片原理只能查询硬件手册解决了{:4_166:}

springvirus 发表于 2024-11-15 07:11:05

非常不错!!

stb988 发表于 2024-12-12 10:20:59

<p>刷新速度还可以啊,这么快</p>

香河英茂工作室 发表于 3 天前

感谢分享。
页: [1]
查看完整版本: AI8051U 驱动 TFT-LCD