基于fbtft的3.5寸lcd|显示驱动ili9488触摸驱动ft6636u

一、屏幕概述与引脚分布

随便买的3.5寸LCD电容触摸屏,分辨率为480*320,最大只支持两点触摸,支持spi或并口,朋友帮忙画的板子。



显示ic引脚与触摸ic引脚如下


使用的milkv duo-64M引脚如下

二、一些细节

参考了以下资料。

Milk-V Duo SPI驱动点亮屏幕(st7789)
Linux SPI LCD驱动移植(基于fbtft)

GitHub - Snitro/fbtft-ili9488: Linux5.10 fbtft ili9488

1.修改设备树

build/boards/cv180x/cv1800b_milkv_duo_sd/dts_riscv/cv1800b_milkv_duo_sd.dts

添加了spi节点下的ili9488,添加了iic节点下fts。内容如下:

&spi2 {
	status = "okay";

    /delete-node/ spidev@0;
	ili9488: ili9488@0{
		compatible = "ilitek,ili9488";
		reg = <0>;
		status = "okay";
		spi-max-frequency = <80000000>;
		spi-cpol;
		spi-cpha;
		rotate = <90>;         //旋转角度,默认竖屏,顺时针旋转
		fps = <60>;
		buswidth = <8>;
		dc-gpios = <&porta 23 GPIO_ACTIVE_HIGH>;	//DC
		reset-gpios = <&porta 24 GPIO_ACTIVE_LOW>; //RST
		led-gpios = <&portb 3 GPIO_ACTIVE_LOW>; //BL

		debug = <0x0>;
	};
};

&i2c1 {
	status = "okay";
	clock-frequency = <400000>;
	
	focaltech@38 {
		status = "okay";
		compatible = "focaltech,fts";
		reg = <0x38>;
		// interrupt-parent = <&porta>;
		// interrupts = <15 IRQ_TYPE_LEVEL_LOW>;
		focaltech,reset-gpios = <&porta 14 GPIO_ACTIVE_LOW>;
		focaltech,irq-gpios = <&porta 15 GPIO_ACTIVE_LOW>;
		focaltech,max-touch-number = <2>;
		focaltech,display-coords = <0 0 320 480>;
	
	};
};

2.配置引脚

build/boards/cv180x/cv1800b_milkv_duo_sd/u-boot/cvi_board_init.c

配置了IIC,SPI和一些GPIO。

// I2C1 --> FT6336u
  PINMUX_CONFIG(PAD_MIPIRX1P, IIC1_SDA);
  PINMUX_CONFIG(PAD_MIPIRX0N, IIC1_SCL);
  PINMUX_CONFIG(SD0_PWR_EN, XGPIOA_14);   // RST
  PINMUX_CONFIG(SPK_EN, XGPIOA_15);       // IRQ
  
  // SPI --> ILI9488
  PINMUX_CONFIG(SD1_CLK, SPI2_SCK);
  PINMUX_CONFIG(SD1_CMD, SPI2_SDO);
  PINMUX_CONFIG(SD1_D0, SPI2_SDI);
  PINMUX_CONFIG(SD1_D3, SPI2_CS_X);
  PINMUX_CONFIG(SPINOR_MISO, XGPIOA_23);	//D/C
  PINMUX_CONFIG(SPINOR_CS_X, XGPIOA_24);	//RST
  PINMUX_CONFIG(ADC1, XGPIOB_3);			//BL

3.完善ili9488

改的比较多,就挑一些讲了。

linux_5.10/drivers/staging/fbtft

fb_ili9488.c添加至上面路径。ili9488与ili9486看起来差别不大,但是很致命。

fb_ili9486.c中,MIPI_DCS_SET_PIXEL_FORMAT, 像素格式为565。

