1. 项目背景与目标
最近在T113平台上接手了一个特殊尺寸LCD屏的驱动移植任务,这是一块400x960分辨率的ST7701S驱动芯片的屏幕。不同于常见的16:9或4:3比例显示屏,这种长条形屏幕在工业控制、智能家居中控等垂直场景有独特优势。
选择从UBOOT开始移植而非内核,主要基于两点考虑:一是UBOOT阶段就能显示logo和调试信息,方便后续开发;二是UBOOT的驱动框架相对简单,适合快速验证硬件连接。实际开发中遇到不少挑战,特别是这种非常规分辨率屏幕的时序配置,以及RGB格式设置的"玄学"问题。
2. 硬件环境搭建
2.1 核心硬件配置
本次使用的硬件平台配置如下:
- 主控芯片:全志T113(ARM Cortex-A7双核)
- 显示接口:RGB 18-bit并行接口
- 屏幕型号:ST7701S驱动IC的LCD模组
- 物理尺寸:42mm(宽) x 102mm(高)
- 分辨率:400(RGB) x 960(V)
- 通信接口:SPI(仅用于初始化配置)
2.2 关键引脚连接
根据设备树配置,硬件连接关系如下表所示:
| 功能信号 | GPIO引脚 | 备注 |
|---|---|---|
| SPI_CS | PE1 | 片选信号 |
| SPI_SDA | PE4 | 数据线 |
| SPI_SCL | PE0 | 时钟线 |
| RESET | PE5 | 复位信号 |
| RGB数据线 | PE组 | 18-bit RGB接口 |
注意:RGB接口使用了pinctrl的rgb18_pins_a/b两组引脚配置,需要确保板级支持包(BSP)中已正确定义这些引脚复用功能。
3. 设备树深度解析
3.1 基础参数配置
c复制&lcd0 {
lcd_used = <1>; // 启用LCD控制器
lcd_driver_name = "st7701SPI"; // 驱动标识名
lcd_if = <0>; // 0表示RGB接口
/* 分辨率设置 */
lcd_x = <400>; // 水平像素数
lcd_y = <960>; // 垂直像素数
/* 物理尺寸(mm) */
lcd_width = <42>;
lcd_height = <102>;
/* 像素时钟 */
lcd_dclk_freq = <24>; // 单位MHz
}
这里有几个关键点需要注意:
- 像素时钟24MHz是根据400x960@60Hz计算得出的理论值,实际调试发现高于此值会出现重影
- 物理尺寸参数会影响DPI(每英寸点数)计算,虽然UBOOT阶段不一定使用,但建议准确填写
3.2 时序参数详解
c复制/* 水平时序 */
lcd_hbp = <10>; // 水平后沿(像素数)
lcd_hfp = <10>; // 水平前沿(像素数)
lcd_hspw = <5>; // 行同步脉宽
lcd_ht = <425>; // 水平总时间=400+10+10+5
/* 垂直时序 */
lcd_vbp = <10>; // 垂直后沿(行数)
lcd_vfp = <10>; // 垂直前沿(行数)
lcd_vspw = <5>; // 场同步脉宽
lcd_vt = <985>; // 垂直总时间=960+10+10+5
时序参数的计算公式:
- 水平总时间 = lcd_x + lcd_hbp + lcd_hfp + lcd_hspw
- 垂直总时间 = lcd_y + lcd_vbp + lcd_vfp + lcd_vspw
调试心得:最初尝试从规格书推导时序参数,但发现实际效果不理想。后来用示波器测量原厂demo板的时序,才得到这组合适的值。建议有条件一定要做硬件信号测量。
3.3 接口配置陷阱
c复制lcd_frm = <1>; // RGB666格式
lcd_hv_clk_phase = <0>; // 时钟相位
lcd_hv_sync_polarity = <0>; // 同步信号极性
lcd_rb_swap = <0>; // 不交换红蓝通道
这里有个大坑:设备树中设置的RGB格式(lcd_frm)实际上没有生效!必须通过ST7701S的3AH命令来设置接口格式。经过反复测试,发现驱动中硬编码的配置会覆盖设备树参数。
4. 驱动开发关键点
4.1 SPI初始化序列
ST7701S需要通过SPI发送初始化命令序列。在UBOOT中,我们需要实现以下关键操作:
- 硬件复位:拉低RESET引脚至少10ms
- 延时等待:复位后需要20ms稳定时间
- 发送初始化命令:按照ST7701S规格书的序列
典型的初始化代码框架:
c复制static void lcd_st7701s_init(void)
{
/* 1. 硬件复位 */
gpio_direction_output(LCD_RESET_GPIO, 0);
mdelay(15);
gpio_direction_output(LCD_RESET_GPIO, 1);
mdelay(20);
/* 2. 发送初始化命令 */
spi_send_cmd(0x11); // Sleep out
mdelay(120);
/* 3AH命令设置接口格式 */
spi_send_cmd(0x3A);
spi_send_data(0x66); // RGB666
/* 其他初始化命令... */
}
4.2 电源管理
由于项目中没有使用PWM控制背光,直接通过GPIO控制:
c复制/* 简单背光控制 */
#define BACKLIGHT_GPIO GPIO_PE(2)
gpio_direction_output(BACKLIGHT_GPIO, 1);
虽然这种常亮方式不够节能,但在UBOOT阶段足够使用。内核驱动中可以进一步完善PWM调光功能。
5. 调试经验与问题解决
5.1 屏幕旋转尝试失败
最初尝试将400x960配置为960x400,希望通过硬件旋转节省软件处理开销。但在设备树和驱动中尝试多种配置均未成功,最终发现:
- ST7701S本身不支持硬件旋转
- T113的LCD控制器旋转功能需要特定配置
- 在UBOOT阶段实现旋转性价比太低
最终方案:保持400x960配置,在应用层通过framebuffer操作实现旋转。
5.2 色彩格式问题
遇到的主要问题表现:
- 设置lcd_frm=1(RGB666)但实际显示为RGB565
- 颜色显示异常,红色和蓝色通道错乱
解决方法:
- 忽略设备树的lcd_frm参数
- 在驱动中硬编码3AH命令为0x66(RGB666)
- 检查lcd_rb_swap参数并根据实际效果调整
5.3 时序参数优化
调试过程中出现的现象与解决方案:
| 现象 | 可能原因 | 解决方案 |
|---|---|---|
| 画面抖动 | 像素时钟过高 | 逐步降低lcd_dclk_freq |
| 边缘模糊 | 同步脉宽不足 | 增加lcd_hspw/lcd_vspw |
| 画面偏移 | 前后沿不匹配 | 调整lcd_hbp/lcd_vbp |
6. 完整驱动实现建议
基于本次移植经验,总结出ST7701S驱动开发的最佳实践:
-
分层实现:
- UBOOT阶段:最小功能集,显示logo和调试信息
- 内核驱动:完整功能,支持动态配置和电源管理
-
配置分离:
- 将屏幕参数放在设备树中
- 驱动只处理通用逻辑
-
调试接口:
- 通过sysfs暴露关键参数
- 支持运行时调整时序
-
电源管理:
- 实现完整的sleep/wakeup序列
- 支持背光PWM控制
以下是内核驱动中建议添加的核心结构体:
c复制struct st7701s_panel {
struct drm_panel base;
struct spi_device *spi;
struct gpio_desc *reset;
struct gpio_desc *bl_gpio;
struct regulator *vdd;
/* 参数可配置 */
u32 width;
u32 height;
u32 hbp, hfp, hsync;
u32 vbp, vfp, vsync;
};
7. 性能优化方向
针对这种特殊分辨率屏幕,后续可以考虑以下优化:
-
内存优化:
- 使用16bit色深节省内存
- 实现局部刷新功能
-
旋转加速:
- 利用DMA2D硬件加速
- 实现90/270度旋转支持
-
动态调频:
- 根据内容复杂度调整刷新率
- 静态画面时降低帧率
-
多屏支持:
- 扩展驱动支持多种ST77xx系列
- 通过设备树自动识别型号
移植这种非常规分辨率屏幕确实会遇到不少挑战,但解决问题的过程也积累了很多有价值的经验。特别是在时序调试阶段,建议准备一台数字示波器,直接观察HSYNC、VSYNC和DE信号的实际波形,这比单纯调整参数要高效得多。
关于RGB格式设置无效的问题,后来查阅全志的SDK才发现,他们的LCD控制器对某些配置项有特殊处理方式。这也提醒我们,在嵌入式开发中,有时候"官方推荐"的配置路径可能比标准方法更有效。