IMX6ULL裸机驱动RGBLCD显示屏原理分析与代码编写

原理分析

像素格式

本章实验中我们使用 ARGB8888 这种像素格式,一个像素占用 4个字节的内存,这四个字节每个位的分配如图

93763bab5b2d420e95faeab697d9d311.png

一个像素点是 4 个字节,其中 bit31~bit24 是 Alpha 通道,bit23~bit16 是RED 通道,bit15~bit14 是 GREEN 通道,bit7~bit0 是 BLUE 通道。所以红色对应的值就是0X00FF0000,蓝色对应的值就是 0X000000FF,绿色对应的值为 0X0000FF00。通过调节 R、G、B的比例可以产生其它的颜色,比如0X00FFFF00就是黄色,0X00000000就是黑色,0X00FFFFFF就是白色。

IMX6ULL支持RGB接口的LCD,RGBLCD的信号线包括R[7:0]、G[7:0]和B[7:0]这24根是数据线,DE、VSYNC、HSYNC 和 PCLK 这四根是控制信号线。对应原理图的LCD_DATA0~23和LCD_DE/VSYNC/HSYNC/ PCLK.后续需要对这些信号线进行初始化设置。

0ea7ac65920f4e7b9a32dda9af7f637d.png

图中三个 SGM3157 的目的是在未使用 RGBLCD 的时候将 LCD_DATA7、LCD_DATA15 和 LCD_DATA23 这三个线隔离开来,因为 ALIENTEK 的屏幕的 LCD_R7/G7/B7这几个线用来设置 LCD 的 ID,所以这几根线上有上拉/下拉电阻。但是 I.MX6U 的 BOOT 设置也用到了 LCD_DATA7、LCD_DATA15 和 LCD_DATA23 这三个引脚,所以接上屏幕以后屏幕上的 ID 电阻就会影响到 BOOT 设置,会导致代码无法运行,所以先将其隔离开来,如果要使用 RGB LCD 屏幕的时候再通过 LCD_DE 将其“连接”起来。

LCD时间参数

假如一个 LCD 的分辨率为 1024*600,那么其扫描如图:

124f9a4f042a4a65bc29fac42eb8843b.png

HSYNC 是水平同步信号,也叫做行同步信号,当产生此信号的话就表示开始显示新的一行了,所以此信号都是在图的最左边。当 VSYNC 信号是垂直同步信号,也叫做帧同步信号,当产生此信号的话就表示开始显示新的一帧图像了,所以此信号在图的上角。

RGBLCD屏幕时序

行显示时序图

75081a35b5ed4e76b57d4f6db127f96f.png

HSYNC:行同步信号,当此信号有效的话就表示开始显示新的一行数据,假设此时是低电平有效。

HSPW:是 HSYNC 信号宽度,也就是 HSYNC 信号持续时间。

HBP/HFP:行同步或场同步信号发出后,视频数据不能立即使能,要留出电子枪回扫的时间。以行扫描为例,从HSYNC结束到DE开始的区间成为行扫描的后肩,从DE结束到HSYNC开始称为前肩。

HOZVAL:显示一行数据所需的时间,假如屏幕分辨率为 1024*600,那么 HOZVAL 就是 1024。

当 HSYNC 信号发出以后,需要等待 HSPW+HBP 个 CLK 时间才会接收到真正有效的像素数据。当显示完一行数据以后需要等待 HFP 个 CLK 时间才能发出下一个 HSYNC 信号,所以显示一行所需要的时间就是:HSPW + HBP + HOZVAL + HFP。

一帧图像就是由很多个行组成

帧显示时序图

b3e3154911c24934a2496cb116ef681a.png

VSYNC:帧同步信号,当此信号有效的话就表示开始显示新的一帧数据,假设此时是低电平有效。

VSPW:是 VSYNC 信号宽度,也就是 VSYNC 信号持续时间,单位为 1 行的时间

VBP/VFP:同上HBP/HFP

显示一帧所需要的时间就是:VSPW+VBP+LINE+VFP 个行时间,最终的计算公式:

T = (VSPW+VBP+LINE+VFP) * (HSPW + HBP + HOZVAL + HFP)

因此我们在配置一款 RGB LCD 的时候需要知道这几个参数:HOZVAL(屏幕有效宽度)、

LINE(屏幕有效高度)、HBP、HSPW、HFP、VSPW、VBP 和 VFP。

像素时钟

像素时钟就是 RGB LCD 的时钟信号,每种型号的屏幕的像素都不一样,这根据生产厂家的手册进行设置。以 ATK7016 这款屏幕为例,显示一帧图像所需要的时钟数为

 (VSPW+VBP+LINE+VFP) * (HSPW + HBP + HOZVAL + HFP)

