这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » DIY与开源设计 » 电子DIY » 【EEPW-Let'sdesign】MicrochipSAMA7D65-V/7E

共1条 1/1 1 跳转至页

【EEPW-Let'sdesign】MicrochipSAMA7D65-V/7EW-11|显示控制器:手搓+踩坑

菜鸟
2026-11-11 23:10:36     打赏

显示是这套模型里最复杂的一块。QEMU 完全没有 Atmel LCDC 的模型,从寄存器到渲染都得自己写。

先说这东西长什么样,再说踩的坑。

控制器长什么样

XLCDC 的寄存器分两层。

全局寄存器管时序和使能:

偏移寄存器作用
0x00–0x14CFG0–CFG5像素时钟、同步极性、时序参数
0x20 / 0x24EN / DIS使能 / 关闭,写哪个位置位或清位
0x28SR状态,反映当前的使能状态
0x2c / 0x38IER / ISR中断使能与状态

图层寄存器每层一块,regs_offset 不同:

图层基址说明
base0x60基础层,RGB
overlay10x160叠加层
HEO0x360高端叠加,支持 YUV 和缩放

每层块里前 7 个是控制寄存器(CHER/CHDR/CHSR/IER/IDR/IMR/ISR),后面是配置字。最关键的一个是 base 层的帧缓冲地址,在 0x78——驱动把 framebuffer 的物理地址写到这里,硬件就该从那儿取像素。

模型怎么实现的

197 行,四块。

第一块,寄存器文件。 一个 uint32_t regs[0x2000/4] 数组兜住整个窗口,读写走默认路径。只有几个寄存器需要特殊处理:

  • EN(0x20):把写进来的位置进 SR

  • DIS(0x24):把写进来的位从 SR 清掉

  • SR(0x28):读的时候直接返回 SR 的值

  • 0x78:记下帧缓冲地址,打印一行日志

  • ISR(0x38):读即清除,同时撤中断

第二块,渲染。 每次 gfx_update 回调触发时:

if (s->fb_addr) {
    size_t row_bytes = s->width * 3;
    for (y = 0; y < s->height; y++) {
        address_space_read(&address_space_memory,
                           s->fb_addr + y * row_bytes,
                           MEMTXATTRS_UNSPECIFIED, s->linebuf, row_bytes);
        /* 逐像素转成 surface 的格式 */
    }
    dpy_gfx_update(s->con, 0, 0, s->width, s->height);
}

核心就一句 address_space_read——直接从客户机内存把帧缓冲读出来,按行处理,再写进 QEMU 的 DisplaySurface。

第三块,帧中断。 一个 16 ms 的 QEMUTimer,到点检查 IER 里 SOF 位有没有开,开了就置 ISR、拉中断线。

第四块,控制台注册。 graphic_console_init() 挂一个 QemuConsole,qemu_console_resize() 设成 480×272。

四块拼起来,链路是:驱动配寄存器 → 写帧缓冲地址 → 客户机画像素到内存 → 模型读内存 → 画进 surface → VNC / 截屏看到。

坑一:MachineState 不是 DeviceState

graphic_console_init() 要一个 DeviceState *。我图省事传了 DEVICE(machine),直接崩:

DEVICE: Object 0x… is not an instance of type device

MachineState 继承的是 Object,不是 Device。后来借用已有的 GIC 对象才把控制台注册上。

这个做法不干净,正规做法是把显示控制器做成独立的 QOM 设备。当时先让它跑起来。

坑二:缺时钟

驱动 probe 到一半失败:

atmel-hlcdc e1400000.lcd-controller: failed to get peripheral clock
atmel-hlcdc: probe of e1400000.lcd-controller failed with error -2

MFD 层要三个时钟:periph_clk(必需)、sys_clk、slow_clk。设备树里一个都没给,补三个 fixed-clock 上去就好。

坑三:2900 万次轮询

接上之后启动卡死,日志刷到几千万行。统计寄存器访问:

29008845  off=0x0028
       2  off=0x040c
       2  off=0x0408

全压在 0x28 一个偏移上。 驱动使能之后要等 SR & SYNC 置位,而模型永远返回 0,于是无限等。

修法就是前面说的:让 SR 跟着 EN/DIS 走。

11_01_寄存器轮询.png【11_01_寄存器轮询】

调试教训:一开始没给日志限流,2900 万行把 QEMU 拖到超时。改成"前 80 条全打、之后每 200 次打一条计数"之后,问题一眼就看出来——所有访问集中在同一个地址,明显是轮询。

坑四:fbcon 把系统拖死

修完轮询,DRM 起来了:

[drm] Initialized atmel-hlcdc 1.0.0
[drm] fb0: atmel-hlcdcdrmf frame buffer device
DRM device successfully registered

然后停在这,98 行,不动了。

中断是好的——35 秒里 ISR 被处理 2000 次,帧中断一直在来。但 info registers 看 PC:

PC -> cpu_v7_do_idle + 0x8
LR -> default_idle_call + 0x20

在睡觉,不是死循环。

最后查出来是 CONFIG_FRAMEBUFFER_CONSOLE=y。fbcon 接管控制台之后跟 fbdev 初始化撞上了。关掉它,只留 CONFIG_FB 和 CONFIG_FB_DEVICE,问题消失。

代价是开机文字不上屏,但 /dev/fb0 有了,这才是我们要的。

坑五:24bpp 不是 32bpp

/dev/fb0 出来之后,参数是:

virtual_size: 480,272
bits_per_pixel: 24
stride: 1440

24bpp,步长 1440(480 × 3)。我一开始按 32bpp 读,画面全错。改成按 3 字节读才对。

画面

往 /dev/fb0 写八条色带:

~ # printf '\\377\\000\\000%.0s' $(seq 1 16320) > /tmp/c.bin
~ # dd if=/tmp/c.bin of=/dev/fb0 bs=1440 seek=0

踩了个小坑:busybox 的 dd 不支持 conv=notrunc,而且 count 只是上限、不会重复输入——输入文件必须是完整的 34 行。

11_02_客户机帧缓冲.png【11_02_客户机帧缓冲】

八条色带,红黄绿青蓝品红白灰,一个不差。





关键词: Microchip     SAMA7D65-V     显示    

共1条 1/1 1 跳转至页

回复

匿名不能发帖!请先 [ 登陆 注册 ]