之前学习了I2C、AT24C02、LM75温度传感器等外设今天完成IMX6ULL片上eLCDIF驱动RGB LCD屏幕点亮4.7寸800*480屏幕在屏幕上打点、画线实时显示LM75采集的温度。本次是RGB并行同步LCD裸机驱动不使用Linux内核驱动直接操作寄存器。一、RGB LCD基础原理1. 屏幕基础参数本次使用4.7寸LCD分辨率 800×480像素格式ARGB8888每个像素4字节R/G/B/A各8bit范围0~255色域还有RGB5652字节占用内存更小本次实验用32bit色刷新率由PCLK像素时钟 前后廊时序共同决定。2. 硬件连接eLCDIF与LCDIMX6ULL内置eLCDIFLCD控制器连接LCD的信号分为两大类24根并行数据线 LCD_DATA0~LCD_DATA23R8bit G8bit B8bit传输像素颜色4根控制信号线PCLK像素时钟每来1个PCLK传输1个像素DE数据使能高电平代表当前数据有效HSYNC行同步信号一行扫描结束触发行同步VSYNC场同步信号一整帧画面扫描完成触发场同步。数据通过DMA直接从内存帧缓存区搬运到eLCDIF不需要CPU逐个搬运像素减轻CPU负担。帧缓存地址我们设_FRAME_RAM_ADDR_。3. 行场时序H/V 同步 前后廊这是RGB LCD最核心的知识点一张完整图像扫描不是直接800×480每一行、每一帧都包含同步脉冲 前廊FP 有效像素 后廊BP行时序 HSYNCHSPW行同步脉冲宽度HFP行前廊行同步结束到有效像素开始的间隔HBP行后廊有效像素结束到下一次行同步。一行总PCLK HSPW HFP 800(有效宽度) HBP场时序 VSYNCVSPW场同步脉冲宽度VFP场前廊VBP场后廊。一帧总行数 VSPW VFP 480(有效高度) VBP只有时序参数和屏幕手册严格匹配屏幕才能正常点亮时序不对会出现花屏、黑屏、闪烁。4. 实验整体流程LCD引脚IOMUX配置复用为eLCDIF功能配置PAD电气属性配置LCD像素时钟PCLKPLL5视频PLL分频得到目标PCLKLCDIF控制器软件复位填充时序参数HSPW/HFP/HBP、VSPW/VFP/VBP、分辨率设置帧缓存地址告诉eLCDIF从内存哪个地址读取像素使能eLCDIF开始输出图像封装底层API打点、清屏、画水平线应用层读取LM75温度LCD屏幕实时刷新温度字符串。二、完整源码解析lcd.h 对应的 lcd.c 完整代码#includelcd.h#includeMCIMX6Y2.h#includefsl_iomuxc.h#includegpt.h#includestdio.hvoidlcd_pad_init(void){//复用功能全部引脚设置为LCDIF功能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_ENABLE_LCDIF_ENABLE,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_GPIO1_IO08_GPIO1_IO08,0);//电气特性配置所有PAD配置0xB9IOMUXC_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_ENABLE_LCDIF_ENABLE,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_GPIO1_IO08_GPIO1_IO08,0xB9);//背光引脚GPIO1_IO08设置为输出高电平点亮背光GPIO1-GDIR|(18);GPIO1-DR|(18);}voidlcd_clk_init(void){//PLL5视频PLL配置先进入旁路断电CCM_ANALOG-PLL_VIDEO|(116);//BYPASS1CCM_ANALOG-PLL_VIDEO|(112);//POWERDOWN1//PLL5计算公式744MHZ 24M *31CCM_ANALOG-PLL_VIDEO_NUM0;CCM_ANALOG-PLL_VIDEO_DENOM1;unsignedintrCCM_ANALOG-PLL_VIDEO;r~(319);r|(219);r~(0x7F0);r|(310);CCM_ANALOG-PLL_VIDEOr;//上电等待PLL锁频CCM_ANALOG-PLL_VIDEO~(112);CCM_ANALOG-PLL_VIDEO|(113);while((CCM_ANALOG-PLL_VIDEO(131))0){printf(Not locked \n);};printf(Is locked \n);//关闭旁路使用PLL5输出CCM_ANALOG-PLL_VIDEO~(116);//BYPASS0//配置CCM时钟选择PLL5作为LCDIF时钟源CCM_ANALOG-MISC2~(330);rCCM-CSCDR2;//CSCDR2[17:15] LCDIF1_PRE_CLK_SEL 010选择PLL5r~(715);r|(215);//预分频PREDr~(712);r|(312);//CSCDR2[LCDIF1_CLK_SEL]r~(79);CCM-CSCDR2r;//后分频PODFCCM-CBCMR~(723);CCM-CBCMR|(523);//打印时钟链路信息调试查看分频unsignedintcscdr2CCM-CSCDR2;unsignedintcbcmrCCM-CBCMR;printf(PRE_CLK_SEL%d, PRED%d(div-%d), CLK_SEL%d\n,(cscdr215)7,(cscdr212)7,((cscdr212)7)1,(cscdr29)7);printf(PODF%d(div-%d)\n,(cbcmr23)7,((cbcmr23)7)1);}voidlcd_reset(void){//LCDIF控制器软件复位LCDIF-CTRL|(131);delay_ms(20);LCDIF-CTRL~(131);LCDIF-CTRL~(130);}structtftlcd_tlcd_dev;voidlcd_init(void){//第一步引脚PAD初始化lcd_pad_init();//第二步配置LCD像素时钟PCLKlcd_clk_init();//填充LCD设备结构体屏幕时序参数lcd_dev.width800;lcd_dev.height480;lcd_dev.pix_size4;//ARGB8888 4字节/像素lcd_dev.vspw3;lcd_dev.vfp13;lcd_dev.vbp32;lcd_dev.hspw48;lcd_dev.hfp40;lcd_dev.hbp88;lcd_dev.frame_addr_FRAME_RAM_ADDR_;//帧缓存起始地址lcd_dev.fore_color0x00FF0000;lcd_dev.back_color0x00FFFFFF;//复位LCDIFlcd_reset();delay_ms(50);//配置eLCDIF控制寄存器LCDIF-CTRL|(119)|(117)|(310)|(38)|(15);LCDIF-CTRL1(0x0716);LCDIF-TRANSFER_COUNT(lcd_dev.height16)|(lcd_dev.width0);LCDIF-CUR_BUFlcd_dev.frame_addr;LCDIF-NEXT_BUFlcd_dev.frame_addr;//行场时序寄存器配置LCDIF-VDCTRL0(128)|(124)|(121)|(120)|(lcd_dev.vspw);LCDIF-VDCTRL1(lcd_dev.heightlcd_dev.vspwlcd_dev.vfplcd_dev.vbp);LCDIF-VDCTRL2(lcd_dev.hspw18)|((lcd_dev.widthlcd_dev.hspwlcd_dev.hfplcd_dev.hbp)0);LCDIF-VDCTRL3((lcd_dev.hspwlcd_dev.hbp)16)|((lcd_dev.vspwlcd_dev.vbp)0);LCDIF-VDCTRL4(118)|(lcd_dev.width0);//使能eLCDIF开始输出画面LCDIF-CTRL|(10);delay_ms(50);}//清屏函数遍历所有像素填充颜色voidscreen_clear(unsignedintcolor){inti0,j0;for(i0;ilcd_dev.height;i){for(j0;jlcd_dev.width;j){lcd_drawpoint(j,i,color);}}}//底层画点函数向帧缓存对应位置写入颜色voidlcd_drawpoint(intx,inty,unsignedintcolor){//边界检查防止越界访问if((xlcd_dev.width||x0)||(ylcd_dev.height||y0)){return;}unsignedint(*pF)[800](unsignedint(*)[800])lcd_dev.frame_addr;pF[y][x]color;}//画水平线APIvoidlcd_h_drawline(intx,inty,unsignedintlength,unsignedintcolor){inti0;for(ix;ixlength;i){lcd_drawpoint(i,y,color);}}main.c 主函数代码#includeMCIMX6Y2.h#includefsl_iomuxc.h#includeled.h#includebeep.h#includekey.h#includeinterrupt.h#includeclock.h#includeepit.h#includegpt.h#includeuart.h#includestdio.h#includestring.h#includei2c.h#includeat24c02.h#includelm75.h#includelcd.h#includeframebuffer.hintmain(void){system_interrupt_init();clock_init();led_init();beep_init();key_init();//epit1_init();gpt1_init();uart1_init();at24c02_init();floattemp0;charbuffer[128]{0};lcd_init();screen_clear(0x00FFFFFF);//清屏填充白色背景//lcd_h_drawline(100, 100, 200, 0x00FF0000);//extern void lcd_show_string(unsigned short x,unsigned short y, unsigned short width, unsigned short height, unsigned char size,char *p);//lcd_show_string(200,200, 300, 32, 32, hello world!);while(1){tempget_temp();//读取LM75温度intm0,n0;m(int)(temp*1000)/1000;n(int)(temp*1000)%1000;//把浮点数温度转为字符串sprintf(buffer,temp %d.%d\n,m,n);//LCD屏幕显示字符串lcd_show_string(200,200,300,32,32,buffer);delay_ms(1000);}return0;}三、代码模块讲解lcd_pad_initIOMUX引脚复用。24根RGB数据线 4根控制信号线全部复用为LCDIF外设功能PAD电气属性统一配置0xB9GPIO1_IO08作为LCD背光GPIO输出高电平点亮背光。这里背光是GPIO控制只有亮灭两种状态后面PWM实验我们就改成PWM驱动背光实现亮度可调。lcd_clk_init配置PLL5视频锁相环分频产生LCD像素时钟PCLK。等待PLL锁频成功配置CCM选择时钟源与分频系数串口打印分频信息用于调试。lcd_reseteLCDIF控制器软件复位上电必须复位保证寄存器处于稳定状态。lcd_init填充tftlcd_t结构体存放屏幕分辨率、前后廊时序、帧缓存地址、前景背景色配置eLCDIF的VDCTRL系列寄存器写入行场同步时序设置CUR_BUF当前帧缓存地址eLCDIF会从这块内存读取像素使能LCDIF屏幕开始输出图像。屏幕绘图APIlcd_drawpoint最底层接口根据(x,y)坐标计算帧缓存内存地址写入颜色值增加边界判断防止越界访问内存screen_clear双重循环遍历全部像素填充统一颜色清屏lcd_h_drawline循环调用打点画一条水平直线。main函数初始化顺序中断 → 系统时钟 → LED、蜂鸣器、按键 → GPT → UART → AT24C02 → LCD。清屏为白色主循环读取LM75温度sprintf将浮点温度转为字符串调用lcd_show_string在屏幕上打印温度1秒刷新一次。四、实验现象开发板上电LCD背光点亮屏幕清为纯白色屏幕(200,200)位置每秒刷新一次LM75采集的温度temp xx.xxx取消注释lcd_h_drawline可以在屏幕画红色水平线取消注释lcd_show_string可以直接在屏幕打印hello world。总结时序参数必须严格匹配屏幕手册HBP/HFP/VBP/VFP、同步脉冲设置错误直接黑屏或者花屏。帧缓存地址不能随便设置要确保是空闲的内存区域不能覆盖程序代码、栈空间。PLL锁频必须等待PLL的lock标志位不等待直接使用时钟PCLK不稳定屏幕闪烁、乱码。引脚复用24根RGB数据线、HSYNC/VSYNC/PCLK/DE任何一根引脚复用错屏幕都会异常。浮点数LCD打印LCD不支持printf用sprintf把数值转字符串调用字库函数lcd_show_string显示。背光GPIO背光引脚忘记设置输出高电平屏幕背光不亮屏幕漆黑但eLCDIF其实已经正常输出图像。