在嵌入式系统中,LCD 屏幕是常见的人机交互设备之一。本文将详细介绍如何使用 I.MX6U 处理器的 eLCDIF(Enhanced LCD Interface) 接口驱动 ALIENTEK ATK7016 屏幕。我们将从硬件初始化、时钟配置、接口设置到 API 函数编写,逐步讲解 LCD 驱动的开发过程。


1. ATK7016 屏幕简介

ATK7016 是一款 7 英寸的 RGB LCD 屏幕,分辨率为 1024×600,支持 RGB888 像素格式。它通过 40-pin FPC 接口 与 I.MX6U 开发板连接,使用 eLCDIF 接口 进行数据传输。

ATK7016 关键参数

参数说明
分辨率1024×600屏幕的像素数量
像素格式RGB888每个像素点由 24 位表示
接口类型RGB 接口支持 DE 模式和 HV 模式
时钟频率51.2 MHz像素时钟频率
时序参数见下文包括 HSPW、HBP、HFP 等

2. 硬件连接

ATK7016 通过 40-pin FPC 接口与 I.MX6U 开发板连接。以下是主要的信号线:

信号线描述I.MX6U 引脚
R[7:0]红色数据线LCD_DATA0~7
G[7:0]绿色数据线LCD_DATA8~15
B[7:0]蓝色数据线LCD_DATA16~23
DE数据使能信号LCD_DE
VSYNC垂直同步信号LCD_VSYNC
HSYNC水平同步信号LCD_HSYNC
PCLK像素时钟信号LCD_CLK

3. 驱动开发步骤

3.1 初始化 LCD 所使用的 IO

首先,需要将 I.MX6U 的引脚复用为 eLCDIF 接口功能。以下是初始化代码示例:

void lcd_io_init(void) {
    // 配置 LCD 数据线 (RGB888)
    IOMUXC_SetPinMux(IOMUXC_GPIO1_IO04_LCD_DATA00, 0); // R0
    IOMUXC_SetPinMux(IOMUXC_GPIO1_IO05_LCD_DATA01, 0); // R1
    // ... 配置其他数据线

    // 配置控制信号线
    IOMUXC_SetPinMux(IOMUXC_GPIO1_IO08_LCD_HSYNC, 0);  // HSYNC
    IOMUXC_SetPinMux(IOMUXC_GPIO1_IO09_LCD_VSYNC, 0);  // VSYNC
    IOMUXC_SetPinMux(IOMUXC_GPIO1_IO10_LCD_DE, 0);     // DE
    IOMUXC_SetPinMux(IOMUXC_GPIO1_IO11_LCD_CLK, 0);    // PCLK

    // 设置引脚电气属性
    IOMUXC_SetPinConfig(IOMUXC_GPIO1_IO04_LCD_DATA00, 0x10B0); // R0
    IOMUXC_SetPinConfig(IOMUXC_GPIO1_IO05_LCD_DATA01, 0x10B0); // R1
    // ... 配置其他引脚
}

3.2 设置 LCD 的像素时钟

ATK7016 的像素时钟频率为 51.2 MHz。我们需要配置 I.MX6U 的 CCM(Clock Control Module) 模块,生成所需的时钟信号。

void lcd_clock_init(void) {
    // 设置 PLL5 为视频时钟源
    CCM_ANALOG->PLL_VIDEO = (1 << 13) | (27 << 0); // PLL5 = 24MHz * (27 + 1) = 672MHz

    // 配置 LCDIF 时钟分频器
    CCM->CSCDR2 = (CCM->CSCDR2 & ~(0x7 << 15)) | (4 << 15); // LCDIF_PRED = 4
    CCM->CSCDR2 = (CCM->CSCDR2 & ~(0x7 << 12)) | (2 << 12); // LCDIF_PODF = 2

    // 启用 LCDIF 时钟
    CCM->CCGR3 |= (3 << 16); // CG15 = 3, 启用 LCDIF 时钟
}

3.3 配置 eLCDIF 接口

