ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

MTK6735平台SPI驱动ST7789 RGB屏的实战指南

MTK6735平台SPI驱动ST7789 RGB屏的实战指南 简介面向嵌入式驱动开发工程师及硬件爱好者提供基于联发科MT6735平台的ST7789 SPI RGB液晶驱动示例解决入门级设备如手机、平板、智能手表显示系统中SPI通信配置、屏参初始化和底层像素控制等典型问题。压缩包为zip格式仅含1个C源文件约5KB大小代码紧凑但覆盖完整适合快速阅读和移植。目前该示例已被787人学习浏览在SPI LCD驱动相关资源中具有一定参考价值。C代码围绕驱动核心展开首先配置SPI的时钟频率、极性、相位保证与ST7789时序匹配随后通过命令/数据双通道写入控制器寄存器完成分辨率、色彩格式、扫描方向等初始化序列再配合帧缓冲管理和背光GPIO控制实现画面刷新与显示更新同时对传输错误做了检测处理便于排查通讯异常。以此可帮助开发者理解从SPI底层通信到LCD显示输出的完整链路为其他MIPI或SPI接口屏的驱动开发提供有力借鉴。1. 一套跑通裸屏的 MTK6735 SPI 驱动到底在调什么手头有一块 4 寸 480x800 的 RGB 并口屏主控是 MTK6735第一反应是按 ST7789 数据手册写初始化序列。实际点屏时才发现问题不在初始化代码而在总线选型和驱动架构MTK6735 的 LCD 接口走的是 MIPI DSIRGB 并口屏根本没有对应的控制器通道只能绕到 SPI 或者 GPIO 模拟并口去刷。压缩包里的 MTK6735.c 做的就是这件事用 SPI 接口把 ST7789 驱动起来代码本身不算复杂但涉及内核态的 SPI 设备注册、帧缓冲映射和背光控制和单片机上直接操作寄存器完全不是一个套路。这篇文章适合正在调 MT6735 平台 LCD 的驱动工程师也适合想搞明白 SPI 屏在内核里是怎么挂载的嵌入式开发者。2. SPI 与 RGB 屏的接口适配从引脚复用说起2.1 为什么不用 MIPI DSIMTK6735 的显示通路默认走 DSI常见做法是配置lk和kernel两套 dts 里的lcd_params让 LCM 驱动通过 DSI 命令或视频模式刷屏。但 ST7789 这种 RGB 并口屏的数据线是 16 位或 18 位并行总线外加 DCLK、HSYNC、VSYNC、DE 这些时序信号直接接到 MT6735 的 DSI 控制器是接不上的需要一颗 RGB 转 DSI 的桥接芯片才可能工作。压缩包里的做法更直接不用桥接芯片直接把 ST7789 当作 SPI 从设备挂在 MT6735 的 SPI 总线上用串行方式推像素数据。代价是刷屏带宽受限但胜在电路简单适合小分辨率屏。2.2 MT6735 的 SPI 控制器与引脚复用配置MT6735 的 SPI 控制器在主控内部引脚通过 GPIO 复用寄存器切到 SPI 功能。常见的接线是 SCK、MOSI、CS、DC命令/数据选择、RESET、背光使能六根线其中 DC 和 RESET 可以用普通 GPIO 控制不占用 SPI 控制器的硬件引脚。实际配置时要注意MT6735 的 GPIO 模式选择在 dts 里通过pinctrl节点完成而不是像单片机那样直接写一个复用寄存器。pio { spi0_pins: spi0_default { pins_cmd_dat { pinmux MTK_PINMUX_TD1__FUNC_SPI0_CLK, MTK_PINMUX_TD0__FUNC_SPI0_MI, MTK_PINMUX_CMD2__FUNC_SPI0_MO, MTK_PINMUX_RDN1__FUNC_SPI0_CS; drive-strength MTK_DRIVE_4mA; }; }; }; spi0 { pinctrl-names default; pinctrl-0 spi0_pins; status okay; st7789: st77890 { compatible mediatek,st7789-spi; reg 0; spi-max-frequency 10000000; }; };这段 dts 的关键在于pinmux配置。MTK_PINMUX_TD1__FUNC_SPI0_CLK这类宏写明了哪个物理引脚复用到 SPI0 的哪个信号复用关系对上之后spi-max-frequency才有意义。驱动强度设为 4mA 是为了在较短排线上保证信号边沿如果屏线超过 10cm可以适当增加到 6mA 或 8mA。提示pinctrl配置出错时SPI 寄存器能读到值但总线上完全无波形因为引脚根本没切到 SPI 功能。排查时先用示波器量 SCK 引脚有没有时钟输出。2.3 GPIO 模拟 SPI 作为备选方案如果 MT6735 的 SPI0 被其他外设占用也可以用 GPIO 模拟 SPI。这种方式不需要pinmux切到 SPI 功能只需要把几个 GPIO 设置成输出模式用gpio_set_value拉电平。代码层面和内核 SPI 框架没关系直接操作 GPIO 就能完成数据传输但刷屏速度受限于 GPIO 翻转频率实际测下来大概只能跑到 1MHz 左右适合做调试阶段的替代方案。3. ST7789 初始化序列的本质寄存器配置与时序参数3.1 初始化命令的字段结构ST7789 的初始化序列每条命令由三部分组成命令字节、参数字节数、参数字节。压缩包里的MTK6735.c用一张二维表来组织这些命令这种结构在内核 LCM 驱动里很常见和lk阶段的lcm_init表是同一个套路。以最常见的0x36命令为例它设置 MADCTL控制像素扫描方向和 RGB/BGR 排列。static const u8 st7789_init_table[][8] { {0x11, 0xFF, 0xFF}, /* 退出睡眠模式参数长度 0xFF 表示命令无参数 */ {0x36, 1, 0x00}, /* MADCTL默认扫描方向RGB 排列 */ {0x3A, 1, 0x05}, /* COLMOD16 位像素格式RGB565 */ {0x35, 0xFF, 0xFF}, /* Tearing Effect Line ON */ {0x29, 0xFF, 0xFF}, /* 开启显示 */ {0x00, 0xFF, 0xFF}, /* 结束标记 */ };这条表里第二列是参数长度0xFF表示没有参数第三列开始才是实际参数。驱动的初始化函数按顺序逐条执行遇到0x00才停止。某些屏的初始化序列需要延时等待比如0x11退出睡眠模式之后必须延时 120ms 才能继续发下一条命令这种延时通常单独处理不会写进表里。3.2 像素格式与 RGB 排列的匹配ST7789 支持 12/16/18 位像素格式由0x3A COLMOD命令的低三位决定。压缩包里的代码设置成0x05对应的 RGB565 格式也就是每像素两个字节。这里有个坑如果 framebuffer 里的数据格式和COLMOD设置不一致屏上颜色会完全错乱。比如 framebuffer 配置成 RGB888但 COLMOD 设成了 RGB565驱动会按 16 位解析内存里的数据每三个字节解析成两个像素图像完全花掉。MADCTL 的0x36命令控制方向常见的几个值寄存器值扫描方向适用场景0x00从上到下从左到右竖屏常规方向0xC0从下到上从右到左横屏或翻转屏0x40上下翻转倒装屏0x80左右翻转镜像屏调试时如果画面上下颠倒或左右镜像不急着改驱动代码先改0x36的参数值验证方向是否正确。3.3 复位时序与上电时序ST7789 要求硬件复位至少保持 10us 低电平释放后等待 120ms 再发初始化命令。很多内核驱动把复位时机放在probe函数里用gpio_set_value拉低再拉高。注意复位引脚必须复用成 GPIO 模式如果被 pinctrl 占用了会拉不动电平复位不彻底屏可能会显示花屏或白屏。static void st7789_reset(struct st7789_dev *dev) { gpio_set_value(dev-reset_gpio, 0); usleep_range(10000, 20000); /* 低电平至少保持 10ms */ gpio_set_value(dev-reset_gpio, 1); msleep(120); /* 等待内部电压稳定 */ }4. 帧缓冲与 SPI 传输RGB565 数据怎么送到 ST77894.1 内存分配与 mmap 映射MTK6735.c的典型做法是在内核里分配一块连续物理内存作为 framebuffer然后把这块内存映射到用户空间这样上层 UI 可以直接往这块内存写像素数据不需要每次刷屏都走系统调用。分配方法有两种kmalloc适合小屏但超过 64KB 容易失败大屏用dma_alloc_coherent更可靠可以保证物理地址连续。480x800 的 RGB565 屏需要 480 * 800 * 2 768000 字节必须用 DMA 分配。dev-fb dma_alloc_coherent(pdev-dev, 480 * 800 * 2, dev-fb_dma, GFP_KERNEL);用户空间拿到/dev/fb0设备节点之后用mmap映射这块缓冲区直接往映射地址写数据就能显示内容。内核驱动只需要在mmap回调里调用dma_mmap_coherent完成映射不需要自己维护用户空间进程的信息。4.2 命令/数据双通道的写屏封装ST7789 通过 DC 引脚区分命令和数据DC 为低时传输的是命令高时传输的是像素数据。SPI 传输本身不关心 DC 状态所以每次写操作之前要先设置 DC 引脚电平再进行 SPI 传输把两者封装成一个完整的写函数。static void st7789_write(struct st7789_dev *dev, u8 dc, const u8 *buf, size_t len) { struct spi_transfer tr { .tx_buf buf, .len len, .speed_hz dev-spi-max_speed_hz, .cs_change 0, }; struct spi_message msg; gpio_set_value(dev-dc_gpio, dc); spi_message_init(msg); spi_message_add_tail(tr, msg); spi_sync(dev-spi, msg); }DC 引脚的电平必须在spi_sync发送数据之前稳定下来中间不能有额外延时。如果 DC 在传输过程中被别的线程误改屏幕会出现命令和数据错位的现象表现为刷屏时随机出现色块。4.3 双缓冲与局部刷新整屏刷新 768000 字节SPI 跑 10MHz 理论上 0.6 秒刷一帧实际上加上命令开销和 GPIO 操作延时实际帧率不到 10fps用户能明显感知到卡顿。优化手段是只刷新变化区域通过0x2A和0x2B命令设置列地址和行地址配合0x2C写显存命令实现局部刷新。这在做电子书、仪表盘这类静态界面时特别有效。static void st7789_update_region(struct st7789_dev *dev, u16 x, u16 y, u16 w, u16 h) { u8 cmd[4]; cmd[0] x 8; cmd[1] x 0xFF; cmd[2] (x w - 1) 8; cmd[3] (x w - 1) 0xFF; st7789_write(dev, 0, (u8[]){0x2A}, 1); st7789_write(dev, 0, cmd, 4); cmd[0] y 8; cmd[1] y 0xFF; cmd[2] (y h - 1) 8; cmd[3] (y h - 1) 0xFF; st7789_write(dev, 0, (u8[]){0x2B}, 1); st7789_write(dev, 0, cmd, 4); st7789_write(dev, 0, (u8[]){0x2C}, 1); st7789_write(dev, 1, (u8 *)dev-fb (y * 480 x) * 2, w * h * 2); }注意st7789_write(dev, 0, (u8[]){0x2A}, 1)这种写法用到了复合字面量内核编译时默认 GNU C 标准直接支持。局部刷新的前提是内核实现了fb_info的fb_ioctl或者自定义 IOCTL否则用户空间只能整屏刷新。5. 背光控制与亮度调节的驱动实现5.1 PWM 背光的内核配置ST7789 本身不控制背光背光电路通常是 LED 串并结构由独立的 PWM 信号调节亮度。MT6735 平台一般用pwm-backlight这个通用驱动在 dts 里指定 PWM 通道和亮度等级表。backlight: pwm-backlight { compatible pwm-backlight; pwms pwm0 0 20000 0; brightness-levels 0 1 2 3 4 5 6 7 8 9 10; default-brightness-level 8; };pwms里的三个数字是 PWM 控制器 phandle、PWM 通道号和周期纳秒这里周期是 20000ns对应 50kHz。早期 MTK 平台还有一个mediatek,pwm-mode的废弃属性新代码里不用管它。5.2 亮度等级线性化问题人眼对亮度的感知不是线性的PWM 占空比从 10% 调到 20%亮度翻倍的感知并不强。所以brightness-levels数组通常不是等差序列而是按照感知亮度调整的曲线。省电模式下可以把低亮度等级压缩到 0~3 的范围内让低端调节更细腻。#define MAX_BRIGHTNESS 255 static int st7789_set_brightness(struct backlight_device *bl) { int brightness bl-props.brightness; struct st7789_dev *dev bl_get_data(bl); if (!dev-pwm) return -ENODEV; pwm_config(dev-pwm, dev-pwm_period * brightness / MAX_BRIGHTNESS, dev-pwm_period); pwm_enable(dev-pwm); return 0; }这段代码里pwm_period * brightness / MAX_BRIGHTNESS就是占空比换算。如果亮度调低之后屏幕闪烁检查 PWM 频率是不是低于 1kHz。ST7789 的背光电源引脚对低频 PWM 敏感频率太低会看到明显的频闪甚至影响拍照时的成像效果。提示背光关闭顺序要放在屏休眠之前先切断背光再发0x28显示关闭命令避免出现瞬间白屏或者残影。6. 疑难排查SPI 屏点不亮时先看这三个位置拿到 SPI 屏驱动上电后屏不亮、花屏或者颜色不对不要急着改驱动代码先按这个顺序查引脚的复用关系对不对SPI 波形能不能抓到初始化序列是不是完整。这三步能覆盖九成以上点不亮的问题。第一引脚复用。用示波器量 SCK 引脚如果没有波形直接看 dts 里的pinctrl配置是否正确。MT6735 的 GPIO 复用表经常变同一个物理引脚在不同项目里可能会被分给 UART 或 SD 卡复用配置冲突时 SPI 根本拉不出波形。第二SPI 波形时序。SCK 空闲电平、数据采样沿这些参数通过spi mode决定ST7789 一般支持 SPI Mode 0 和 Mode 3。内核里在 driver 的.probe函数中设置dev-spi-mode SPI_MODE_0如果默认值不对命令发了但控制器不认表现是屏无任何响应。第三初始化序列是否合法。某些 ST7789 的参数版本不一样新的 ST7789V 对0xE0到0xE4这组伽马校正命令的默认值和旧版不同直接照抄旧屏的初始化表显示效果会偏色或对比度异常。如果在调试过程中遇到屏幕明明能亮但显示内容全花的情况大概率是 COLMOD 设置和 framebuffer 的 bits_per_pixel 不一致检查0x3A参数和fb_info里的var.bits_per_pixel是否都为 16。也可以用cat /sys/class/graphics/fb0/bits_per_pixel在 shell 里直接确认内核实际的位深配置。最后SPI 刷屏的带宽瓶颈就在总线频率上如果整个屏幕刷新需要超过 200ms 才能完成考虑缩小刷新窗口或者把 SPI 时钟调到max_speed_hz允许的上限但要注意排线过长时高频信号反射会导致丢字节这是 SPI 刷屏永远的权衡。本文还有配套的精品资源点击获取
返回列表