= (3 + 20 + 600 + 12) * (20 + 140 + 1024 + 160)

= 635 * 1344

= 853440。

显示一帧图像需要853440个时钟数,那么显示60帧就是:853440 * 60 = 51206400≈51.2M,所以像素时钟就是 51.2MHz。

 

I.MX6U 的 eLCDIF 接口时钟图如图

3c9544928f0c475795a36bc47914d0cc.png

后续需要对这个接口时钟进行设置,可以看我之前写的博客有时钟这方面的分析

显存

显存:显示存储空间,采用ARGB8888=32bit=4B。这4个字节的数据表示一个像素点的信息,必须得存起来。1024*600*4=2.5MB。因此需要流出2.5MB的内存给LCD用,方法很简单,直接定义一个32位的数组,u32 lcdframe[1024*600];

 

6ULL LCDIF控制器接口原理

1、这里直接将需要设置的寄存器列出来了,不然真的太多了,寄存器的详细解释请看IMX6ULL的参考手册。只需要看需要设置的寄存器就行。这里使用DOTCLK接口,也就是VSYNC、HSYNC、ENABLE(DE)和DOTCLK(PCLK)

2、LCDIF_CTRL寄存器,bit0必须置1,bit1设置数据格式24位全部有效,设置为0。Bit5设置LCDIF接口工作在主机模式下,要置1。Bit9:8设置输入像素格式位24bit,写3。Bit11:10,设置数据传输宽度位24bit,写3。Bit13:12设置数据交换,我们不需要交换设置位0。Bit15:14设置输入数据交换,不交换设置位0。Bit17置1,LCDIF工作在DOTCLK模式下。Bit19必须置1,因为工作在DOTCL模式。Bit31是复位功能必须置0.

3、LCDIF_CTRL1寄存器的bit19:16设置位0X7。24位的格式,

4、LCDIF_TRANSFER_COUNT寄存器的bit15:0是LCD一行的像素数,1024。Bit31:16是LCD一共有多少行,600行。

5、LCDIF_VDCTRL0寄存器,bit17:0为vspw参数。Bit20设置vsync信号的宽度单位 ,设置为1。Bit21设置为1。Bit24设 置ENABLE信号极性,为0的时候是低电平有效,为1是高电平,我们设置为1。Bit25设置CLK信号极性,设置为0.。bit26设置HSYNC信号极性,设置0,低电平有效,bit27设置VSYNC信号极性,设置为0,低电平有效。Bit28设置1,开始ENABLE信号。Bit29设置为0,VSYNC输出。

6、LCDIF_VDCTRL1寄存器为两个VSYNC信号之间的长度,那就是VSPW+ VBPD+HEIGHT+VFP。

7、LCDIF_VDCTRL2寄存器bit17:0是两个HSYNC信号之间的长度,那就是hspw+hbp+width+hfp。Bit31:18为hspw。

8、LCDIF_VDCTRL3寄存器,bit15:0是vbp+vspw。Bit27:16是hbp+hspw。

9、LCDIF_VDCTRL4寄存器,bit17:0是一行有多少个像素点,1024

10、LCDIF_CUR_BUF,LCD当前缓存,显存首地址。

11、LCDIF_NEXT_BUF,LCD下一帧数据首地址。

12、LCD IO初始化。

LCD像素时钟的设置。

LCD需要一个CLK信号,这个时钟信号是6ULL的CLK引脚发送给RGB LCD的。比如7寸1024*600的屏幕需要51.2MHz的CLK。

LCDIF_CLK_ROOT就是6ULL的像素时钟。设置PLL5(video PLL)为LCD的时钟源,

PLL5 CLK=Fref*DIV_SELECT=,DIV_SELECT就是CCM_ANALOG_PLL_VIDEO的bit6:0,也就是DIV_SELEC位,可选范围27-54。设置PLL_VIDEO寄存器的bit20:19为2,表示1分频。设置CCM_ANALOG_MISC2寄存器的bit31:30为0,也就是VIDOE_DIV为0,1分频

不使用小数分频器

因此CCM_ANALOG_PLL_VIDEO_NUM=0、CCM_ALALOG_PLL_VIDEO_DENOM=0。

CCM_CSCDR2寄存器的bit17:15,设置LCDIF_PRE_CLK_SEL,选择LCDIF_CLK_ROOT的时钟源,设置为0x2。表示LCDIF时钟源为PLL5。Bit14:12为LCDIF_PRED位,设置前级分频,可以设置0~7,分别对应1~8分频。

CCM_CBCMR寄存器,bit25:23为LCDIF_PODF,设置第二级分频,可以设置为0~7,分别对应1~8分频。

