news 2026/5/8 9:26:34

手把手教你为i.MX6ULL开发板驱动1.3寸ST7789 TFT屏(含完整设备树与驱动代码)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
手把手教你为i.MX6ULL开发板驱动1.3寸ST7789 TFT屏(含完整设备树与驱动代码)

i.MX6ULL开发板驱动1.3寸ST7789 TFT屏全流程实战指南

在嵌入式Linux开发中,显示设备的驱动往往是项目开发的关键环节之一。本文将详细介绍如何在i.MX6ULL开发板上驱动1.3寸ST7789 TFT屏幕的全过程,从硬件连接到设备树配置,再到驱动编写和测试,提供一套完整的解决方案。

1. 硬件准备与连接

在开始软件配置前,确保已准备好以下硬件组件:

  • i.MX6ULL开发板(如正点原子阿尔法)
  • 1.3寸ST7789驱动的TFT屏幕(240×240分辨率)
  • 杜邦线若干
  • 5V/3.3V电源适配器

ST7789通常通过SPI接口与主控通信,需要连接以下信号线:

TFT屏引脚开发板GPIO功能描述
VCC3.3V电源正极
GNDGND电源地
SCLSPI3_SCLK时钟信号
SDASPI3_MOSI数据输入
RESGPIO1_IO01复位信号
DCGPIO1_IO04数据/命令选择
CSGPIO1_IO20片选信号

硬件连接注意事项:

  • 确保电源电压匹配,ST7789通常工作在3.3V
  • 信号线长度不宜过长,避免信号完整性问题
  • 如果屏幕有背光控制,可连接到PWM输出引脚实现亮度调节

2. 设备树配置与编译

设备树是Linux内核识别硬件的重要配置文件,我们需要为SPI接口和GPIO添加相应节点。

2.1 修改设备树源文件

找到开发板对应的设备树文件(如imx6ull-alientek-emmc.dts),添加以下内容:

/* 在根节点下添加GPIO控制节点 */ / { ips_reset: ips-reset { compatible = "gpio-reset"; gpios = <&gpio1 1 GPIO_ACTIVE_LOW>; status = "okay"; }; ips_dc: ips-dc { compatible = "gpio-control"; gpios = <&gpio1 4 GPIO_ACTIVE_HIGH>; status = "okay"; }; }; /* 在iomuxc节点中添加引脚复用配置 */ &iomuxc { pinctrl_ips: ipsgrp { fsl,pins = < MX6UL_PAD_GPIO1_IO01__GPIO1_IO01 0x10B0 /* RESET */ MX6UL_PAD_GPIO1_IO04__GPIO1_IO04 0x10B0 /* DC */ MX6UL_PAD_UART2_CTS__GPIO1_IO20 0x10B0 /* CS */ >; }; }; /* 配置SPI3控制器 */ &ecspi3 { fsl,spi-num-chipselects = <1>; cs-gpios = <&gpio1 20 GPIO_ACTIVE_LOW>; pinctrl-names = "default"; pinctrl-0 = <&pinctrl_ecspi3 &pinctrl_ips>; status = "okay"; st7789: st7789@0 { compatible = "sitronix,st7789"; spi-max-frequency = <50000000>; reg = <0>; reset-gpios = <&gpio1 1 GPIO_ACTIVE_LOW>; dc-gpios = <&gpio1 4 GPIO_ACTIVE_HIGH>; width = <240>; height = <240>; buswidth = <8>; fps = <30>; }; };

2.2 设备树编译与更新

配置完成后,需要编译设备树并更新到开发板:

# 编译设备树 make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- dtbs # 将生成的dtb文件拷贝到开发板 scp arch/arm/boot/dts/imx6ull-alientek-emmc.dtb root@开发板IP:/boot/ # 在开发板上更新设备树 cp /boot/imx6ull-alientek-emmc.dtb /sys/firmware/devicetree/base/

验证设备树节点是否成功创建:

# 检查SPI设备节点 ls /sys/bus/spi/devices/spi3.0/ # 检查GPIO控制节点 ls /sys/class/gpio/

3. Linux驱动开发

ST7789驱动可以采用标准的SPI框架实现,下面展示关键部分的驱动代码。

3.1 驱动框架初始化

#include <linux/module.h> #include <linux/spi/spi.h> #include <linux/delay.h> #include <linux/gpio/consumer.h> #define DRIVER_NAME "st7789" struct st7789_data { struct spi_device *spi; struct gpio_desc *reset_gpio; struct gpio_desc *dc_gpio; u16 width; u16 height; }; static int st7789_write_command(struct st7789_data *st7789, u8 cmd) { int ret; struct spi_transfer xfer = { .len = 1, .tx_buf = &cmd, }; struct spi_message msg; gpiod_set_value(st7789->dc_gpio, 0); // 命令模式 spi_message_init(&msg); spi_message_add_tail(&xfer, &msg); ret = spi_sync(st7789->spi, &msg); return ret; } static int st7789_write_data(struct st7789_data *st7789, u8 *data, size_t len) { int ret; struct spi_transfer xfer = { .len = len, .tx_buf = data, }; struct spi_message msg; gpiod_set_value(st7789->dc_gpio, 1); // 数据模式 spi_message_init(&msg); spi_message_add_tail(&xfer, &msg); ret = spi_sync(st7789->spi, &msg); return ret; }

