一、环境及demo目标
1. 我使用的内核版本是5.2.y。
2. 我使用的并不是Licheepi官方给出的RGB屏,而是2.4寸ST7789V-TFTLCD屏幕,分辨率为240*320。因此需要在linux Kernel中对屏幕进行适配,这个demo最终能将终端显示在屏幕上。
二、硬件连接
licheepi开发板与TFT屏幕之间按照如下线序进行连接:
TFT屏幕 | licheepi开发板 | 备注 |
---|---|---|
3.3V | 3.3V | 必须接 |
GND | GND | 必须接 |
BL | PWM0 | 背光引脚,不需要调节的话直接接3V3 |
SCK | SPI-SCK | 必须接 |
RST | U2R | 必须接,可以自己改 |
CS | SPI-CS | 必须接 |
DC | U2T | 必须接 |
MOSI(SDI) | MOSI | 必须接 |
MISO(SDO) | MISO | 可不接 |
三、配置Linux Kernel的menuconfig
1. 进入/home/daemonchen/linux/V3S/linux_kernel/linux/linux-zero-5.2.y文件夹,在Licheepi_MY_Ethernet_defconfig的基础上进行修改。
cd /home/daemonchen/linux/V3S/linux_kernel/linux/linux-zero-5.2.y
export ARCH=arm
export CROSS_COMPILE=$CROSS_COMPILE
make Licheepi_MY_Ethernet_defconfig
make menuconfig
2. 进入menuconfig以后按照以下路径进行修改:
#使能ST7789V芯片
Device Drivers --->
[*] Staging drivers --->
<*> Support for small TFT LCD display modules --->
<*> FB driver for the ST7789V LCD Controller
#如有需要,使能开机企鹅logo,一般用作屏幕测试
Device Drivers --->
Graphics support --->
[*] Bootup logo --->
[*] Standard black and white Linux logo (NEW)
[*] Standard 16-color Linux logo (NEW)
[*] Standard 224-color Linux logo (NEW)
3. 保存退出
4. 为了方便下次直接调用这些配置,使用下列命令将配置保存为Licheepi_MY_LCD_defconfig.
make savedefconfig
cp defconfig arch/arm/configs/Licheepi_MY_LCD_defconfig
四、更改设备树及内核文件
1. 首先打开arch/arm/boot/dts/Makefile,注释掉sun8i-v3s-licheepi-zero-with-480x272-lcd.dtb/sun8i-v3s-licheepi-zero-with-800x480-lcd.dtb/sun8i-v3s-licheepi-zero-with-1024x600-lcd.dtb,我们只编译sun8i-v3s-licheepi-zero-dock.dtb和sun8i-v3s-licheepi-zero.dtb,注意“\”后面千万不要有空格!如下图所示:
2. 打开arch/arm/boot/dts下的sun8i-v3s.dtsi,做如下更改:
注释掉57-64行(关闭默认视频输出):
注释掉353-356行(关闭pwm0,与屏幕引脚冲突):
注释掉 464-473行(关闭uart2,与屏幕引脚冲突):
保存退出。
3. 打开arch/arm/boot/dts下的sun8i-v3s-licheepi-zero-dock.dts,做如下更改:
在文件末尾加上spi0的追加节点,即st7789v的设备树节点:
&spi0{
status = "okay";
st7789v: st7789v@0{
compatible = "sitronix,st7789v";
reg = <0>;
status = "okay";
spi-max-frequency = <36000000>;
spi-cpol;
spi-cpha;
rotate = <90>;
fps = <30>;
buswidth = <8>;
rgb;
tft-dc-gpios = <&pio 1 0 GPIO_ACTIVE_HIGH>; // PB0
tft-reset-gpios = <&pio 1 1 GPIO_ACTIVE_HIGH>; // PB1
tft-led-gpios = <&pio 1 4 GPIO_ACTIVE_HIGH>; // PB4
width = <240>;
height= <320>;
debug = <0>;
};
};
如下图所示:
4. 打开drivers/staging/fbtft/fbtft-core.c文件,修改fbtft_request_one_gpio函数,注意需要在该文件上引入下面头文件,否则编译报错。
#include <linux/of_gpio.h>
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 ret = 0;
if (of_find_property(node, name, NULL)) {
*gpiop = devm_gpiod_get_index(dev, dev->driver->name, index,
GPIOD_OUT_HIGH);
if (IS_ERR(*gpiop)) {
ret = PTR_ERR(*gpiop);
dev_err(dev,
"Failed to request %s GPIO:%d\n", name, ret);
return ret;
}
fbtft_par_dbg(DEBUG_REQUEST_GPIOS, par, "%s: '%s' GPIO\n",
__func__, name);
}
return ret;
*/
struct device *dev = par->info->device;
struct device_node *node = dev->of_node;
int gpio, flags, ret = 0;
enum of_gpio_flags of_flags;
char gpio_names[32];
//sprintf(gpio_names, "%s-gpios", name);
sprintf(gpio_names, "%s", name);
printk("@ gpio_names = %s\n", gpio_names);
if (of_find_property(node, gpio_names, NULL)) {
gpio = of_get_named_gpio_flags(node, gpio_names, index, &of_flags);
printk ("@ gpio = %d | ENOENT = %d | EPROBE_DEFER = %d \n", gpio, ENOENT, EPROBE_DEFER);
if (gpio == -ENOENT)
return 0;
if (gpio == -EPROBE_DEFER)
return gpio;
if (gpio < 0) {
dev_err(dev,
"failed to get '%s' from DT\n", gpio_names);
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",
gpio_names, gpio, ret);
return ret;
}
*gpiop = gpio_to_desc(gpio);
fbtft_par_dbg(DEBUG_REQUEST_GPIOS, par, "%s: '%s' = GPIO%d\n",
__func__, gpio_names, gpio);
}
return ret;
}
5. 打开drivers/staging/fbtft/fbtft-core.c文件,修改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, 0);
usleep_range(20, 40);
gpiod_set_value_cansleep(par->gpio.reset, 1);
msleep(120);
*/
if (!par->gpio.reset)
return;
fbtft_par_dbg(DEBUG_RESET, par, "%s()\n", __func__);
gpiod_set_value_cansleep(par->gpio.reset, 1);
usleep_range(20, 40);
gpiod_set_value_cansleep(par->gpio.reset, 0);
msleep(120);
gpiod_set_value_cansleep(par->gpio.reset, 1);
msleep(10);
}
6. 打开drivers/staging/fbtft/fbtft-core.c文件,修改fbtft_request_gpios_dt函数里面,reset和dc引脚名称,从reset-gpios、dc-gpios和 led-gpios 改为tft-reset-gpios 、 tft-dc-gpios 和tft-led-gpios。
7. 打开drivers/staging/fbtft/fbtft-bus.c文件,修改fbtft_write_vmem16_bus8函数,直接替换
int fbtft_write_vmem16_bus8(struct fbtft_par *par, size_t offset, size_t len)
{
u16 *vmem16;
__be16 *txbuf16 = par->txbuf.buf;
size_t remain;
size_t to_copy;
size_t tx_array_size;
int i;
int ret = 0;
size_t startbyte_size = 0;
fbtft_par_dbg(DEBUG_WRITE_VMEM, par, "%s(offset=%zu, len=%zu)\n",
__func__, offset, len);
remain = len / 2;
vmem16 = (u16 *)(par->info->screen_buffer + offset);
if (par->gpio.dc)
{
//printk("dc拉高!\n");
gpiod_set_value(par->gpio.dc, 1);
}
/* non buffered write */
if (!par->txbuf.buf)
return par->fbtftops.write(par, vmem16, len);
/* buffered write */
tx_array_size = par->txbuf.len / 2;
if (par->startbyte) {
txbuf16 = par->txbuf.buf + 1;
tx_array_size -= 2;
*(u8 *)(par->txbuf.buf) = par->startbyte | 0x2;
startbyte_size = 1;
}
while (remain) {
to_copy = min(tx_array_size, remain);
dev_dbg(par->info->device, "to_copy=%zu, remain=%zu\n",
to_copy, remain - to_copy);
for (i = 0; i < to_copy; i++)
txbuf16[i] = cpu_to_be16(vmem16[i]);
vmem16 = vmem16 + to_copy;
ret = par->fbtftops.write(par, par->txbuf.buf,
startbyte_size + to_copy * 2);
if (ret < 0)
return ret;
remain -= to_copy;
}
return ret;
}
五、编译内核和设备树
1. 输入以下命令进行编译:
make -j16
make -j16 modules
sudo make -j16 modules_install
make dtbs
2. 将zImage和sun8i-v3s-licheepi-zero-dock.dtb复制到nfs目录下:
cp arch/arm/boot/zImage /home/daemonchen/linux/tftpboot -f
cp arch/arm/boot/dts/sun8i-v3s-licheepi-zero-dock.dtb /home/daemonchen/linux/tftpboot -f
六、启动测试
1. 使用SecureCRT查看串口0输出的log,在uboot界面配置网络相关变量为:
setenv ipaddr 192.168.147.132
setenv gatewayip 192.168.147.2
setenv netmask 255.255.255.0
setenv serverip 192.168.147.130
saveenv
2. 将终端投射在屏幕上(即在bootargs中加入console=tty0):
setenv bootcmd 'tftp 0x41000000 zImage; tftp 0x41800000 sun8i-v3s-licheepi-zero-dock.dtb; bootz 0x41000000 - 0x41800000'
setenv bootargs 'console=tty0 console=ttyS0,115200 root=/dev/nfs rw nfsroot=192.168.147.130:/home/daemonchen/linux/nfs/rootfs ip=192.168.147.132:192.168.147.130:192.168.147.2:255.255.255.0::eth0::off init=/linuxrc'
saveenv
3. 启动测试:
boot
启动成功后Linux内核输出以下log:
屏幕端显示企鹅logo和终端信息:
至此TFT屏幕适配linux内核完成!
标签:par,V3S,适配,全志,dev,fbtft,ret,gpio,size From: https://blog.csdn.net/Jlinkneeder/article/details/141435866