继续设置CCM_CSCDR2寄存器的bit11:9为LCDIF_CLK_SEL,选择LCD CLK的最终时钟源,设置为0,LCDIF的最终时钟源来源于pre-muxed LCDIF clock

LCD驱动程序编写

如果使用的正点原子的开发板和RGB屏幕,那么在驱动LCD之前,要先读取屏幕ID。根据不同的屏幕id对屏幕进行参数的配置

bsp_lcd.h

#ifndef __BSP_LCD_H
#define __BSP_LCD_H
#include "imx6ul.h"
#include "bsp_gpio.h"
#include "bsp_delay.h"
#include "bsp_clk.h"
//屏幕ID
#define ATK4342     0X4342//4.3寸480*272
#define ATK4384     0X4384//4.3寸800*480
#define ATK7084     0x7084//7寸800*480
#define ATK7016     0x7016//7寸1024*600
#define ATK1018     0x1018//10.1寸1280*800
//lcd显存首地址
#define LCD_FRAMEBUF_ADDR   (0X89000000)
/* 颜色 ARGB8888 */
#define LCD_BLUE		  0x000000FF
#define LCD_GREEN		  0x0000FF00
#define LCD_RED 		  0x00FF0000
#define LCD_CYAN		  0x0000FFFF
#define LCD_MAGENTA 	  0x00FF00FF
#define LCD_YELLOW		  0x00FFFF00
#define LCD_LIGHTBLUE	  0x008080FF
#define LCD_LIGHTGREEN	  0x0080FF80
#define LCD_LIGHTRED	  0x00FF8080
#define LCD_LIGHTCYAN	  0x0080FFFF
#define LCD_LIGHTMAGENTA  0x00FF80FF
#define LCD_LIGHTYELLOW   0x00FFFF80
#define LCD_DARKBLUE	  0x00000080
#define LCD_DARKGREEN	  0x00008000
#define LCD_DARKRED 	  0x00800000
#define LCD_DARKCYAN	  0x00008080
#define LCD_DARKMAGENTA   0x00800080
#define LCD_DARKYELLOW	  0x00808000
#define LCD_WHITE		  0x00FFFFFF
#define LCD_LIGHTGRAY	  0x00D3D3D3
#define LCD_GRAY		  0x00808080
#define LCD_DARKGRAY	  0x00404040
#define LCD_BLACK		  0x00000000
#define LCD_BROWN		  0x00A52A2A
#define LCD_ORANGE		  0x00FFA500
#define LCD_TRANSPARENT   0x00000000

extern struct tftlcd_typedef tftlcd_dev;

//lcd屏幕信息结构体
struct tftlcd_typedef{
    unsigned short height;//屏幕高度
    unsigned short width;//屏幕宽度
    unsigned char pixsize;//每个像素占用的字节数
    unsigned short vspw;
    unsigned short vbpd;
    unsigned short vfpd;
    unsigned short hspw;
    unsigned short hbpd;
    unsigned short hfpd;

    unsigned int framebuffer;//屏幕显存起始地址
    unsigned int forecolor;//前景色
    unsigned int backcolor;//背景色

};

unsigned short LCD_Read_PanelID(void);
void LCD_Init(void);
void LCD_Clk_Init(unsigned char loopDiv,unsigned char preDiv,unsigned char Div);
void LCD_GPIO_Init(void);
void LCD_Reset(void);
void LCD_UnReset(void);
void LCD_Enable(void);
void LCDIF_Init(void);
inline void LCD_DrawPoint(unsigned short x,unsigned short y ,unsigned int color);
inline unsigned int LCD_ReadPoint(unsigned short x,unsigned short y);
void LCD_Clear(unsigned int color);
void LCD_Fill(unsigned short x1,unsigned short y1,unsigned short x2,unsigned short y2,unsigned int color);
#endif

bsp_lcd.c

#include "bsp_lcd.h"
#include "stdio.h"

//屏幕参数结构体变量
struct tftlcd_typedef tftlcd_dev;


