日落果

日落果的随手记

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,現在螢幕應該會回到黑屏狀態。

載入中......
此文章數據所有權由區塊鏈加密技術和智能合約保障僅歸創作者所有。