Luckfox Pico SPI LCD驱动深度实战ST7735/GC9306屏幕全适配指南在嵌入式开发领域显示设备的集成往往是项目成功的关键环节之一。作为一款高性价比的嵌入式开发板Luckfox Pico凭借其出色的性能和灵活的扩展能力成为众多开发者的首选。本文将深入探讨如何在Luckfox Pico上实现SPI接口LCD屏幕ST7735/GC9306的完整驱动适配从硬件连接到内核配置再到驱动开发提供一站式解决方案。1. 硬件准备与引脚规划在开始软件配置前合理的硬件连接是项目成功的基础。Luckfox Pico开发板提供了丰富的GPIO资源我们需要根据SPI LCD的接口需求进行合理分配。典型SPI LCD引脚需求SPI_CLK时钟信号SPI_MOSI主设备输出从设备输入SPI_CS片选信号可选部分LCD使用GPIO模拟DC数据/命令选择RESET复位信号BL背光控制对于Luckfox Pico开发板我们推荐使用SPI0接口具体引脚分配如下表所示LCD引脚Luckfox Pico引脚功能说明SCL/CLKGPIO1_C1 (SPI0_CLK)SPI时钟SDA/MOSIGPIO1_C2 (SPI0_MOSI)数据输出CSGPIO1_C3片选可复用MISODCGPIO1_D0数据/命令选择RESETGPIO1_D1复位信号BLGPIO0_A4背光控制注意部分LCD模块可能不需要硬件复位引脚RESET此时可将该引脚直接连接到开发板的3.3V电源。背光控制引脚BL也非必须如需常亮可直接接电源。硬件连接时需特别注意电平匹配问题。大多数SPI LCD模块工作电压为3.3V与Luckfox Pico的GPIO电平兼容无需额外电平转换电路。但若使用5V供电的LCD模块必须添加电平转换器否则可能损坏开发板。2. 内核配置与设备树修改完成硬件连接后下一步是配置Linux内核以支持SPI接口和LCD驱动。Luckfox Pico基于Rockchip RV1106芯片其设备树配置与常见ARM平台略有不同。2.1 设备树(DTS)配置详解设备树是Linux内核描述硬件的重要机制我们需要在rv1103g-luckfox-pico.dts文件中添加SPI和LCD相关配置。首先确保SPI0控制器已启用spi0 { status okay; pinctrl-names default; pinctrl-0 spi0m0_pins; #address-cells 1; #size-cells 0; };接下来为LCD的各个控制引脚定义GPIO节点/ { /* LCD_CS -- 使用MISO引脚 */ gpio1pc3: gpio1pc3 { compatible regulator-fixed; pinctrl-names default; pinctrl-0 gpio1_pc3; regulator-name gpio1_pc3; regulator-always-on; }; /* LCD背光控制 */ gpio0pa4: gpio0pa4 { compatible regulator-fixed; pinctrl-names default; pinctrl-0 gpio0_pa4; regulator-name gpio0_pa4; regulator-always-on; }; /* LCD数据/命令选择 */ gpio1pd0: gpio1pd0 { compatible regulator-fixed; pinctrl-names default; pinctrl-0 gpio1_pd0; regulator-name gpio1_pd0; regulator-always-on; }; /* LCD复位信号 */ gpio1pd1: gpio1pd1 { compatible regulator-fixed; pinctrl-names default; pinctrl-0 gpio1_pd1; regulator-name gpio1_pd1; regulator-always-on; }; };然后在pinctrl节点中定义各GPIO的引脚控制属性pinctrl { /* LCD_CS -- 使用MISO引脚 */ gpio1-pc3 { gpio1_pc3: gpio1-pc3 { rockchip,pins 1 RK_PC3 RK_FUNC_GPIO pcfg_pull_none; }; }; /* LCD背光控制 */ gpio0-pa4 { gpio0_pa4: gpio0-pa4 { rockchip,pins 0 RK_PA4 RK_FUNC_GPIO pcfg_pull_none; }; }; /* LCD数据/命令选择 */ gpio1-pd0 { gpio1_pd0: gpio1-pd0 { rockchip,pins 1 RK_PD0 RK_FUNC_GPIO pcfg_pull_none; }; }; /* LCD复位信号 */ gpio1-pd1 { gpio1_pd1: gpio1-pd1 { rockchip,pins 1 RK_PD1 RK_FUNC_GPIO pcfg_pull_none; }; }; };最后在SPI0节点下添加LCD设备spi0 { lcd: lcd0 { status okay; compatible sitronix,st7735; // 或 sitronix,gc9306 reg 0; spi-max-frequency 6000000; // SPI时钟频率 spi-cpol; // SPI时钟极性 spi-cpha; // SPI时钟相位 rotate 0; // 显示旋转角度 fps 30; // 帧率 rgb; // RGB颜色顺序 buswidth 8; // 数据总线宽度 // 控制引脚定义 cs gpio1 RK_PC3 GPIO_ACTIVE_LOW; // 片选 led gpio0 RK_PA4 GPIO_ACTIVE_LOW; // 背光 dc gpio1 RK_PD0 GPIO_ACTIVE_HIGH; // 数据/命令选择 reset gpio1 RK_PD1 GPIO_ACTIVE_LOW; // 复位 debug 0x7; // 调试级别 }; };2.2 内核配置选项除了设备树修改还需要确保内核配置了相关驱动支持。在luckfox_rv1106_linux_defconfig文件中添加以下配置# SPI主控制器支持 CONFIG_SPI_MASTERy CONFIG_SPI_DESIGNWAREy CONFIG_SPI_DW_MMIOy # Framebuffer支持 CONFIG_FBy CONFIG_FB_TFTy # LCD驱动支持 CONFIG_FB_TFT_ST7735y CONFIG_FB_TFT_GC9306y这些配置项确保内核编译时包含了必要的SPI控制器和LCD驱动支持。3. 驱动移植与定制对于ST7735和GC9306这类常见LCD控制器Linux内核通常已经提供了基础驱动支持位于drivers/staging/fbtft目录。但根据具体硬件和内核版本可能需要进行适当调整。3.1 添加驱动文件首先在drivers/staging/fbtft/Makefile中添加驱动编译选项obj-$(CONFIG_FB_TFT_GC9306) fb_gc9306.o obj-$(CONFIG_FB_TFT_ST7735) fb_st7735.o然后在drivers/staging/fbtft/Kconfig中定义驱动配置选项config FB_TFT_GC9306 tristate FB driver for the GC9306 LCD Controller depends on FB_TFT help Generic Framebuffer support for GC9306 config FB_TFT_ST7735 tristate FB driver for the ST7735 LCD Controller depends on FB_TFT help Generic Framebuffer support for ST77353.2 修改fbtft核心驱动由于内核版本差异可能需要调整fbtft核心驱动以适配Luckfox Pico的硬件特性。主要修改集中在fbtft-core.c文件中#include linux/gpio.h #include linux/of_gpio.h static int fbtft_request_one_gpio(struct fbtft_par *par, const char *name, int index, struct gpio_desc **gpiop) { struct device *dev par-info-device; struct device_node *node dev-of_node; int gpio, flags, ret 0; enum of_gpio_flags of_flags; if (of_find_property(node, name, NULL)) { gpio of_get_named_gpio_flags(node, name, index, of_flags); if (gpio -ENOENT) return 0; if (gpio -EPROBE_DEFER) return gpio; if (gpio 0) { dev_err(dev, failed to get %s from DT\n, name); return gpio; } flags (of_flags OF_GPIO_ACTIVE_LOW) ? GPIOF_OUT_INIT_LOW : GPIOF_OUT_INIT_HIGH; ret devm_gpio_request_one(dev, gpio, flags, dev-driver-name); if (ret) { dev_err(dev, gpio_request_one(%s%d) failed with %d\n, name, gpio, ret); return ret; } *gpiop gpio_to_desc(gpio); fbtft_par_dbg(DEBUG_REQUEST_GPIOS, par, %s: %s GPIO%d\n, __func__, name, gpio); } return ret; } static void fbtft_reset(struct fbtft_par *par) { if (!par-gpio.reset) return; fbtft_par_dbg(DEBUG_RESET, par, %s()\n, __func__); gpiod_set_value_cansleep(par-gpio.reset, 1); usleep_range(20, 40); gpiod_set_value_cansleep(par-gpio.reset, 0); msleep(120); gpiod_set_value_cansleep(par-gpio.reset, 1); msleep(120); gpiod_set_value_cansleep(par-gpio.cs, 0); /* 激活芯片 */ msleep(120); }这些修改主要涉及GPIO请求和复位序列的调整确保与Luckfox Pico的硬件特性兼容。3.3 ST7735驱动实现ST7735驱动的核心是初始化序列和显示控制。以下是关键部分的实现static int init_display(struct fbtft_par *par) { par-fbtftops.reset(par); // 硬件复位 write_reg(par, 0x01); // 软件复位 mdelay(150); write_reg(par, 0x11); // 退出睡眠模式 mdelay(500); // ST7735帧率设置 write_reg(par, 0xB1, 0x05, 0x3C, 0x3C); write_reg(par, 0xB2, 0x05, 0x3C, 0x3C); write_reg(par, 0xB3, 0x05, 0x3C, 0x3C, 0x05, 0x3C, 0x3C); write_reg(par, 0xB4, 0x03); // 列反转 // ST7735电源序列 write_reg(par, 0xC0, 0x28, 0x08, 0x04); write_reg(par, 0xC1, 0xC0); write_reg(par, 0xC2, 0x0D, 0x00); write_reg(par, 0xC3, 0x8D, 0x2A); // VCOM设置 write_reg(par, 0xC4, 0x8D, 0xEE); // VCOM偏移 // 显示方向和RGB模式 write_reg(par, 0x36, 0xC0); // ST7735 Gamma序列 write_reg(par, 0xE0, 0x04, 0x22, 0x07, 0x0A, 0x2E, 0x30, 0x25, 0x2A, 0x28, 0x26, 0x2E, 0x3A, 0x00, 0x01, 0x03, 0x13); write_reg(par, 0xE1, 0x04, 0x16, 0x06, 0x0D, 0x2D, 0x26, 0x23, 0x27, 0x27, 0x25, 0x2D, 0x3B, 0x00, 0x01, 0x04, 0x13); write_reg(par, 0x3A, 0x05); // 65K色模式 write_reg(par, 0x29); // 开启显示 mdelay(100); return 0; }3.4 GC9306驱动实现GC9306驱动的实现与ST7735类似但初始化序列有所不同static int init_display(struct fbtft_par *par) { par-fbtftops.reset(par); // 硬件复位 mdelay(50); // 显示控制设置 write_reg(par, 0xFE); write_reg(par, 0xEF); write_reg(par, MIPI_DCS_SET_ADDRESS_MODE, 0x48); // 显示方向 write_reg(par, MIPI_DCS_SET_PIXEL_FORMAT, MIPI_DCS_PIXEL_FMT_16BIT); // 65K色模式 // GC9306电源序列 write_reg(par, 0xA4, 0x44, 0x44); write_reg(par, 0xA5, 0x42, 0x42); write_reg(par, 0xAA, 0x88, 0x88); write_reg(par, 0xAE, 0x2B); write_reg(par, 0xE8, 0x11, 0x0B); write_reg(par, 0xE3, 0x01, 0x10); write_reg(par, 0xFF, 0x61); write_reg(par, 0xAC, 0x00); write_reg(par, 0xAF, 0x67); // 显示窗口设置 (240x320) write_reg(par, 0x2A, 0x00, 0x00, 0x00, 0xEF); // 列地址设置 write_reg(par, 0x2B, 0x00, 0x00, 0x01, 0x3F); // 行地址设置 write_reg(par, 0x2C); // 开始写入显存 // GC9306 Gamma序列 write_reg(par, 0xF0, 0x02, 0x00, 0x00, 0x1B, 0x1F, 0x0B); write_reg(par, 0xF1, 0x01, 0x03, 0x00, 0x28, 0x2B, 0x0E); write_reg(par, 0xF2, 0x0B, 0x08, 0x3B, 0x04, 0x03, 0x4C); write_reg(par, 0xF3, 0x0E, 0x07, 0x46, 0x04, 0x05, 0x51); write_reg(par, 0xF4, 0x08, 0x15, 0x15, 0x1F, 0x22, 0x0F); write_reg(par, 0xF5, 0x0B, 0x13, 0x11, 0x1F, 0x21, 0x0F); write_reg(par, 0x11); // 退出睡眠模式 mdelay(100); write_reg(par, 0x29); // 开启显示 mdelay(100); return 0; }4. 系统集成与测试完成驱动开发后需要将修改集成到系统并验证功能是否正常。4.1 编译与部署在内核根目录执行make menuconfig确保已启用以下选项Device Drivers → Graphics support → Frame buffer Devices → Support for small TFT LCD display modules选中FB_TFT_ST7735和FB_TFT_GC9306驱动编译内核和设备树make ARCHarm CROSS_COMPILEarm-linux-gnueabihf- -j$(nproc) make ARCHarm CROSS_COMPILEarm-linux-gnueabihf- dtbs将生成的设备树和内核镜像部署到开发板。4.2 功能验证系统启动后可以通过以下命令验证LCD驱动是否正常工作# 查看内核消息确认驱动加载 dmesg | grep fb_ # 预期输出示例 [ 1.234567] graphics fb0: fb_st7735 frame buffer, 128x160, 40 KiB video memory, 4 KiB buffer memory, fps30, spi0.0 at 6 MHz # 测试显示功能 - 随机噪声 cat /dev/urandom /dev/fb0 # 测试显示功能 - 清屏 cat /dev/zero /dev/fb0如果上述命令执行后LCD屏幕显示相应变化说明驱动工作正常。4.3 性能优化默认配置下SPI时钟频率设置为6MHz这是较为保守的值。根据实际硬件性能可以适当提高时钟频率以获得更好的显示性能spi0 { lcd: lcd0 { spi-max-frequency 24000000; // 提高到24MHz }; };但需注意过高的SPI时钟频率可能导致信号完整性问题表现为显示异常或闪烁。建议逐步提高频率并观察显示效果。5. 高级应用与图形界面集成基础驱动工作正常后可以进一步集成图形用户界面(GUI)框架如LVGL、Qt等实现更丰富的交互体验。5.1 LVGL集成示例LVGL(Light and Versatile Graphics Library)是一款轻量级开源图形库非常适合嵌入式系统。以下是在Luckfox Pico上集成LVGL的基本步骤下载LVGL源码git clone https://github.com/lvgl/lvgl.git创建简单的显示测试程序#include unistd.h #include fcntl.h #include sys/ioctl.h #include linux/fb.h #include lvgl/lvgl.h int main(int argc, char *argv[]) { // 初始化LVGL lv_init(); // 获取framebuffer信息 int fbfd open(/dev/fb0, O_RDWR); struct fb_var_screeninfo vinfo; ioctl(fbfd, FBIOGET_VSCREENINFO, vinfo); // 初始化显示驱动 lv_disp_drv_t disp_drv; lv_disp_drv_init(disp_drv); disp_drv.hor_res vinfo.xres; disp_drv.ver_res vinfo.yres; disp_drv.flush_cb my_flush_cb; // 需要实现刷新回调 lv_disp_drv_register(disp_drv); // 创建简单界面 lv_obj_t *label lv_label_create(lv_scr_act()); lv_label_set_text(label, Hello, Luckfox Pico!); lv_obj_align(label, LV_ALIGN_CENTER, 0, 0); // 主循环 while(1) { lv_timer_handler(); usleep(5000); } return 0; }编译并运行程序应该能在LCD上看到Hello, Luckfox Pico!的文字显示。5.2 显示旋转与方向控制大多数SPI LCD支持通过软件命令改变显示方向。在驱动中我们通过set_var函数实现这一功能static int set_var(struct fbtft_par *par) { u8 madctl_par 0; if (par-bgr) madctl_par | MADCTL_BGR; switch (par-info-var.rotate) { case 0: madctl_par 0xC0; // 0度 break; case 90: madctl_par 0x70; // 90度 break; case 180: madctl_par 0x00; // 180度 break; case 270: madctl_par 0xA0; // 270度 break; default: return -EINVAL; } write_reg(par, MIPI_DCS_SET_ADDRESS_MODE, madctl_par); return 0; }用户可以通过修改/sys/class/graphics/fb0/rotate文件来动态改变显示方向# 旋转90度 echo 90 /sys/class/graphics/fb0/rotate5.3 背光控制优化背光控制通常通过PWM实现平滑的亮度调节。Luckfox Pico提供了多个PWM通道我们可以利用其中一个来控制LCD背光。首先在设备树中配置PWMpwm0 { status okay; pinctrl-names active; pinctrl-0 pwm0m0_pins; };然后将LCD的背光控制连接到PWM0spi0 { lcd: lcd0 { led pwm0 0 0; // 使用PWM0控制背光 }; };系统启动后可以通过以下命令调节背光亮度0-255# 设置50%亮度 echo 128 /sys/class/backlight/backlight/brightness这种实现方式比简单的GPIO控制更加灵活可以实现平滑的亮度过渡和节能效果。