☰
I.MX6U 裸机开发22. LCDID读取及IO初始化
2026/9/30 1:44:51 网站建设 项目流程

I.MX6U 裸机开发22. LCDID读取及IO初始化

    • 一、实验内容
      • 1. 读屏 ID原理
        • 1.1 硬件上 ID 编码规则
        • 1.2 模拟开关作用
        • 1.3 为什么是 DATA23 / 15 / 7?为什么是 GPIO3 的 28 / 20 / 12?
        • 1.4 电平转成 `0x4384`
        • 1.5 读取步骤(对应代码)
      • 2. 支持的屏幕 ID
      • 3. 程序流程
      • 4. 预期串口输出
    • 二、调试方式(与前课不同)
      • 虚拟机 Ubuntu 改成 WSL
      • 1. 编译(WSL)
      • 2. USB-OTG 下载(本课主用,Windows)
      • 3. 串口查看日志
      • 4. (可选)仍用 SD 卡
      • 5. 故障排查
    • 三、读取 ID 参考代码

本课在板子上读取正点原子 LCD 的型号 ID,并通过串口printf打印结果。
调试方式与前面课程不同:本课将使用 Windows 下 USB-OTG(HID SDP)下载到内存运行,不再依赖 SD 卡烧录。

名词速览(本课首次集中用到)

  • USB-OTG(On-The-Go):板子上那路可当「从设备」连电脑的 USB;本课用它做下载,不是串口那根线。
  • HID(Human Interface Device):一种 USB 设备类,常见于键鼠;i.MX 下载模式也伪装成 HID,方便免装专用驱动。
  • SDP(Serial Download Protocol):NXP 规定的「串行下载协议」,规定主机如何把镜像写进芯片内存并跳转执行。
  • DDR:板载大容量动态内存,程序下载后在这里运行;掉电/按 RESET 后内容丢失(USB 下载模式尤其如此)。

⭐ 本次验证目标:USB 下载启动功能

本课核心目的是验证USB OTG 下载 → DDR 内存运行这条链路是否可用,LCD ID 读取只是验证时的观察输出。验证成功的判定标准:

  1. 拨码0100(USB 下载模式)后,设备管理器出现SE Blank 6ULL(VID_15A2 & PID_0080)
    • SE Blank 6ULL:芯片处于「空白片 / 串行下载」状态时,Windows 看到的产品名。
    • VID / PID:USB 厂商号 / 产品号;15A2是 Freescale/NXP,0080对应 i.MX6ULL 下载模式。
  2. dl.py三步全部 OK:DCD_WRITE→WRITE_FILE→JUMP_ADDRESS(见下文调试节解释)
  3. 程序在 DDR 中跑起来:串口打印LCD ID = 0x4384(ATK4384),LED0 每 500ms 闪烁

三条都满足,即说明 USB 下载启动功能验证通过;之后其他课程也可复用此方式快速调试,无需反复烧 SD 卡。


一、实验内容

1. 读屏 ID原理

先澄清一点:本课读的不是液晶控制器芯片内部的「厂商 ID 寄存器」,也没有对屏幕发 RGB/SPI 读命令。
正点原子的做法是:在屏幕模组背面用M0 / M1 / M2 三个电阻焊点预先焊成不同的高低电平组合,相当于给每款屏打了一个硬件「型号跳线」;MCU 上电后用 GPIO 把这 3 位读出来,再查表映射成软件里的ATK4384这类 ID。

不同尺寸/分辨率的屏,LCDIF 时序、像素时钟、分辨率都不一样。若写死一套参数,换一块屏就会花屏或点不亮。所以要先识别「接的是哪一块」,后面初始化才选对参数。

1.1 硬件上 ID 编码规则

在开发板屏幕背面,可以看到丝印和 M0、M1、M2 的电阻焊点:

每一款屏出厂时,M0~M2 上会选择性地焊接上拉或下拉电阻(有的脚接高、有的脚接低),形成 3 bit 组合。
MCU 把对应引脚配成输入后,读到的就是这 3 个电平:

M2M1M0idx(软件拼出来的数)对应型号(本工程约定)
0000ATK4342
0011ATK7084
0102ATK7016
1004ATK4384(本板实测)
1015ATK1018

所以「能读 ID」的本质是:读 3 个 GPIO 的高低电平,不是访问某个神秘的内存地址。

1.2 模拟开关作用

RGB 屏正常工作时,LCD_DATA0~23要传像素数据。若 ID 电阻网络一直挂在 DATA 线上,会干扰显示。
正点原子的做法是:ID 信号走LCD_DATAxS一侧,真正进 MCU 的是LCD_DATAx;中间用模拟开关(SGM)决定是否把「屏上的 M0~M2」接到 MCU。

