Milk-V Duo SPI驱动点亮屏幕(st7789)

本来想等大佬发点亮屏幕的教程,发现一直没有相关信息,无奈只有自己动手丰衣足食了(本来想简单点亮sd1306,结果大意烧了 :smiling_face_with_tear:手头上只有这一块st7789,那就干SPI吧)

首先我先声明,我是新手,以下教程是基于自己检索资料总结的,由于第一次接触linux开发板,花费了大量时间尝试才成功,如以下教程中有误导或者错误之处,还请各位大佬嘴下留情 :joy:

1. 修改st7789驱动
官方的sdk是linux5.10内核,可以使用fbtft里的驱动,直接修改st7789v驱动来用
以下是需要修改的目录文件:

linux_5.10/drivers/staging/fbtft/fb_st7789v.c

①修改init_display(屏幕初始化方法,一般可以在屏幕的官方的示例里找到,用STM32的代码照着改就行了,st7789就直接用下面的代码,其他SPI的屏幕需要参考其他资料进行修改)

static int init_display(struct fbtft_par *par)
{
    par->fbtftops.reset(par);
    mdelay(50);
    write_reg(par,0x36,0x00);
    write_reg(par,0x3A,0x05);
    write_reg(par,0xB2,0x0C,0x0C,0x00,0x33,0x33);
    write_reg(par,0xB7,0x35);
    write_reg(par,0xBB,0x19);
    write_reg(par,0xC0,0x2C);
    write_reg(par,0xC2,0x01);
    write_reg(par,0xC3,0x12);
    write_reg(par,0xC4,0x20);
    write_reg(par,0xC6,0x0F);
    write_reg(par,0xD0,0xA4,0xA1);
    write_reg(par,0xE0,0xD0,0x04,0x0D,0x11,0x13,0x2B,0x3F,0x54,0x4C,0x18,0x0D,0x0B,0x1F,0x23);
    write_reg(par,0xE1,0xD0,0x04,0x0C,0x11,0x13,0x2C,0x3F,0x44,0x51,0x2F,0x1F,0x1F,0x20,0x23);
    write_reg(par,0x21);
    write_reg(par,0x11);
    mdelay(50);
    write_reg(par,0x29);
    mdelay(200);
    return 0;
}

②修改fbtft_display(如果你的屏幕高宽参数不一致,请修改)

static struct fbtft_display display = {
	.regwidth = 8,
	.width = 240, //宽
	.height = 320, //高
	.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,
	},
};

linux_5.10/drivers/staging/fbtft/fbtft-core.c

①加入include

#include <linux/gpio.h> //add
#include <linux/of_gpio.h> //add

②修改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;
}

③修改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);
}

以上st7789的驱动部分就修改完成了,当然也有可能有人会问fbtft_request_gpios里加引号的参数不用加-gpios吗,我的回答是,不用,设备树里匹配就没有问题

2. 修改pinmux
很重要,Duo的管脚配置全在这,管脚具体定义功能和默认管脚配置可以参考:

CV181x/CV180x HDK 开发文档汇总 (Hardware Development Document)

里面的CV180xB_QFN68_PINOUT_V0.1_CN.xlsx文件
对应Duo上的管脚位置参考:
https://milkv.io/files/duo/duo-schematic-v1.1.pdf

接下来我们开始修改pinmux

u-boot-2021.10/board/cvitek/cv180x/board.c

pinmux_config(PINMUX_SPI2); //sdk默认把这里注释了,取消掉
PINMUX_CONFIG(SPINOR_CS_X, XGPIOA_24); //添加,这里的默认管脚功能并不是GPIO,修改之
PINMUX_CONFIG(SPINOR_MISO, XGPIOA_23); //添加,这里也一样
cvi_board_init();

以上就完成了pinmux的修改,其实除开修改board.c以外,也可以修改

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

具体方法就不写了,大家这么聪明肯定会
对了,我这里将准备连接SPI的dc和reset管脚改成GPIOA24和GPIOA23,参考图红框管脚:

3. 修改设备树
这里也很重要,有个坑就踩在这里面,开始!

build/boards/cv180x/cv1800b_sophpi_duo_sd/dts_riscv/cv1800b_sophpi_duo_sd.dts

需要在这个文件里添加一段代码

&spi2 {
	status = "okay";
	/delete-node/ spidev@0; //两个选择,要么写这个,要么改st7789里的reg
	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>; //配置连接dc线的管脚,对应上一节的pinmux配置
		reset = <&porta 23 GPIO_ACTIVE_HIGH>; //配置连接reset线的管脚
		debug = <0x0>;
	};
};

