【全志T113-S3_100ask】15-1 内核5.4驱动spi屏幕——ILI9341
- 背景
- (一)spi设备树
- 1、修改设备树
- 2、完善设备树
- (二)使能内核
- (三)兼容性修改
- (四)测试
背景
- 本来想直接驱动mipi屏幕的,但是发现有一点点难度,所以想先研究一下小屏幕如何驱动。
- 本文章使用的芯片为全志T113-s3,目前使用的核心板是100ask的,但是官方开发板上面的spi接口不齐全,所以自制了一块板子进一步学习。
(一)spi设备树
1、修改设备树
在原本的设备树中,配置是错误的,引脚都对不上,应该是参考D1-H的文档,然后拷贝过来的。
然后看了芯片的手册,引脚配置如下:
在 Function4 中有spi1相关的配置,从PD10到PD15
修改设备树 spi1 的引脚,如下:
spi1_pins_a: spi1@0 {
pins = "PD11", "PD12", "PD13","PD14", "PD15"; /*clk mosi miso hold wp*/
function = "spi1";
drive-strength = <10>;
};
spi1_pins_b: spi1@1 {
pins = "PD10";
function = "spi1";
drive-strength = <10>;
bias-pull-up; // only CS should be pulled up
};
spi1_pins_c: spi1@2 {
allwinner,pins = "PD10", "PD11", "PD12", "PD13","PD14", "PD15";
allwinner,function = "gpio_in";
allwinner,muxsel = <0>;
drive-strength = <10>;
};
此时spi总线已经配置好了
2、完善设备树
因为我们本次驱动的屏幕为ili9341
,改屏幕的驱动已经在内核里,直接使能使用即可,但是我们要修改设备树,参考修改手册:
手册上说明,一定要配置
- compatible: "adafruit,yx240qv29", "ilitek,ili9341"
- dc-gpios: D/C pin
- reset-gpios: Reset pin
但是可以参考一下配置进行设置:
在 spi1 节点下:
ili9341@0{
#address-cells = <1>;
#size-cells = <1>;
compatible = "ilitek,ili9341";
reg = <0>;
spi-max-frequency = <32000000>;
dc-gpios = <&pio PD 16 GPIO_ACTIVE_HIGH>;
reset-gpios = <&pio PD 17 GPIO_ACTIVE_HIGH>;
rotate = <90>;
spi-cpol; //SPI引脚模式
spi-cpha; //SPI引脚模式
bgr;//颜色格式为RGB
fps = <30>;
buswidth = <8>;
// backlight = <&backlight>;
status = "okay";
};
如下所示:
我们还用到了dc和rst引脚,这两个引脚可以随便找个io,但是不能和其他引脚冲突。
需要在pio节点下配置
lcd_dc: lcd_dc{
allwinner,pins = "PD16";
};
lcd_rst: lcd_rst{
allwinner,pins = "PD17";
};
如下所示:
此时已经将设备树修改完毕。
(二)使能内核
1、在内核目录下如 linux-origin_master ,输入make menuconfig
进入内核菜单设置页面
2、然后左斜杠 / 进入搜索,输入 ili9341
,回车即可找到相关的驱动
然后使能该项(别问我怎么使能)
或者一步步找到该驱动
Location: │
│ -> Device Drivers │
│ -> Staging drivers (STAGING [=y]) │
│ -> Support for small TFT LCD display modules (FB_TFT [=y])
(三)兼容性修改
- 对于比较高版本的内核比如我现在的5.4,如果直接编译的话,是使用不了屏幕的,因为内核在不断更新,但是对于屏幕的驱动,并没有及时更新,甚至这个驱动代码可能是八年前的代码。
- 主要修改的文件为
fbtft-core.c
- 参考路径:build/linux-origin_master/drivers/staging/fbtft/fbtft-core.c
1- 在 fbtft-core.c 添加头文件
#include "linux/gpio.h"
#include "linux/of_gpio.h"
2- 找到里面的 fbtft_request_one_gpio()
函数,替换该函数的内容,修改后如下:
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;
}
//active low translates to initially low
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;
}
3- 找到里面的 fbtft_request_gpios_dt()
函数,替换该函数的内容,修改后如下:
static int fbtft_request_gpios_dt(struct fbtft_par *par)
{
int i;
int ret;
ret = fbtft_request_one_gpio(par, "reset-gpios", 0, &par->gpio.reset);
if (ret)
return ret;
ret = fbtft_request_one_gpio(par, "dc-gpios", 0, &par->gpio.dc);
if (ret)
return ret;
ret = fbtft_request_one_gpio(par, "rd-gpios", 0, &par->gpio.rd);
if (ret)
return ret;
ret = fbtft_request_one_gpio(par, "wr-gpios", 0, &par->gpio.wr);
if (ret)
return ret;
ret = fbtft_request_one_gpio(par, "cs-gpios", 0, &par->gpio.cs);
if (ret)
return ret;
ret = fbtft_request_one_gpio(par, "latch-gpios", 0, &par->gpio.latch);
if (ret)
return ret;
for (i = 0; i < 16; i++) {
ret = fbtft_request_one_gpio(par, "db-gpios", i,
&par->gpio.db[i]);
if (ret)
return ret;
ret = fbtft_request_one_gpio(par, "led-gpios", i,
&par->gpio.led[i]);
if (ret)
return ret;
ret = fbtft_request_one_gpio(par, "aux-gpios", i,
&par->gpio.aux[i]);
if (ret)
return ret;
}
return 0;
}
4- 找到里面的 fbtft_reset()
函数,替换该函数的内容,修改后如下:
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);
msleep(10);
gpiod_set_value_cansleep(par->gpio.reset, 0);
msleep(200);
gpiod_set_value_cansleep(par->gpio.reset, 1);
msleep(10);
}
修改完以上三个函数,就可以编译内核和buildroot。后续步骤省略。
(四)测试
1、接线
按照屏幕的接线方案进行接线,一一对应就好没啥好说的。
2、上电
上电之后屏幕由白到黑,应该是驱动加载成功了。
3、查看启动时的内核信息
# dmesg | grep "fb"
[ 4.470009] fbtft_of_value: buswidth = 8
[ 4.474498] fbtft_of_value: rotate = 90
[ 4.478946] fbtft_of_value: fps = 30
[ 4.950907] graphics fb0: fb_ili9341 frame buffer, 320x240, 150 KiB video memory, 16 KiB buffer memory, fps=33, spi1.0 at 32 MHz
4、花点测试(随机填充)
cat /dev/urandom > /dev/fb0
5、自带工具测试
# fb-test
fb-test 1.1.1 (rosetta)
fb res 320x240 virtual 320x240, line_len 640, bpp 16
现象如下:
# fb-test-rect
rect 1.1.1 (rosetta)
现象如下:
至此,屏幕驱动完毕。可以进一步研究c语言驱动屏幕,或者lvgl驱动。
END.