控制脚接到LCD_VSYNC(原理图里的 SGM CTRL):

  • 读 ID 前:把 VSYNC 配成 GPIO输出高→ 开关导通 → MCU 才能采到 M0~M2
  • 平时显示:开关断开(或不走这条读 ID 路径),DATA 线专心传像素

一句话:VSYNC 在这里临时当「开关使能」用,不是在读场同步波形。

1.3 为什么是 DATA23 / 15 / 7?为什么是 GPIO3 的 28 / 20 / 12?

这些不是软件随便选的「地址」,而是原理图已经焊死的连线,再对照芯片手册的引脚复用表(IOMUXC)查出来的:

屏上丝印接到 MCU 的 LCD 信号RGB 含义IOMUXC 宏(切到 GPIO)实际 GPIO
M0LCD_DATA23R7(红最高位)IOMUXC_LCD_DATA23_GPIO3_IO28GPIO3_IO28
M1LCD_DATA15G7(绿最高位)IOMUXC_LCD_DATA15_GPIO3_IO20GPIO3_IO20
M2LCD_DATA07B7(蓝最高位)IOMUXC_LCD_DATA07_GPIO3_IO12GPIO3_IO12
开关控制LCD_VSYNC行同步IOMUXC_LCD_VSYNC_GPIO3_IO03GPIO3_IO03

为何挑 R7/G7/B7(DATA23/15/7)这三根?

  1. 硬件设计如此:正点原子把 M0/M1/M2 复用到 RGB 总线最高位这三根线上,读完 ID 后同一组脚仍可改回 LCDIF 功能去显示。
  2. 软件侧只能跟着原理图走:你在代码里写gpio_pinread(GPIO3, 28),是因为手册写明「焊盘 LCD_DATA23 复用到 GPIO 时 = GPIO3 的 bit28」;换成别的脚号,读到的就不是 M0。
  3. 「GPIO3_IO28」不是内存地址:它表示「GPIO 控制器 3 的第 28 号引脚」。底层会去操作该控制器的 DR/PSR 等寄存器,但你选 28/20/12 的依据是引脚编号,不是随便填的十六进制外设基址。

代码里两步缺一不可:

  1. IOMUXC_SetPinMux(..._GPIO3_IOxx, 0):把焊盘从 LCDIF 功能切到 GPIO
  2. gpio_init+gpio_pinread:按输入方式读电平

若只配 GPIO 方向、不改 IOMUXC,脚仍接在 LCD 控制器上,读 ID 会失败。

1.4 电平转成0x4384
idx = M0 | (M1 << 1) | (M2 << 2)

即:M0 为 bit0,M1 为 bit1,M2 为 bit2。例如本板 ATK4384:

  • 读到 M0=0, M1=0, M2=1 →idx = 0b100 = 4
  • if (idx == 4) return ATK4384; /* 0x4384 */

0x4384本身只是正点原子/本工程约定的型号常量,方便后面switch (lcdid)选分辨率;硬件上真正有信息量的是那 3 bit 的idx。

1.5 读取步骤(对应代码)
  1. LCD_VSYNC→ GPIO 输出高,打开模拟开关
  2. LCD_DATA23/15/7→ GPIO 输入(必要时配上下拉,与电阻网络匹配)
  3. 读 GPIO3_IO28 / 20 / 12,拼出idx
  4. 查表返回ATKxxxx

配置引脚时会用到IOMUXC(IO Multiplexer Controller,引脚复用控制器):同一焊盘可切到 LCD 功能或 GPIO 功能,读 ID 时要切到 GPIO。

2. 支持的屏幕 ID

idxID型号规格
00x4342ATK43424.3 寸 480×272
10x7084ATK70847 寸 800×480
20x7016ATK70167 寸 1024×600
40x4384ATK43844.3 寸 800×480
50x1018ATK101810.1 寸 1280×800

表里的ID是软件约定的型号编码(不是面板内部寄存器里读出来的厂商 ID);后续初始化 LCD 时序、分辨率时,就靠这个值选参数。

3. 程序流程

上电 / USB 下载跳转 ├─ bsp_int_init() ├─ imx6u_clkinit() / clk_enable() ├─ led_init() / beep_init() / gpt1_delay_init() ├─ uart_init(UART1) # 115200 ├─ lcd_init() # 读 ID 并 printf └─ while(1) LED0 每 500ms 翻转