build/boards/default/dts/cv180x/cv180x_base.dtsi

修改spi2:spi2@041A0000,这里特别重要,坑就在这,我没加怎么操作都驱动不了屏幕

        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; //添加这个地方,作用是上拉
	};

设备树到此配置完成,注意注意切勿踩坑

4. 添加内核SPI配置和st7789驱动
这里有两个方法修改
①在启动编译环境后,输入menuconfig_kernel进入内核图形配置去设置,我被这个弄晕了,这里就不使用它了,不过有个小tips,在界面时按"/"键去搜索方法很好用,后面输出控制台配置都靠它
②直接添加到.config或者cvitek_cv1800b_sophpi_duo_sd_defconfig(SPI内核配置参考论坛mollysama大佬的)

linux_5.10/build/cv1800b_sophpi_duo_sd/.config
or
build/boards/cv180x/cv1800b_sophpi_duo_sd/linux/cvitek_cv1800b_sophpi_duo_sd_defconfig

CONFIG_SPI=y
CONFIG_SPI_MASTER=y
CONFIG_SPI_DESIGNWARE=y
CONFIG_SPI_DW_MMIO=y
CONFIG_SPI_SPIDEV=y
CONFIG_FB=y
CONFIG_FB_TFT=y
CONFIG_FB_TFT_ST7789V=y

5. 编译、连线、测试
①编译这块就不多解释了,翻翻其他大佬的帖子

source build/cvisetup.sh
defconfig cv1800b_sophpi_duo_sd
build_all
pack_sd_image

②连线这块解释一下,前面的pinmux的配置,使用的是硬件SPI方式,定义的管脚,请参照上面修改pinmux时放的pdf文件,下面是我连线的配置

Duo       |       st7789
GND       |         GND
3.3v(out) |         VCC
GPIO23    |         SCL
GPIO22    |         SDA
GPIOA23   |         RES
GPIOA24   |         DC
GPIO18    |         CS

③连好线,烧录好sd,插到duo上,插上usb通电,执行下面的代码测试

cat /dev/urandom > /dev/fb0  //显示花屏
cat /dev/zero > /dev/fb0  //清空屏幕,也就是黑屏了

以上操作,如果你都正确执行了,并且测试时执行第一条能正常花屏,表示SPI驱动st7789是100%成功了,但是会发现echo xxxx > /dev/fb0 屏幕像素点第一行能显示几个像素,这不是问题,其实你写一个程序来填充屏幕的像素点是正常的

6. 屏幕输出正常显示、输出内核加载信息并展示console
内核.config设置

CONFIG_TTY=y
CONFIG_VT=y
CONFIG_CONSOLE_TRANSLATIONS=y
CONFIG_VT_CONSOLE=y
CONFIG_HW_CONSOLE=y
CONFIG_VT_HW_CONSOLE_BINDING=y
CONFIG_FB=y
CONFIG_FB_CMDLINE=y
CONFIG_FB_NOTIFY=y
CONFIG_FONT_SUPPORT=y
CONFIG_FONTS=y
CONFIG_FONT_8x16=y
CONFIG_VGA_CONSOLE=y
CONFIG_DUMMY_CONSOLE=y
CONFIG_DUMMY_CONSOLE_COLUMNS=80
CONFIG_DUMMY_CONSOLE_ROWS=25
CONFIG_FRAMEBUFFER_CONSOLE=y
CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY=y

相关内核选项,可以在内核配置图形界面中找到,如果想翻翻各种配置的详解,那就在各个目录的Kconfig文件中了

最后,需要达到下图的效果,在屏幕驱动加载完成后输出内核加载信息,需要在uboot中添加启动参数console=tty0,其实也可以在编译前/u-boot-2021.10/include/configs/cv180x-asic.h文件中加入,至于setenv bootargs saveenv方法保存,这个得去uboot里开启相应指令才行

over!

13 Likes

加了文末的内核选项并修改uboo的启动参数,编译启动,屏幕并不会显示控制台打印

	#define SET_BOOTARGS "setenv bootargs ${root} ${mtdparts} " \
					"console=tty0 console=$consoledev,$baudrate $othbootargs;"
Kernel command line: root=/dev/mmcblk0p2 rootwait rw console=tty0 console=ttyS0,115200 earlycon=sbi loglevel=9 riscv.fwsz=0x80000
1 Like

