原理分析
像素格式
本章实验中我们使用 ARGB8888 这种像素格式,一个像素占用 4个字节的内存,这四个字节每个位的分配如图
一个像素点是 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.后续需要对这些信号线进行初始化设置。
图中三个 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,那么其扫描如图:
HSYNC 是水平同步信号,也叫做行同步信号,当产生此信号的话就表示开始显示新的一行了,所以此信号都是在图的最左边。当 VSYNC 信号是垂直同步信号,也叫做帧同步信号,当产生此信号的话就表示开始显示新的一帧图像了,所以此信号在图的上角。
RGBLCD屏幕时序
行显示时序图
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。
一帧图像就是由很多个行组成
帧显示时序图
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 接口时钟图如图
后续需要对这个接口时钟进行设置,可以看我之前写的博客有时钟这方面的分析
显存
显存:显示存储空间,采用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;
}
效果演示