硬件驱动——I.MX6ULL裸机启动(9)(RGBLCD相关设置)

一.RGBLCD相关定义和概念

1.LCD全称是liquid  crystal  display,就是液晶显示器.

是现在最常用到的显示器,手机,电脑,各种人机交互设备等基本上都用到了LCD,最常见就是手机和电脑显示器。我们这里所提及的LCD不包括触摸功能,触摸功能是通过另外的设备实现的,LCD在这里只是输出设备

        LCD的原理:LCD的构造是在两片平行的玻璃基板当中放置液晶盒,下基板玻璃上设置TFT(薄膜晶体管),上基板玻璃上设置彩色滤光片,通过TFT上的信号与电压改变来控制液晶分子的转动方向,从而达到控制每个像素点偏振光出射与否而达到显示目标。

2.分辨率

LCD显示器,有720P,1080P,2K,4K等LCD显示器分辨率。LCD显示器都是由一个一个的像素点组成,像素点就类似一个灯(在OLED显示器中,像素点就是一个小灯),这个小灯是RGB灯,也就是由R(红色),G(绿色)和B(蓝色)这三种颜色组成的,而RGB就是光的三原色。1080P的意思就是一个LCD屏幕上的像素数量是1920*1080个,也就是这个屏幕一列1080个像素点,一共1920列。

        上图是1080P显示器的像素示意图,X轴就是LCD显示器的横轴,Y轴就是显示器的竖轴。图中的小方块就是像素点,一共有1920*1080 = 2073600个像素点。左上角的A点是第一个像素点,右下角的C点就是最后一个像素点。在LCD尺寸不变的情况下,分辨率越高越清晰。同样的,分辨率不变的情况下,LCD尺寸越小越清晰。

        LCD显示器的分辨率是一个很重要的参数,但是并不是分辨率越高的LCD就越好。衡量一款LCD的好坏,分辨率只是其中的一个参数,还有色彩还原程度,色彩偏离,亮度,可视角度,屏幕刷新率等其它参数。

3.像素格式

        一个像素点就相当于一个RGB小灯,通过控制R,G,B这三种颜色的亮度就可以显示出各种各样的色彩一般一个R,G,B这三部分分别使用8bit的数据,那么一个像素点就是24bit,也就是说一个像素点3个字节,这种像素格式成为RGB888。如果再加入8bit的透明通道的话一个像素点就是32bit,也就是4个字节,这种像素格式称为RGB8888。

如上图,一个像素点是4个字节,其中bit31~bit24是透明通道,bit23~bit16是red通道,bit15~bit8是green通道,bit7~bit0是blue通道。所以红色对应的值就是0x00FF0000,蓝色对应的值就是0x000000FF,绿色对应的值是0x0000FF00

4.LCD硬件接口

        LCD屏幕或者说显示器有很多种接口,比如在显示器上常见的VGA,HDMI,DP等等,但是IMX6ULL开发板不支持这些接口。该开发板支持RGB接口的LCD,RGBLCD接口的信号线如下表:

        R,G,B这24根是数据线,其他的是控制线

5.LCD时间参数

        如果将LCD显示一帧图像的过程想象成绘画,那么在现实化的过程中就是和用一只“笔”在不同的像素点画上不同的颜色。这根笔按照从左到右,从上到下的顺序扫描每一个像素点,并且在像素画上对应的颜色,当画到最后一个像素点的时候一幅图像就绘制好了。假设一个LCD的分辨率为1024*600,那么其扫描如图:

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

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

6.LCD的时序

        

        上图就是RGBLCD的行显示时序,其中有几个重要参数:

(1)HSYNC:行同步信号,当此信号有效的话就表示开始显示新的一行数据,通过查阅手册可以知道此信号是低电平有效还是高电平有效。

(2)HSPW:有些地方也叫做thp,是HSYNC信号宽度,也就是HSYNC信号持续时间。HSYNC信号不是一个脉冲,而是需要持续一段时间才是有效的,单位为CLK

(3)HBP:有些地方叫做thb,术语叫做行同步信号后肩

(4)HOZVAL:有些地方叫做thf,显示一行数据所需的时间,假设屏幕分辨率为1024*600,那么HOZVAL就是1024

(5)HFP:有些地方叫做thf,术语叫做行同步信号前肩

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

        上图就是RGBLCD的帧显示时序,我们来分析一下其中的几个参数:

(1)VSYNC帧同步信号,当此信号有效的话就表示开始显示新的一帧数据,查手册可以知道此信号低电平有效还是高电平有效