在上面的基础上,再在内核配置添加下面的配置就可以出来了

#
# Console display driver support
#
CONFIG_VGA_CONSOLE=y
CONFIG_DUMMY_CONSOLE=y
CONFIG_DUMMY_CONSOLE_COLUMNS=80
CONFIG_DUMMY_CONSOLE_ROWS=25
CONFIG_FRAMEBUFFER_CONSOLE=y
CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY=y
3 Likes

感谢指正,我查看了.config文件,的确需要加入这些。

屏幕偏移该怎么解决?屏幕是240*280的分辨率,在驱动写入分辨率,有一块花屏

修改init_display这个初始化是必须的吗,用原来的初始化有什么问题吗?

这个得在fb_st7789v.c中set_addr_win方法调整

必须的,主要是每个型号的屏幕不同,使用的初始化方法不同,我的屏幕驱动本身是st7789,而内核中自带的驱动是st7789v,并不通用

您好,加载和卸载模块都没报错,lsmod也能看到加载成功的模块,但是在dev下面没有生成fb节点,这种一般是什么情况阿

已经解决,但是又有新问题,执行命令会显示没有空间

[root@milkv]~# cat /dev/zero > /dev/fb0
cat: write error: No space left on device

我用echo /dev/urandom > /dev/fb0
或者echo 11111111111 > /dev/fb0
屏幕没有任何变化

初始化的时候我用万用表去量SPI引脚电压没有任何波动的,难道引脚没有初始化吗
我使用cvi_pinmux读取引脚状态和在board里面设置的是不一致的。

[root@milkv]~# cvi_pinmux -r SD1_CMD
SD1_CMD function:
[ ] PWR_SD1_CMD
[ ] SPI2_SDO
[ ] IIC3_SCL
[v] PWR_GPIO_22
[ ] CAM_VS0
[ ] EPHY_LNK_LED
[ ] PWR_SPINOR1_MOSI
[ ] PWM_8

register: 0x300109c
value: 3
[root@milkv]~# cvi_pinmux -r SD1_D3
SD1_D3 function:
[ ] PWR_SD1_D3
[ ] SPI2_CS_X
[ ] IIC1_SCL
[v] PWR_GPIO_18
[ ] CAM_MCLK0
[ ] UART3_CTS
[ ] PWR_SPINOR1_CS_X
[ ] PWM_4

register: 0x300108c
value: 3

[root@milkv]/mnt/system/ko# cvi_pinmux -r SPINOR_CS_X
SPINOR_CS_X function:
[ ] SPINOR_CS_X
[ ] SPINAND_CS
[v] XGPIOA_24

register: 0x3001040
value: 3
[root@milkv]/mnt/system/ko# cvi_pinmux -r SPINOR_MISO
SPINOR_MISO function:
[ ] SPINOR_MISO
[ ] SPINAND_MISO
[v] XGPIOA_23

register: 0x300103c
value: 3

当我用这个软件将spi设置为正确的复用功能,再次测量初始化时候的io电压发现是有变化的,但是屏幕依然无法显示任何东西

可以看下驱动初始化的寄存器对不对,st7789和st7789v不太一样

这里配置了4个pinmux为gpio,设备树有跟着设置吗?

我是照您帖子上的,设备数就配置了st7789v的节点,dc和reset引脚

我使用的就是st7789v所以初始化部分我是灭有动的,就修改了fbtft_request_one_gpio这里面的

如果使用硬件spi,也就是屏幕对应端口直接gpio23,gpio22,gpio18,设备树这样配置就没问题了,如果配置pinmux使用软件spi,需要设备树中定义相关spi的,如scl,sda,cs

我是按照上面图片上也就是和您一样的方式用的是硬件spi,还有软件spi吗,现在屏幕接上去只有背光亮起来,无法显示内容,dc和cs用万用表测量io没有变化,所以我怀疑是不是io初始化有问题

我的意思是按照你用cvi_pinmux读取SD1_CMD和SD1_D3引脚本身配置就是不对的,不应该为gpio,应该是SPI2_SDO和SPI2_CS_X

这么说我GPIO设置没有生效了,这个在这u-boot-2021.10/board/cvitek/cv180x/board.c里面设置就行了吧,这里面我也修改了的。

是的,在最原始sdk中取消SPI2的pinmux注释就可以了,我没有查看过新版本sdk,不知道这块有没有其他变动,你可以仔细查查这个文件里关于SPI2的详细配置来确认引脚设置没有出错