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

张开发
2026/4/18 2:32:30 15 分钟阅读

分享文章

手把手教你为i.MX6ULL开发板驱动1.3寸ST7789 TFT屏(附完整设备树与驱动代码)
i.MX6ULL开发板驱动ST7789 TFT屏全流程实战指南在嵌入式Linux开发中显示设备的驱动实现往往是项目开发的关键环节之一。本文将详细介绍如何在i.MX6ULL开发板上驱动1.3寸ST7789 TFT屏幕的全过程从硬件连接到设备树配置再到驱动编写与测试提供一套完整的解决方案。1. 硬件准备与环境搭建1.1 硬件清单与连接首先需要确认手头的硬件设备是否符合要求i.MX6ULL开发板本文以Alientek的i.MX6ULL开发板为例1.3寸TFT屏幕240×240分辨率ST7789驱动芯片连接线材杜邦线或其他可靠的连接方式硬件连接对照表TFT屏引脚开发板对应引脚功能描述GNDGND地线VCC3.3V电源SCLUART2_CTRSPI时钟SDAUART2_RXDSPI数据RESGPIO1_IO01复位信号DCGPIO1_IO04数据/命令选择BLKNC背光控制(可选)提示实际连接时请务必参考开发板和屏幕的具体规格书不同厂商的引脚定义可能有所差异。1.2 开发环境准备在开始开发前需要确保开发环境已正确配置交叉编译工具链arm-linux-gnueabihf-Linux内核源码与开发板运行的版本一致NFS或TFTP服务用于快速传输文件到开发板串口调试工具如minicom或putty# 检查交叉编译工具链是否安装成功 arm-linux-gnueabihf-gcc -v2. 设备树配置与修改2.1 设备树基础概念设备树(Device Tree)是描述硬件配置的数据结构Linux内核通过它来识别和管理硬件资源。对于i.MX6ULL开发板我们需要修改设备树来添加对ST7789屏幕的支持。2.2 具体修改步骤找到开发板对应的设备树文件通常是imx6ull-alientek-emmc.dts添加以下内容// 在根节点/下添加引脚控制节点 { ipsRes { #address-cells 1; #size-cells 1; compatible liefyuan-ipsRes; pinctrl-names default; pinctrl-0 pinctrl_ipsRes; res-gpio gpio1 1 GPIO_ACTIVE_HIGH; status okay; }; ipsDc { #address-cells 1; #size-cells 1; compatible liefyuan-ipsDc; pinctrl-names default; pinctrl-0 pinctrl_ipsDc; dc-gpio gpio1 4 GPIO_ACTIVE_HIGH; status okay; }; } // 在iomuxc节点中添加引脚复用配置 iomuxc { pinctrl_ipsRes: ipsRes { fsl,pins MX6UL_PAD_GPIO1_IO01__GPIO1_IO01 0x10B0 ; }; pinctrl_ipsDc: ipsDc { fsl,pins MX6UL_PAD_GPIO1_IO04__GPIO1_IO04 0x10B0 ; }; } // 配置SPI3控制器 ecspi3 { fsl,spi-num-chipselects 1; cs-gpio gpio1 20 GPIO_ACTIVE_LOW; pinctrl-names default; pinctrl-0 pinctrl_ecspi3; status okay; spidev: ipsTft0 { compatible alientek,ipsTft; spi-max-frequency 1000000000; reg 0; }; }2.3 设备树编译与更新修改完成后需要编译设备树并更新到开发板# 编译设备树 make dtbs # 将生成的.dtb文件拷贝到开发板的/boot目录 cp arch/arm/boot/dts/imx6ull-alientek-emmc.dtb /tftpboot/重启开发板后可以通过以下命令检查设备树节点是否添加成功# 查看SPI3控制器节点 ls /sys/firmware/devicetree/base/soc/aips-bus02000000/spba-bus02000000/ecspi020100003. 驱动程序设计3.1 驱动框架搭建Linux设备驱动通常采用字符设备框架下面展示驱动的主要结构#include linux/module.h #include linux/spi/spi.h #define ipsTft_CNT 1 #define ipsTft_NAME ipsTft struct ipsTft_dev { dev_t devid; struct cdev cdev; struct class *class; struct device *device; struct device_node *nd; int major; void *private_data; int dc_gpio; int res_gpio; int cs_gpio; }; static struct ipsTft_dev ipsTftdev; static const struct file_operations ipsTft_ops { .owner THIS_MODULE, .open ipsTft_open, .release ipsTft_release, }; static struct spi_driver ipsTft_driver { .probe ipsTft_probe, .remove ipsTft_remove, .driver { .owner THIS_MODULE, .name ipsTft, .of_match_table ipsTft_of_match, }, .id_table ipsTft_id, };3.2 SPI通信实现ST7789通过SPI接口进行通信需要实现基本的读写函数static s32 ipsTft_write_regs(struct ipsTft_dev *dev, u8 *buf, u8 len) { int ret; struct spi_message m; struct spi_transfer *t; struct spi_device *spi (struct spi_device *)dev-private_data; t kzalloc(sizeof(struct spi_transfer), GFP_KERNEL); t-tx_buf buf; t-len len; spi_message_init(m); spi_message_add_tail(t, m); ret spi_sync(spi, m); kfree(t); return ret; } void write_command(struct ipsTft_dev *dev, u8 cmd) { gpio_set_value(dev-dc_gpio, 0); ipsTft_write_regs(dev, cmd, 1); } void write_data(struct ipsTft_dev *dev, u8 data) { gpio_set_value(dev-dc_gpio, 1); ipsTft_write_regs(dev, data, 1); }3.3 屏幕初始化序列ST7789芯片需要按照特定序列进行初始化struct spi_lcd_cmd { u8 reg_addr; u8 len; int delay_ms; }; static struct spi_lcd_cmd cmds[] { {0x36, 1, 30}, // MADCTL: Memory Data Access Control {0x3A, 1, 30}, // COLMOD: Interface Pixel Format {0xB2, 5, 30}, // PORCTRL: Porch Setting // ... 更多初始化命令 {0x11, 0, 120}, // SLPOUT: Sleep Out {0x29, 0, 30}, // DISPON: Display On }; void ipsTft_reginit(struct ipsTft_dev *dev) { int i, j; // 硬件复位 gpio_set_value(ipsTftdev.res_gpio, 0); mdelay(20); gpio_set_value(ipsTftdev.res_gpio, 1); mdelay(20); // 发送初始化序列 for (i 0; i ARRAY_SIZE(cmds); i) { write_command(dev, cmds[i].reg_addr); for (j 0; j cmds[i].len; j) { write_data(dev, spi_lcd_datas[n]); } if (cmds[i].delay_ms) { mdelay(cmds[i].delay_ms); } } // 清屏测试 LCD_Clear(dev, RED); printk(ips init finish!\n); }4. 驱动测试与应用4.1 编译与加载驱动编写Makefile文件进行驱动编译KERNELDIR : /path/to/your/kernel CURRENT_PATH : $(shell pwd) obj-m : tft.o build: kernel_modules kernel_modules: $(MAKE) -C $(KERNELDIR) M$(CURRENT_PATH) modules clean: $(MAKE) -C $(KERNELDIR) M$(CURRENT_PATH) clean编译并加载驱动# 编译驱动 make # 将驱动拷贝到开发板 scp tft.ko root开发板IP:/lib/modules/$(uname -r)/kernel/drivers/tft/ # 加载驱动 depmod -a modprobe tft4.2 测试程序开发编写简单的测试程序验证驱动功能#include stdio.h #include fcntl.h #include unistd.h int main(int argc, char *argv[]) { int fd; if (argc ! 2) { printf(Usage: %s /dev/ipsTft\n, argv[0]); return -1; } fd open(argv[1], O_RDWR); if(fd 0) { perror(open device failed); return -1; } printf(TFT screen test started\n); close(fd); return 0; }编译测试程序arm-linux-gnueabihf-gcc tftTestApp.c -o tftTestApp4.3 功能验证在开发板上运行测试程序./tftTestApp /dev/ipsTft如果一切正常应该能看到屏幕先显示红色然后测试程序能够正常打开设备文件。5. 高级功能实现5.1 显示优化技巧为了提高显示效果可以实现以下优化双缓冲机制减少屏幕闪烁局部刷新只更新变化的部分DMA传输减轻CPU负担// 示例实现简单的双缓冲 void LCD_Refresh(struct ipsTft_dev *dev) { Address_set(dev, 0, 0, LCD_W-1, LCD_H-1); write_command(dev, 0x2C); spi_write(dev, front_buffer, LCD_W*LCD_H*2); swap_buffers(); }5.2 性能调优通过调整SPI时钟频率和优化数据传输方式提高性能提高SPI时钟在设备树中调整spi-max-frequency批量传输减少单次传输的开销命令合并将多个小命令合并发送// 示例批量绘制水平线 void LCD_DrawHLine(struct ipsTft_dev *dev, u16 x, u16 y, u16 len, u16 color) { u16 i; Address_set(dev, x, y, xlen-1, y); write_command(dev, 0x2C); gpio_set_value(dev-dc_gpio, 1); for(i0; ilen; i) { write_data(dev, color8); write_data(dev, color); } }5.3 添加Framebuffer支持为了实现更通用的显示支持可以为驱动添加Linux Framebuffer接口static struct fb_ops ipsTft_fb_ops { .owner THIS_MODULE, .fb_fillrect ipsTft_fb_fillrect, .fb_copyarea ipsTft_fb_copyarea, .fb_imageblit ipsTft_fb_imageblit, .fb_setcolreg ipsTft_fb_setcolreg, }; static int ipsTft_fb_probe(struct platform_device *pdev) { struct fb_info *info; info framebuffer_alloc(sizeof(struct ipsTft_dev), pdev-dev); // ... 初始化framebuffer register_framebuffer(info); return 0; }6. 常见问题解决在实际开发过程中可能会遇到各种问题下面列出一些常见问题及解决方法屏幕无显示检查电源和背光是否正常确认SPI时钟极性(CPOL)和相位(CPHA)设置正确验证复位信号是否正常发出显示花屏或错位检查屏幕初始化序列是否正确确认像素格式设置(如RGB565)与实际情况匹配验证行列地址设置函数是否正确SPI通信失败使用逻辑分析仪抓取SPI波形检查设备树中SPI控制器的配置降低SPI时钟频率测试性能不足启用DMA传输增加SPI时钟频率实现双缓冲机制注意调试时建议先使用较低的SPI时钟频率确保通信稳定后再逐步提高频率。7. 项目扩展与进阶完成基础驱动后可以考虑以下扩展方向触摸屏支持为屏幕添加触摸功能GUI框架移植移植LittlevGL、Qt等GUI框架多屏支持实现主副屏显示不同内容低功耗优化合理利用睡眠模式降低功耗// 示例进入睡眠模式 void LCD_Sleep(struct ipsTft_dev *dev) { write_command(dev, 0x10); // SLPIN mdelay(120); } // 示例唤醒屏幕 void LCD_Wakeup(struct ipsTft_dev *dev) { write_command(dev, 0x11); // SLPOUT mdelay(120); }在实际项目中ST7789驱动的稳定性往往取决于细节处理。例如正确的时序控制、电源管理和异常处理都会显著影响最终用户体验。通过本文介绍的方法开发者应该能够建立起完整的开发流程并具备解决常见问题的能力。

更多文章