日落果

日落果的随手记

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 に接続するために 2 つのピンが必要であることがわかります。ここでは 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); // ピン 9
PINMUX_CONFIG(SD1_CMD, SPI2_SDO); // ピン 10
PINMUX_CONFIG(SD1_D0, SPI2_SDI);  // ピン 11
PINMUX_CONFIG(SD1_D3, SPI2_CS_X); // ピン 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.cfbtft_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;
		}

		// アクティブローは初期状態をローに変換します
		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 のスクリーン設定を変更し、幅と高さをスクリーンのサイズに変更します:

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

	/* スリープモードをオフにする */
	write_reg(par, MIPI_DCS_EXIT_SLEEP_MODE);
	mdelay(120);

	/* ピクセルフォーマットを 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 と VRH レジスタの値はコマンド書き込みから来ます
	 * (NVM ではなく)
	 */
	write_reg(par, VDVVRHEN, 0x01);

	/*
	 * VRH =  4.3V + (VCOM + VCOM オフセット + 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 カーネルのデフォルトと異なる場合は、スクリーンオフセットを設定するためにメーカーの資料を参照する必要があります:

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 に全てゼロを書き込むと、スクリーンは黒い状態に戻るはずです。

読み込み中...
文章は、創作者によって署名され、ブロックチェーンに安全に保存されています。