内核里已经提供spi接口小屏的设备驱动,在内核的配置选项:
make menuconfig ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- Device Drivers ---> Graphics support ---> <*> Support for small TFT LCD display modules ---> ... //屏的驱动IC型号 <*> FB driver for the ILI9340 LCD Controller //drivers/video/fbtft/fb_ili9340.c <*> FB driver for the ST7735R LCD Controller //drivers/video/fbtft/fb_st7735r.c ... <M> Module to for adding FBTFT devices //drivers/video/fbtft/fbtft_device.c
保存退出后,重编内核镜像和编译驱动模块.
使用新内核镜像启动系统后:
加载驱动模块并指定参数, 如型号为ili9340的屏:
modprobe fbtft_device name=adafruit22a gpios="reset:8,dc:7" busnum=0
加载成功后, 会产生一个/dev/fb8设备文件。
修改QT环境变量: export QT_QPA_PLATFORM=linuxfb:fb=/dev/fb8
生效后,执行Qt程序,效果如下:
在内核源码目录里:
[yyx@localhost linux-3.4.112]$ vim drivers/video/fbtft/fb fb_bd663474.c fb_ili9486.c fb_ssd1351.c fbtft-io.c fb_hx8340bn.c fb_pcd8544.c fb_st7735r.c fbtft-sysfs.c fb_hx8347d.c fb_ra8875.c fbtft-bus.c fb_tinylcd.c fb_hx8353d.c fb_s6d02a1.c fbtft-core.c fb_tls8204.c fb_ili9320.c fb_s6d1121.c fbtft_device.c fb_upd161704.c fb_ili9325.c fb_ssd1289.c fbtft_device.ko fb_watterott.c fb_ili9340.c fb_ssd1306.c fbtft_device.mod.c fb_ili9341.c fb_ssd1331.c fbtft.h
fb_xxxx.c就是具体型号的驱动IC设备驱动:
每个设备驱动源码里都会调用init_display函数用于初始化屏,我们可以根据此函数里设置的寄存器和值,确认屏驱动IC的型号:
static int init_display(struct fbtft_par *par) { ... par->fbtftops.reset(par); write_reg(par, 0xEF, 0x03, 0x80, 0x02); write_reg(par, 0xCF, 0x00 , 0XC1 , 0X30); ... }
每个屏由一个struct fbtft_display对象来描述:
static struct fbtft_display display = { .regwidth = 8, .width = WIDTH, .height = HEIGHT, .fbtftops = { .init_display = init_display, //屏的初始化 .set_addr_win = set_addr_win, //设置屏的显示开始坐标 .set_var = set_var, //设置屏的翻转角度 }, }; FBTFT_REGISTER_DRIVER(DRVNAME, &display);
调用宏FBTFT_REGISTER_DRIVER后都会产生和注册一个struct spi_driver对象和一个struct platform_driver对象.
#define FBTFT_REGISTER_DRIVER(_name, _display) \ \ static int fbtft_driver_probe_spi(struct spi_device *spi) \ { \ return fbtft_probe_common(_display, spi, NULL); \ } \ \ static int fbtft_driver_remove_spi(struct spi_device *spi) \ { \ struct fb_info *info = spi_get_drvdata(spi); \ \ return fbtft_remove_common(&spi->dev, info); \ } \ \ static int fbtft_driver_probe_pdev(struct platform_device *pdev) \ { \ return fbtft_probe_common(_display, NULL, pdev); \ } \ \ static int fbtft_driver_remove_pdev(struct platform_device *pdev) \ { \ struct fb_info *info = platform_get_drvdata(pdev); \ \ return fbtft_remove_common(&pdev->dev, info); \ } static struct spi_driver fbtft_driver_spi_driver = { \ .driver = { \ .name = _name, \ .owner = THIS_MODULE, \ }, \ .probe = fbtft_driver_probe_spi, \ .remove = fbtft_driver_remove_spi, \ }; \ \ static struct platform_driver fbtft_driver_platform_driver = { \ .driver = { \ .name = _name, \ .owner = THIS_MODULE, \ }, \ .probe = fbtft_driver_probe_pdev, \ .remove = fbtft_driver_remove_pdev, \ }; static int __init fbtft_driver_module_init(void) \ { \ int ret; \ \ ret = spi_register_driver(&fbtft_driver_spi_driver); \ if (ret < 0) \ return ret; \ return platform_driver_register(&fbtft_driver_platform_driver); \ } \ \ static void __exit fbtft_driver_module_exit(void) \ { \ spi_unregister_driver(&fbtft_driver_spi_driver); \ platform_driver_unregister(&fbtft_driver_platform_driver); \ } \ \ module_init(fbtft_driver_module_init); \ module_exit(fbtft_driver_module_exit);
我们可以通过描述spi设备或者平台设备来与屏的设备驱动匹配,但描述设备的参数有点复杂,所以内核里提供了fbtft_device.c,它把我们提供的模块参数,生成相应的spi设备或平台设备,并且提供相关的资源信息.
在fbtft_device.c里,它已定好多的设备名, 每个设备使用一个具体的屏型号:
static struct fbtft_device_display displays[] = { { .name = "adafruit18", //设备名 .spi = &(struct spi_board_info) { .modalias = "fb_st7735r", //屏的型号 .max_speed_hz = 32000000, .mode = SPI_MODE_0, .platform_data = &(struct fbtft_platform_data) { .display = { .buswidth = 8, .backlight = 1, }, .gpios = (const struct fbtft_gpio []) { { "reset", 25 }, { "dc", 24 }, { "led", 18 }, {}, }, .gamma = ADAFRUIT18_GAMMA, } } }, { .name = "adafruit18_green", .spi = &(struct spi_board_info) { .modalias = "fb_st7735r", ...
加载此驱动模块时,除了需要指定设备名外,还需要指定多个参数,具体可以通过modinfo查看:
modinfo /lib/modules/3.4.112/kernel/drivers/video/fbtft/fbtft_device. ko 常用的模块参数有: gpios用于指定reset, dc, led等具体使用哪个io口 mode用于指定spi使用哪种时序 busnum用于指定使用第几个spi控制器 rotate用于指定翻转角度 name用于指定设备名 如:modprobe fbtft_device name=adafruit22a gpios="reset:8,dc:7" busnum=0 debug=0 rotate=90 gpios="reset:8,dc:7"表示reset引脚是接在GPIOA(8), dc引脚是接在GPIOA(7) 在头文件里: arch/arm/mach-sunxi/include/mach/gpio.h #define SUNXI_PA_BASE 0 #define GPIOA(n) (SUNXI_PA_BASE + (n)) 所以得出GPIOA(8)的值是8
注意,不同的内核fbtft放置的目录可能不同,比如在周立功的A6G2C的内核下,fbtft在如下目录:
linux-src-a0722e0\A7-linux-src\drivers\staging\fbtft