news 2026/7/10 1:23:27

lk2nd 移植实战:为广信 EF33 老年机添加 ST7789V SPI 屏支持的 2 个核心补丁

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
lk2nd 移植实战:为广信 EF33 老年机添加 ST7789V SPI 屏支持的 2 个核心补丁

为广信EF33老年机移植lk2nd:ST7789V SPI屏幕驱动开发全记录

引言

在嵌入式系统开发领域,Bootloader移植往往是项目成功的关键第一步。当面对一款采用高通msm8909平台的广信EF33老年机时,原厂锁定的fastboot模式让lk2nd移植成为唯一选择。本文将详细记录如何通过逆向工程手段,为这款设备特有的ST7789V SPI屏幕开发驱动补丁的全过程。

不同于常见的MIPI接口,SPI屏幕在移动设备中相对少见,这为驱动开发带来了独特挑战。我们将从硬件分析开始,逐步深入到寄存器配置、GPIO映射和时钟树调整,最终提供两个可直接应用于lk2nd的补丁文件。这些实战经验对于任何需要在高通平台添加非标准显示设备的开发者都具有参考价值。

1. 硬件分析与逆向工程准备

1.1 设备基础信息确认

通过adb shell获取的硬件信息显示,这款广信EF33老年机采用以下配置:

  • 高通msm8909平台(骁龙210)
  • 512MB RAM + 4GB ROM
  • 单摄像头,无触摸屏
  • 2.4GHz WCN3610v1 WiFi模块

关键发现来自/proc/cmdline中的显示参数:

mdss_mdp3.panel=0:spi:0:qcom,mdss_spi_st7789v_ctc_qvga_nrx_cmd

这明确指出了屏幕型号为ST7789V,采用SPI接口。

1.2 调试接口利用

由于fastboot被厂商锁定,我们通过以下途径获取硬件信息:

  1. 挂载debugfs获取GPIO状态:
mount -t debugfs debugfs /sdcard/debug cat /sdcard/debug/gpio

输出显示关键GPIO:

gpio-928 (dc-gpios ) out hi gpio-1022 (red ) out lo gpio-1023 (green ) out hi
  1. 通过EDL模式提取固件,获得lk二进制文件。高通平台的Bootloader带有ELF头,这大大简化了逆向工程工作。

1.3 Ghidra逆向分析

将提取的lk二进制导入Ghidra后,我们重点关注以下数据结构:

struct mdss_spi_cmd { int size; // 指令长度 char *payload; // 指令内容 int wait; // 延迟时间 uint8_t cmds_post_tg; };

通过交叉引用分析,我们定位到屏幕初始化序列存储在st7789v_ctc_qvga_nrx_cmd_on_command数组中。与标准ST7789V数据手册对比,发现厂商对以下参数做了定制:

寄存器标准值定制值作用
0x360x000xC0屏幕扫描方向
0xB20x0C0x0C空周期控制
0xB70x350x75门控控制

2. lk2nd移植核心步骤

2.1 基础环境搭建

使用lk2nd的experimental-rebase分支而非官方LK,原因在于:

  • 更好的GCC新版本兼容性
  • 已包含msm8909基础支持
  • 社区维护更活跃

编译命令:

make TOOLCHAIN_PREFIX=arm-none-eabi- msm8909 -j8

2.2 屏幕驱动集成

target/msm8909/oem_panel.c中添加以下关键修改:

+#include "include/panel_st7789v_ctc_qvga_nrx_cmd.h" enum { AUO_390P_CMD_PANEL, ST7789v2_QVGA_SPI_CMD_PANEL, + ST7789V_CTC_QVGA_NRX_CMD_PANEL, UNKNOWN_PANEL }; static struct panel_list supp_panels[] = { {"auo_390p_cmd", AUO_390P_CMD_PANEL}, {"ST7789V2_qvga_cmd", ST7789v2_QVGA_SPI_CMD_PANEL}, + {"st7789v_ctc_qvga_nrx_cmd", ST7789V_CTC_QVGA_NRX_CMD_PANEL}, };

初始化函数中绑定逆向得到的数据结构:

case ST7789V_CTC_QVGA_NRX_CMD_PANEL: panelstruct->paneldata = &st7789v_ctc_qvga_nrx_cmd_panel_data; panelstruct->panelres = &st7789v_ctc_qvga_nrx_cmd_panel_res; panelstruct->color = &st7789v_ctc_qvga_nrx_cmd_color; pinfo->spi.panel_cmds = st7789v_ctc_qvga_nrx_cmd_on_command; pan_type = PANEL_TYPE_SPI; break;

2.3 SPI总线配置调整

通过逆向设备树发现屏幕连接在BLSP1_QUP5,而非lk2nd默认的QUP4。需要以下修改:

