日落果

日落果的随手记

twitter
github

[嵌入式 Linux 开发入门]使用 Milk-V Duo 驱动 st7789v 小屏幕

接线#

在开始之前,首先确定你的屏幕的引脚定义,下面是我买到的屏幕:

image

通过查阅店家给的屏幕资料,得出引脚定义如下:

引脚定义
BLK背光
CSSPI 片选
DC屏幕数据
RES屏幕重置
SDASPI 数据
SCLSPI 时钟
VCC电源
GNDGND

image

通过 Milk-V Duo 的文档可以看出,Milk-V Duo 有一个硬件 SPI,除此之外需要两个引脚来接 DCRES,这里我选择 GPIOA24 跟 GPIOA23,由于屏幕不需要输出数据,所以只需的数据引脚接到开发板的 SPI2_TX 即可。

这里我直接将 3V3 接到 BLK 引脚,无需 PWM 调光。

于是我的接线如下:

屏幕     开发板
BLK --- 3V3
CS  --- SPI2_CSn
SDA --- SPI2_TX
SCL --- SPI2_CLK
DC  --- GPIOA24
RES --- GPIOA23

修改引脚定义#

由于 Milk-V Duo 开发板的 SPI2 是复用引脚且默认功能不是 SPI2,所以需要修改引脚定义。

Milk-V Duo 的引脚定义在 build/boards/cv180x/cv1800b_milkv_duo_sd/u-boot/cvi_board_init.c 文件中的 cvi_board_init 内声明,这里我们直接把 SD1_CLK, SD1_CMD, SD1_D0, SD1_D3 直接修改为 SPI2 的对应引脚(参考上面 Milk-V Duo 引脚定义图)。

PINMUX_CONFIG(SD1_CLK, SPI2_SCK); // Pin 9
PINMUX_CONFIG(SD1_CMD, SPI2_SDO); // Pin 10
PINMUX_CONFIG(SD1_D0, SPI2_SDI);  // Pin 11
PINMUX_CONFIG(SD1_D3, SPI2_CS_X); // Pin 12

内核配置#

首先配置 device tree

&spi2 {
	status = "okay";
	/delete-node/ spidev@0;
	st7789v: st7789v@0{
		compatible = "sitronix,st7789v";
		reg = <0>;
		status = "okay";
		spi-max-frequency = <48000000>;
		spi-cpol;
		spi-cpha;
		rotate = <0>;
		fps = <60>;
		rgb;
		buswidth = <8>;
		dc = <&porta 24 GPIO_ACTIVE_HIGH>;
		reset = <&porta 23 GPIO_ACTIVE_HIGH>;
		debug = <0x0>;
	};
};

这里把 dc reset 设置成你自己接的 GPIO 端口号。

然后启用内核中的 ST7789V 支持跟 FB 支持,如果你跟我一样使用官方的 buildroot 的话可以直接修改 build/boards/cv180x/cv1800b_milkv_duo_sd/linux/cvitek_cv1800b_milkv_duo_sd_defconfig 这个文件。

这里我们不动别的配置,只增加 FB_TFT 的支持跟 ST7789V 的驱动:

CONFIG_FB_TFT=y
CONFIG_FB_TFT_ST7789V=y

接下来修改 cv180x 的 dtsi,文件默认在 build/boards/default/dts/cv180x/cv180x_base.dtsi

将 SPI 引脚默认上拉:

spi2:spi2@041A0000 {
	compatible = "snps,dw-apb-ssi";
	reg = <0x0 0x041A0000 0x0 0x10000>;
	clocks = <&clk CV180X_CLK_SPI>;
	#address-cells = <1>;
	#size-cells = <0>;

	bias-pull-up; // 上拉
	};

修改驱动#

首先修改 linux_5.10/drivers/staging/fbtft/fbtft-core.c 中的 fbtft_request_one_gpio 函数,这里的修改依据 https://github.com/notro/fbtft/blob/e9fc10a080a6c52e46e004c4c2bc9fd3cf3d7445/fbtft-core.c#L166-L204 来,如果不修改在初始化屏幕时候会报错:

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;
}

同时在文件最上面引入头文件:

#include "fbtft.h"
#include "internal.h"

接下来修改 st7789v 的驱动,也就是 linux_5.10/drivers/staging/fbtft/fb_st7789v.c,这里先修改 fbtft_display 中的屏幕配置,将 width 跟 height 改为你屏幕的大小:

static struct fbtft_display display = {
	.regwidth = 8,
	.width = 240,
	.height = 280,
	.gamma_num = 2,
	.gamma_len = 14,
	.gamma = HSD20_IPS_GAMMA,
	.fbtftops = {
		.init_display = init_display,
		.set_var = set_var,
		.set_gamma = set_gamma,
		.blank = blank,
		.set_addr_win = set_addr_win
		},
};