(2)VSPW:也可称为tvp,是VSYNC信号宽度,也就是VSYNC信号持续时间,单位为一行的时间

(3)VBP:也可称为tvb,术语是帧同步信号后肩,单位为一行的时间

(4)LINE:也可称为tvd,显示一帧有效数据所需的时间,假设屏幕分辨率为1024*600,那么LINE就是600行的时间

(5)VFP:也可称为tvf,术语是帧同步信号前肩,单位为一行的时间

        显示一帧所需要的时间就是:VSPW+VBP+LINE+VFP个行时间,最终的计算公式:为T = (VSPW+VBP+LINE+VFP)*(HSPW+HBP+HOZVAL+HFP)

7.像素时钟

     我们用的显示屏的分辨率是800*480,其时间参数如下:

   

        显示一帧图像所需要的时钟数是:
        (VSPW+VBP+LINE+VFP) * (HSPW + HBP + HOZVAL + HFP)= (3 + 32 + 480 + 13) * (48 + 88 + 800 + 40) =。显示一帧图像需要515328个时钟数,那么显示60帧就是:515328 * 60 = 30,919,680≈31M,所以像素时钟就是 31MHz

        这里的31MHz其实指的就是IMX这片Soc的LCD控制器的工作频率。

        

        图示1的位置是一个选择器,相当于是选择eLCDIF的时钟源。时钟源选择PPL5。配置PLL5,此后的2,3位置是两个分频器,在经过一个选择器最终到达eLCDIF外设,eLCDIF外设,eLCDIF外设的实际时钟名为LCDIF1_CLK_ROOT。

        这里其实主要问题是凑几个数,最终使LCDIF1_CLK_ROOT的值恰好是31MHz。我们的计划是这样的:选通PLL5作为时钟源是肯定的,这里先把PLL5的倍频因子设置为42,那么从PLL5输出的频率就是1008MHz,之后在2这个位置4分频,再在3这个位置8分频,那么结果为1008/4/8=31.5

MHz,最后再把这四个位置选通。
        注意: CCM_ANALOG_PLL_VIDEOn还有两个域需要设置,POST_DIV_SLECT(bit20:19),是个分频器,我们需要设置为1分频; ENABLE(bit13),用于使能PLL5,需要置1来使能PLL5。此外CCM_ANALOG_MISC2n寄存器里还有一个分频器VIDEO_DIV(bit 31:30),需要设置为1分频。

8.显存问题

        关于显存的问题,如果采用 ARGB8888 格式的话一个像素需要 4 个字节的内存来存放像素
数据
,那么 1024*600 分辨率就需要 800*480*4=38400B≈1.5MB 内存。但是 RGB LCD 内部是没有内存的,所以就需要在开发板上的 DDR3 中分出一段内存作为 RGB LCD 屏幕的显存,我们如果要在屏幕上显示什么图像的话直接操作这部分显存即可。 eLCDIF控制器有两个寄存器LCDIF_CUR_BUF和LCDIF_NEXT_BUF。LCDIF_CUR_BUF故名思意就是当前缓冲,我们只需把这个寄存设置为上面所说的1.5MB的首地址,那么eLCDIF就会自动把数组里面的值按照RGB888的格式显示到LCD上。 LCDIF_NEXT_BUF呢?就是下一帧的意思,就是当前的数据显示完以后自动切换到LCDIF_NEXT_BUF。一般来说GPU负责渲染,渲染的其实是下一帧,也就是说GPU渲染的总是下一帧图像,一次类推。当然也可以把这两个寄存器设置为同一个地址,那么修改的同时就直接显示出来了,但这样做连续高速变化的图像时可能会有撕裂的情况发生。        

        LCD裸机驱动的初始化流程为:初始化IO引脚---->初始化像素时钟----->初始化eLCDIF

二.相关代码

#include "lcd.h"
#include "MCIMX6Y2.h"
#include "fsl_iomuxc.h"
#include "gpio.h"
#include "delay.h"