//lcd初始化
void LCD_Init(void){
    unsigned short lcdID = 0;
    lcdID =  LCD_Read_PanelID();
    printf("lcd_ID = %#x\r\n",lcdID);
    //初始化屏幕io
    LCD_GPIO_Init();
    LCD_Reset();
    //复位要等个十或二十毫秒
    delayms(10);
    LCD_UnReset();

    //根据不同的屏幕id来设置屏幕参数
    //每个使用RGB8888的像素字节都是4;这里默认前景色是白色,后景色是黑色
    tftlcd_dev.pixsize = 4;
    tftlcd_dev.framebuffer = LCD_FRAMEBUF_ADDR;
    tftlcd_dev.forecolor = LCD_WHITE;
    tftlcd_dev.backcolor = LCD_BLACK;
     //根据不同的屏幕ID对不同屏幕进行参数配置
     switch(lcdID){
        case ATK4342:
            {
                tftlcd_dev.height=272;
                tftlcd_dev.width=480;
                tftlcd_dev.hspw = 1;
                tftlcd_dev.hbpd = 40;
                tftlcd_dev.hfpd = 5;
                tftlcd_dev.vspw = 1;
                tftlcd_dev.vbpd = 8;
                tftlcd_dev.vfpd = 8;
                LCD_Clk_Init(27,8,8);
                 printf("ATK4342\r\n");
            }
            break;
        case ATK7084:
             {
                tftlcd_dev.height=480;
                tftlcd_dev.width=800;
                tftlcd_dev.hspw = 1;
                tftlcd_dev.hbpd = 46;
                tftlcd_dev.hfpd = 210;
                tftlcd_dev.vspw = 1;
                tftlcd_dev.vbpd = 23;
                tftlcd_dev.vfpd = 22;
                LCD_Clk_Init(30,3,7);
                printf("ATK7084\r\n");
            }
            break;
        case ATK7016:
             {
                tftlcd_dev.height=600;
                tftlcd_dev.width=1024;
                tftlcd_dev.hspw = 20;
                tftlcd_dev.hbpd = 140;
                tftlcd_dev.hfpd = 160;
                tftlcd_dev.vspw = 3;
                tftlcd_dev.vbpd = 20;
                tftlcd_dev.vfpd = 12;
                LCD_Clk_Init(32,3,5);
                printf("ATK7016\r\n");
            }
            break;
        case ATK4384:
             {
                tftlcd_dev.height=480;
                tftlcd_dev.width=800;
                tftlcd_dev.hspw = 48;
                tftlcd_dev.hbpd = 88;
                tftlcd_dev.hfpd = 40;
                tftlcd_dev.vspw = 3;
                tftlcd_dev.vbpd = 32;
                tftlcd_dev.vfpd = 13;
                LCD_Clk_Init(42,4,8);
                printf("ATK4384\r\n");
            }
            break;
        default :
            break;
    }
    //配置LCDIF控制器接口
    LCDIF_Init();
}
/*像素时钟初始化
* 不使用小数分频器,分子和分母寄存器都设置为0 CCM_ANALOG_PLL_VIDEO_NUM 、CCM_ANALOG_PLL_VIDEO_DENOM
* loopDiv:寄存器CCM_ANALOG_PLL_VIDEOn的DIV_SELECT位 27~54
* preDive:寄存器CCM_CSCDR2的LCDIF1_PRED位,LCDIF1时钟的预分频器1~8分频
* Div:寄存器CCM_CBCMR的LCDIF1_PODF位,LCDIF1时钟的后置分频器
* LCD_CLK = 24 *loopDiv / preDiv / Div
*/
void LCD_Clk_Init(unsigned char loopDiv,unsigned char preDiv,unsigned char Div){
    CCM_ANALOG->PLL_VIDEO_NUM = 0;
    CCM_ANALOG->PLL_VIDEO_DENOM = 0;

    //CCM_ANALOG->PLL_VIDEO &= ~(0X18207F);
    printf("PLL_VIDEO_1 = %#x\r\n",CCM_ANALOG->PLL_VIDEO);
    CCM_ANALOG->PLL_VIDEO &= ~(3<<19);
    CCM_ANALOG->PLL_VIDEO &= ~(1<<13);
    CCM_ANALOG->PLL_VIDEO &= ~(127<<0);
    CCM_ANALOG->PLL_VIDEO &= ~(1<<12);
    CCM_ANALOG->PLL_VIDEO &= ~(1<<16);
    CCM_ANALOG->PLL_VIDEO |= (1<<20);
    CCM_ANALOG->PLL_VIDEO |= (1<<13);
    CCM_ANALOG->PLL_VIDEO |= (loopDiv<<0);
    //CCM_ANALOG->PLL_VIDEO = (2<<19)|(1<<13)|(loopDiv<<0);
    printf("PLL_VIDEO_2 = %#x\r\n",CCM_ANALOG->PLL_VIDEO);

    CCM_ANALOG->MISC2 &= ~(3<<30);
    //CCM_ANALOG->MISC2 |= 0<<30;

    CCM->CSCDR2 &= ~(7<<15);
    CCM->CSCDR2 |= (2<<15);

    CCM->CSCDR2 &= ~(7<<12);
    CCM->CSCDR2 |= ((preDiv - 1)<<12);

    CCM->CBCMR &= ~(7<<23);
    CCM->CBCMR |= ((Div - 1)<<23);

    CCM->CSCDR2 &= ~(7<<9);
    CCM->CSCDR2 |= (0<<9);
}