接着按照屏厂给的资料,修改初始化函数:

static int init_display(struct fbtft_par *par)
{

	par->fbtftops.reset(par);
	mdelay(100);

	/* turn off sleep mode */
	write_reg(par, MIPI_DCS_EXIT_SLEEP_MODE);
	mdelay(120);

	/* set pixel format to RGB-565 */
	write_reg(par, MIPI_DCS_SET_PIXEL_FORMAT, MIPI_DCS_PIXEL_FMT_16BIT);

	write_reg(par, VCMOFSET, 0x1A);

	if (HSD20_IPS)
		write_reg(par, PORCTRL, 0x05, 0x05, 0x00, 0x33, 0x33);

	else
		write_reg(par, PORCTRL, 0x08, 0x08, 0x00, 0x22, 0x22);

	/*
	 * VGH = 12.2V
	 * VGL = -10.43V
	 */
	if (HSD20_IPS)
		write_reg(par, GCTRL, 0x05);
	else
		write_reg(par, GCTRL, 0x35);

	/* VCOM */
	write_reg(par, VCOMS, 0x3F);

	/*
	 * VDV and VRH register values come from command write
	 * (instead of NVM)
	 */
	write_reg(par, VDVVRHEN, 0x01);

	/*
	 * VRH =  4.3V + (VCOM + VCOM offset + VDV)
	 */
	if (HSD20_IPS)
		write_reg(par, VRHS, 0x0F);
	else
		write_reg(par, VRHS, 0x0B);

	/* VDV = 0V */
	write_reg(par, VDVS, 0x20);

	/* 111 Hz */
	write_reg(par, FRC, 0x01);

	/*
	 * AVDD = 6.8V
	 * AVCL = -4.8V
	 * VDS = 2.3V
	 */
	write_reg(par, PWCTRL1, 0xA4, 0xA1);

	/* 卖屏幕的人给你参考代码里的 我也不知道哪里来的 */
	write_reg(par, 0xE8, 0x03);
	write_reg(par, ETC, 0x09, 0x09, 0x08);
	write_reg(par, 0xE0, 0xD0, 0x05, 0x09, 0x09, 0x08, 0x14, 0x28, 0x33, 0x3F, 0x07, 0x13, 0x14, 0x28, 0x30);
	write_reg(par, 0xE1, 0xD0, 0x05, 0x09, 0x09, 0x08, 0x03, 0x24, 0x32, 0x32, 0x3B, 0x14, 0x13, 0x28, 0x2F);

	if (HSD20_IPS)
		write_reg(par, MIPI_DCS_ENTER_INVERT_MODE);

	write_reg(par, MIPI_DCS_SET_DISPLAY_ON);

	return 0;
}

如果你的屏幕跟我一样大小与 linux kernel 默认不一样,那么还需要参考屏厂的资料设置屏幕 offset:

static void set_addr_win(struct fbtft_par *par, int xs, int ys, int xe,
							   int ye)
{
	unsigned int x = xs, y = xe;

	write_reg(par, MIPI_DCS_SET_COLUMN_ADDRESS,
			  (x >> 8), x, (y >> 8), y);

	x = ys + 20;
	y = ye + 20;

	write_reg(par, MIPI_DCS_SET_PAGE_ADDRESS,
			  (x >> 8), x, (y >> 8), y);

	write_reg(par, MIPI_DCS_WRITE_MEMORY_START);
}

检测屏幕是否工作#

如果你的操作没错的话,在上电 ssh 上去之后执行 dmesg | grep st7789v 应该可以看出 fb0 被正确的初始化了:

[root@milkv-duo]~# dmesg | grep st7789v
[    0.813120] fb_st7789v spi0.0: fbtft_property_value: buswidth = 8
[    0.819555] fb_st7789v spi0.0: fbtft_property_value: debug = 0
[    0.825703] fb_st7789v spi0.0: fbtft_property_value: rotate = 0
[    0.831919] fb_st7789v spi0.0: fbtft_property_value: fps = 60
[    1.357745] graphics fb0: fb_st7789v frame buffer, 240x280, 131 KiB video memory, 4 KiB buffer memory, fps=62, spi0.0 at 48 MHz

执行 cat /dev/random > /dev/fb0 给 fb0 内写随机数据,这时候看屏幕应该是像这样的:

image

再执行 cat /dev/zero > /dev/fb0,向 fb0 内全写 0,现在屏幕应该会回到黑屏状态。

加载中...
此文章数据所有权由区块链加密技术和智能合约保障仅归创作者所有。