void init_lcd_io(void)
{
     /*1.引脚初始化*/
    IOMUXC_SetPinMux(IOMUXC_LCD_DATA00_LCDIF_DATA00,0);
    IOMUXC_SetPinMux(IOMUXC_LCD_DATA01_LCDIF_DATA01,0);
    IOMUXC_SetPinMux(IOMUXC_LCD_DATA02_LCDIF_DATA02,0);
    IOMUXC_SetPinMux(IOMUXC_LCD_DATA03_LCDIF_DATA03,0);
    IOMUXC_SetPinMux(IOMUXC_LCD_DATA04_LCDIF_DATA04,0);
    IOMUXC_SetPinMux(IOMUXC_LCD_DATA05_LCDIF_DATA05,0);
    IOMUXC_SetPinMux(IOMUXC_LCD_DATA06_LCDIF_DATA06,0);
    IOMUXC_SetPinMux(IOMUXC_LCD_DATA07_LCDIF_DATA07,0);
    IOMUXC_SetPinMux(IOMUXC_LCD_DATA08_LCDIF_DATA08,0);
    IOMUXC_SetPinMux(IOMUXC_LCD_DATA09_LCDIF_DATA09,0);
    IOMUXC_SetPinMux(IOMUXC_LCD_DATA10_LCDIF_DATA10,0);
    IOMUXC_SetPinMux(IOMUXC_LCD_DATA11_LCDIF_DATA11,0);
    IOMUXC_SetPinMux(IOMUXC_LCD_DATA12_LCDIF_DATA12,0);
    IOMUXC_SetPinMux(IOMUXC_LCD_DATA13_LCDIF_DATA13,0);
    IOMUXC_SetPinMux(IOMUXC_LCD_DATA14_LCDIF_DATA14,0);
    IOMUXC_SetPinMux(IOMUXC_LCD_DATA15_LCDIF_DATA15,0);
    IOMUXC_SetPinMux(IOMUXC_LCD_DATA16_LCDIF_DATA16,0);
    IOMUXC_SetPinMux(IOMUXC_LCD_DATA17_LCDIF_DATA17,0);
    IOMUXC_SetPinMux(IOMUXC_LCD_DATA18_LCDIF_DATA18,0);
    IOMUXC_SetPinMux(IOMUXC_LCD_DATA19_LCDIF_DATA19,0);
    IOMUXC_SetPinMux(IOMUXC_LCD_DATA20_LCDIF_DATA20,0);
    IOMUXC_SetPinMux(IOMUXC_LCD_DATA21_LCDIF_DATA21,0);
    IOMUXC_SetPinMux(IOMUXC_LCD_DATA22_LCDIF_DATA22,0);
    IOMUXC_SetPinMux(IOMUXC_LCD_DATA23_LCDIF_DATA23,0);

    IOMUXC_SetPinMux(IOMUXC_LCD_CLK_LCDIF_CLK,0);
    IOMUXC_SetPinMux(IOMUXC_LCD_HSYNC_LCDIF_HSYNC,0);
    IOMUXC_SetPinMux(IOMUXC_LCD_VSYNC_LCDIF_VSYNC,0);
    IOMUXC_SetPinMux(IOMUXC_LCD_ENABLE_LCDIF_ENABLE,0);
    IOMUXC_SetPinMux(IOMUXC_GPIO1_IO08_GPIO1_IO08,0);
    
    /*设置电气属性*/
    /*
    *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 高转换率
    */
    IOMUXC_SetPinConfig(IOMUXC_LCD_DATA00_LCDIF_DATA00,0xB9);
    IOMUXC_SetPinConfig(IOMUXC_LCD_DATA01_LCDIF_DATA01,0xB9);
    IOMUXC_SetPinConfig(IOMUXC_LCD_DATA02_LCDIF_DATA02,0xB9);
    IOMUXC_SetPinConfig(IOMUXC_LCD_DATA03_LCDIF_DATA03,0xB9);
    IOMUXC_SetPinConfig(IOMUXC_LCD_DATA04_LCDIF_DATA04,0xB9);
    IOMUXC_SetPinConfig(IOMUXC_LCD_DATA05_LCDIF_DATA05,0xB9);
    IOMUXC_SetPinConfig(IOMUXC_LCD_DATA06_LCDIF_DATA06,0xB9);
    IOMUXC_SetPinConfig(IOMUXC_LCD_DATA07_LCDIF_DATA07,0xB9);
    IOMUXC_SetPinConfig(IOMUXC_LCD_DATA08_LCDIF_DATA08,0xB9);
    IOMUXC_SetPinConfig(IOMUXC_LCD_DATA09_LCDIF_DATA09,0xB9);
    IOMUXC_SetPinConfig(IOMUXC_LCD_DATA10_LCDIF_DATA10,0xB9);
    IOMUXC_SetPinConfig(IOMUXC_LCD_DATA11_LCDIF_DATA11,0xB9);
    IOMUXC_SetPinConfig(IOMUXC_LCD_DATA12_LCDIF_DATA12,0xB9);
    IOMUXC_SetPinConfig(IOMUXC_LCD_DATA13_LCDIF_DATA13,0xB9);
    IOMUXC_SetPinConfig(IOMUXC_LCD_DATA14_LCDIF_DATA14,0xB9);
    IOMUXC_SetPinConfig(IOMUXC_LCD_DATA15_LCDIF_DATA15,0xB9);
    IOMUXC_SetPinConfig(IOMUXC_LCD_DATA16_LCDIF_DATA16,0xB9);
    IOMUXC_SetPinConfig(IOMUXC_LCD_DATA17_LCDIF_DATA17,0xB9);
    IOMUXC_SetPinConfig(IOMUXC_LCD_DATA18_LCDIF_DATA18,0xB9);
    IOMUXC_SetPinConfig(IOMUXC_LCD_DATA19_LCDIF_DATA19,0xB9);
    IOMUXC_SetPinConfig(IOMUXC_LCD_DATA20_LCDIF_DATA20,0xB9);
    IOMUXC_SetPinConfig(IOMUXC_LCD_DATA21_LCDIF_DATA21,0xB9);
    IOMUXC_SetPinConfig(IOMUXC_LCD_DATA22_LCDIF_DATA22,0xB9);
    IOMUXC_SetPinConfig(IOMUXC_LCD_DATA23_LCDIF_DATA23,0xB9);

    IOMUXC_SetPinConfig(IOMUXC_LCD_CLK_LCDIF_CLK,0xB9);
    IOMUXC_SetPinConfig(IOMUXC_LCD_HSYNC_LCDIF_HSYNC,0xB9);
    IOMUXC_SetPinConfig(IOMUXC_LCD_VSYNC_LCDIF_VSYNC,0xB9);
    IOMUXC_SetPinConfig(IOMUXC_LCD_ENABLE_LCDIF_ENABLE,0xB9);
    IOMUXC_SetPinConfig(IOMUXC_GPIO1_IO08_GPIO1_IO08,0xB9);
    
    struct GPIO_Type_t t = 
    {
        .direction = gpio_output,
        .defalut_value = 1
    };
    init_gpio(GPIO1,8,&t);
    write_gpio(GPIO1,8,1);
}