  1. 时钟寄存器定义(platform/msm8909/include/platform/iomap.h):
+#define GCC_BLSP1_QUP6_SPI_APPS_CBCR (CLK_CTL_BASE + 0x701C) +#define GCC_BLSP1_QUP6_SPI_APPS_CMD_RCGR (CLK_CTL_BASE + 0x7024)
  1. GPIO配置(platform/msm8909/gpio.c):
void gpio_config_blsp_spi(uint8_t blsp_id, uint8_t qup_id) { if(blsp_id == BLSP_ID_1 && qup_id == QUP_ID_5) { gpio_tlmm_config(8, 1, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_16MA, GPIO_DISABLE); // MOSI gpio_tlmm_config(9, 1, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_16MA, GPIO_DISABLE); // MISO gpio_tlmm_config(10, 1, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_16MA, GPIO_DISABLE); // CS gpio_tlmm_config(11, 1, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_16MA, GPIO_DISABLE); // CLK } }
  1. 修改默认QUP ID(target/msm8909/include/target/display.h):
-#define SPI_QUP_ID 4 +#define SPI_QUP_ID 5

3. 关键问题解决与调试

3.1 白屏问题排查

初期烧录后出现白屏但背光亮起的现象,通过以下步骤定位问题:

  1. 确认初始化日志显示驱动已加载:
[LK] panel st7789v_ctc_qvga_nrx_cmd init
  1. 使用逻辑分析仪抓取SPI信号,发现CS线无活动

  2. 对比设备树与代码,发现QUP配置不匹配:

qcom,mdss_spi_client { reg = <0x00>; label = "MDSS SPI QUP5 CLIENT"; spi-max-frequency = <0x2faf080>; // 50MHz };

3.2 时钟配置验证

BLSP时钟树配置需要确保:

  1. 核心时钟(BLSP1_AHB_CLK)已使能
  2. SPI应用时钟(BLSP1_SPI_APPS_CLK)正确分频
  3. 时钟源选择GPLL0(800MHz)

调试时添加的日志输出:

dprintf(INFO, "BLSP1_CLK_ENA: 0x%x\n", readl(GCC_BLSP1_AHB_CBCR)); dprintf(INFO, "SPI_APPS_CLK_SRC: 0x%x\n", readl(GCC_BLSP1_QUP5_SPI_APPS_CMD_RCGR));

3.3 电源时序调整

ST7789V要求严格的电源上电顺序:

  1. VCCIO (1.8V) 先上电
  2. 延迟10ms后上电VCC (2.8V)
  3. 再延迟5ms后释放复位

通过修改panel_st7789v_ctc_qvga_nrx_cmd_reset_seq实现:

static struct reset_seq st7789v_ctc_qvga_nrx_cmd_reset_seq = { .sequence = { {1, 0, 1}, // GPIO928 (RESET) 拉低 {10, 0, 0}, // 保持10ms {0, 1, 1}, // GPIO928 拉高 {120, 0, 0} // 保持120ms }, .size = 4 };

4. 完整补丁文件

4.1 oem_panel.c补丁

--- a/target/msm8909/oem_panel.c +++ b/target/msm8909/oem_panel.c @@ -54,6 +56,7 @@ #include "include/panel_auo_390p_cmd.h" #include "include/panel_st7789v2_qvga_spi_cmd.h" #include "include/panel_gc9305_qvga_spi_cmd.h" +#include "include/panel_st7789v_ctc_qvga_nrx_cmd.h" enum { AUO_390P_CMD_PANEL, @@ -86,6 +89,7 @@ static struct panel_list supp_panels[] = { {"auo_390p_cmd", AUO_390P_CMD_PANEL}, {"ST7789V2_qvga_cmd", ST7789v2_QVGA_SPI_CMD_PANEL}, + {"st7789v_ctc_qvga_nrx_cmd", ST7789V_CTC_QVGA_NRX_CMD_PANEL}, {"gc9305_qvga_cmd", GC9305_QVGA_SPI_CMD_PANEL}, UNKNOWN_PANEL }; @@ -502,20 +506,22 @@ uint32_t platform_type = board_platform_id(); uint32_t platform_subtype = board_hardware_subtype(); int32_t panel_override_id; + int oem_panel_id = oem_read_panel_id(); + dprintf(INFO, "[HACK] oem_panel_id :%d\n",oem_panel_id); - if (panel_name) { + if (1) { panel_override_id = panel_name_to_id(supp_panels, - ARRAY_SIZE(supp_panels), panel_name); + ARRAY_SIZE(supp_panels), "st7789v_ctc_qvga_nrx_cmd"); if (panel_override_id < 0) { dprintf(CRITICAL, "Not able to search the panel:%s\n", - panel_name); + "st7789v_ctc_qvga_nrx_cmd"); } else if (panel_override_id < UNKNOWN_PANEL) { /* panel override using fastboot oem command */ panel_id = panel_override_id; dprintf(INFO, "OEM panel override:%s\n", - panel_name); + "st7789v_ctc_qvga_nrx_cmd"); goto panel_init; } }

4.2 gpio.c补丁

--- a/platform/msm8909/gpio.c +++ b/platform/msm8909/gpio.c @@ -124,13 +124,6 @@ GPIO_8MA, GPIO_DISABLE); break; case QUP_ID_5: - /* configure I2C SDA gpio */ - gpio_tlmm_config(18, 3, GPIO_OUTPUT, GPIO_NO_PULL, - GPIO_8MA, GPIO_DISABLE); - - /* configure I2C SCL gpio */ - gpio_tlmm_config(19, 3, GPIO_OUTPUT, GPIO_NO_PULL, - GPIO_8MA, GPIO_DISABLE); break; default: dprintf(CRITICAL, "Incorrect QUP id %d\n",qup_id); @@ -147,51 +140,33 @@ { if(blsp_id == BLSP_ID_1) { switch (qup_id) { - case QUP_ID_3: - /* configure SPI MOSI gpio */ - gpio_tlmm_config(12, 1, GPIO_OUTPUT, GPIO_NO_PULL, - GPIO_16MA, GPIO_DISABLE); - - /* configure SPI MISO gpio */ - gpio_tlmm_config(13, 1, GPIO_OUTPUT, GPIO_NO_PULL, - GPIO_16MA, GPIO_DISABLE); - - /* configure SPI CS_N gpio */ - gpio_tlmm_config(14, 1, GPIO_OUTPUT, GPIO_NO_PULL, - GPIO_16MA, GPIO_DISABLE); - - /* configure SPI CLK gpio */ - gpio_tlmm_config(15, 1, GPIO_OUTPUT, GPIO_NO_PULL, - GPIO_16MA, GPIO_DISABLE); - break; - case QUP_ID_4: + case QUP_ID_5: /* configure SPI MOSI gpio */ - gpio_tlmm_config(16, 1, GPIO_OUTPUT, GPIO_NO_PULL, + gpio_tlmm_config(8, 1, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_16MA, GPIO_DISABLE); /* configure SPI MISO gpio */ - gpio_tlmm_config(17, 1, GPIO_OUTPUT, GPIO_NO_PULL, + gpio_tlmm_config(9, 1, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_16MA, GPIO_DISABLE); /* configure SPI CS_N gpio */ - gpio_tlmm_config(18, 1, GPIO_OUTPUT, GPIO_NO_PULL, + gpio_tlmm_config(10, 1, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_16MA, GPIO_DISABLE); /* configure SPI CLK gpio */ - gpio_tlmm_config(19, 1, GPIO_OUTPUT, GPIO_NO_PULL, + gpio_tlmm_config(11, 1, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_16MA, GPIO_DISABLE); - break; - - case QUP_ID_0: - case QUP_ID_1: - case QUP_ID_2: - case QUP_ID_5: default: dprintf(CRITICAL, "Incorrect QUP id %d\n",qup_id); ASSERT(0);

5. 验证与后续工作

成功烧录修改后的lk2nd后,设备能够正常显示Bootloader界面。通过以下命令验证显示子系统状态:

fastboot oem panel-info

输出应包含:

Panel: st7789v_ctc_qvga_nrx_cmd Interface: SPI Resolution: 240x320

后续可继续完善:

  1. 添加动态屏幕旋转支持
  2. 实现帧缓冲控制台
  3. 优化SPI传输效率(启用DMA模式)
  4. 为主线Linux内核开发对应的tinydrm驱动

这个案例展示了即使面对非标准硬件配置,通过系统化的逆向工程和模块化修改,也能成功实现Bootloader的深度定制。最重要的是保持对硬件文档的细致分析和对日志信息的充分利用,这两者往往是解决复杂移植问题的关键。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/10 1:21:39

DeepSeek V4 API价格策略解析与成本优化实战指南

&#x1f680; 30款热门AI模型一站整合&#xff0c;DeepSeek/GLM/Qwen 随心用&#xff0c;限时 5 折。 &#x1f449; 点击领海量免费额度 DeepSeek V4 API 的价格策略最近引起了广泛关注&#xff0c;特别是高峰时段价格翻倍的现象。作为国内领先的大模型服务提供商&#xf…

作者头像 李华
网站建设 2026/7/10 1:17:32

macOS Catalina下Jupyter Notebook配置全指南

1. 项目概述&#xff1a;为什么在 macOS Catalina 上跑 Jupyter Notebook 不是“装完就用”那么简单你刚把 MacBook 升级到 macOS Catalina 10.15&#xff0c;兴冲冲打开终端想启动jupyter notebook&#xff0c;结果弹出一连串报错&#xff1a;zsh: command not found: jupyter…

作者头像 李华
网站建设 2026/7/10 1:15:17

OCRAutoScore:基于多模态AI的智能阅卷系统架构解析

OCRAutoScore&#xff1a;基于多模态AI的智能阅卷系统架构解析 【免费下载链接】OCRAutoScore OCR自动化阅卷项目 项目地址: https://gitcode.com/gh_mirrors/oc/OCRAutoScore 在教育数字化转型的背景下&#xff0c;传统手工阅卷面临着效率低下、评分主观性强、数据分析…

作者头像 李华
网站建设 2026/7/10 1:15:11

在线算法在实时推荐系统中的应用与性能保障7

引言 实时推荐系统的背景与重要性在线算法的定义及其在推荐系统中的核心作用文章目标与结构概述 在线算法的基础理论 在线算法的核心概念与特点&#xff08;实时性、增量更新、有限历史数据&#xff09;常见在线算法分类&#xff08;如在线梯度下降、Bandit算法、流式聚类等…

作者头像 李华
网站建设 2026/7/10 1:14:54

代理模式和适配器模式区别

代理模式 vs 适配器模式 完整详解核心区别 一、先分别吃透两种模式本质 1. 适配器模式&#xff08;Adapter&#xff0c;结构型&#xff09; 核心目的&#xff1a;做「转换兼容」 两个已有接口不匹配&#xff0c;通过适配器做一层接口转换&#xff0c;让原本不能协作的两类代…

作者头像 李华