看日志的话,我觉得重点不在 poweroff 本身,而是在小核进入 ultra standby 以后,仍然有 AMP/rpmsg/rproc 相关路径在试图和“大核/远端”通信,最后卡在 msgbox 队列满,然后走到 rproc_common_remove() 后异常。这个更像是 standby 收尾顺序或某个外设/AMP 资源没有正确静默,而不是单纯 GPIO 配置问题。
可以按下面几条线排:
先把 WiFi / ISP / VIN / camera 相关功能彻底裁掉验证
你硬件没有接摄像头,但小核日志里仍然有:
[VIN]csi_link_deinit standby_mode is 1
buffer "isp0_load_rpbuf" ...
[ISP]isp0 exit end!!!
wlan_fmac_xrlink_deinit start
rpmsg_xradio_deinit这说明小核的低功耗流程里还在跑 ISP/VIN/WLAN/XRadio 相关 deinit。自制板如果没有摄像头、没有 WiFi,建议先做一个“最小系统 standby”镜像:
dts 里 disable vin/isp/csi 相关节点;
disable wlan/xradio 相关节点和固件加载;
Tina menuconfig 里能关的 ISP/VIN/WLAN/RPBUF demo 先关掉;
小核工程里如果有对应模块初始化,也先裁掉或让 deinit 直接返回。
如果裁掉后能正常拉低 PL0/PL1/PL2/PL6,说明问题就在这些 AMP 外设资源释放顺序上。
重点查 hal_msgbox_channel_send_data queue is full 的发送方
这条日志很关键:
hal_msgbox_channel_send_data(764): message queue is full for a long time!!!
rv-vring-ipi: Failed to send message to remote
rproc_common_notify: notify failed进入 ultra standby 后,大核已经在关机/停 remoteproc 流程里了,但小核还在通过 vring/msgbox 发 notify。正常低功耗流程应该先停止业务线程、停止 rpmsg/rpbuf 发送,再关 IPI/msgbox/rproc。如果顺序反了,就会出现发送队列满、notify failed,后面 remove rproc 时更容易跑飞。
建议在小核代码里给以下函数加调用栈/模块名 log:
hal_msgbox_channel_send_data()
msgbox_ipi_notify()
rproc_common_notify()
rproc_common_remove()
rpmsg_xradio_deinit()
rpbuf_free_buffer_internal()目标是确认到底是谁在 standby 期间最后还在发 IPI。尤其是 rpbuf_free_buffer_internal() 释放 buffer 时是否会通知 remote,如果 remote 已经停了,这里就不应该继续 notify。
确认 standby 命令和电源模式是否匹配
你现在是:
echo 1 > /sys/class/ae350_standby/use_ultra_standby
poweroff这条路径走的是“关机 + ultra standby”的组合,和普通 mem suspend 不是一回事。建议对照文档确认当前 SDK 推荐入口,是不是还需要配置 standby bin / cpusys / wakeup source / power domain。可以分别测:
echo mem > /sys/power/state以及你现在的 poweroff 路径,看是哪一条会触发跑飞。如果 mem 正常、poweroff 异常,那问题基本就在 fastboot/poweroff ultra standby 的收尾流程;如果两条都异常,再看小核通用 standby 流程。
PL0/PL1/PL2/PL6 未拉低,可能是流程还没走到 PMIC/GPIO poweroff 阶段
现在日志停在:
remove rproc...后面乱码,说明还没完成最终电源控制动作。也就是说 PL0/PL1/PL2/PL6 没拉低不一定是 pinmux 配错,而可能是小核在执行电源控制前已经异常了。
建议在小核控制 PL0/PL1/PL2/PL6 的函数前后加 log,例如:
printf("standby: before power gpio off
");
printf("standby: set PL0/PL1/PL2/PL6 low
");
printf("standby: after power gpio off
");如果这些 log 没出现,先解决前面的 rpmsg/msgbox/rproc;如果出现了但电平没变,再查 pinmux、gpio bank 电源域、是否被其它功能复用。
自制板还要核对 RTC/PL 电源域和唤醒脚
PL3 配唤醒源时,需要确认:
PL bank 在 ultra standby 下是否仍有供电;
PL3 的上下拉和有效电平是否正确;
唤醒源配置是否写入小核/standby 固件实际使用的配置,而不仅是 Linux dts;
参考板上由 PMIC 或外部电源控制的 rail,你的板子是否等价。
不过从你当前日志看,优先级还是 msgbox queue full -> rproc remove -> 乱码 这条链。
我建议先做一个最小化实验:
关闭 VIN/ISP/WLAN/XRadio;
保留串口、PL3 唤醒、必要电源 GPIO;
重新测 use_ultra_standby + poweroff;
在 rproc_common_remove() 后面、真正拉 GPIO 前后加 log。
如果最小系统可以进休眠,再逐个打开 ISP/WLAN 等模块,就能定位是哪一个 deinit 阶段把 msgbox/rpmsg 搞挂。现在这份日志里最可疑的是 WLAN/XRadio 的 rpmsg 注销和 rpbuf 释放顺序。
这个现象建议不要先从“dclk 有了所以设备树没问题”这个方向判断,dclk 只能说明 DE/TCON/LCD 时钟链路起来了,不等于内核已经匹配并执行了你改的 panel init 阵列。F133/Tina 这套 disp2 里,panel init 通常是在 open flow 里被调用的,前提是内核选中的 lcd panel 驱动和你改的那个驱动真正匹配上了。可以按下面顺序排一下:
先确认内核实际加载/匹配的是不是你改的 ST7701S 驱动
看设备树里的 lcd_driver_name 是否和 panel 驱动结构体里的 .name 完全一致,比如 st7701s / default_lcd / 自己改的名字,大小写、下划线都要一致。很多时候 u-boot 能亮,是因为 u-boot 里的 panel 名和流程对了;到 kernel 这边虽然用“同一份 dts”,但 kernel 的 panel 注册表/宏开关/名字没对上,最后走了别的 panel 或者默认流程。
确认该 panel 文件真的被编进当前运行的 kernel
不只是编译通过,还要确认 Makefile/Kconfig 里这个 panel 被选中,panels.c 或对应注册入口里有调用/注册到你的 panel。建议在更靠前的位置加 log:
panel 文件的注册函数/模块 init 处;
sunxi_lcd_set_panel_funs() 附近;
LCD_open_flow() / LCD_panel_init() 入口。
如果注册处都没打印,说明文件没进镜像或没被注册;如果注册了但 open flow 没进,再查 disp/lcd enable 流程。
确认运行的是新 kernel/dtb,不是旧镜像
Tina 上很容易只更新了源码目录或 rootfs,实际 boot 分区里的 boot.img / kernel / dtb 还是旧的。可以临时在内核启动早期或 panel 文件全局入口加一个非常独特的 log 字符串,确认启动日志里能看到。看不到的话,先别继续怀疑屏参。
检查内核阶段是否启用了 lcd0,以及是否被 simplefb/bootlogo 接管
u-boot 点亮屏以后,内核可能只是继承 framebuffer/时钟状态,或者只初始化了显示控制器,没重新跑 panel 初始化序列。可以看启动 log 里的 disp, lcd, panel 相关输出,也可以检查 /sys/class/disp/disp/attr/sys 里 lcd0/屏幕状态。若 lcd0 没 enable,panel init 不会被调用。
dts 重点看这些项 lcd_used = <1>、lcd_if、lcd_driver_name、reset/power/enable gpio、pinctrl、电源 regulator 这些都要和 kernel 驱动期望一致。尤其是 lcd_driver_name,它决定走哪个 panel 函数表;dclk 对了只能证明时序参数大体生效,不能证明 MIPI/DBI/8080/SPI panel init 发出去了。
我建议你先在内核里加三处 log 定位:
// 1. panel 文件注册入口
printk("QL2017: st7701s panel registered in kernel\n");
// 2. LCD_open_flow 入口
printk("QL2017: LCD_open_flow sel=%d\n", sel);
// 3. 真正发送初始化数组前
printk("QL2017: ST7701S kernel init array start\n");然后根据结果分叉:
1 都没有:当前运行镜像不是你编译的,或该 panel 文件没编进内核。
1 有、2 没有:panel 注册了,但 lcd 没有被 open,查 lcd_used / disp enable / framebuffer 流程。
1、2 有、3 没有:open flow 里没有走到 panel init,查 open flow 配置或条件分支。
3 有但屏不亮:这时才回到初始化数组、reset 时序、电源、lane/format 等屏参问题。
总之,这个问题大概率不是“设备树完全没问题/有问题”这么简单,而是要确认 kernel 阶段实际匹配到的 panel 函数表和 open flow。先把调用链打穿,基本很快能定位。
这个复刻很有意思,感谢分享!
喵喵机这类小东西看起来简单,其实挺适合练手:机械结构、热敏头驱动、走纸控制、电源瞬态、蓝牙/串口协议、外壳装配,全都能沾一点。能把它复刻出来,比单纯买一个成品好玩多了。
如果后面继续完善,我觉得可以重点试试这几项:
换一卷新的热敏纸对比一下,先排除纸老化导致的显色问题。
适当调高加热时间/占空比,但要注意别让热敏头过热。
打印一张灰阶或不同密度的测试图,方便判断是热敏纸问题、电源问题,还是驱动参数偏保守。
如果电源余量不大,打印大面积黑块时可以观察一下电压跌落,热敏打印机这东西吃电流还是挺凶的。
总之这个项目很有 DIY 味道:小机器、大乐趣、旧纸也有旧纸的年代感。期待后续更新,最好再来点结构图/驱动细节,让大家一起围观这只“电子小猫”怎么吐纸。
这个问题可以拆成一个比较明确的逆向任务,不建议一上来只靠模拟器手动下断点硬撞。GBA 游戏里这类“地图 + 怪物 + 掉落物”的关系通常不会散落在逻辑代码里,而是几张表组合起来:地图/房间表、敌人生成表、敌人类型表、掉落组表、物品 ID 表、随机数判断逻辑。只要先把表结构定位出来,后面就能批量扫出结果。
我建议你把资料按下面几类整理出来,别人接手会省很多时间:
ROM 的版本信息:美版/欧版/日版、CRC32/MD5/SHA1,最好说明是否汉化、改版或打过补丁。不同版本地址可能完全不同。
你已有的金手指代码,尤其是“强制掉落/背包物品/当前地图/当前敌人/经验金钱”相关代码。金手指地址往往能反推 RAM 结构,再由 RAM 写入点追到 ROM 表。
反编译工程或符号:如果是 Ghidra/IDA/mGBA trace,至少给出可公开下载的工程、函数列表,或者你怀疑相关的函数片段。
稀有背包道具的物品 ID 清单:已有英文名、中文名、背包显示位置、金手指写入值都可以。没有 ID 的话,先通过背包修改码或存档差分把 ID 定出来。
一个可复现的存档:站在会刷怪、会掉落的地图入口处,最好再配一个已经联机解锁后的存档,避免大家花时间补前置条件。
技术路线大概是这样:
先用金手指或存档差分确定物品 ID 和背包数据结构。比如改一个背包格,比较修改前后的 SRAM/RAM,就能知道物品 ID、数量、属性字段分别在哪里。
在 mGBA 里对“获得物品/写入背包”的 RAM 地址下写断点,触发掉落时看调用栈,找到处理掉落的函数。
从掉落函数往前追,一般能看到 RNG、掉落率、drop table index、enemy id 之类的读取。这里不要只看汇编流程,重点看它从 ROM 读了哪些常量表。
找到 enemy id -> drop group -> item id 的表之后,再反查 enemy id 出现在哪些 map/spawn 表里,就能得到“具体地图 + 具体敌人”。
最后写个脚本扫 ROM 表,把所有目标 item id 对应的 enemy/map 全列出来,再进游戏抽样验证。
如果你愿意继续推进,建议下一步不要只发“所有反编译代码”,而是先发一个最小资料包:ROM 版本校验值 + 目标稀有道具名称/ID + 相关金手指 + 一个能触发掉落的存档 + 你已经下过断点的地址。这样论坛里懂 ARM/GBA 的人可以直接从“背包写入点”和“掉落函数”开始看,成功率会高很多。
我也补一句:如果那些道具必须联机事件解锁,掉落逻辑可能还会受一个全局 flag 控制。这个 flag 也要通过联机前后存档差分找出来,否则只定位掉落表可能会发现“表里有,但游戏永远不进这个分支”。
这个现象我感觉重点不在 “gpio-keys 不能用”,而是在 把一个输出控制脚塞进 gpio-keys 以后,pinctrl 太早把它拉成了输出高。这类问题很像:系统还没进屋,门口保安已经按了电源键,内核只能一脸懵地说“我刚挂载完分区,怎么人没了”。
几个点可以分开看:
echo 44 > /sys/class/gpio/export 报 Device or resource busy 是正常的。
这个脚已经被 gpio-keys 申请走了,debug 里也能看到:
gpio-44 ( |KEY-VOLUMEUP ) in lo IRQ ACTIVE LOW被 input 驱动占用后,sysfs 再 export 就会 busy。这个不是根因,反而说明设备树生效了。
gpio_power 里面这个节点很可疑:
pwr: pwr {
gpios = <&gpio0 RK_PB6 GPIO_ACTIVE_LOW>;
label = "Core Power Off";
}; gpio-keys 的子节点本质上应该是“输入按键”,通常要有 linux,code,而且 pinctrl 也应该配输入/上拉。你这里 PB6 又在 pinctrl 里写成:
<0 RK_PB6 RK_FUNC_GPIO &pcfg_output_high>这等于在 gpio-keys probe 前后把一个 key 类节点配置成输出高。debug 里也显示:
gpio-14 ( |pwr ) out hi ACTIVE LOW 所以它已经不是“按键”了,更像一个电源控制脚。放在 gpio-keys 下面不合适。
建议先把 PB6 从 gpio-keys 里拿掉,只保留真正的输入键:
gpio_power: gpio-power {
status = "okay";
compatible = "gpio-keys";
pinctrl-names = "default";
pinctrl-0 = <&pwrbtn>;
key_screenlock: key-screenlock {
label = "GPIO_Key_Screenlock";
linux,code = <KEY_SCREENLOCK>;
gpios = <&gpio0 RK_PB2 GPIO_ACTIVE_LOW>;
wakeup-source;
debounce-interval = <100>;
};
}; 然后 pwrbtn 里也只放 PB2:
pwrbtn: pwrbtn {
rockchip,pins = <0 RK_PB2 RK_FUNC_GPIO &pcfg_pull_up>;
}; 注意 Rockchip pinctrl 一般是 rockchip,pins,不是 rockchip,pin,不同内核可能兼容情况不一样,最好和原厂 dtsi 里的写法保持一致。
如果 PB6 真的是“关机/电源保持/PMIC 控制”脚,那应该单独用合适的驱动,比如 gpio-poweroff、regulator、gpio-hog,或者板级电源控制节点,不要混在 gpio-keys 里。否则它就像一个披着按键马甲的电源开关,input 子系统还没开始工作,它已经开始决定整机命运了。
&gpio0/&gpio1/&gpio2 这些节点一般 SoC dtsi 已经定义好了,不建议在板级 dts 里重复加:
gpio-controller;
interrupt-controller;除非你确认原始 dtsi 缺失。板级通常只需要引用 GPIO,不需要重新声明控制器属性。
我建议排查顺序:
第一步:删掉 pwr: pwr 和 PB6 的 pcfg_output_high,只保留输入按键,看能不能稳定 boot;
第二步:每组 gpio-keys 分开打开,先 screenlock,再 volume,再 gamepad;
第三步:启动后用 evtest 或 getevent 看 input 事件;
第四步:PB6 如果确实要控制电源,再单独建 poweroff/regulator/gpio-hog 逻辑。
一句话:gpio-keys 只适合管“别人按我”,不适合管“我去拉别人电源”。 现在这个配置看起来就是把门铃和总闸接到一个驱动下面了,能不玄学才怪。
这篇总结很到位,尤其是“能亮 ≠ 眼图有余量”这句,建议贴在 HDMI 调试工位旁边,旁边再配一行小字:别急着改寄存器,示波器不会陪你演戏。
顺便回一下楼上的 HDMI IN:过程大方向类似,但锅的分布会换一下。HDMI OUT 还能靠 PHY swing / pre-emphasis 补一点妆;HDMI IN 更像当接收端判卷,前面线材、ESD、阻抗、端接、均衡、时钟恢复哪个环节抽风,最后都会变成“我明明接了,怎么它说没信号”。
所以不建议直接“拉线就完事”。至少要先确认:
差分 100Ω、P/N 等长、lane 间长度别太放飞;
ESD 电容别选成“高速信号安乐椅”;
连接器到芯片路径少过孔、少 stub、参考地连续;
HPD、5V、DDC/EDID 这些低速信号别忽略;
如果芯片侧有 EQ / termination / lane swap / polarity 配置,也要确认驱动阶段真的配进去了。
一句话:HDMI IN 也不是“线通了就行”,它只是把“我发得好不好”变成了“别人发来的烂摊子我能不能接住”。硬件先别挖坑,软件才有机会优雅填坑。
这个帖子很实用,AXP2101 这种 PMU 真正调起来,资料是一部分,现场能不能快速看清状态更关键。楼主后面贴的这些 debugfs/sysfs 路径特别有用,建议调板子时先把这几条命令收起来。
我补充一个排查思路,尤其是遇到“掉电重启、充电异常、电压不对、按键行为不对”这几类问题时:
先确认 I2C 设备是否正常起来
AXP2101 常见地址是 1-0034,先看目录是否存在:
ls /sys/devices/platform/soc/twi1/i2c-1/1-0034/如果这个都没有,先别看 regulator,优先查 twi 节点、I2C 地址、PMU 中断脚、电源和 dts。
用 regmap 看寄存器快照
cat /sys/kernel/debug/regmap/1-0034/registers这个对比“正常板”和“异常板”很方便。比如掉电、充电状态、IRQ 状态、电源输出开关,有时候一眼就能看出差异。
用 regulator_summary 看各路电压是否真的打开
cat /sys/kernel/debug/regulator/regulator_summary这个比只看 dts 靠谱。dts 写了不代表运行时一定开了,也不代表 consumer 绑定对了。尤其 DCDC/ALDO/BLDO/DLDO 这些,最好确认 use/open/voltage 是否符合预期。
充电和电池状态走 power_supply
cat /sys/class/power_supply/battery/status
cat /sys/class/power_supply/battery/voltage_now
cat /sys/class/power_supply/battery/capacity
cat /sys/class/power_supply/battery/temp如果电量显示很离谱,不一定是驱动坏了,可能是电池模型/内阻/容量参数没匹配。AXP2101 的电量计这块最好按实际电池做验证,不要直接拿 demo 板参数量产。
PEK 按键用 evtest 看最直接
evtest /dev/input/eventX能看到 KEY_POWER 按下/松开,说明输入事件链路通了。长按关机/重启这种再去查 PMU 配置和系统策略。
掉电重启类问题要分硬件和策略
如果是按 reset/PEK 后掉电,建议同时看:
PMU 寄存器状态;
各路 regulator 是否被关;
dts 里各路电源 always-on/boot-on 是否合理;
电池/USB/DCIN 供电路径是否切换异常;
长按按键策略是不是触发 poweroff 而不是 reset。
我感觉 AXP2101 调试最怕“只看现象不看状态”。把 regmap、regulator_summary、power_supply、evtest 这几套信息同时抓下来,再对照原理图和 dts,基本能把问题分到 I2C/电源树/充电/按键策略这几层。
最近整理 HDMI 热插拔相关问题,发现这类故障经常不是“HDMI 完全不能用”,而是冷启动能亮、插拔后不亮,或者换显示器后 mode 没更新。这里把排查思路记一下,后面遇到类似问题可以少绕一点。
HDMI 热插拔的第一信号是 HPD。显示器插上后,HPD 电平应该变化,驱动再去读 EDID、选择 mode、重新 enable 输出。
如果插拔时系统完全没反应,优先查:
HDMI HPD 引脚是否接对;
DTS/pinctrl 里 HPD 是否配置;
是否有 GPIO/IRQ 被别的功能占用;
HDMI 5V 是否正常输出;
线材/转接头是否把 HPD 断了。
很多“热插拔不生效”其实还没走到 EDID 阶段,HPD 事件就丢了。
插入显示器后,正常流程应该是读取 EDID,再根据显示器支持的分辨率选择输出模式。
如果 HPD 有变化,但插上后仍然黑屏,可以重点看:
DDC 的 SCL/SDA 是否正常;
I2C 上拉、电平转换是否合适;
EDID 是否读取失败或读到全 0/错误数据;
是否复用了错误的旧 EDID;
是否 fallback 到显示器不支持的 mode。
有条件的话,逻辑分析仪看 DDC 很直观。软件层面可以看 dmesg 里有没有 EDID read failed、mode invalid 之类信息。
有些板子开机前 HDMI 已经插着能亮,但运行中拔插就不行。这个时候说明基本显示链路没问题,问题更可能在:
HPD 中断没有配置好;
驱动没有处理 disconnect/connect;
用户态没有重新 modeset;
framebuffer/DRM 没有重新绑定输出;
HDMI PHY 没有在拔插后重新 enable。
所以调试时建议分别测三种场景:
开机前插着 HDMI;
开机后再插 HDMI;
显示中拔掉再插回。
这三种能暴露不同问题。
如果是老 BSP/disp2,一般看 disp/hdmi 相关 log,以及 sys_config/dts 里的 hdmi_used、screen 输出配置。
如果是主线 DRM/KMS,可以用:
modetest -M sun4i-drm看 connector 状态是否从 disconnected 变成 connected,mode 列表是否刷新。也可以用用户态程序重新 set mode,确认是不是内核已识别但应用没处理。
热插拔调试阶段不要一开始就依赖自动 EDID 选最高分辨率。可以先固定 720p60 或 1080p60 中较稳的模式,确认拔插后能重新出图。
如果固定模式能亮,自动模式不亮,大概率是 EDID/mode 选择问题;如果固定模式也不亮,就回头查 HPD、PHY、TCON/DE 重新 enable。
我一般会这样排:
量 HDMI 5V 和 HPD 电平;
看插拔时内核有没有 HPD/connector 事件;
看 EDID 是否重新读取成功;
固定 720p60 测输出;
看拔插后 HDMI PHY/TCON/DE 是否重新 enable;
最后再查应用层是否重新 modeset 或刷新 framebuffer。
简单说,HDMI 热插拔要拆成三步:检测到插拔、读到显示器、重新出图。只要按这三层分开验证,基本能定位是硬件 HPD/DDC 问题、驱动热插拔流程问题,还是用户态没有重新设置显示模式。