//读取lcd屏幕id
/*
 * 读取屏幕ID,
 * 描述:LCD_DATA23=R7(M0);LCD_DATA15=G7(M1);LCD_DATA07=B7(M2);
 * 		M2:M1:M0
 *		0 :0 :0	//4.3寸480*272 RGB屏,ID=0X4342
 *		0 :0 :1	//7寸800*480 RGB屏,ID=0X7084
 *	 	0 :1 :0	//7寸1024*600 RGB屏,ID=0X7016
 *  	1 :0 :1	//10.1寸1280*800,RGB屏,ID=0X1018
 *		1 :0 :0	//4.3寸800*480 RGB屏,ID=0X4384
 * @param 		: 无
 * @return 		: 屏幕ID
 */
unsigned short LCD_Read_PanelID(void){
    unsigned char idx = 0;//这里数据类型使用unsigned char 而不使用unsigned int 我认为是因为这样节省内存空间,
    //unsigned char的取值范围是0-255,占用一个字节内存空间,
    //而unsigned int 的取值范围较大,占用四个或八个字节的内存空间
    //打开模拟开关,设置LCD_VSYNC为高点平
	IOMUXC_SetPinMux(IOMUXC_LCD_VSYNC_GPIO3_IO03,0);		
	IOMUXC_SetPinConfig(IOMUXC_LCD_VSYNC_GPIO3_IO03,0X10B0);
    gpio_pin_config_t LCDio;
    LCDio.direction = kGPIO_DigitalOutput;
    LCDio.outputLogic =1;
    gpio_init(GPIO3,3,&LCDio);

    //读取m0 m1 m2寄存器的值,先复用为gpio再读取寄存器dr的值
    //m0
    IOMUXC_SetPinMux(IOMUXC_LCD_DATA23_GPIO3_IO28,0);	
	IOMUXC_SetPinConfig(IOMUXC_LCD_DATA23_GPIO3_IO28,0xF080);
    LCDio.direction = kGPIO_DigitalInput;//可以重复利用
    gpio_init(GPIO3,28,&LCDio);
    int m0 = gpio_pinread(GPIO3,28);
	//m1
    IOMUXC_SetPinMux(IOMUXC_LCD_DATA15_GPIO3_IO20,0);	
	IOMUXC_SetPinConfig(IOMUXC_LCD_DATA15_GPIO3_IO20,0xF080);
    LCDio.direction = kGPIO_DigitalInput;//可以重复利用
    gpio_init(GPIO3,20,&LCDio);
    int m1 = gpio_pinread(GPIO3,20);
    //m2
    IOMUXC_SetPinMux(IOMUXC_LCD_DATA07_GPIO3_IO12,0);
	IOMUXC_SetPinConfig(IOMUXC_LCD_DATA07_GPIO3_IO12,0xF080);
    LCDio.direction = kGPIO_DigitalInput;//可以重复利用
    gpio_init(GPIO3,12,&LCDio);
    int m2 = gpio_pinread(GPIO3,12);

    idx =(unsigned char )m0;
    idx |= (unsigned char)m1<<1;
    idx |= (unsigned char)m2<<2;
    switch(idx){
        case 0:
            return ATK4342;
            break;
        case 1:
            return ATK7084;
            break;
        case 2:
            return ATK7016;
            break;
        case 4:
            return ATK4384;
            break;
        case 5:
            return ATK1018;
            break;
        default :
            break;
    }
    return 0;

}
//初始化屏幕io
/* 2、配置LCD IO属性	
	 *bit 16:0 HYS关闭
	 *bit [15:14]: 0 默认22K上拉
	 *bit [13]: 0 pull功能
	 *bit [12]: 0 pull/keeper使能 
	 *bit [11]: 0 关闭开路输出
	 *bit [7:6]: 10 速度100Mhz
	 *bit [5:3]: 111 驱动能力为R0/7
	 *bit [0]: 1 高转换率
 */
  void LCD_GPIO_Init(void){
    IOMUXC_SetPinMux(IOMUXC_LCD_DATA00_LCDIF_DATA00,0);	
	IOMUXC_SetPinConfig(IOMUXC_LCD_DATA00_LCDIF_DATA00,0xB9);
    IOMUXC_SetPinMux(IOMUXC_LCD_DATA01_LCDIF_DATA01,0);	
	IOMUXC_SetPinConfig(IOMUXC_LCD_DATA01_LCDIF_DATA01,0xB9);
    IOMUXC_SetPinMux(IOMUXC_LCD_DATA02_LCDIF_DATA02,0);	
	IOMUXC_SetPinConfig(IOMUXC_LCD_DATA02_LCDIF_DATA02,0xB9);
    IOMUXC_SetPinMux(IOMUXC_LCD_DATA03_LCDIF_DATA03,0);	
	IOMUXC_SetPinConfig(IOMUXC_LCD_DATA03_LCDIF_DATA03,0xB9);
    IOMUXC_SetPinMux(IOMUXC_LCD_DATA04_LCDIF_DATA04,0);	
	IOMUXC_SetPinConfig(IOMUXC_LCD_DATA04_LCDIF_DATA04,0xB9);
    IOMUXC_SetPinMux(IOMUXC_LCD_DATA05_LCDIF_DATA05,0);	
	IOMUXC_SetPinConfig(IOMUXC_LCD_DATA05_LCDIF_DATA05,0xB9);
    IOMUXC_SetPinMux(IOMUXC_LCD_DATA06_LCDIF_DATA06,0);	
	IOMUXC_SetPinConfig(IOMUXC_LCD_DATA06_LCDIF_DATA06,0xB9);
    IOMUXC_SetPinMux(IOMUXC_LCD_DATA07_LCDIF_DATA07,0);	
	IOMUXC_SetPinConfig(IOMUXC_LCD_DATA07_LCDIF_DATA07,0xB9);
    IOMUXC_SetPinMux(IOMUXC_LCD_DATA08_LCDIF_DATA08,0);	
	IOMUXC_SetPinConfig(IOMUXC_LCD_DATA08_LCDIF_DATA08,0xB9);
    IOMUXC_SetPinMux(IOMUXC_LCD_DATA09_LCDIF_DATA09,0);	
	IOMUXC_SetPinConfig(IOMUXC_LCD_DATA09_LCDIF_DATA09,0xB9);
    IOMUXC_SetPinMux(IOMUXC_LCD_DATA10_LCDIF_DATA10,0);	
	IOMUXC_SetPinConfig(IOMUXC_LCD_DATA10_LCDIF_DATA10,0xB9);
    IOMUXC_SetPinMux(IOMUXC_LCD_DATA11_LCDIF_DATA11,0);	
	IOMUXC_SetPinConfig(IOMUXC_LCD_DATA11_LCDIF_DATA11,0xB9);
    IOMUXC_SetPinMux(IOMUXC_LCD_DATA12_LCDIF_DATA12,0);	
	IOMUXC_SetPinConfig(IOMUXC_LCD_DATA12_LCDIF_DATA12,0xB9);
    IOMUXC_SetPinMux(IOMUXC_LCD_DATA13_LCDIF_DATA13,0);	
	IOMUXC_SetPinConfig(IOMUXC_LCD_DATA13_LCDIF_DATA13,0xB9);
    IOMUXC_SetPinMux(IOMUXC_LCD_DATA14_LCDIF_DATA14,0);	
	IOMUXC_SetPinConfig(IOMUXC_LCD_DATA14_LCDIF_DATA14,0xB9);
    IOMUXC_SetPinMux(IOMUXC_LCD_DATA15_LCDIF_DATA15,0);	
	IOMUXC_SetPinConfig(IOMUXC_LCD_DATA15_LCDIF_DATA15,0xB9);
    IOMUXC_SetPinMux(IOMUXC_LCD_DATA16_LCDIF_DATA16,0);	
	IOMUXC_SetPinConfig(IOMUXC_LCD_DATA16_LCDIF_DATA16,0xB9);
    IOMUXC_SetPinMux(IOMUXC_LCD_DATA17_LCDIF_DATA17,0);	
	IOMUXC_SetPinConfig(IOMUXC_LCD_DATA17_LCDIF_DATA17,0xB9);
    IOMUXC_SetPinMux(IOMUXC_LCD_DATA18_LCDIF_DATA18,0);	
	IOMUXC_SetPinConfig(IOMUXC_LCD_DATA18_LCDIF_DATA18,0xB9);
    IOMUXC_SetPinMux(IOMUXC_LCD_DATA19_LCDIF_DATA19,0);	
	IOMUXC_SetPinConfig(IOMUXC_LCD_DATA19_LCDIF_DATA19,0xB9);
    IOMUXC_SetPinMux(IOMUXC_LCD_DATA20_LCDIF_DATA20,0);	
	IOMUXC_SetPinConfig(IOMUXC_LCD_DATA20_LCDIF_DATA20,0xB9);
    IOMUXC_SetPinMux(IOMUXC_LCD_DATA21_LCDIF_DATA21,0);	
	IOMUXC_SetPinConfig(IOMUXC_LCD_DATA21_LCDIF_DATA21,0xB9);
    IOMUXC_SetPinMux(IOMUXC_LCD_DATA22_LCDIF_DATA22,0);	
	IOMUXC_SetPinConfig(IOMUXC_LCD_DATA22_LCDIF_DATA22,0xB9);
    IOMUXC_SetPinMux(IOMUXC_LCD_DATA23_LCDIF_DATA23,0);	
	IOMUXC_SetPinConfig(IOMUXC_LCD_DATA23_LCDIF_DATA23,0xB9);


    IOMUXC_SetPinMux(IOMUXC_LCD_ENABLE_LCDIF_ENABLE,0);	
	IOMUXC_SetPinConfig(IOMUXC_LCD_ENABLE_LCDIF_ENABLE,0xB9);
    IOMUXC_SetPinMux(IOMUXC_LCD_HSYNC_LCDIF_HSYNC,0);	
	IOMUXC_SetPinConfig(IOMUXC_LCD_HSYNC_LCDIF_HSYNC,0xB9);
    IOMUXC_SetPinMux(IOMUXC_LCD_VSYNC_LCDIF_VSYNC,0);	
	IOMUXC_SetPinConfig(IOMUXC_LCD_VSYNC_LCDIF_VSYNC,0xB9);
    IOMUXC_SetPinMux(IOMUXC_LCD_CLK_LCDIF_CLK,0);	
	IOMUXC_SetPinConfig(IOMUXC_LCD_CLK_LCDIF_CLK,0xB9);

    //点亮背光,背光不点亮,屏幕不显示;背光初始化,io8输出高点平到屏幕
    IOMUXC_SetPinMux(IOMUXC_GPIO1_IO08_GPIO1_IO08,0);	
	IOMUXC_SetPinConfig(IOMUXC_GPIO1_IO08_GPIO1_IO08,0xB9);
    gpio_pin_config_t Bl_config;
    Bl_config.direction = kGPIO_DigitalOutput;
    Bl_config.outputLogic =1;
    gpio_init(GPIO1,8,&Bl_config);
    gpio_pinwrite(GPIO1,8,1);
  }