关键代码:

  • board/imx6u/bsp_lcd.c—lcd_read_id()/lcd_init()
  • board/imx6u/bsp_uart.c— UART1(异步串口,接 CH340 后变成电脑上的 COM 口)
  • lib/stdio/lib/printf.c— 裸机 printf(没有操作系统标准库,自己往 UART 丢字符)
  • bootloader/start.S— 启动汇编(设栈、清 BSS、跳进main)

4. 预期串口输出

本板使用正点原子 4.3 寸 800×480 屏(ATK4384),串口输出类似:

LCD ID = 0x4384

(具体值取决于所接屏幕;本板实测为 ATK4384。)


二、调试方式(与前课不同)

对比项前几课(常见)本课
编译环境虚拟机里的 UbuntuWSL(Windows 子系统里的 Linux)交叉编译
下载介质SD 卡 +imxdownload(多在虚拟机里操作)USB OTG + Windows 侧tools/dl.py
运行位置从 SD 启动下载到DDR 内存后跳转(掉电丢失)
USB / 串口虚拟机可做 USB 透传后再在 Ubuntu 里用在 Windows 本机操作(本仓库 WSL1 看不到 USB)
拨码SD 启动USB 下载模式0100

拨码 / BOOT_MODE:板子上的拨码开关告诉 BootROM「从哪启动」。0100表示进入Serial Downloader(串行下载器),只等电脑通过 USB 送程序,不会去读 SD。

虚拟机 Ubuntu 改成 WSL

前几课习惯在VMware / VirtualBox 里装 Ubuntu:交叉编译、imxdownload烧 SD、有时再把 USB 设备透传进虚拟机。能用,但有几处别扭:

  • 工程在 Windows 盘与虚拟机磁盘之间来回拷,或要配共享文件夹
  • USB 透传偶发不稳定(拔插、占用冲突)
  • 虚拟机本身占内存、开机慢

本课起改用WSL(Windows Subsystem for Linux):在 Windows 里直接跑一套 Ubuntu 用户态,用来装arm-linux-gnueabihf-gcc做交叉编译。代码就放在 Windows 路径(如D:\documents\...),WSL 里用/mnt/d/...访问同一份文件,编译产物两边立刻可见,不必再拷来拷去。

本仓库当前是WSL1:

  • 优点:和 Windows 文件系统集成紧、访问D:盘快
  • 限制:看不到 USB 设备(没有真正的 Linux USB 协议栈),所以
    • 编译:仍在 WSL
    • USB 下载(dl.py)、串口助手(COM7):必须在Windows完成

若仍想用虚拟机,只要工具链一致、能生成同样的load.imx即可;本课文档按 WSL + Windows 下载来写。

1. 编译(WSL)

cd/mnt/d/documents/learn_i.mx6u/22_lcdidmakecleanmake

产物(生成在工程目录,Windows 资源管理器里也能直接看到):

  • uart_printf.bin— 裸机二进制(纯机器码,还不能直接给 BootROM 用)
  • load.imx— 带IVT + DCD头的 NXP 镜像(USB 下载 / SD 烧录都用它)

补充两个头信息名词:

  • IVT(Image Vector Table,镜像向量表):文件最前面一块结构,告诉 BootROM「程序入口在哪、DCD 在哪、镜像自身加载地址(self)是多少」。
  • DCD(Device Configuration Data):一串「写寄存器」命令,主要用来在跳进程序前初始化 DDR。没有 DCD,DDR 还没起来,大程序根本写不进去。

2. USB-OTG 下载(本课主用,Windows)

硬件准备:

  1. 拨码拨到USB 下载模式(正点原子:0100,BOOT_MODE=10)
  2. USB 线接板子OTG Type-C(不要插成串口那个口)
  3. 另一根线接USB 转串口 Type-C(板上是CH340芯片,把 UART 转成电脑 COM 口;本机为COM7)
  4. 设备管理器应出现 HID:VID_15A2 & PID_0080,描述SE Blank 6ULL

依赖:

pip install hidapi pyserial

下载并可选读串口:

# 脚本默认下载 22_lcdid\load.imx,并尝试读 COM7python D:\documents\learn_i.mx6u\tools\dl.py# 已用串口助手占用 COM7 时,只下载:python D:\documents\learn_i.mx6u\tools\dl.py--no-serial# 指定镜像 / 串口:python D:\documents\learn_i.mx6u\tools\dl.py D:\documents\learn_i.mx6u\22_lcdid\load.imx--com COM7

