ILI9341在milkv duos的移植(FrameBuffer 框架 lvgl可用)

在社区目前还没有找到对 ili9341 的移植文档, 于是我打算研究一下.由于我手中的开发板是 milkv duos sd 版本, 于是以 duos 为例.

ili9341侧移植

准备工作

  1. 官方sdk: GitHub - milkv-duo/duo-buildroot-sdk: Milk-V Duo Official buildroot SDK

  2. 编译工具链: GitHub - milkv-duo/host-tools(其实不用下载, 官方sdk中包含)

移植

  1. 修改设备树

    在 build/boards/cv181x/cv1813h_milkv_duos_sd/dts_riscv/cv1813h_milkv_duos_sd.dts 下找到 spi3 的部分(这里引脚可以自定义).

    &spi3 {
        status = "okay";
        /delete-node/ spidev@0;
    
        ili9341: ili9341@0 {
            compatible = "ilitek,ili9341";
            status = "okay";
            reg = <0>;
            spi-cpol;
            spi-cpha;
            rotate = <90>;
            fps = <60>;
            rgb;
            buswidth = <8>;
            dc = <&porta 18 GPIO_ACTIVE_HIGH>; // DC引脚
            reset = <&porta 28 GPIO_ACTIVE_LOW>; // RESET引脚
            spi-max-frequency = <40000000>;
            debug = <0x0>;
        };  
    };
    
  2. 修改 ili9341 驱动代码

    这里注释掉的部分都是源码

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

      引入头文件

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

      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);
          usleep_range(20, 40);
          gpiod_set_value_cansleep(par->gpio.reset, 0);
          msleep(120);
          gpiod_set_value_cansleep(par->gpio.reset, 1);
          msleep(10);
      */
      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);
      }
      

      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 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;
      }
      
    • linux_5.10/drivers/staging/fbtft/fbtft-bus.c 中

      /* 16 bit pixel over 8-bit databus */
      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;
      }
      
  3. 修改 config 文件

    在 build/boards/cv181x/cv1813h_milkv_duos_sd/linux/cvitek_cv1813h_milkv_duos_sd_defconfig 增加以下几行

    // CONFIG_FB=y 在新版有, 不用加了, 没有可以加一下
    CONFIG_FB_TFT=y
    CONFIG_FB_TFT_ILI9341=y
    CONFIG_SPI_MASTER=y
    CONFIG_SPI_DESIGNWARE=y
    
  4. 编译

    回到 sdk 的根目录, 直接编译就可以了, 具体参考 简介 | Milk-V

  5. 测试

    进入 milkv duos 中, 你会看到 /dev/fb0, 尝试驱动它

lvgl侧移植

  1. lv_port_linux: https://github.com/lvgl/lv_port_linux:

  2. lv_drivers: GitHub - lvgl/lv_drivers: TFT and touch pad drivers for LVGL embedded GUI library

  3. lvgl: GitHub - lvgl/lvgl: Embedded graphics library to create beautiful UIs for any MCU, MPU and display type.

移植

可以尝试自己移植一下, 也可以直接利用官方提供的框架

  1. 自己动手丰衣足食

    这里移植 lvgl 的话可以参考 嵌入式 Linux 下的 LVGL 移植_lvgl 在linux 板上-CSDN博客

  2. 使用官方移植好的框架

    GitHub - milkv-duo/duo-lvgl-fb-demo

需要注意

  1. 编译工具链的替换

    编译工具链的根目录就在 sdk 的 host-tools 目录下, 在移植好 lvgl 之后, 在Makefile 下记得更换工具链.

    CC := 绝对路径/duo-buildroot-sdk-offical/host-tools/gcc/riscv64-linux-musl-x86_64/bin/riscv64-unknown-linux-musl-gcc
    
  2. 在执行 demo 的时候可能会报错

    ioctl(FBIOBLANK): Invalid argument
    

    这个时候直接注释掉 lv_drivers/display/fbdev.c 下的代码

    // Make sure that the display is on.
    // if (ioctl(fbfd, FBIOBLANK, FB_BLANK_UNBLANK) != 0) {
    //     perror("ioctl(FBIOBLANK)");
    //     return;
    // }
    

最终效果

PS

我屏幕的颜色貌似不太正确, 偏黄, 想请教一下大佬颜色的问题怎么解决呢?

1 Like

Try rotating the display in the dts to 0
rotate = <0>;

2 Likes

thank you,I will have a try.

1 Like

hello, it is strange if I change the “rotate” to “<0>”.

1 Like

Ok, try add

rgb;

to dts

https://forums.raspberrypi.com/viewtopic.php?t=358240

1 Like


Thank you my friend. Maybe I have two problems, one is the color display problem, I have added "rgb’ as you say at the beginning, which seems to be unable to solve the problem, and the other is the overlapping problem of the images on my screen, I also changed the “rotate = <0>” of the images according to what you said, which resulted in this situation.

1 Like

OK, the it might be the color order

bgr;

?

1 Like

well, It seem that I solve the problem. The problem seem to appear on the config of lvgl. now I use the st7789 screen. Thank you my friend!!!

1 Like

Ok, cool that is good to hear

2 Likes