//复位
void LCD_Reset(void){
      LCDIF->CTRL = 1<<31;
  }

void LCD_UnReset(void){
      LCDIF->CTRL = 0<<31;
  }
//使能lcdif控制器
void LCD_Enable(void){
    LCDIF->CTRL |= 1<<0;
}

//配置LCDIF控制器接口
void LCDIF_Init(void){
    LCDIF->CTRL = 0;
    LCDIF->CTRL = (1<<5)|(3<<8)|(3<<10)|(1<<17)|(1<<19)|(0<<1)|(0<<12)|(0<<14);
    
    LCDIF->CTRL1 = (7<<16);
    
    LCDIF->TRANSFER_COUNT = (tftlcd_dev.height<<16) | (tftlcd_dev.width<<0);
    
    LCDIF->VDCTRL0 = (tftlcd_dev.vspw<<0)|(1<<20)|(1<<21)|(1<<24)|(0<<25)|(0<<26)|(0<<27)|(1<<28)|(0<<29);

    //根据原理 垂直的参数单位是th
    LCDIF->VDCTRL1 = (tftlcd_dev.height + tftlcd_dev.vspw + tftlcd_dev.vbpd + tftlcd_dev.vfpd);

    //根据原理 水平的参数单位是tCLK
    LCDIF->VDCTRL2 =((tftlcd_dev.width + tftlcd_dev.hspw + tftlcd_dev.hbpd + tftlcd_dev.hfpd)<<0)| (tftlcd_dev.hspw<<18);

    LCDIF->VDCTRL3 = ((tftlcd_dev.vspw + tftlcd_dev.vbpd)<<0)|((tftlcd_dev.hspw + tftlcd_dev.hbpd)<<16);
    
    LCDIF->VDCTRL4 = (tftlcd_dev.width<<0)|(1<<18);
    
    LCDIF->CUR_BUF = ((unsigned int)tftlcd_dev.framebuffer)<<0;

    LCDIF->NEXT_BUF = ((unsigned int)tftlcd_dev.framebuffer)<<0;
    //使能lcdif控制器 配置CTRL寄存器的0位为1
    LCD_Enable();
    delayms(10);
    LCD_Clear(LCD_BLUE);
}