脚本做的事(走SDP over HID,即用 HID 报告封装 SDP 命令):

  1. DCD_WRITE:把 DCD 数据先写到OCRAM(片上 SRAM,地址0x00910000)。
    DDR 还没初始化时不能往 DDR 写;OCRAM 上电即可用,BootROM 在这里执行 DCD 去「点亮」DDR。
  2. WRITE_FILE:DDR 就绪后,把整份load.imx写到 IVT 里的self地址(本课为0x877FF400,在 DDR 里)。
  3. JUMP_ADDRESS:让 BootROM 跳到 IVT,再按 IVT 里的entry进入你的程序。

成功时终端类似:

[1] DCD_WRITE ... status=0x128A8A12 [OK] [2] WRITE_FILE ... status=0x88888888 [OK] [3] JUMP_ADDRESS ... [OK] LCD ID = 0x4384

失败后请按板子RESET,重新出现 “SE Blank 6ULL” 后再下载。
提醒:USB 下载模式下按 RESET = 重新进下载器,不会自动再跑刚才 DDR 里的程序;要看串口输出必须再执行一次dl.py。

3. 串口查看日志

参数值
端口COM7(以本机设备管理器为准)
波特率115200
数据位 / 停止位 / 校验8 / 1 / 无

波特率:每秒传输的符号速率;双方必须一致,否则就是乱码。本课 UART1 配成 115200 8N1(8 数据位、无校验、1 停止位)。

注意:

  1. printf只在启动时打一次,先开串口再下载,或用dl.py自动读
  2. 两个 Type-C:OTG = 下载,CH340 = 日志,插反会失败
  3. COM7 被 sscom 等占用时,dl.py打不开串口,用--no-serial,日志改在串口助手看

4. (可选)仍用 SD 卡

与前课相同,适合需要掉电保存时:

cd/mnt/d/documents/learn_i.mx6u/22_lcdid../tools/imxdownload uart_printf.bin /dev/sdX# 先用 lsblk 确认设备号

拨码改回 SD 启动后上电。此时程序在 SD 里,按 RESET 会重新启动应用,串口会再次打印 LCD ID——这点和 USB 下载模式相反。

5. 故障排查

下载后 ├─ LED0 闪? │ ├─ 是 → 查串口口/COM/波特率/是否错过启动打印 │ └─ 否 → 程序没跑起来 │ ├─ 无 SE Blank 6ULL → 拨码、OTG 线、按 RESET │ └─ 有设备但脚本失败 → 按 RESET 后重跑 dl.py └─ 串口乱码 → 波特率是否 115200

三、读取 ID 参考代码

下面用到的IOMUXC_SetPinMux/IOMUXC_SetPinConfig就是在配引脚复用和电气特性(上下拉、驱动强度等);gpio_init/gpio_pinread则是把脚当普通输入输出用。

/** * 正点原子 LCD:M0~M2 组合 * LCD_DATA23=R7[M0]; LCD_DATA15=G7[M1]; LCD_DATA7=B7[M2] */unsignedshortlcd_read_id(void){unsignedcharidx=0;gpio_pin_config_tlcdid_io;IOMUXC_SetPinMux(IOMUXC_LCD_VSYNC_GPIO3_IO03,0);IOMUXC_SetPinConfig(IOMUXC_LCD_VSYNC_GPIO3_IO03,0x10B0);lcdid_io.direction=kGPIO_DigitalOutput;lcdid_io.outputLogic=1;/* 打开模拟开关 */gpio_init(GPIO3,3,&lcdid_io);IOMUXC_SetPinMux(IOMUXC_LCD_DATA07_GPIO3_IO12,0);IOMUXC_SetPinConfig(IOMUXC_LCD_DATA07_GPIO3_IO12,0xF080);IOMUXC_SetPinMux(IOMUXC_LCD_DATA15_GPIO3_IO20,0);IOMUXC_SetPinConfig(IOMUXC_LCD_DATA15_GPIO3_IO20,0xF080);IOMUXC_SetPinMux(IOMUXC_LCD_DATA23_GPIO3_IO28,0);IOMUXC_SetPinConfig(IOMUXC_LCD_DATA23_GPIO3_IO28,0xF080);lcdid_io.direction=kGPIO_DigitalInput;gpio_init(GPIO3,12,&lcdid_io);gpio_init(GPIO3,20,&lcdid_io);gpio_init(GPIO3,28,&lcdid_io);idx=(unsignedchar)gpio_pinread(GPIO3,28);idx|=(unsignedchar)gpio_pinread(GPIO3,20)<<1;idx|=(unsignedchar)gpio_pinread(GPIO3,12)<<2;if(idx==0)returnATK4342;if(idx==1)returnATK7084;if(idx==2)returnATK7016;if(idx==4)returnATK4384;if(idx==5)returnATK1018;return0;}

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询