目录
一.背景
像素时钟
二.实现
1.初始化
2.配置时钟
CCM_ANALOG->PLL_VIDEO
LCDIF->CTRL
CTRL1
TRANSEFR_COUNT
CUR_BUF,NEXT_BUF
VDCTRL0
VDCTRL1
VDCTRL2
VDCTRL3
VDCTRL4
一.背景
屏幕分辨率:横向像素点个数*纵向像素点个数(800 * 480)1920 * 10801080P2K 2560 * 1440
屏幕尺寸:inch "英寸,左上角到右下角之间的距离1" = 2.54cm6.22" 4.3"1920 * 1080分辨率相同,尺寸越小,图像显示效果越好尺寸相同,分辨率越高,图像显示效果越好
像素点颜色:RGB888:每个像素点占3个字节8bit面板1670万色RGB565:每个像素点占2个字节ARGB:每个像素点占4个字节RGB101010: 10bit面板10亿色 贵 视频剪辑8抖10bit
显存:专门存放图像数据的内存空间800 * 480 * 4 = 1.5M(512MB 0x80000000 - 0xA0000000)显存:0x89000000
像素时钟
二.实现
1.初始化
// 1. 设置lcd引脚复用功能 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_CLK_LCDIF_CLK, 0); // pclk IOMUXC_SetPinMux(IOMUXC_LCD_HSYNC_LCDIF_HSYNC, 0); // HSYNC IOMUXC_SetPinMux(IOMUXC_LCD_VSYNC_LCDIF_VSYNC, 0); // VSYNC IOMUXC_SetPinMux(IOMUXC_LCD_ENABLE_LCDIF_ENABLE, 0); // DE IOMUXC_SetPinMux(IOMUXC_GPIO1_IO08_GPIO1_IO08, 0); // blt_pwm为什么硬件叫blt_pwm,软件却配成 GPIO?
之所以注释写着blt_pwm,是因为在硬件原理图上,这个引脚确实连接到了 LCD 的背光控制电路。硬件工程师给它起名叫“背光 PWM”,是因为它最终要控制背光。但是,控制背光有两种常见做法:
硬件 PWM 控制(ALT0 模式):把引脚配置成
PWM1_OUT,让芯片内部的 PWM 模块输出连续方波,通过改变占空比来无级调节背光亮度(比如从 10% 调到 100%)。软件 GPIO 控制(ALT5 模式,即老师的做法):把引脚配置成普通 GPIO。软件只能输出
0(低电平,背光灭)或1(高电平,背光亮)。如果只是需要开灯/关灯,配置成 GPIO 最简单。
如果想调节亮度但不想用硬件 PWM,可以在 Linux 驱动里写个定时器,让 GPIO 不断拉高拉低,模拟 PWM 波形(软件模拟 PWM)。
// 2. 设置lcd引脚电气属性 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_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);一共设置24个数据引脚(R,G,B各8个)
// 3. 配置pwm引脚 GPIO_Type_t t = { .direction = gpio_output, .default_value = 1 }; gpio_init(GPIO1, 8, &t); gpio_set_value(GPIO1, 8, 1);2.配置时钟
要配置31MHZ时钟, 先通过倍频让PLL5输出1008MHZ,再通过两个预分频器除以4和8输出为31.5MHZ
可设置NUM为0,DENOM为1,DIV_SELECT为42即可将24MHZ倍频为1008MHZ
预分频器默认为1分频即可。
再通过CSDR2寄存器选通PLL5时钟且配置第一个分频因子为4,通过CBCMR配置第二个分频因子为8。
CCM_ANALOG->PLL_VIDEO
是 i.MX6UL 芯片内部视频锁相环(Video PLL)的主控制寄存器
// lcd时钟初始化 void lcd_clock_init(void) { // BYPASS CCM_ANALOG->PLL_VIDEO |= (1 << 16); // Powerdown PLL CCM_ANALOG->PLL_VIDEO |= (1 << 12);//写1关闭 CCM_ANALOG->PLL_VIDEO_NUM = 0; CCM_ANALOG->PLL_VIDEO_DENOM = 1; // 设置42倍频 CCM_ANALOG->PLL_VIDEO &= ~(0x7F << 0); CCM_ANALOG->PLL_VIDEO |= (42 << 0); // 设置1分频 CCM_ANALOG->PLL_VIDEO &= ~(3 << 19); CCM_ANALOG->PLL_VIDEO |= (1 << 20); // poweron PLL CCM_ANALOG->PLL_VIDEO &= ~(1 << 12);//写0开启 // enable PLL5 output CCM_ANALOG->PLL_VIDEO |= (1 << 13); // 轮询等待时钟频率稳定 while ((CCM_ANALOG->PLL_VIDEO & (1 << 31)) == 0); // 切回 PLL5 CCM_ANALOG->PLL_VIDEO &= ~(1 << 16); // 选通门 pll5 + 4分频 unsigned int t = CCM->CSCDR2; t &= ~(7 << 15); t |= (1 << 16); t &= ~(7 << 12); t |= (3 << 12); t &= ~(7 << 9); CCM->CSCDR2 = t; // 8分频 CCM->CBCMR |= (7 << 23); }lcd寄存器
LCDIF->CTRL
CTRL1
TRANSEFR_COUNT
CUR_BUF,NEXT_BUF
存放显存地址
VDCTRL0
VDCTRL1
VDCTRL2
VDCTRL3
VDCTRL4
18位,让VSYNC/HSYNC/DOTCLK等工作,
// lcd参数配置 lcd_dev.width = 800; lcd_dev.height = 480; lcd_dev.hspw = 48; lcd_dev.hfp = 40; lcd_dev.hbp = 88; lcd_dev.vspw = 3; lcd_dev.vfp = 13; lcd_dev.vbp = 32; lcd_dev.pix_size = 4; lcd_dev.frame_buffer = LCD_FRAMEBUF_ADDR; lcd_dev.fore_color = 0x00FF0000; lcd_dev.back_color = 0x00FFFFFF; // LCD寄存器 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 << 0); 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.vfp + lcd_dev.vbp + lcd_dev.vspw); LCDIF->VDCTRL2 = (lcd_dev.hspw << 18) | (lcd_dev.width + lcd_dev.hfp + lcd_dev.hbp + lcd_dev.hspw); LCDIF->VDCTRL3 = ((lcd_dev.hspw + lcd_dev.hbp) << 16) | ((lcd_dev.vspw + lcd_dev.vbp) << 0); LCDIF->VDCTRL4 |= (1 << 18) | (lcd_dev.width << 0); LCDIF->CTRL |= (1 << 0);//最后使能注意,CTRL1位要用=,因为要置0x0111,即7,而16-19位默认为0x1111,所以若使用|=则19位仍为1.