本来想等大佬发点亮屏幕的教程,发现一直没有相关信息,无奈只有自己动手丰衣足食了(本来想简单点亮sd1306,结果大意烧了 手头上只有这一块st7789,那就干SPI吧)
首先我先声明,我是新手,以下教程是基于自己检索资料总结的,由于第一次接触linux开发板,花费了大量时间尝试才成功,如以下教程中有误导或者错误之处,还请各位大佬嘴下留情
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的管脚配置全在这,管脚具体定义功能和默认管脚配置可以参考:
里面的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!