void init_lcd_clk(void)
{
    CCM_ANALOG->PLL_VIDEO |= (1 << 16);
    CCM_ANALOG->PLL_VIDEO |= (1 << 12);
    CCM_ANALOG->PLL_VIDEO_NUM = 0;
    CCM_ANALOG->PLL_VIDEO_DENOM = 1;
    //PLL_AUDIO 42分频  1分频   24 * 42 = 1008
    CCM_ANALOG->PLL_VIDEO &= ~(0x7F << 0);
    CCM_ANALOG->PLL_VIDEO |= (42 << 0);
    CCM_ANALOG->PLL_VIDEO &= ~(3 << 19);
    CCM_ANALOG->PLL_VIDEO |= (2 << 19);
    CCM_ANALOG->PLL_VIDEO &= ~(1 << 12);
    CCM_ANALOG->PLL_VIDEO |= (1 << 13);
    while((CCM_ANALOG->PLL_VIDEO & (1 << 31)) == 0);
    CCM_ANALOG->PLL_VIDEO &= ~(1 << 16);

    //PLL5作为时钟
    unsigned int t;
    t = CCM->CSCDR2;
    t &= ~(7 << 15);
    t |= (2 << 15);
    t &= ~(7 << 12);    
    t |= (3 << 12);     //设置分频  4   1008 / 4 = 252
    t &= ~(7 << 9);     //时钟from divided pre-muxed LCDIF1 clock
    CCM->CSCDR2 = t;
    CCM->CBCMR |= (7 << 23);    //设置分频  8   252 / 8 = 31.5


}

void reset_lcd(void)
{
    LCDIF->CTRL |= (1 << 31);
    delayms(20);
    LCDIF->CTRL &= ~(3 << 30);
}

struct tftlcd_t lcd_dev;
void lcd_clear(unsigned int color)
{
    unsigned int (*p)[800] = (unsigned int (*)[800])lcd_dev.frame_buffer;   //指向帧缓存首地址
    int i,j;    //缓冲区总长度
    for(i = 0; i < lcd_dev.height; ++i)
    {
        for(j = 0; j < lcd_dev.width;++j)
        {
            p[i][j] = color;
        }
    }
 
}