3.2 屏幕初始化序列

ST7789需要按照特定序列进行初始化,以下是关键初始化步骤:

static int st7789_init_sequence(struct st7789_data *st7789) { int ret = 0; // 硬件复位 gpiod_set_value(st7789->reset_gpio, 0); msleep(20); gpiod_set_value(st7789->reset_gpio, 1); msleep(120); // 发送初始化命令 const u8 init_cmds[] = { 0x36, 0x00, // MADCTL: Memory Data Access Control 0x3A, 0x05, // COLMOD: Interface Pixel Format 0xB2, 0x0C, 0x0C, 0x00, 0x33, 0x33, // PORCTRL: Porch Setting 0xB7, 0x35, // GCTRL: Gate Control 0xBB, 0x19, // VCOMS: VCOM Setting 0xC0, 0x2C, // LCMCTRL: LCM Control 0xC2, 0x01, // VDVVRHEN: VDV and VRH Command Enable 0xC3, 0x12, // VRHS: VRH Set 0xC4, 0x20, // VDVS: VDV Set 0xC6, 0x0F, // FRCTRL2: Frame Rate Control 0xD0, 0xA4, 0xA1, // PWCTRL1: Power Control 1 0xE0, // PVGAMCTRL: Positive Voltage Gamma Control 0xD0, 0x04, 0x0D, 0x11, 0x13, 0x2B, 0x3F, 0x54, 0x4C, 0x18, 0x0D, 0x0B, 0x1F, 0x23, 0xE1, // NVGAMCTRL: Negative Voltage Gamma Control 0xD0, 0x04, 0x0C, 0x11, 0x13, 0x2C, 0x3F, 0x44, 0x51, 0x2F, 0x1F, 0x1F, 0x20, 0x23, 0x21, // INVON: Display Inversion On 0x11, // SLPOUT: Sleep Out 0x29, // DISPON: Display On }; for (int i = 0; i < ARRAY_SIZE(init_cmds); ) { if (init_cmds[i] == 0xE0 || init_cmds[i] == 0xE1) { ret = st7789_write_command(st7789, init_cmds[i]); i++; ret |= st7789_write_data(st7789, (u8 *)&init_cmds[i], 14); i += 14; } else if (i < ARRAY_SIZE(init_cmds) - 1 && init_cmds[i+1] != 0xE0 && init_cmds[i+1] != 0xE1) { ret = st7789_write_command(st7789, init_cmds[i]); i++; ret |= st7789_write_data(st7789, (u8 *)&init_cmds[i], 1); i++; } else { ret = st7789_write_command(st7789, init_cmds[i]); i++; } if (ret < 0) { dev_err(&st7789->spi->dev, "Init sequence error at cmd 0x%02x\n", init_cmds[i-1]); return ret; } msleep(10); } return 0; }

3.3 实现帧缓冲接口

为了与Linux显示子系统集成,需要实现帧缓冲(fb)接口:

#include <linux/fb.h> static int st7789_fb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, u_int transp, struct fb_info *info) { // 设置颜色寄存器 return 0; } static int st7789_fb_blank(int blank_mode, struct fb_info *info) { // 控制屏幕空白模式 struct st7789_data *st7789 = info->par; switch (blank_mode) { case FB_BLANK_UNBLANK: st7789_write_command(st7789, 0x29); // DISPON break; case FB_BLANK_NORMAL: case FB_BLANK_VSYNC_SUSPEND: case FB_BLANK_HSYNC_SUSPEND: case FB_BLANK_POWERDOWN: st7789_write_command(st7789, 0x28); // DISPOFF break; } return 0; } static struct fb_ops st7789_fb_ops = { .owner = THIS_MODULE, .fb_setcolreg = st7789_fb_setcolreg, .fb_blank = st7789_fb_blank, .fb_fillrect = cfb_fillrect, .fb_copyarea = cfb_copyarea, .fb_imageblit = cfb_imageblit, }; static int st7789_probe(struct spi_device *spi) { struct fb_info *info; struct st7789_data *st7789; int ret = 0; // 分配帧缓冲结构 info = framebuffer_alloc(sizeof(*st7789), &spi->dev); if (!info) return -ENOMEM; st7789 = info->par; st7789->spi = spi; // 获取GPIO资源 st7789->reset_gpio = devm_gpiod_get(&spi->dev, "reset", GPIOD_OUT_LOW); if (IS_ERR(st7789->reset_gpio)) { ret = PTR_ERR(st7789->reset_gpio); goto err_fb_release; } st7789->dc_gpio = devm_gpiod_get(&spi->dev, "dc", GPIOD_OUT_LOW); if (IS_ERR(st7789->dc_gpio)) { ret = PTR_ERR(st7789->dc_gpio); goto err_fb_release; } // 初始化帧缓冲信息 info->fbops = &st7789_fb_ops; info->flags = FBINFO_FLAG_DEFAULT; info->pseudo_palette = &st7789->pseudo_palette; // 设置显示参数 info->var.xres = st7789->width; info->var.yres = st7789->height; info->var.xres_virtual = info->var.xres; info->var.yres_virtual = info->var.yres; info->var.bits_per_pixel = 16; info->var.red.offset = 11; info->var.red.length = 5; info->var.green.offset = 5; info->var.green.length = 6; info->var.blue.offset = 0; info->var.blue.length = 5; // 分配显示缓冲区 info->screen_size = info->var.xres * info->var.yres * info->var.bits_per_pixel / 8; info->screen_buffer = dma_alloc_coherent(&spi->dev, info->screen_size, &info->fix.smem_start, GFP_KERNEL); if (!info->screen_buffer) { ret = -ENOMEM; goto err_fb_release; } info->fix.smem_len = info->screen_size; info->fix.type = FB_TYPE_PACKED_PIXELS; info->fix.visual = FB_VISUAL_TRUECOLOR; info->fix.line_length = info->var.xres * info->var.bits_per_pixel / 8; // 初始化屏幕 ret = st7789_init_sequence(st7789); if (ret) goto err_dma_free; // 注册帧缓冲设备 ret = register_framebuffer(info); if (ret < 0) goto err_dma_free; spi_set_drvdata(spi, info); dev_info(&spi->dev, "fb%d: %s frame buffer device\n", info->node, info->fix.id); return 0; err_dma_free: dma_free_coherent(&spi->dev, info->screen_size, info->screen_buffer, info->fix.smem_start); err_fb_release: framebuffer_release(info); return ret; }

4. 驱动编译与测试

4.1 编写Makefile

obj-m := st7789.o KDIR := /path/to/kernel/source PWD := $(shell pwd) all: $(MAKE) -C $(KDIR) M=$(PWD) modules clean: $(MAKE) -C $(KDIR) M=$(PWD) clean

4.2 编译与加载驱动

# 交叉编译驱动 make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- # 将驱动拷贝到开发板 scp st7789.ko root@开发板IP:/lib/modules/$(uname -r)/kernel/drivers/video/ # 在开发板上加载驱动 depmod -a modprobe st7789

4.3 测试驱动功能

编写简单的测试程序验证驱动功能:

#include <stdio.h> #include <fcntl.h> #include <sys/ioctl.h> #include <linux/fb.h> #include <sys/mman.h> int main() { int fbfd = open("/dev/fb0", O_RDWR); if (fbfd == -1) { perror("open fb device"); return -1; } // 获取屏幕信息 struct fb_var_screeninfo vinfo; ioctl(fbfd, FBIOGET_VSCREENINFO, &vinfo); // 映射帧缓冲 char *fbp = mmap(0, vinfo.yres_virtual * vinfo.xres_virtual * 2, PROT_READ | PROT_WRITE, MAP_SHARED, fbfd, 0); // 绘制彩色条纹 for (int y = 0; y < vinfo.yres; y++) { for (int x = 0; x < vinfo.xres; x++) { int offset = (y * vinfo.xres + x) * 2; if (x < 80) { // 红色区域 *(unsigned short *)(fbp + offset) = 0xF800; } else if (x < 160) { // 绿色区域 *(unsigned short *)(fbp + offset) = 0x07E0; } else { // 蓝色区域 *(unsigned short *)(fbp + offset) = 0x001F; } } } munmap(fbp, 0); close(fbfd); return 0; }

编译并运行测试程序:

arm-linux-gnueabihf-gcc -o fb_test fb_test.c ./fb_test

5. 性能优化与调试技巧

5.1 SPI传输优化

ST7789支持最高80MHz的SPI时钟,但在实际应用中需要考虑信号完整性和功耗:

// 在驱动probe函数中添加 spi->max_speed_hz = 50000000; // 50MHz spi->mode = SPI_MODE_3; // CPOL=1, CPHA=1 spi_setup(spi);

优化建议:

  • 使用DMA传输减少CPU开销
  • 批量发送像素数据而非单像素传输
  • 合理使用双缓冲技术减少画面撕裂

5.2 常见问题排查

问题1:屏幕无显示

  • 检查电源和背光是否正常
  • 用逻辑分析仪确认SPI信号
  • 验证复位时序是否正确
  • 检查设备树配置是否生效

问题2:显示花屏

  • 降低SPI时钟频率测试
  • 检查数据/命令(DC)信号时序
  • 确认颜色格式配置(通常为RGB565)

问题3:刷新率低

  • 优化区域刷新而非全屏刷新
  • 使用硬件加速功能
  • 检查SPI总线是否被其他设备占用

5.3 高级功能实现

部分刷新(Partial Refresh)

void st7789_set_window(struct st7789_data *st7789, u16 x1, u16 y1, u16 x2, u16 y2) { u8 buf[4]; st7789_write_command(st7789, 0x2A); // CASET buf[0] = x1 >> 8; buf[1] = x1 & 0xFF; buf[2] = x2 >> 8; buf[3] = x2 & 0xFF; st7789_write_data(st7789, buf, 4); st7789_write_command(st7789, 0x2B); // RASET buf[0] = y1 >> 8; buf[1] = y1 & 0xFF; buf[2] = y2 >> 8; buf[3] = y2 & 0xFF; st7789_write_data(st7789, buf, 4); st7789_write_command(st7789, 0x2C); // RAMWR }

睡眠模式与低功耗

void st7789_enter_sleep(struct st7789_data *st7789) { st7789_write_command(st7789, 0x10); // SLPIN msleep(120); // 等待屏幕完全进入睡眠 } void st7789_exit_sleep(struct st7789_data *st7789) { st7789_write_command(st7789, 0x11); // SLPOUT msleep(120); // 等待屏幕完全唤醒 }

通过以上完整的开发流程,开发者可以成功在i.MX6ULL开发板上驱动ST7789 TFT屏幕,并实现高性能的图形显示功能。实际项目中,还可以进一步优化驱动性能,添加触摸支持等功能,打造更完善的嵌入式显示解决方案。

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

多终端命令历史实时同步工具multicli的设计与部署指南

1. 项目概述&#xff1a;一个命令&#xff0c;多端同步如果你和我一样&#xff0c;日常开发需要在多个终端环境之间频繁切换——比如本地的 macOS 终端、远程的 Linux 服务器&#xff0c;甚至 Windows 上的 WSL——那你一定对“命令历史不同步”这件事深恶痛绝。在服务器上敲了…

作者头像 李华
网站建设 2026/5/8 9:24:29

LLM动态干预技术:实时调控与合规实践

1. 项目概述 大型语言模型&#xff08;LLM&#xff09;正在重塑人机交互的边界&#xff0c;但如何让这些"黑箱"系统按照人类意图稳定输出&#xff0c;一直是业界痛点。去年我在参与某智能客服系统升级时&#xff0c;就遇到过模型突然输出不合规回复的棘手情况。动态干…

作者头像 李华
网站建设 2026/5/8 9:18:34

如何用Boss直聘批量投简历工具实现3天投递100+岗位

如何用Boss直聘批量投简历工具实现3天投递100岗位 【免费下载链接】boss_batch_push Boss直聘批量投简历&#xff0c;批量发送自定义招呼语 项目地址: https://gitcode.com/gh_mirrors/bo/boss_batch_push 还在为求职效率低下而烦恼吗&#xff1f;每天花费数小时手动筛选…

作者头像 李华
网站建设 2026/5/8 9:16:33

XHS-Downloader 终极指南:三步实现小红书内容高效采集与下载

XHS-Downloader 终极指南&#xff1a;三步实现小红书内容高效采集与下载 【免费下载链接】XHS-Downloader 小红书&#xff08;XiaoHongShu、RedNote&#xff09;链接提取/作品采集工具&#xff1a;提取账号发布、收藏、点赞、专辑作品链接&#xff1b;提取搜索结果作品、用户链…

作者头像 李华
网站建设 2026/5/8 9:15:56

基于Claude的自我学习AI智能体框架:架构、实现与优化

1. 项目概述&#xff1a;一个让Claude学会自我进化的开源框架 最近在AI开源社区里&#xff0c;有个项目让我眼前一亮—— ychampion/claude-self-learning 。这名字听起来就挺有意思的&#xff0c;对吧&#xff1f;一个能让Claude&#xff08;Anthropic公司那个著名的AI助手&…

作者头像 李华
网站建设 2026/5/8 9:15:43

视频扩散模型VerseCrafter:原理、优化与应用实践

1. 视频生成技术的前世今生 去年在帮一个影视工作室做后期时&#xff0c;他们需要补拍几个特定天气的镜头&#xff0c;但实地拍摄成本实在太高。当时试用了几个AI视频生成工具&#xff0c;效果都不尽人意——要么人物动作僵硬得像提线木偶&#xff0c;要么场景变换时出现诡异的…

作者头像 李华