Linux内核里已经提供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
...
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程序,效果如下:
在内核源码目录里:
[jk@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