static const s16 default_init_sequence[] = {
	...
	/* Interface Pixel Format */
	-1, MIPI_DCS_SET_PIXEL_FORMAT, 0x55,
	...

ili9488在数据手册里写不进支持rgb565,还支持rgb666。但是实际上,只能使用rgb666,不然有问题。

所以fb_ili9488.c应如下

static const s16 default_init_sequence[] = {
	...
	/* Interface Pixel Format */
	-1, MIPI_DCS_SET_PIXEL_FORMAT, 0x66,
	...

其次,像素格式改了,绘制显存的方式也应该改变。

static struct fbtft_display display = {
	.regwidth = 8,
	.width = WIDTH,
	.height = HEIGHT,
	.init_sequence = default_init_sequence,
	.fbtftops = {
		.set_addr_win = set_addr_win,
		.set_var = set_var,
		.write_vmem = fbtft_write_vmem18to24_bus8,
	},
}

相较于ili9486,多了“write_vmem = fbtft_write_vmem18to24_bus8”,定义一个新的函数,将rgb666以3字节发给显存,github里写的,不是我写的,我只是修改了,感谢Snitro

linux_5.10/drivers/staging/fbtft/fbtft-bus.c
/* 16bpp converted to 18bpp stored in 24-bit over 8-bit databus */
int fbtft_write_vmem18to24_bus8(struct fbtft_par *par, size_t offset, size_t len)
{
	u16 *vmem16;
	u8 *txbuf = par->txbuf.buf;
	size_t remain;
	size_t to_copy;
	size_t tx_array_size;
	int i;
	int ret = 0;

	/* remaining number of pixels to send */
	remain = len / 2;
	vmem16 = (u16 *)(par->info->screen_buffer + offset);

	if ((int)(par->gpio.dc) != -1)
		gpiod_set_value(par->gpio.dc, 1);

	/* number of pixels that fits in the transmit buffer */
	tx_array_size = par->txbuf.len / 3;

	while (remain) {
		/* number of pixels to copy in one iteration of the loop */
		to_copy = min(tx_array_size, remain);
		dev_dbg(par->info->device, "    to_copy=%zu, remain=%zu\n",
						to_copy, remain - to_copy);

		for (i = 0; i < to_copy; i++) {
			u16 pixel = vmem16[i];
			u16 b = pixel & 0x1f;
			u16 g = (pixel & (0x3f << 5)) >> 5;
			u16 r = (pixel & (0x1f << 11)) >> 11;

			txbuf[i * 3 + 2] = (r & 0x1F) << 3;
			txbuf[i * 3 + 1] = (g & 0x3F) << 2;
			txbuf[i * 3 + 0] = (b & 0x1F) << 3;
		}

		vmem16 = vmem16 + to_copy;
		ret = par->fbtftops.write(par, par->txbuf.buf, to_copy * 3);
		if (ret < 0)
			return ret;
		remain -= to_copy;
	}

	return ret;
}
EXPORT_SYMBOL(fbtft_write_vmem18to24_bus8);

记得去fbtft.h中声明。

linux_5.10/drivers/staging/fbtft/fbtft.h
int fbtft_write_vmem18to24_bus8(struct fbtft_par *par, size_t offset, size_t len);

最后,添加进Kconfig和Makefile,使其能在内核被选择,才能被编译。

linux_5.10/drivers/staging/fbtft/Kconfig
linux_5.10/drivers/staging/fbtft/Makefile

一些细节请看上面的参考链接。

4.添加触摸驱动

linux_5.10/drivers/input/touchscreen

focaltech_touch整个文件夹放入上面的路径。

修复了一些bug,功能就保留了最基本的触摸和手势(但是我不知道手势怎么用,单片机给的例程还有近距离唤醒)。

然后,请注意,如“修改设备树”的注释一样,这个屏幕默认竖屏,如果旋转了屏幕,会出现显示坐标系与触摸坐标系不一致的情况。

为了解决问题就有了下面的修改。

#if FTS_MT_PROTOCOL_B_EN
static int fts_input_report_b(struct fts_ts_data *data)
{
   			......
            input_report_abs(data->input_dev, ABS_MT_TOUCH_MAJOR, events[i].area);
#ifdef ROTATE_0
            input_report_abs(data->input_dev, ABS_MT_POSITION_X, events[i].x);
            input_report_abs(data->input_dev, ABS_MT_POSITION_Y, events[i].y);
#endif
#ifdef ROTATE_90
            input_report_abs(data->input_dev, ABS_MT_POSITION_X, events[i].y);
            input_report_abs(data->input_dev, ABS_MT_POSITION_Y, 320-events[i].x);
#endif
#ifdef ROTATE_180
            input_report_abs(data->input_dev, ABS_MT_POSITION_X, 320-events[i].x);
            input_report_abs(data->input_dev, ABS_MT_POSITION_Y, 480-events[i].y);
#endif
#ifdef ROTATE_270
            input_report_abs(data->input_dev, ABS_MT_POSITION_X, 480-events[i].y);
            input_report_abs(data->input_dev, ABS_MT_POSITION_Y, events[i].x);
#endif
			......

请在fts_input_report_b函数之前#define ROTATE_X。

最后,添加进Kconfig和Makefile,使其能在内核被选择,才能被编译。

linux_5.10/drivers/input/touchscreen/Kconfig
linux_5.10/drivers/input/touchscreen/Makefile

我之前想在设备树节点里添加旋转角度,然后读取存入变量,但是这样每次触摸要switch判断,影响还是有的。

其次,这个320和480在设备树节点focaltech,display-coords里能读出来,这么写才规范。但是我懒,3.5寸的基本都是这个分辨率了,而且又不一定是同款触摸芯片。


三、在内核中添加

位于SDK内的根目录。

//配置环境
source device/milkv-duo/boardconfig.sh
source build/milkvsetup.sh
defconfig cv1800b_milkv_duo_sd

如果之前没有配置过内核请按以下方式操作。

menuconfig_kernel
//Exit 退出,保存

cp build/boards/cv180x/cv1800b_milkv_duo_sd/linux/cvitek_cv1800b_milkv_duo_sd_defconfig linux_5.10/.config

menuconfig_kernel

编译时用的内核配置文件在build里,默认内核里配置是空的,生成后退出,然后使用milkv的覆盖掉。


触摸

  1. 进入Device Drivers
  2. 进入Input device support
  3. 勾选<*>Event interface
  4. 勾选并进入<*>Touchscreens
  5. 勾选[*]Focaltech Touchscreen

显示

  1. 进入Device Drivers
  2. 进入[*]Staging drivers
  3. 进入<*>Support forsmall TFT LCD display modules
  4. 勾选<*>FB driver for the ILI9488 Controller

退出保存,编译,烧写。

四、效果与资源

使用了LVGL测试,**lv_demo_benchmark();**结果为37FPS。


实际使用流畅度还可以,cpu占用基本在20%左右。(在解锁了1Ghz加上打了spi-dma的补丁后,之前没有使用dma,cpu占用约80%)

因为这个搞得比较不规范,所以就直接提供文件了。

给milkv duo使用的触摸屏驱动

3 Likes

Very nice, the display colour not ok yet, similar issue to SPI/FB/DRM display being worked on ? - Milk-V Duo tinydrm驱动屏幕(ili9488/st7789) - #10 by mark.birss

Oh, no. In fact, the results look good. It’s just that the screen has a poor viewing angle, and due to the shooting problem, the colors don’t appear well.

1 Like

好像那个duo 1ghz的布丁不太稳定,哈哈哈哈可以考虑屏幕硬件开源,这样可能有更多的朋友可以试试

Ah OK, might have a look the fbtft driver you used as my previous attempt only had white screen at bootup