eLCDIF 接口的配置包括设置控制寄存器、时序参数和帧缓冲区。以下是关键寄存器的配置:

3.3.1 控制寄存器(CTRL 和 CTRL1)

  • CTRL:配置数据宽度、像素格式等。
  • CTRL1:配置 DE 模式、VSYNC 极性等。
void lcdif_config(void) {
    // 配置 CTRL 寄存器
    LCDIF->CTRL = (0x1F << 11) | (0x3 << 8) | (0x1 << 5); // 24-bit 数据宽度,RGB888 格式

    // 配置 CTRL1 寄存器
    LCDIF->CTRL1 = (1 << 0); // DE 模式
}

3.3.2 时序参数寄存器(VDCTRL0~4)

  • VDCTRL0:配置 VSYNC 和 HSYNC 的脉冲宽度。
  • VDCTRL1:配置垂直和水平的前后肩时间。
  • VDCTRL2:配置有效显示区域的大小。
void lcdif_timing_config(void) {
    // 配置 VDCTRL0
    LCDIF->VDCTRL0 = (3 << 29) | (20 << 18) | (1 << 17); // VSPW=3, HSPW=20

    // 配置 VDCTRL1
    LCDIF->VDCTRL1 = (20 << 16) | (140 << 0); // VBP=20, HBP=140

    // 配置 VDCTRL2
    LCDIF->VDCTRL2 = (600 << 16) | (1024 << 0); // LINE=600, HOZVAL=1024

    // 配置 VDCTRL3
    LCDIF->VDCTRL3 = (12 << 16) | (160 << 0); // VFP=12, HFP=160
}

3.3.3 帧缓冲区寄存器(CUR_BUF 和 NEXT_BUF)

  • CUR_BUF:当前帧缓冲区的地址。
  • NEXT_BUF:下一帧缓冲区的地址。
void lcdif_framebuffer_config(uint32_t fb_address) {
    LCDIF->CUR_BUF = fb_address;    // 设置当前帧缓冲区
    LCDIF->NEXT_BUF = fb_address;   // 设置下一帧缓冲区
}

3.4 编写 API 函数

为了在 LCD 上显示内容,我们需要编写一些基本的 API 函数,例如画点、画线、画圆和字符串显示函数。

3.4.1 画点函数

void lcd_draw_pixel(uint16_t x, uint16_t y, uint32_t color) {
    uint32_t *fb = (uint32_t *)FRAME_BUFFER;
    fb[y * 1024 + x] = color; // 1024 是屏幕的宽度
}

3.4.2 画线函数

void lcd_draw_line(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint32_t color) {
    int dx = abs(x2 - x1);
    int dy = abs(y2 - y1);
    int sx = (x1 < x2) ? 1 : -1;
    int sy = (y1 < y2) ? 1 : -1;
    int err = dx - dy;

    while (1) {
        lcd_draw_pixel(x1, y1, color);
        if (x1 == x2 && y1 == y2) break;
        int e2 = 2 * err;
        if (e2 > -dy) { err -= dy; x1 += sx; }
        if (e2 < dx)  { err += dx; y1 += sy; }
    }
}

3.4.3 字符串显示函数

void lcd_draw_string(uint16_t x, uint16_t y, const char *str, uint32_t color) {
    while (*str) {
        lcd_draw_char(x, y, *str, color);
        x += 8; // 假设每个字符宽度为 8 像素
        str++;
    }
}

4. 总结

通过以上步骤,我们成功使用 I.MX6U 的 eLCDIF 接口驱动了 ATK7016 屏幕。关键步骤包括:

  1. 初始化 IO:将引脚复用为 eLCDIF 功能。
  2. 设置像素时钟:配置 CCM 模块生成 51.2 MHz 的时钟信号。
  3. 配置 eLCDIF 接口:设置控制寄存器、时序参数和帧缓冲区。
  4. 编写 API 函数:实现画点、画线、字符串显示等功能。

希望本文能帮助你理解 LCD 驱动的开发过程。如果你有更多问题,欢迎继续提问!