//画点函数
inline void LCD_DrawPoint(unsigned short x,unsigned short y ,unsigned int color){
    *(unsigned int *)(tftlcd_dev.framebuffer + tftlcd_dev.pixsize * (tftlcd_dev.width *y + x)) = color;
}

//读点函数
inline unsigned int LCD_ReadPoint(unsigned short x,unsigned short y){
    return *(unsigned int *)(tftlcd_dev.framebuffer + tftlcd_dev.pixsize *(tftlcd_dev.width *y + x));
}

//清屏函数
void LCD_Clear(unsigned int color){
    unsigned int num = 0;
    unsigned int i;
    unsigned int *start_addr = (unsigned int *)(tftlcd_dev.framebuffer);
    num = (unsigned int)(tftlcd_dev.width * tftlcd_dev.height);
    for(i = 0;i < num;i++){
        start_addr[i] = color;
    }
    printf("清屏函数\r\n");
}

//矩形填充函数
void LCD_Fill(unsigned short x1,unsigned short y1,unsigned short x2,unsigned short y2,unsigned int color){
    if(x1<0) x1 = 0;
    if(y1<0) y1 = 0;
    if(x2>tftlcd_dev.width) x2 = tftlcd_dev.width-1;
    if(y2>tftlcd_dev.height) y2 = tftlcd_dev.height-1;
    int x,y;
    for(x = x1;x<x2;x++){
        for(y = y1;y<y2;y++){
            LCD_DrawPoint(x,y,color);
        }
    }


}