void init_lcd(void)
{
    init_lcd_io();
    reset_lcd();
    init_lcd_clk();
    

    lcd_dev.width = 800;
    lcd_dev.height = 480;
    lcd_dev.hspw = 48;
    lcd_dev.hbp = 88;
    lcd_dev.hfp = 40;
    lcd_dev.vspw = 3;
    lcd_dev.vbp = 32;
    lcd_dev.vfp = 13;
    lcd_dev.pix_size = 4;
    lcd_dev.frame_buffer = LCD_FRAMEBUFFER_ADDR;
    lcd_dev.fore_color = 0x0000FF00;
    lcd_dev.back_color = 0x00FFFFFF;

    LCDIF->CTRL |= (1 << 19) | (1 << 17) | (3 << 10) | (3 << 8) | (1 << 5);
   
    
    LCDIF->CTRL1 = (7 << 16);
    LCDIF->TRANSFER_COUNT = (lcd_dev.height << 16) | (lcd_dev.width);
    LCDIF->CUR_BUF = lcd_dev.frame_buffer;
    LCDIF->NEXT_BUF = lcd_dev.frame_buffer;
    LCDIF->VDCTRL0 = (1 << 28) | (1 << 24) | (1 << 21) | (1 << 20) | (lcd_dev.vspw << 0);
    LCDIF->VDCTRL1 = lcd_dev.height + lcd_dev.vspw + lcd_dev.vfp + lcd_dev.vbp;
    LCDIF->VDCTRL2 = (lcd_dev.hspw << 18) | (lcd_dev.hspw + lcd_dev.hfp + lcd_dev.hbp + lcd_dev.width);
    LCDIF->VDCTRL3 = ((lcd_dev.hspw + lcd_dev.hbp) << 16) | (lcd_dev.vspw + lcd_dev.vbp);
    LCDIF->VDCTRL4 = (1 << 18) | (lcd_dev.width);
    LCDIF->CTRL |= (1 << 0);
    delayms(20);
}

void lcd_drawpoint(int x, int y, unsigned int color)
{
    unsigned int *p = (unsigned int *)lcd_dev.frame_buffer;
    if(x >= lcd_dev.width || y >= lcd_dev.height)
    {
        return;
    }
    *(p + lcd_dev.width * y + x) = color;
}
#ifndef __LCD_H__
#define __LCD_H__
extern void init_lcd(void);
extern void init_lcd_clk(void);
extern void init_lcd_io(void);
extern void reset_lcd(void);
extern void lcd_clear(unsigned int color);
extern void lcd_drawpoint(int x, int y, unsigned int color);
struct tftlcd_t
{
    unsigned short height;
    unsigned short width;
    unsigned short pix_size;    //每像素所占字节数

    unsigned short vspw;        //帧同步信号宽度
    unsigned short vbp;         //帧同步信号后肩
    unsigned short vfp;         //帧同步信号前肩

    unsigned short hspw;        //行同步信号宽度
    unsigned short hbp;         //行同步信号后肩
    unsigned short hfp;         //行同步信号前肩

    unsigned int frame_buffer;
    unsigned int fore_color;
    unsigned int back_color;

};
extern struct tftlcd_t lcd_dev;
#define LCD_FRAMEBUFFER_ADDR        (0x89000000)
#define COLOR(r, g, b) ((r << 16) | (g << 8) | (b << 0))
#endif
#include "beep.h"
#include "led.h"
#include "key.h"
#include "core_ca7.h"
#include "interrupt.h"
#include "clock.h"
#include "epit.h"
#include "gpt.h"
#include "delay.h"
#include "uart.h"
#include "stdio.h"
#include "i2c.h"
#include "string.h"
#include "lm75.h"
#include "adc.h"
#include "adxl345.h"
#include "spi.h"
#include "lcd.h"
#include "framebuffer.h"
int main(void)
{
    init_clock();
    system_interrupt_init();
    init_beep();
    init_led();
//    init_key();
//    init_epit1();
    init_gpt1();
    init_uart1();
    init_i2c1();
    init_adc1_channle1();
    init_spi3();
    init_adxl345();
    init_lcd();
    lcd_clear(0x00FFFFFF);
    // lcd_draw_rectangle(100,100,300,400);
  //  lcd_fill(100,100,300,400,0x00FF0000);
    // char *s = "Hello World!";
    // lcd_show_string(400,240,12 * strlen(s),24,24,s);
    // s = "China";
    // lcd_show_string(100,100,12 * strlen(s),24,24,s);
    while(1)
    {
        // delayms(500);
        // lcd_clear(0x00FF0000);
        // delayms(500);
        // lcd_clear(0x0000FF00);
        // delayms(500);
        // lcd_clear(0x000000FF);
        char buffer[32];
        float f;
        f = lm75_get_temperature();
        int k = f * 10;
        int m = k / 10;
        int n = k % 10;
        sprintf(buffer,"%d.%d          ",m,n);
        lcd_show_string(100,100,16 * strlen(buffer),32,32,buffer);
    }

    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值