main.c

其中printf重写打印输出到串口调试工具

#include "bsp_clk.h"

#include "bsp_delay.h"

#include "bsp_led.h"

#include "bsp_beep.h"

#include "bsp_key.h"

#include "bsp_int.h"

#include "bsp_uart.h"

#include "stdio.h"

#include "bsp_lcd.h"

#include "bsp_lcdapi.h"

/*

 * @description	: main函数

 * @param 		: 无

 * @return 		: 无

 */



unsigned int background[10] = {LCD_BLUE,LCD_GREEN,LCD_CYAN,LCD_YELLOW,LCD_DARKMAGENTA,

								LCD_ORANGE,LCD_TRANSPARENT,LCD_LIGHTYELLOW,LCD_WHITE,LCD_LIGHTGREEN};

int main(void)

{

	unsigned int color = 0;



	static unsigned char state = 0;

	int_init(); 				/* 初始化中断(一定要最先调用!) */

	imx6u_clkinit();			/* 初始化系统时钟 			*/

	delay_init();				/* 初始化延时 			*/

	clk_enable();				/* 使能所有的时钟 			*/

	led_init();					/* 初始化led 			*/

	beep_init();				/* 初始化beep	 		*/

	uart_init();				/* 初始化串口,波特率115200 */

	LCD_Init();



	LCD_DrawPoint(0,0,LCD_RED);

	LCD_DrawPoint(tftlcd_dev.width-1,0,LCD_RED);

	LCD_DrawPoint(0,tftlcd_dev.height-1,LCD_RED);

	LCD_DrawPoint(tftlcd_dev.width-1,tftlcd_dev.height-1,LCD_RED);



	color = LCD_ReadPoint(0,0);

	printf("color(0,0) =  %#x\r\n",color);



	color = LCD_ReadPoint(tftlcd_dev.width-1,0);

	printf("color(tftlcd_dev.width-1,0) =  %#x\r\n",color);



	color = LCD_ReadPoint(0,tftlcd_dev.height-1);

	printf("color(0,tftlcd_dev.height-1) =  %#x\r\n",color);



	color = LCD_ReadPoint(tftlcd_dev.width-1,tftlcd_dev.height-1);

	printf("color(tftlcd_dev.width-1,tftlcd_dev.height-1) =  %#x\r\n",color);



	//LCD_Fill(0,0,500,500,LCD_RED);

	tftlcd_dev.forecolor = LCD_RED;

	lcd_show_string(10,40,260,32,32,(char *)"ALPAH IMX6U");

	lcd_show_string(10,80,240,24,24,(char *)"RGBLCD TEST");

	lcd_show_string(10,110,240,16,16,(char *)"ATOM&ALIENTEK");

	lcd_show_string(10,130,240,12,12,(char *)"2024/11/13");



	int index = 0;

	while(1)				

	{	

		if(index ==10){

			index = 0;

		}

		state = !state;

		led_switch(LED0,state);

		delayms(100);

		LCD_Clear(background[index]);

		tftlcd_dev.backcolor = background[index];

		lcd_show_string(10,40,260,32,32,(char *)"ALPAH IMX6U");

		lcd_show_string(10,80,240,24,24,(char *)"RGBLCD TEST");

		lcd_show_string(10,110,240,16,16,(char *)"ATOM&ALIENTEK");

		lcd_show_string(10,130,240,12,12,(char *)"2024/11/13");

		index++;

	}

	return 0;

}

效果演示

493155aa127d4157a38d9d5691bc2c1b.gif

 

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值