导航

    全志在线开发者论坛

    • 注册
    • 登录
    • 搜索
    • 版块
    • 话题
    • 在线文档
    • 社区主页
    1. 主页
    2. livpo
    3. 最佳
    • 资料
    • 关注 0
    • 粉丝 10
    • 我的积分 5363
    • 主题 49
    • 帖子 127
    • 最佳 32
    • 群组 0

    livpo 发布的最佳帖子

    • 回复: 【水经验混下载权限专用贴】如何升级LV2拉取SDK

      @ccccchduan 这里更强

      发布在 灌水区
      livpo
      livpo
    • 全志T527芯片详解【二】:高清图像编解码

      lQDPJwCnMd6-YonNBDjNB4CwzbOAv_WBaOcGOqDoP29MAA_1920_1080.jpg

      硬件模块加持

      T527集成了多个图形显示和编解码相关的硬件模块,为高清图像显示、高清视频播放和多路高清摄像头输入提供了强大的硬件基础:

      • ARM Mail-G57 GPU
      • 自研显示引擎(Display Engine)
      • 去隔行处理单元(De-interIace)
      • 2D图像加速单元(Graphic2D)
      • 视频编解码引擎(Video Engine)
      • 自研视觉处理器(ISP)
      • 视觉缩放处理单元(VIPP)
      • ......

      lQDPKGbzjfGhoonNBDjNB4Cw8gXbT21isxkGOqDoP4czAA_1920_1080.jpg

      在硬件加速模块加持下,T527可以轻松实现4K视频的录制和播放,并支持H.264\H.265\VP9等多种主流的编解码格式,同时支持多路编解码的场景。

      Video decoder

      • H.265 MP decoder up to 4K@60fps
      • H.264 BL/MP/HP decoder up to 4K@30fps
      • VP9 decoder up to 4K@60fps
      • Multi-format 1080p@60fps video playback,including VP8,MPEG1/2SP/MP,MPEG4,SP/ASP,AVS+/AVS JIZHUN

      Video encoder

      • H.264 encoder up to 4K@25fps
      • MJPEG encoder up to 4K@15fps
      • JPEG encoder up to 8K x 8K resolution

      丰富的图像输入输出接口

      T527上集成了大量图像输入、输出接口,通过与编解码模块的配合,可以实现4K+1080P的双屏幕异显和实现多达6路摄像头图像的交织输入,为如:收银机、智能座舱、360°摄像头等需要多屏幕交互或多摄像头输出的场景提供了高效的解决方案。

      lQDPJwtYxhuEgonNBDjNB4CwBpiufeS3k_EGOqDoP4czBA_1920_1080.jpg

      屏幕接口:MIPI DSI 、LVDS、HDMI、eDP、RGB.......

      • HDMI2.0b up to 4K@60fps
      • 4+4-lane MIPI-DSI output,supporting up to 2.5K@60fps and 4K@45fps
      • 2xLVDS interface with dual link, up to I080p@60fps
      • 2xRGB interfaces with DE/SYNC mode, up to I080p@60fps
      • eDP1.3 up to 2.5K@60fps and 4K@30fps

      摄像头接口:MIPI CSI、Parallel CSI
      Parallel CSl interface

      • 8/10/12/16-bit width
      • Supports BT.656 up to 4720P@30fps and BT.1120 up to 41080P@30fps

      MIPI CSI interface

      • 24 lane/42 lane/4+2*2 lane MIPICSI,flexible combination, up to 2.0 Gbit/s per lane in HS transmission,compliant with MIPI-CSI2V1.1 and MIPI DPHYV1.1
      • Maximumvideocaptureresolution of 8M@30fps

      自研高清画质算法矩阵

      全志在显示引擎上硬件固化了自研的高清画质算法矩阵AWonder1.0,提供了多维度的图像处理算法,包括:

      • 智能降噪
      • 明亮动态
      • 清晰细节
      • 绚丽色彩
      • 精锐去隔行
      • 臻彩HDR
      • AI-PQ
      • ......

      lQDPJw45du3JIonNBDjNB4Cwshub3fRp1HgGOqDoP4czAw_1920_1080.jpg

      帮助图像减少噪点、提升对比度、减少锯齿、丰富色彩等,并通过AI场景和人像识别,令图像更清晰、色彩更自然。

      自研安防级专业图像信号处理器

      T527集成了全志自研的安防级专业图像信号处理器 (ISP),该ISP在前序版本上大幅升级多个模块,能更好地应对如行车、户外等运动和光照复杂的场景。

      • 时空域多级降噪
      • 宽动态合成
      • 局部色调映射算法
      • ......

      lQDPKcua0W388onNBDjNB4CwrhXRQy32o-EGOqDoP4czAg_1920_1080.jpg

      同时全志自研推出AI-ISP智能图像处理算法「全志智影」,通过ISP和NPU深度配合,提升了图像的感光度,帮助实现黑光全彩等功能,在车载场景下可以实现全天候无盲区的行车安全监控。

      相比传统ISP算法,「全志智影」AI-ISP能够更好地提升暗部清晰度及颜色表现,改善画面整体亮度及色度噪点表现,并大程度地抑制运动拖影的出现。

      自研图像和视觉参数调节工具

      T527配套了全志自研量产工具

      • 屏幕画质调试工具:TigerLCD
      • 视觉图像调试工具:TigerISP

      通过可视化的软件工具,帮助工程师更便捷地对图像参数进行调试,减少工程师工作量,提升开发效率,让图像色彩显示更精准。

      T527长图2-横版6.jpg

      发布在 T Series
      livpo
      livpo
    • 【R128】应用开发案例——点亮一颗 LED 灯

      基于R128-S2设计的全套开发板已上线淘宝百问网韦东山老师个人店进行售卖,包含黑色的DshanMCU-R128s2-R16N16模组和全套的DshanMCU-R128s2-DEVKIT。

      • DshanMCU-R128s2-R16N16模组:39.9元
      • DshanMCU-R128s2-DEVKIT开发板:59.9元

      R128开发板购买链接:https://item.taobao.com/item.htm?spm=a21n57.1.0.0.46b0523cMfarLo&id=736154682975&ns=1&abbucket=5#detail

      “主图_01”为智能对象-1.jpg

      点亮一颗 LED 灯

      本文案例代码 下载地址
      点亮一颗 LED 灯案例代码 https://www.aw-ol.com/downloads?cat=24

      首先我们搭建电路,如下:

      840BF3F7B6CB5E82808FF0DE2BF2E083.png

      引脚 LED
      PA18 红色 LED
      PA13 绿色 LED
      PA12 黄色 LED

      载入方案

      我们使用的开发板是 R128-Devkit,需要开发 C906 核心的应用程序,所以载入方案选择r128s2_module_c906

      $ source envsetup.sh 
      $ lunch_rtos 1
      

      image-20230802110150203.png

      勾选 GPIO 驱动

      mrtos_menuconfig 找到下列驱动

      Drivers Options  --->
          soc related device drivers  --->
                  GPIO devices --->
                      [*] enable GPIO driver
                      [*] enbale GPIO hal APIs Test command
      

      image-20230803111740921.png

      编写程序

      打开你喜欢的编辑器,修改文件:lichee/rtos/projects/r128s2/module_c906/src/main.c

      image-20230803112142315.png

      引入头文件

      #include <hal_gpio.h>
      

      image-20230803112159250.png

      使用 GPIO 配置引脚

      配置 GPIO 的上下拉状态

      使用 hal_gpio_set_pull(gpio_pin_t pin, gpio_pull_status_t pull); 来设置。这里我们设置 PA18 引脚为默认上拉状态。

      hal_gpio_set_pull(GPIOA(18), GPIO_PULL_UP);
      

      配置 GPIO 输入输出模式

      使用 hal_gpio_set_direction(gpio_pin_t pin, gpio_direction_t direction); 来设置 GPIO 的输入输出模式,这里配置为输出模式。

      hal_gpio_set_direction(GPIOA(18), GPIO_DIRECTION_OUTPUT);
      

      配置 GPIO 的 MUX 功能

      GPIO 通常有多种功能,需要配置 MUX 选择需要的功能,使用 hal_gpio_pinmux_set_function(gpio_pin_t pin, gpio_muxsel_t function_index); 来设置 GPIO 的复用功能,这里配置为GPIO 输出模式(GPIO_MUXSEL_OUT)

      hal_gpio_pinmux_set_function(GPIOA(18), GPIO_MUXSEL_OUT);
      

      配置 GPIO 的电平

      使用 hal_gpio_set_data(gpio_pin_t pin, gpio_data_t data); 来配置 GPIO 的电平,这里配置 PA18 为高电平点亮 LED

      hal_gpio_set_data(GPIOA(18), GPIO_DATA_HIGH);
      

      完整的配置 GPIO

      hal_gpio_set_pull(GPIOA(18), GPIO_PULL_UP); // 配置 GPIO 的上下拉状态
      hal_gpio_set_direction(GPIOA(18), GPIO_DIRECTION_OUTPUT);  // 配置 GPIO 输入输出模式
      hal_gpio_pinmux_set_function(GPIOA(18), GPIO_MUXSEL_OUT);  // 配置 GPIO 的 MUX 功能
      hal_gpio_set_data(GPIOA(18), GPIO_DATA_HIGH);  // 配置 GPIO 的电平
      

      以此类推,我们同时配置 PA18, PA13,PA12 的 GPIO

      hal_gpio_set_pull(GPIOA(18), GPIO_PULL_UP);
      hal_gpio_set_direction(GPIOA(18), GPIO_DIRECTION_OUTPUT);
      hal_gpio_pinmux_set_function(GPIOA(18), GPIO_MUXSEL_OUT);
      hal_gpio_set_data(GPIOA(18), GPIO_DATA_HIGH);
      
      hal_gpio_set_pull(GPIOA(13), GPIO_PULL_UP);
      hal_gpio_set_direction(GPIOA(13), GPIO_DIRECTION_OUTPUT);
      hal_gpio_pinmux_set_function(GPIOA(13), GPIO_MUXSEL_OUT);
      hal_gpio_set_data(GPIOA(13), GPIO_DATA_HIGH);
      
      hal_gpio_set_pull(GPIOA(12), GPIO_PULL_UP);
      hal_gpio_set_direction(GPIOA(12), GPIO_DIRECTION_OUTPUT);
      hal_gpio_pinmux_set_function(GPIOA(12), GPIO_MUXSEL_OUT);
      hal_gpio_set_data(GPIOA(12), GPIO_DATA_HIGH);
      

      结果

      编译固件后烧录,可以看到三色 LED 灯同时亮起。

      image-20230803112914576.png

      流水灯

      为了实现流水灯,我们先实现一个 sleep 函数

      static inline int msleep(int ms) {
          vTaskDelay(ms / portTICK_RATE_MS); 
      }
      

      然后实现流水灯逻辑即可,之前已经设置过的GPIO状态不需要重复设置。

      while (1) {
          hal_gpio_set_data(GPIOA(18), GPIO_DATA_HIGH);
          hal_gpio_set_data(GPIOA(13), GPIO_DATA_LOW);
          hal_gpio_set_data(GPIOA(12), GPIO_DATA_LOW);
          msleep(100);
          hal_gpio_set_data(GPIOA(18), GPIO_DATA_LOW);
          hal_gpio_set_data(GPIOA(13), GPIO_DATA_HIGH);
          hal_gpio_set_data(GPIOA(12), GPIO_DATA_LOW);
          msleep(100);
          hal_gpio_set_data(GPIOA(18), GPIO_DATA_LOW);
          hal_gpio_set_data(GPIOA(13), GPIO_DATA_LOW);
          hal_gpio_set_data(GPIOA(12), GPIO_DATA_HIGH);
          msleep(100);
      }
      

      9E9A18F0EF91DCE086CBA812EF9E2ECA.png

      发布在 A Series
      livpo
      livpo
    • 【R128】基础组件开发指南——显示与屏幕驱动

      基于R128-S2设计的全套开发板已上线淘宝百问网韦东山老师个人店进行售卖,包含黑色的DshanMCU-R128s2-R16N16模组和全套的DshanMCU-R128s2-DEVKIT。

      • DshanMCU-R128s2-R16N16模组:39.9元
      • DshanMCU-R128s2-DEVKIT开发板:59.9元

      R128开发板购买链接:https://item.taobao.com/item.htm?spm=a21n57.1.0.0.46b0523cMfarLo&id=736154682975&ns=1&abbucket=5#detail

      “主图_01”为智能对象-1.jpg

      显示与屏幕驱动

      RTOS 提供了一套完整的屏幕驱动,支持 RGB, i8080, SPI, DBI 格式的屏幕。

      (1)RGB 接口

      RGB接口在全志平台又称HV接口(Horizontal同步和Vertical同步)。有些LCD屏支持高级的功能比如 gamma,像素格式的设置等,但是 RGB 协议本身不支持图像数据之外的传输,所以无法通过 RGB 管脚进行对 LCD 屏进行配置,所以拿到一款 RGB 接口屏,要么不需要初始化命令,要么这个屏会提供额外的管脚给 SoC 来进行配置,比如 SPI 和 I2C 等。RGB 屏幕有许多格式,不同的位宽,不同的时钟周期。下表是位宽与时钟周期的区别。

      位宽 时钟周期数 颜色数量和格式 并行\串行 RGB
      24 bits 1 cycle 16.7M colors, RGB888 并行
      18 bits 1 cycle 262K colors, RGB666 并行
      16 bits 1 cycle 65K colors, RGB565 并行
      6 bits 3 cycles 262K colors, RGB666 串行
      6 bits 3 cycles 65K colors, RGB565 串行

      串行 RGB 是相对于并行 RGB 来说,而并不是说它只用一根线来发数据,只要通过多个时钟周期才能把一个像素的数据发完,那么这样的 RGB 接口就是串行 RGB。

      (2)I8080 屏幕

      Intel 8080 接口屏(又称 MCU 接口),很老的协议,一般用在分辨率很小的屏上。

      管脚的控制脚有6种:

      1. CS 片选信号,决定该芯片是否工作.
      2. RS 寄存器选择信号,低表示选择 index 或者 status 寄存器,高表示选择控制寄存器。实际场景中一般接SoC的LCD_DE脚(数据使能脚)
      3. WR (低表示写数据) 数据命令区分信号,也就是写时钟信号,一般接 SoC 的 LCD_CLK 脚
      4. RD (低表示读数据)数据读信号,也就是读时钟信号,一般接 SoC 的 LCD_HSYNC 脚
      5. RESET 复位LCD( 用固定命令系列 0 1 0来复位)
      6. Data 是双向的数据通路

      I8080 根据的数据位宽接口有 8/9/16/18,连哪些脚参考,即使位宽一样,连的管脚也不一样,还要考虑的因素是 RGB 格式。

      1. RGB565,总共有 65K 这么多种颜色
      2. RGB666,总共有 262K 那么多种颜色
      3. 9bit 固定为 262K

      (3)SPI 屏幕

      SPI LCD 是使用 SPI 总线传输图像数据的屏幕,只会出现在很低分辨率的屏幕上。一般来说开屏前都需要初始化操作。

      适配 LCD 屏幕的步骤

      1. 确保全志显示框架的内核配置有使能
      2. 前期准备以下资料和信息:
        1. 屏手册。主要是描述屏基本信息和电气特性等,向屏厂索要。
        2. Driver IC 手册。主要是描述屏 IC 的详细信息。这里主要是对各个命令进行详解,对我们进行初始化定制有用,向屏厂索要。
        3. 屏时序信息。请向屏厂索要。
        4. 屏初始化代码,请向屏厂索要。一般情况下 DSI 和 I8080 屏等都需要初始化命令对屏进行初始化。
        5. 万用表。调屏避免不了测量相关电压。
      3. 通过第2步屏厂提供的资料,定位该屏的类型,然后选择一个已有同样类型的屏驱动作为模板进行屏驱动添加或者直接在上面修改。
      4. 修改屏驱动目录下的 panel.c 和 panel.h。在全局结构体变量 panel_array 中新增刚才添加 strcut __lcd_panel 的变量指针。panel.h 中新增 strcut __lcd_panel 的声明。
      5. 修改 Makefile。在 lcd 屏驱动目录的上一级的 Makefile 文件中的disp-objs中新增刚才添加屏驱动.o
      6. 修改 sys_config.fex 中的 lcd0 节点。
      7. 编译测试

      LCD 屏幕驱动源码

      LCD 屏幕驱动源码结构

      .
      ├── Kconfig
      ├── Makefile
      ├── disp
      │   ├── Kconfig
      │   ├── Makefile
      │   ├── de                            # Display Engine 层驱动,包括图层与显示控制
      │   │   ├── Makefile
      │   │   ├── bsp_display.h
      │   │   ├── disp_capture.c
      │   │   ├── disp_capture.h
      │   │   ├── disp_device.c
      │   │   ├── disp_device.h
      │   │   ├── disp_display.c
      │   │   ├── disp_display.h
      │   │   ├── disp_enhance.c
      │   │   ├── disp_enhance.h
      │   │   ├── disp_features.c
      │   │   ├── disp_features.h
      │   │   ├── disp_hdmi.c
      │   │   ├── disp_hdmi.h
      │   │   ├── disp_lcd.c
      │   │   ├── disp_lcd.h
      │   │   ├── disp_manager.c
      │   │   ├── disp_manager.h
      │   │   ├── disp_private.c
      │   │   ├── disp_private.h
      │   │   ├── disp_smart_backlight.c
      │   │   ├── disp_smart_backlight.h
      │   │   ├── disp_tv.c
      │   │   ├── disp_tv.h
      │   │   ├── disp_vdevice.c
      │   │   ├── disp_vdevice.h
      │   │   ├── include.h
      │   │   └── lowlevel_v2x               # DISP 底层驱动,硬件寄存器交互
      │   │       ├── Makefile
      │   │       ├── de_ase.c
      │   │       ├── de_ase_type.h
      │   │       ├── de_bws.c
      │   │       ├── de_bws_type.h
      │   │       ├── de_ccsc.c
      │   │       ├── de_clock.c
      │   │       ├── de_clock.h
      │   │       ├── de_csc.h
      │   │       ├── de_csc_type.h
      │   │       ├── de_dcsc.c
      │   │       ├── de_dsi.c
      │   │       ├── de_dsi.h
      │   │       ├── de_dsi_28.c
      │   │       ├── de_dsi_type.h
      │   │       ├── de_dsi_type_28.h
      │   │       ├── de_eink.c
      │   │       ├── de_eink.h
      │   │       ├── de_enhance.c
      │   │       ├── de_enhance.h
      │   │       ├── de_fcc.c
      │   │       ├── de_fcc_type.h
      │   │       ├── de_fce.c
      │   │       ├── de_fce_type.h
      │   │       ├── de_feat.c
      │   │       ├── de_feat.h
      │   │       ├── de_gsu.c
      │   │       ├── de_gsu_type.h
      │   │       ├── de_hal.c
      │   │       ├── de_hal.h
      │   │       ├── de_lcd.c
      │   │       ├── de_lcd.h
      │   │       ├── de_lcd_sun50iw10.c
      │   │       ├── de_lcd_type.h
      │   │       ├── de_lti.c
      │   │       ├── de_lti_type.h
      │   │       ├── de_peak.c
      │   │       ├── de_peak_type.h
      │   │       ├── de_rtmx.c
      │   │       ├── de_rtmx.h
      │   │       ├── de_rtmx_type.h
      │   │       ├── de_scaler.h
      │   │       ├── de_scaler_table.c
      │   │       ├── de_scaler_table.h
      │   │       ├── de_smbl.c
      │   │       ├── de_smbl.h
      │   │       ├── de_smbl_tab.h
      │   │       ├── de_smbl_type.h
      │   │       ├── de_vep.h
      │   │       ├── de_vep_table.c
      │   │       ├── de_vep_table.h
      │   │       ├── de_vsu.c
      │   │       ├── de_vsu_type.h
      │   │       ├── de_wb.c
      │   │       ├── de_wb.h
      │   │       ├── de_wb_type.h
      │   │       ├── disp_al.c
      │   │       ├── disp_al.h
      │   │       ├── disp_eink_data.c
      │   │       ├── disp_eink_data.h
      │   │       ├── disp_waveform.c
      │   │       ├── disp_waveform.h
      │   │       ├── rtmx_eink.c
      │   │       └── rtmx_eink.h
      │   ├── dev_disp.c                 # DISP 公共端口
      │   ├── dev_disp.h
      │   ├── disp_debug.c
      │   ├── disp_debug.h
      │   ├── disp_sys_intf.c
      │   ├── disp_sys_intf.h
      │   ├── lcd                        # LCD 面板驱动,包括自定义初始化控制,上下电时序控制
      │   │   ├── Kconfig
      │   │   ├── S6D7AA0X01.c
      │   │   ├── S6D7AA0X01.h
      │   │   ├── VVX07H005A10.c
      │   │   ├── VVX07H005A10.h
      │   │   ├── WilliamLcd.c
      │   │   ├── WilliamLcd.h
      │   │   ├── b080uan01_mipi1200x1920.c
      │   │   ├── b080uan01_mipi1200x1920.h
      │   │   ├── cl40bc1019_cpu.c
      │   │   ├── cl40bc1019_cpu.h
      │   │   ├── cpu_gg1p4062utsw.c
      │   │   ├── cpu_gg1p4062utsw.h
      │   │   ├── default_eink.c
      │   │   ├── default_eink.h
      │   │   ├── default_panel.c
      │   │   ├── default_panel.h
      │       └── wtq05027d01.h
      ├── soc                             # SoC 层特化驱动
      │   ├── Kconfig
      │   ├── Makefile
      │   ├── VVX07H005A10_mipi_config.c
      │   ├── disp_board_config.c
      │   ├── disp_board_config.h
      │   ├── he0801a068_mipi_config.c
      │   ├── platform_resource.c
      │   ├── platform_resource.h
      │   ├── sun20iw2.c
      └── tv                               # TV 驱动,R128不使用
          ├── Makefile
          ├── de_tve_sun8iw11.c
          ├── de_tve_sun8iw7.c
          ├── de_tve_v1.c
          ├── de_tvec.h
          ├── drv_tv.c
          ├── drv_tv.h
          ├── gm7121.c
          ├── tv_ac200.c
          ├── tv_ac200.h
          ├── tv_ac200_lowlevel.c
          └── tv_ac200_lowlevel.h
      

      屏驱动源码位置

      RGB 面板驱动

      对于不需要初始化的 RGB 屏幕(一般是 40PIN,50PIN)使用 default_panel.c

      lichee/rtos‑hal/hal/source/disp2/disp/lcd/default_panel.c
      

      LCD 面板特化驱动

      部分 LCD 面板需要写 IIC,SPI初始化,或者有特殊的上下电要求,需要编写特化的屏幕驱动

      lichee/rtos‑hal/hal/source/disp2/disp/lcd/
      

      配置文件

      其中 “芯片型号” 例如 r128s3,和 “板子名称” 例如 pro,请根据实际替换。

      board/芯片型号/板子名称/configs/
      

      屏幕驱动配置

      lcd 相关代码包含在 disp 驱动模块中,执行命令进入 menuconfig 配置主界面,并按以下步骤操作:

      添加新屏

      添加一款新屏幕通常需要以下步骤:

      • panel.c 和 panel.h,当用户添加新屏驱动时,是需要修改这两个文件的,需要将屏结构体变量添加到全局结构体变量panel_array中。
      • lcd_source.c 和 lcd_source.h,这两个文件实现的是给屏驱动使用的函数接口,比如电源开关,gpio,dsi 读写接口等,用户不需要修改只需要用。
      • 屏驱动。除了上面提到的源文件外,其它的一般一个 c 文件和一个 h 文件就代表一个屏驱动。
      • 在屏驱动源码位置的上一级,有用户需要修改的 Makefile 文件。

      我们可以打开 lichee/rtos‑hal/hal/source/disp2/disp/lcd/default_panel.c 作为屏驱动的例子,在该文件的最后:

      struct __lcd_panel default_panel = {
          /* panel driver name, must mach the lcd_drv_name in sys_config.fex */
          .name = "default_lcd",
          .func = {
              .cfg_panel_info = LCD_cfg_panel_info,
              .cfg_open_flow = LCD_open_flow,
              .cfg_close_flow = LCD_close_flow,
          },
      };
      
      • 该全局变量 default_panel 的成员 name 与 lcd_driver_name 必须一致,这个关系到驱动能否找到指定的文件。

      • 接 下 来 是 func 成 员 的 初 始 化, 这 里 最 主 要 实 现 三 个 回 调 函 数。LCD_cfg_panel_info, LCD_open_flow 和 LCD_close_flow。

      • 开关屏流程即屏上下电流程,屏手册或者 driver IC 手册中里面的 Power on Sequence 和 Power off Sequence。用于开关屏的操作流程如下图所示

      image-20230508110033147.png

      • 其中,LCD_open_flow 和 LCD_close_flow 称为开关屏流程函数。方框中的函数,如 LCD_power_on,称为开关屏步骤函数。
      • 不需要进行初始化操作的 LCD 屏,例如部分 RGB 屏等,LCD_panel_init 及 LCD_panel_exit 这些函数可以为空。

      LCD_open_flow

      LCD_open_flow 函数只会在系统初始化的时候调用一次,执行每个 LCD_OPEN_FUNC 即是把对应的开屏步骤函数进行注册,先注册先执行,但并没有立刻执行该开屏步骤函数。

      函数原型:

      static __s32 LCD_open_flow(__u32 sel)
      

      函数常用内容为:

      static __s32 LCD_open_flow(__u32 sel)
      {
          LCD_OPEN_FUNC(sel, LCD_power_on,10);
          LCD_OPEN_FUNC(sel, LCD_panel_init, 50);
          LCD_OPEN_FUNC(sel, sunxi_lcd_tcon_enable, 100);
          LCD_OPEN_FUNC(sel, LCD_bl_open, 0);
          return 0;
      }
      

      如上,调用四次 LCD_OPEN_FUNC 注册了四个回调函数,对应了四个开屏流程, 先注册先执行。实际上注册多少个函数是用户自己的自由,只要合理即可。

      1. LCD_power_on 即打开 LCD 电源,再延迟 10ms;这个步骤一般用于打开 LCD 相关电源和相关管脚比如复位脚。这里一般是使用电源控制函数说明和管脚控制函数说明进行操作。
      2. LCD_panel_init 即初始化屏,再延迟 50ms;不需要初始化的屏,可省掉此步骤,这个函数一般用于发送初始化命令给屏进行屏初始化。如果是 I8080 屏用I8080 接口函数说明,如果是其它情况比如 i2c 或者 spi 可以看使用 iic/spi 串行接口初始化,也可以用 GPIO 来进行模拟。
      3. sunxi_lcd_tcon_enable 打开 TCON,再延迟 100ms;这一步是固定的,表示开始发送图像信号。
      4. LCD_bl_open 打开背光,再延迟 0ms。前面三步搞定之后才开背光,这样不会看到闪烁。这里一般使用的函数请看背光控制函数说明。

      LCD_OPEN_FUNC

      注册开屏步骤函数到开屏流程中,记住这里是注册不是执行!

      函数原型:

      void LCD_OPEN_FUNC(__u32 sel, LCD_FUNC func, __u32 delay)
      

      参数说明:

      func 是一个函数指针,其类型是:void (*LCD_FUNC) (__u32 sel),用户自己定义的函数必须也要用统一的形式。比如:

      void user_defined_func(__u32 sel)
      {
      	// do something
      }
      

      delay 是执行该步骤后,再延迟的时间,时间单位是毫秒。

      LCD_OPEN_FUNC 的第二个参数是前后两个步骤的延时长度,单位 ms,注意这里的数值请按照屏手册规定去填,乱填可能导致屏初始化异常或者开关屏时间过长,影响用户体验。

      LCD_close_flow

      与 LCD_open_flow 对应的是 LCD_close_flow,它用于注册关屏函数。使用 LCD_CLOSE_FUNC 进行函数注册,先注册先执行。这里只是注册回调函数,不是立刻执行。

      static s32 LCD_close_flow(u32 sel)
      {
          /* close lcd backlight, and delay 0ms */
          LCD_CLOSE_FUNC(sel, LCD_bl_close, 0);
          /* close lcd controller, and delay 0ms */
          LCD_CLOSE_FUNC(sel, sunxi_lcd_tcon_disable, 50);
          /* open lcd power, than delay 200ms */
          LCD_CLOSE_FUNC(sel, LCD_panel_exit, 100);
          /* close lcd power, and delay 500ms */
          LCD_CLOSE_FUNC(sel, LCD_power_off, 0);
          return 0;
      }
      
      • 先关闭背光,这样整个关屏过程,用户不会看到闪烁的过程;
      • 关闭 TCON(即停止发送数据)再延迟 50ms;
      • 执行关屏代码,再延迟 200ms;(不需要初始化的屏,可省掉此步骤)
      • 最后关闭电源,再延迟 0ms。

      LCD_cfg_panel_info

      配置的 TCON 扩展参数,比如 gamma 功能和颜色映射功能。

      函数原型:

      static void LCD_cfg_panel_info(__panel_extend_para_t *info)
      

      TCON 的扩展参数只能在屏文件中配置,参数的定义:

      lcd_frm

      Lcd Frame Rate Modulator, FRM 是解决由于 PIN 减少导致的色深问题,有些 LCD 屏的像素格式是 18bit 色深(RGB666)或 16bit 色深(RGB565),建议打开 FRM 功能,通过 dither 的方式弥补色深,使显示达到 24bit 色深(RGB888)的效果。如下图所示,上图是色深为 RGB66 的 LCD 屏显示,下图是打开 dither 后的显示,打开 dither 后色彩渐变的地方过度平滑。

      参数设置相应值对应含义为:

      0:RGB888 ‑‑ RGB888 direct
      1:RGB888 ‑‑ RGB666 dither
      2:RGB888 ‑‑ RGB565 dither
      

      image-20230508111409720.png

      lcd_gamma_en

      Lcd Gamma Correction Enable,设置相应值的对应含义为:

      0:LCD 的 Gamma 校正功能关闭
      1:LCD 的 Gamma 校正功能开启
      

      设置为 1 时,需要在屏驱动中对 lcd_gamma_tbl[256] 进行赋值。

      lcd_cmap_en

      Lcd Color Map Enable, 设置为 1 时,需要对 lcd_cmap_tbl [2][3][4] 进行赋值 Lcd Color Map Table。

      每个像素有 R、G、B 三个单元,每四个像素组成一个选择项,总共有 12 个可选。数组第一维表示奇偶行,第二维表示像素的 RGB,第三维表示第几个像素,数组的内容即表示该位置映射到的内容。

      LCD CMAP 是对像素的映射输出功能,只有像素有特殊排布的 LCD 屏才需要配置。

      LCD CMAP 定义每行的 4 个像素为一个总单元,每个像素分 R、G、B 3 个小单元,总共有 12 个小单元。通过 lcd_cmap_tbl 定义映射关系,输出的每个小单元可随意映射到 12 个小单元之一。

      __u32 lcd_cmap_tbl[2][3][4] = {
          {
              {LCD_CMAP_G0,LCD_CMAP_B1,LCD_CMAP_G2,LCD_CMAP_B3},
              {LCD_CMAP_B0,LCD_CMAP_R1,LCD_CMAP_B2,LCD_CMAP_R3},
              {LCD_CMAP_R0,LCD_CMAP_G1,LCD_CMAP_R2,LCD_CMAP_G3},
          },
          {
              {LCD_CMAP_B3,LCD_CMAP_G2,LCD_CMAP_B1,LCD_CMAP_G0},
              {LCD_CMAP_R3,LCD_CMAP_B2,LCD_CMAP_R1,LCD_CMAP_B0},
              {LCD_CMAP_G3,LCD_CMAP_R2,LCD_CMAP_G1,LCD_CMAP_R0},
          },
      };
      

      如上,上三行代表奇数行的像素排布,下三行代表偶数行的像素排布;

      每四个像素为一个单元,第一列代表每四个像素的第一个像素映射,第二列代表每四个像素的第二个像素映射,以此类推。

      如上的定义,像素的输出格式如下图所示。

      image-20230508111448521.png

      lcd_rb_swap

      调换 TCON 模块 RGB 中的 R 分量和 B 分量。

      0:不变
      1:调换R分量和B分量
      

      需要 gamma 校正,或色彩映射,在 sys_config.fex 中将相应模块的 enable 参数置 1,lcd_gamma_en, lcd_cmap_en,并且填充 3 个系数表,lcd_gamma_tbl, lcd_cmap_tbl,注意的是:gamma,模板提供了 18 段拐点值,然后再插值出所有的值(255 个)。可以往相应表格内添加子项以补充细节部分。cmap_tbl 的大小是固定的,不能减小或增加表的大小。最终生成的 gamma 表项是由 rgb 三个 gamma 值组成的,各占 8bit。目前提供的模板中,三个 gamma 值是相同的。

      延时函数

      函数原型

      (毫秒级别)

      s32 sunxi_lcd_delay_ms(u32 ms)
      

      (微秒级别)

      s32 sunxi_lcd_delay_us(u32 us)
      

      图像数据使能函数

      打开 LCD 控制器,开始刷新 LCD 显示

      void sunxi_lcd_tcon_enable(u32 screen_id)
      

      关闭 LCD 控制器,停止刷新数据

      void sunxi_lcd_tcon_disable(u32 screen_id)
      

      背光控制函数

      打开背光,操作的是 sys_config.fex 中 lcd_bl 配置的 gpio。

      void sunxi_lcd_backlight_enable(u32 screen_id)
      

      关闭背光,操作的是 sys_config.fex 中 lcd_bl 配置的 gpio。

      void sunxi_lcd_backlight_disable(u32 screen_id)
      

      打开PWM背光,打开时 pwm 将往外输出 pwm 波形。对应的是 lcd_pwm_ch 所对应的那一路 pwm。

      s32 sunxi_lcd_pwm_enable(u32 screen_id)
      

      关闭PWM背光,打开时 pwm 将往外输出 pwm 波形。对应的是 lcd_pwm_ch 所对应的那一路 pwm。

      s32 sunxi_lcd_pwm_disable(u32 screen_id)
      

      电源控制函数

      打开 Lcd 电源,操作的是 sys_config.fex 中的 lcd_power/lcd_power1/lcd_power2。( pwr_id标识电源索引)

      void sunxi_lcd_power_enable(u32 screen_id, u32 pwr_id)
      

      关闭 Lcd 电源,操作的是 sys_config.fex 中的 lcd_power/lcd_power1/lcd_power2。( pwr_id标识电源索引)

      void sunxi_lcd_power_disable(u32 screen_id, u32 pwr_id)
      
      1. pwr_id = 0:对应于 sys_config.fex 中的 lcd_power。

      2. pwr_id = 1:对应于 sys_config.fex 中的 lcd_power1。

      3. pwr_id = 2:对应于 sys_config.fex 中的 lcd_power2。

      4. pwr_id = 3:对应于 sys_config.fex 中的 lcd_power3。

      sunxi_lcd_pin_cfg

      配置 lcd 的 io

      函数原型

      s32 sunxi_lcd_pin_cfg(u32 screen_id, u32 bon)
      

      配置 lcd 的 data/clk 等 pin,对应 sys_config.fex 中的 lcdd0‑lcdd23/lcddclk/lcdde/lcdhsync/lcdvsync。

      参数:

      • Bon: 1: 为开,0:为配置成 disable 状态。

      I8080 接口函数说明

      显示驱动提供 5 个接口函数可供使用。如下:

      sunxi_lcd_cpu_write

      设定 CPU 屏的指定寄存器为指定的值。

      函数原型

      void sunxi_lcd_cpu_write(__u32 sel, __u32 index, __u32 data)
      {
          sunxi_lcd_cpu_write_index(sel, index);
          sunxi_lcd_cpu_wirte_data(sel, data);
      }
      

      实现了 8080 总线上的两个写操作

      • sunxi_lcd_cpu_write_index 实现第一个写操作,这时 PIN 脚 RS(A1)为低电平,总线数据上的数据内容为参数 index 的值。
      • sunxi_lcd_cpu_wirte_data 实现第二个写操作,这时 PIN 脚 RS(A1)为高电平,总线数据上的数据内容为参数 data 的值。
      sunxi_lcd_cpu_write_index

      设定 CPU 屏为指定寄存器。

      void sunxi_lcd_cpu_write_index(__u32 sel,__u32 index)
      

      参数:

      • sel:显示屏 id
      • index: 要设定的寄存器
      sunxi_lcd_cpu_write_data

      设定 CPU 屏寄存器的值为指定的值

      void sunxi_lcd_cpu_write_data(__u32 sel, __u32 data)
      

      参数:

      • sel:显示屏 id
      • index: 要设定的寄存器的值
      tcon0_cpu_rd_24b_data

      读操作

      s32 tcon0_cpu_rd_24b_data(u32 sel, u32 index, u32 *data, u32 size)
      

      参数:

      • sel:显示屏 id
      • index: 要读取的寄存器
      • data:用于存放读取接口的数组指针,用户必须保证其有足够空间存放数据
      • size:要读取的字节数

      管脚控制函数

      sunxi_lcd_gpio_set_value

      LCD_GPIO PIN 脚上输出高电平或低电平

      s32 sunxi_lcd_gpio_set_value(u32 screen_id, u32 io_index, u32 value)
      

      参数:

      • io_index = 0:对应于 sys_config.fex 中的 lcd_gpio_0。
      • io_index = 1:对应于 sys_config.fex 中的 lcd_gpio_1。
      • io_index = 2:对应于 sys_config.fex 中的 lcd_gpio_2。
      • io_index = 3:对应于 sys_config.fex 中的 lcd_gpio_3。
      • value = 0:对应 IO 输出低电平。
      • value = 1:对应 IO 输出高电平。

      只用于该 GPIO 定义为输出的情形。

      sunxi_lcd_gpio_set_direction

      设置 LCD_GPIO PIN 脚为输入或输出模式

      s32 sunxi_lcd_gpio_set_direction(u32 screen_id, u32 io_index, u32 direction)
      

      参数:

      • io_index = 0:对应于 sys_config.fex 中的 lcd_gpio_0。
      • io_index = 1:对应于 sys_config.fex 中的 lcd_gpio_1。
      • io_index = 2:对应于 sys_config.fex 中的 lcd_gpio_2。
      • io_index = 3:对应于 sys_config.fex 中的 lcd_gpio_3。
      • direction = 0:对应 IO 设置为输入。
      • direction = 1:对应 IO 设置为输出。

      一部分屏需要进行初始化操作,在开屏步骤函数中,对应于 LCD_panel_init 函数,提供了几种方式对屏的初始化。对于 CPU 屏,是通过 8080 总线的方式,使用的是 LCDIO(PD,PH)进行初始化。这种初始化方式,其总线的引脚位置定义与 CPU 屏一致。对于 SPI/IIC 初始化的 LCD,使用独立的IO初始化。

      使用 SPI 初始化

      一般使用 GPIO 模拟的方式初始化 SPI 屏幕,其中 SPI 模拟如下所示

      #define spi_scl_1 sunxi_lcd_gpio_set_value(0, 3, 1)  // 配置 lcd_gpio_3 为 SCL
      #define spi_scl_0 sunxi_lcd_gpio_set_value(0, 3, 0)
      #define spi_sdi_1 sunxi_lcd_gpio_set_value(0, 2, 1)  // 配置 lcd_gpio_2 为 SDI
      #define spi_sdi_0 sunxi_lcd_gpio_set_value(0, 2, 0)
      #define spi_cs_1 sunxi_lcd_gpio_set_value(0, 1, 1)   // 配置 lcd_gpio_1 为 CS
      #define spi_cs_0 sunxi_lcd_gpio_set_value(0, 1, 0)
      
      static void spi_write_cmd(u8 value)
      {
      	int i;
      
      	spi_cs_0;
      	spi_scl_0;
      
      	spi_sdi_0;
      	spi_scl_1;
      	spi_scl_0;
      
      	for (i = 0; i < 8; i++) {
      		if (value & 0x80)
      			spi_sdi_1;
      		else
      			spi_sdi_0;
      		value <<= 1;
      
      		spi_scl_1;
      		spi_scl_0;
      	}
      	spi_cs_1;
      }
      
      static void spi_write_data(u8 value)
      {
      	int i;
      
      	spi_cs_0;
      	spi_scl_0;
      
      	spi_sdi_1;
      	spi_scl_1;
      	spi_scl_0;
      
      	for (i = 0; i < 8; i++) {
      		if (value & 0x80)
      			spi_sdi_1;
      		else
      			spi_sdi_0;
      		value <<= 1;
      
      		spi_scl_1;
      		spi_scl_0;
      	}
      	spi_cs_1;
      }
      

      然后就可以调用 spi_write_cmd(u8 value) 与 spi_write_data(u8 value) 函数写入初始化命令。

      也可以使用 硬件 SPI 初始化屏幕,代码如下

      static int spi_init(void)
      {
          int ret = ‑1;
          struct spi_master *master;
          master = spi_busnum_to_master(1);
          if (!master)
          {
              lcd_fb_wrn("fail to get master\n");
              goto OUT
          }
          spi_device = spi_alloc_device(master);
          if (!spi_device)
          {
              lcd_fb_wrn("fail to get spi device\n");
              goto OUT;
          }
          spi_device‑> bits_per_word = 8;
          spi_device‑> max_speed_hz = 50000000; /*50MHz*/
          spi_device‑> mode = SPI_MODE_0;
          ret = spi_setup(spi_device);
          if (ret)
          {
              lcd_fb_wrn("Faile to setup spi\n");
              goto FREE;
          }
          lcd_fb_inf("Init spi1:bits_per_word:%d max_speed_hz:%d mode:%d\n", spi_device‑> bits_per_word,
                     spi_device‑> max_speed_hz, spi_device‑> mode);
          ret = 0;
          goto OUT;
      FREE:
          spi_master_put(master);
          kfree(spi_device);
          spi_device = NULL;
      OUT:
          return ret;
      }
      
      static int comm_out(unsigned int sel, unsigned char cmd)
      {
          struct spi_transfer t;
          if (!spi_device)
              return ‑1;
          DC(sel, 0);
          memset(&t, 0, sizeof(struct spi_transfer));
          t.tx_buf = &cmd;
          t.len = 1;
          t.bits_per_word = 8;
          t.speed_hz = 24000000;
          return spi_sync_transfer(spi_device, &t, 1);
      }
      

      首先调用 spi_init 函数对 spi 硬件进行初始化,spi_init 函数可以分为几个步骤,第一获取 master;根据实际的硬件连接,选择 spi(代码中选择了 spi1),如果这一步返回错误说 spi 没有配置好。第二步设置 spi device,这里包括最大速度,spi 传输模式,以及每个字包含的比特数。最后调用 spi_setup 完成 master 和 device 的关联。

      comm_out 是一个 spi 传输的例子,核心就是 spi_sync_transfer 函数。

      并行 RGB 接口

      当我们配置并行 RGB 接口时,在配置里面并不需要区分是 24 位,18 位和 16 位,最大位宽是哪种是参考 pin mux 表格,如果 LCD 屏本身支持的位宽比 SoC 支持的位宽少,当然只能选择少的一方。

      因为不需要初始化,RGB 接口极少出现问题,重点关注 lcd 的 timing 的合理性,也就是lcd_ht,lcd_hspw,lcd_hbp,lcd_vt,lcd_vspw 和 lcd_vbp 这个属性的合理性。

      下面是典型并行 RGB 接口 sys_config.fex 配置示例,其中用空行把配置分成几个部分

      ;--------------------------------------------------
      ;Parallel RGB LCD
      ;--------------------------------------------------
      [lcd0]
      ; Part 1
      lcd_used            = 1
      lcd_driver_name     = "default_lcd"
      
      ; Part 2
      lcd_if              = 0
      
      ; Part 3
      lcd_x               = 480
      lcd_y               = 480
      lcd_width           = 150
      lcd_height          = 94
      lcd_rb_swap         = 0
      lcd_dclk_freq       = 21
      lcd_hv_clk_phase    = 1
      
      ; Part 4
      lcd_backlight       = 150
      lcd_pwm_used        = 1
      lcd_pwm_ch          = 5
      lcd_pwm_freq        = 5000
      lcd_pwm_pol         = 1
      
      ; Part 5
      lcd_hbp             = 80
      lcd_ht              = 648
      lcd_hspw            = 8
      lcd_vbp             = 10
      lcd_vt              = 522
      lcd_vspw            = 2
      lcd_lvds_if         = 0
      lcd_lvds_colordepth = 1
      lcd_lvds_mode       = 0
      lcd_frm             = 1
      lcd_io_phase        = 0x0000
      lcd_gamma_en        = 0
      lcd_bright_curve_en = 0
      lcd_cmap_en         = 0
      
      deu_mode            = 0
      lcdgamma4iep        = 22
      smart_color         = 90
      
      ; Part 6
      ;LCD_D2-LCD_D7
      lcd_gpio_4               = port:PA00<8><0><3><0>
      lcd_gpio_5               = port:PA01<8><0><3><0>
      lcd_gpio_6               = port:PA02<8><0><3><0>
      lcd_gpio_7               = port:PA03<8><0><3><0>
      lcd_gpio_8               = port:PA04<8><0><3><0>
      lcd_gpio_9               = port:PA05<8><0><3><0>
      
      ;LCD_D10-LCD_D15
      lcd_gpio_10              = port:PA11<8><0><3><0>
      lcd_gpio_11              = port:PA10<8><0><3><0>
      lcd_gpio_12              = port:PA08<8><0><3><0>
      lcd_gpio_13              = port:PA07<8><0><3><0>
      lcd_gpio_14              = port:PA06<8><0><3><0>
      lcd_gpio_15              = port:PA09<8><0><3><0>
      
      ;LCD_D18-LCD_D23
      lcd_gpio_16              = port:PA12<8><0><3><0>
      lcd_gpio_17              = port:PA13<8><0><3><0>
      lcd_gpio_18              = port:PA14<8><0><3><0>
      lcd_gpio_19              = port:PA15<8><0><3><0>
      lcd_gpio_20              = port:PB03<8><0><3><0>
      lcd_gpio_21              = port:PB02<8><0><3><0>
      
      ;LCD_VSYNC, LCD_HSYNC, LCD_DCLK, LCD_DE
      lcd_gpio_0              = port:PA18<8><0><3><0>
      lcd_gpio_1              = port:PA19<8><0><3><0>
      lcd_gpio_2              = port:PA20<8><0><3><0>
      lcd_gpio_3              = port:PA21<8><0><3><0>
      
      1. 第一部分,决定该配置是否使用,以及使用哪个屏驱动,lcd_driver_name 决定了用哪个屏驱动来初始化,这里是 default_lcd,是针对不需要初始化设置的 RGB 屏。
      2. 第二部分决定下面的配置是一个并行 RGB 的配置。
      3. 第三部分决定 SoC 中的 LCD 模块发送时序。请查看屏时序参数说明。
      4. 第四部分决定背光(pwm 和 lcd_bl_en)。请看背光相关参数。
      5. 第五部分是显示效果部分的配置,如果非 24 位的 RGB,那么一般情况下需要设置lcd_frm。
      6. 第六部分就是电源和管脚配置。是用 RGB666 还是 RGB888,需要根据实际 pinmux 表来决定,如果该芯片只有 18 根 rgb 数据则只能 rgb18。请看电源和管脚参数。

      串行 RGB 接口

      串行 RGB 是相对于并行 RGB 来说,而并不是说它只用一根线来发数据,只要通过多个时钟周期才能把一个像素的数据发完,那么这样的 RGB 接口就是串行 RGB。

      同样与并行 RGB 接口一样,配置中并不需要也无法体现具体是哪种串行 RGB 接口,你要做的就是把硬件连接对就行。

      这里需要注意的是,对于该接口,SoC 总共需要三个周期才能发完一个 pixel,所以我们配置时序的时候,需要满足 lcd_dclk_freq*3=lcd_ht*lcd_vt60,或者 lcd_dclk_freq=lcd_ht/3*lcd_vt*60 要么 3 倍 lcd_ht 要么 3 倍 lcd_dclk_freq。

      I8080 接口

      Intel 8080 接口屏 (又称 MCU 接口) 很老的协议,一般用在分辨率很小的屏上

      管脚的控制脚有 6 种:

      管脚 作用说明
      CS 片选信号 决定该芯片是否工作。
      RS 寄存器选择信号 低表示选择 index 或者 status 寄存器,高表示选择控制寄存器。实际场景中一般接 SoC 的 LCD_DE 脚(数据使能脚)。
      WR 数据命令区分信号 即写时钟信号,一般接 SoC 的 LCD_CLK 脚。(低表示写数据)
      RD 数据读信号 即读时钟信号,一般接 SoC 的 LCD_HSYNC 脚。(低表示读数据)
      RESET 复位 LCD(用固定命令系列 0 1 0 来复位)
      Data 双向数据

      I8080 根据 Data 的数据位宽接口有 8/9/16/18,连哪些脚参考,即使位宽一样,连的管脚也不一样,还要考虑的因素是 rgb 格式。

      1. RGB565,总共有 65K 种颜色
      2. RGB666,总共有 262K 种颜色。
      3. 9bit 固定为 262K。

      RGB 和 I8080 管脚配置示意图

      image-20230508170051477.png

      sys_config 参数说明

      LCD 接口参数说明

      lcd_driver_name

      Lcd 屏驱动的名字(字符串),必须与屏驱动的名字对应。

      lcd_model_name

      Lcd 屏模型名字,非必须,可以用于同个屏驱动中进一步区分不同屏。

      lcd_if

      Lcd Interface,设置相应值的对应含义为:

      0:HV RGB接口
      1:CPU/I80接口
      

      lcd_hv_if

      Lcd HV panel Interface, 这个参数只有在 lcd_if=0 时才有效。定义 RGB 同步屏下的几种接口类型,设置相应值的对应含义为:

      0:Parallel RGB
      8:Serial RGB
      10:Dummy RGB
      11:RGB Dummy
      12:Serial YUV (CCIR656)
      

      lcd_hv_clk_phase

      这个参数只有在 lcd_if=0 时才有效。定义 RGB 同步屏的 clock 与 data 之间的相位关系。总共有 4个相位可供调节,设置相应值的对应含义为:

      0: 0 degree
      1: 90 degree
      2: 180 degree
      3: 270 degree
      

      lcd_hv_sync_polarity

      这个参数只有在 lcd_if=0 时才有效。定义 RGB 同步屏的 hsync 和 vsync 的极性。设置相应值的对应含义为:

      0:vsync active low,hsync active low
      1:vsync active high,hsync active low
      2:vsync active low,hsync active high
      3:vsync active high,hsync active high
      

      lcd_hv_srgb_seq

      这个参数只有在 lcd_if=0 且 lcd_hv_if=8(Serial RGB)时才有效。定义奇数行 RGB 输出的顺序:

      0: Odd lines R‑G‑B; Even line R‑G‑B
      1: Odd lines B‑R‑G; Even line R‑G‑B
      2: Odd lines G‑B‑R; Even line R‑G‑B
      4: Odd lines R‑G‑B; Even line B‑R‑G
      5: Odd lines B‑R‑G; Even line B‑R‑G
      6: Odd lines G‑B‑R; Even line B‑R‑G
      8: Odd lines R‑G‑B; Even line B‑R‑G
      9: Odd lines B‑R‑G; Even line G‑B‑R
      10: Odd lines G‑B‑R; Even line G‑B‑R
      

      lcd_hv_syuv_seq

      这个参数只有在 lcd_if=0 且 lcd_hv_if=12(Serial YUV)时才有效。定义 YUV 输出格式:

      0:YUYV
      1:YVYU
      2:UYVY
      3:VYU
      

      lcd_hv_syuv_fdly

      这个参数只有在 lcd_if=0 且 lcd_hv_if=12(Serial YUV)时才有效。定义 CCIR656 编码时 F 相对有效行延迟的行数:

      0:F toggle right after active video line
      1:Delay 2 lines (CCIR PAL)
      2:Delay 3 lines (CCIR NTSC)
      

      lcd_cpu_if

      这个参数只有在 lcd_if=1 时才有效, 具体时序可参照RGB 和 I8080 管脚配置示意图中 CPU 那几列。设置相应值的对应含义为:

      0:18bit/1cycle (RGB666)
      2: 16bit/3cycle (RGB666)
      4:16bit/2cycle (RGB666)
      6:16bit/2cycle (RGB666)
      8:16bit/1cycle (RGB565)
      10:9bit/1cycle (RGB666)
      12:8bit/3cycle (RGB666)
      14:8bit/2cycle (RGB565)
      

      lcd_cpu_te

      设置相应值的对应含义为,设置为 0 时,刷屏间隔时间为 lcd_ht × lcd_vt;设置为 1 或 2 时,刷屏间隔时间为两个 te 脉冲:

      0:frame trigged automatically
      1:frame trigged by te rising edge
      2:frame trigged by te falling edge
      

      lcd_cpu_mode

      设置相应值的对应含义为,设置为 0 时,刷屏间隔时间为 lcd_ht × lcd_vt;设置为 1 或 2 时,刷屏间隔时间为两个 te 脉冲:

      0:中断自动根据时序,由场消隐信号内部触发
      1:中断根据数据Block的counter触发或者由外部te触发。
      

      屏时序参数说明

      下面几个参数对于调屏非常关键,决定了发送端(SoC)发送数据时序。由于涉及到发送端和接收端的调试,除了分辨率和尺寸之外,其它几个数值都不是绝对不变的,两款一样分辨率,同种接口的屏,它们的数值也有可能不一样。

      获取途径如下:

      1. 询问 LCD 屏厂。
      2. 从屏手册或者 Driver IC 手册中查找(向屏厂索要这些文档)
      3. 在前面两步都搞不定的情况下,可以根据 vesa 标准来设置,主要是 DMT 和 CVT 标准。

      由下面两条公式得知,我们不需要设置 lcd_hfp 和 lcd_vfp 参数,因为驱动会自动根据其它几个已知参数中算出 lcd_hfp 和 lcd_vfp。

      LCD 时序参数

      lcd_x

      显示屏的水平像素数量,即屏分辨率中的宽

      lcd_y

      显示屏的垂直行数,即屏分辨率中的高。

      lcd_ht

      指一行总的 dclk 的 cycle 个数。

      lcd_hbp

      指有效行间,行同步信号(hsync)开始,到有效数据开始之间的 dclk 的 cycle 个数,包括同步信号区。包含了 hspw 段,即lcd_hbp=实际的hbp+实际的hspw。

      lcd_hspw

      指行同步信号的宽度。单位为 1 个 dclk 的时间(即是 1 个 data cycle 的时间)。

      lcd_vt

      指一场的总行数。

      lcd_vbp

      指场同步信号(vsync)开始,到有效数据行开始之间的行数,包括场同步信号区。包含了 vspw 段,即 lcd_vbp= 实际的 vbp+ 实际的 vspw。

      lcd_vspw

      指场同步信号的宽度。单位为行。

      lcd_dclk_freq

      传输像素传送频率(单位为 MHz)。

      fps = (lcd_dclk_freq * 1000 * 1000) / (ht * vt)
      

      这个值根据以下公式计算

      lcd_dclk_freq = lcd_ht * lcd_vt * fps
      

      注意:

      1. 后面的三个参数都是从屏手册中获得,fps 一般是 60。
      2. 如果是串行接口,发完一个像素需要 2 到 3 个周期的,那么可以用以下公式计算:
      lcd_dclk_freq * cycles = lcd_ht * lcd_vt * fps
      lcd_dclk_freq = lcd_ht * cycles * lcd_vt * fps
      

      lcd_width

      此参数描述 lcd 屏幕的物理宽度,单位是 mm,用于计算 dpi。

      lcd_height

      此参数描述 lcd 屏幕的物理高度,单位是 mm,用于计算 dpi。

      背光相关参数

      目前用得比较广泛的就是 pwm 背光调节,原理是利用 pwm 脉冲开关产生的高频率闪烁效应,通过调节占空比,达到欺骗人眼,调节亮暗的目的。

      lcd_pwm_used

      是否使用 pwm,此参数标识用以背光亮度的控制

      lcd_pwm_ch

      此参数标识使用的 Pwm 通道,这里是指使用 SoC 哪个 pwm 通道,通过查看原理图连接可知。

      lcd_pwm_freq

      这个参数配置 PWM 信号的频率,单位为 Hz。

      • 频率不宜过低否则很容易就会看到闪烁,频率不宜过快否则背光调节效果差。部分屏手册会标明所允许的 pwm 频率范围,请遵循屏手册固定范围进行设置。
      • 在低亮度的时候容易看到闪烁,是正常现象,目前已知用上 pwm 的背光都是如此。

      lcd_pwm_pol

      这个参数配置 PWM 信号的占空比的极性。设置相应值对应含义为:

      0:active high
      1:active low
      

      lcd_pwm_max_limit

      Lcd backlight PWM 最高限制,以亮度值表示。

      比如 150,则表示背光最高只能调到 150,0‑255 范围内的亮度值将会被线性映射到 0‑150 范围内。用于控制最高背光亮度,节省功耗

      lcd_bl_en

      背光使能脚,非必须,看原理图是否有,用于使能或者禁止背光电路的电压。

      示例:

      lcd_bl_en = port:PD24<1><2><default><1>
      

      含义:PD24 输出高电平时打开 LCD 背光;下拉,默认高电平。

      • 第一个尖括号:功能分配。1 为输出。
      • 第二个尖括号:内置电阻。使用 0 的话,标示内部电阻高阻态,如果是 1 则是内部电阻上拉,2就代表内部电阻下拉。使用 default 的话代表默认状态,即电阻上拉。其它数据无效。
      • 第三个尖括号:驱动能力。default 表驱动能力是等级 1。
      • 第四个尖括号:电平。0 为低电平,1 为高电平。

      需要在屏驱动调用相应的接口进行开、关的控制。

      一般来说,高电平是使能,在这个前提下,建议将内阻电阻设置成下拉,防止硬件原因造成的上拉,导致背光提前亮。默认电平填写高电平,这是 uboot 显示过度到内核显示、平滑无闪烁的需要。

      lcd_bl_n_percent

      背光映射值,n 为 (0‑100)。

      此功能是针对亮度非线性的 LCD 屏的,按照配置的亮度曲线方式来调整亮度变化,以使亮度变化更线性。

      比如 lcd_bl_50_percent = 60,表明将 50% 的亮度值调整成 60%,即亮度比原来提高 10%。

      修改此属性不当可能导致背光调节效果差。

      lcd_backlight

      背光默认值,0‑255。

      此属性决定在 uboot 显示 logo 阶段的亮度,进入都内核时则是读取保存的配置来决定亮度。

      显示 logo 阶段,一般来说需要比较亮的亮度,业内做法都是如此。

      显示效果相关参数

      lcd_frm

      Lcd Frame Rate Modulator, FRM 是解决由于 PIN 减少导致的色深问题,有些 LCD 屏的像素格式是 18bit 色深(RGB666)或 16bit 色深(RGB565),建议打开 FRM 功能,通过 dither 的方式弥补色深,使显示达到 24bit 色深(RGB888)的效果。如下图所示,上图是色深为 RGB66 的 LCD 屏显示,下图是打开 dither 后的显示,打开 dither 后色彩渐变的地方过度平滑。

      参数设置相应值对应含义为:

      0:RGB888 ‑‑ RGB888 direct
      1:RGB888 ‑‑ RGB666 dither
      2:RGB888 ‑‑ RGB565 dither
      

      image-20230508111409720 (1).png

      lcd_gamma_en

      Lcd Gamma Correction Enable,设置相应值的对应含义为:

      0:LCD 的 Gamma 校正功能关闭
      1:LCD 的 Gamma 校正功能开启
      

      设置为 1 时,需要在屏驱动中对 lcd_gamma_tbl[256] 进行赋值。

      lcd_cmap_en

      Lcd Color Map Enable, 设置为 1 时,需要对 lcd_cmap_tbl [2][3][4] 进行赋值 Lcd Color Map Table。

      每个像素有 R、G、B 三个单元,每四个像素组成一个选择项,总共有 12 个可选。数组第一维表示奇偶行,第二维表示像素的 RGB,第三维表示第几个像素,数组的内容即表示该位置映射到的内容。

      LCD CMAP 是对像素的映射输出功能,只有像素有特殊排布的 LCD 屏才需要配置。

      LCD CMAP 定义每行的 4 个像素为一个总单元,每个像素分 R、G、B 3 个小单元,总共有 12 个小单元。通过 lcd_cmap_tbl 定义映射关系,输出的每个小单元可随意映射到 12 个小单元之一。

      __u32 lcd_cmap_tbl[2][3][4] = {
          {
              {LCD_CMAP_G0,LCD_CMAP_B1,LCD_CMAP_G2,LCD_CMAP_B3},
              {LCD_CMAP_B0,LCD_CMAP_R1,LCD_CMAP_B2,LCD_CMAP_R3},
              {LCD_CMAP_R0,LCD_CMAP_G1,LCD_CMAP_R2,LCD_CMAP_G3},
          },
          {
              {LCD_CMAP_B3,LCD_CMAP_G2,LCD_CMAP_B1,LCD_CMAP_G0},
              {LCD_CMAP_R3,LCD_CMAP_B2,LCD_CMAP_R1,LCD_CMAP_B0},
              {LCD_CMAP_G3,LCD_CMAP_R2,LCD_CMAP_G1,LCD_CMAP_R0},
          },
      };
      

      如上,上三行代表奇数行的像素排布,下三行代表偶数行的像素排布;

      每四个像素为一个单元,第一列代表每四个像素的第一个像素映射,第二列代表每四个像素的第二个像素映射,以此类推。

      如上的定义,像素的输出格式如下图所示。

      image-20230508111448521 (1).png

      lcd_rb_swap

      调换 TCON 模块 RGB 中的 R 分量和 B 分量。

      0:不变
      1:调换R分量和B分量
      

      需要 gamma 校正,或色彩映射,在 sys_config.fex 中将相应模块的 enable 参数置 1,lcd_gamma_en, lcd_cmap_en,并且填充 3 个系数表,lcd_gamma_tbl, lcd_cmap_tbl,注意的是:gamma,模板提供了 18 段拐点值,然后再插值出所有的值(255 个)。可以往相应表格内添加子项以补充细节部分。cmap_tbl 的大小是固定的,不能减小或增加表的大小。最终生成的 gamma 表项是由 rgb 三个 gamma 值组成的,各占 8bit。目前提供的模板中,三个 gamma 值是相同的。

      电源和管脚参数

      lcd_power

      配置好之后,需要在屏驱动调用相应的接口进行开、关的控制。

      注意:如果有多个电源需要打开,则定义 lcd_power1,lcd_power2 等。

      lcd_pin_power

      用法 lcd_power一致,区别是用户设置之后,不需要在屏驱动中去操作,而是驱动框架自行在屏驱动之前使能,在屏驱动之后禁止。

      注意:如果需要多组,则添加 lcd_pin_power1,lcd_pin_power2 等。除了 lcddx 之外,这里的电源还有可能是 pwm 所对应管脚的电源。

      lcd_gpio_0

      示例:

      lcd_gpio_0 = port:PD25<0><0><default><0>
      

      含义:lcd_gpio_0 引脚为 PD25。

      • 第一个尖括号:功能分配。1 为输出。
      • 第二个尖括号:内置电阻。使用 0 的话,标示内部电阻高阻态,如果是 1 则是内部电阻上拉,2就代表内部电阻下拉。使用 default 的话代表默认状态,即电阻上拉。其它数据无效。
      • 第三个尖括号:驱动能力。default 表驱动能力是等级 1。
      • 第四个尖括号:电平。0 为低电平,1 为高电平。

      注意:如果有多个 gpio 脚需要控制,则定义 lcd_gpio_0,lcd_gpio_1 等。

      1. 配置 LCD 的控制 PIN。可以在屏驱动调用相应的接口进行拉高,拉低的控制,例如 LCD 的 RESET 脚等。
      2. 配置 LCD 的数据 PIN。重点关注 PIN 脚的复用功能数值,具体的 IO 对应关系可参考 user manual 手册进行配置

      调试

      系统起来之后可以输入disp相关调试命令,来协助调试。

      选项 参数 解释 举例
      空 空 打印出当前显示的信息 disp
      -c Screen_id,color 模式 显示 colorbar。共有 8 种模式,0 到 8 disp ‑c 0 8
      -b Screen_id, 背光值 调整 lcd 背光,背光值范围时 0 到 255 disp ‑b 0 255
      -d Screen_id, 文件路径 抓 DE 图层回写到文件 disp ‑d 0 /sdmmc/xx.bmp
      -s Screen_id,显示类型,显示分辨率 切换显示类型或分辨率 disp ‑s 0 1 4 打开LCD 显示

      查看显示信息

      输入disp命令,会有 Log 打印信息。以下信息是所有信息中最重要的。

      disp
      
      screen 0:
      derate 297000000 hz, ref_fps:60
      mgr0: 1280x800 fmt[rgb] cs[0x204] range[full] eotf[0x4] bits[8bits] err[0] force_sync[0] unblank direct_show[false]
      lcd output backlight( 50) fps:60.9 1280x 800
      
      err:0 skip:31 irq:1942 vsync:0 vsync_skip:0
      BUF enable ch[1] lyr[0] z[0] prem[N] a[globl 255] fmt[ 8] fb[1280, 800;1280, 800;1280, 800] crop[ 0, 0,1280, 800] frame[
      	0, 0,1280, 800] addr[ 0, 0, 0] flags[0x 0] trd[0,0]
      

      lcd output

      表示当前显示接口是 LCD 输出。

      1280x800

      表示当前 LCD 的分辨率,与 sys_config.fex 中 lcd0 的设置一样。

      ref_fps:60

      是根据你在 sys_config.fex 的 lcd0 填的时序算出来的理论值。

      fps:60.9

      后面的数值是实时统计的,正常来说应该是在 60(期望的 fps) 附近,如果差太多则不正常,重新检查屏时序,和在屏驱动的初始化序列是否有被调用到。

      irq:1942

      这是 vsync 中断的次数,每加 1 都代表刷新了一帧,正常来说是一秒 60(期望的 fps)次,重复 cat sys,如果无变化,则异常。

      BUF

      开头的表示图层信息,一行 BUF 表示一个图层,如果一个 BUF 都没有出现,那么将是黑屏,不过和屏驱动本身关系就不大了,应该查看应用层 & 框架层。

      err:0

      这个表示缺数,如果数字很大且一直变化,屏幕会花甚至全黑,全红等。

      skip:31

      这个表示跳帧的数量,如果这个数值很大且一直变化,有可能卡顿,如果数字与 irq 后面的数字一样,说明每一帧都跳,会黑屏(有背光)

      查看时钟信息

      hal_ccmu
      

      这个命令可以看哪个时钟是否使能,然后频率是多少。与显示相关的是 tcon,pll_video等。

      查看接口自带 colorbar

      显示是一整条链路,中间任何一个环节出错,最终的表现都是显示异常,图像显示异常几个可能原因:

      1. 图像本身异常。
      2. 图像经过 DE(Display Engine)后异常。
      3. 图像经过接口模块后异常。这是我们关注的点。

      有一个简单的方法可以初步判断,接口模块(tcon 和 dsi 等)可以自己输出内置的一些 patten,比如说彩条,灰阶图,棋盘图等。当接口输出这些内置 patten 的时候,如果这时候显示就异常,这说明了:

      1. LCD 的驱动或者配置有问题
      2. LCD 屏由于外部环境导致显示异常

      显示自带 patten 的方式:

      disp ‑c 0 X
      

      上面的操作是显示 colorbar,其中的 X 可以是 0 到 8

      FAQ

      屏显示异常

      总结过往经验,绝大部分屏显异常都是由于上下电时序和 timing 不合理导致。

      黑屏‑无背光

      问题表现:完全黑屏,背光也没有

      1. 屏驱动添加失败。驱动没有加载屏驱动,导致背光电源相关函数没有运行到。这个你可以通过相关模块的测试命令定位下。
      2. pwm 配置和背光电路的问题,pwm 的信息可以查看 pwm 模块测试命令和背光相关参数,另外就是直接测量下硬件测量下相关管脚和电压,再检查屏是否初始化成功。

      黑屏‑有背光

      黑屏但是有背光,可能有多种原因导致,请依次按以下步骤检查

      1. 没送图层。如果应用没有送任何图层那么表现的现象就是黑屏,通过查看显示信息一小节可以确定有没有送图层。如果确定没有图层,可以通过查看接口自带 colorbar,确认屏能否正常显示。
      2. SoC 端的显示接口模块没有供电。SoC 端模块没有供电自然无法传输视频信号到屏上。
      3. 复位脚没有复位。如果有复位脚,请确保硬件连接正确,确保复位脚的复位操作有放到屏驱动中。
      4. sys_config.fex 中 lcd0 有严重错误。第一个是 lcd 的 timing 搞错了,请严格按照屏手册中的提示来写。参考屏时序参数说明。第二个就是,接口类型搞错,比如接的 DSI 屏,配置却写成LVDS 的。
      5. 屏的初始化命令不对。包括各个步骤先后顺序,延时等,这个时候请找屏厂确认初始化命令。

      闪屏

      分为几种:

      1. 屏的整体在闪:这个最大可能是背光电路的电压不稳定,检查电压
      2. 屏部分在闪,而且是概率性:sys_config.fex 中的时序填写不合理。
      3. 屏上由一个矩形区域在闪:屏极化导致,需要关机放一边再开机则不会。

      条形波纹

      有些 LCD 屏的像素格式是 18bit 色深(RGB666)或 16bit 色深(RGB565),建议打开 FRM 功能,通过 dither 的方式弥补色深,使显示达到 24bit 色深(RGB888)的效果。

      设置 [lcd0] 的 lcd_frm 属性可以改善这种现象。

      背光太亮或者太暗

      重新配置背光参数

      花屏

      花屏的第一个原因是 fps 过高,超过屏的限制:

      FPS 异常是一件非常严重的事情,关系到整个操作系统的稳定,如果 fps 过高会造成系统带宽增加,送显流程异常,fps 过高还会造成 LCD 屏花屏不稳定,容易造成 LCD 屏损坏,FPS 过低则造成用户体验过差。

      1. 通过查看查看显示信息一节,可以得知现在的实时统计的 fps
      2. 如果 fps 离正常值差很多,首先检查 sys_config.fex 中 [lcd0] 节点,所填信息必须满足下面公式:
      lcd_dclk_freq * num_of_pixel_clk = lcd_ht * lcd_vt * fps / 1e9
      

      其中,num_of_pixel_clk 通常为 1,表示发送一个像素所需要的时钟周期为 1 一个,低分辨率的MCU 和串行接口通常需要 2 到 3 个时钟周期才能发送完一个像素。

      如果上面填写没有错,通过查看查看时钟信息可以确认下几个主要时钟的频率信息,把这些信息和 sys_config.fex 发给维护者进一步分析。

      RGB 接口或者 I8080 接口显示抖动有花纹

      1. 改大时钟管脚的管脚驱动能力
      2. 修改时钟相位,也就是修改 lcd_hv_clk_phase。由于发送端和接收端时钟相位的不同导致接收端解错若干位。

      LCD 屏出现极化和残影

      何谓液晶极化现象:实际上就是液晶电介质极化。就是在外界电场作用下,电介质内部沿电场方向产生感应偶极矩,在电解质表明出现极化电荷的现象叫做电介质的极化。

      通俗的讲就是在液晶面板施加一定电压后,会聚集大量电荷,当电压消失的时候,这些聚集的电荷也要释放,但由于介电效应,这些聚集的电荷不会立刻释放消失,这些不会马上消失的惰性电荷造成了液晶的 DC 残留从而形成了极化现象。

      几种常见的液晶极化现象

      1. 液晶长期静止某个画面的时候,切换到灰阶画面的时候出现屏闪,屏闪一段时间后消失。这种现象属于残留电荷放电的过程。
      2. 液晶长期静止某个画面的时候,出现四周发黑中间发白的现象,业内称为黑白电视框异常。
      3. 非法关机的时候,重新上电会出现屏闪,屏闪一定时间后消失。与第一种原因相同。
      4. 残影现象:当液晶静止在一个画面比较久的情况下,切换其他画面出现的镜像残留。残影的本质来说是液晶 DC 残留电荷导致,某种意义来说也属于液晶极化现象

      针对液晶屏出现极化和残影现象,有如下对策。

      1. 调整 vcom 电压大小。

      VCOM 是液晶分子偏转的参考电压,要求要稳定,对液晶显示有直接影响,具体的屏不同的话也是不同的。电压的具体值是根据输入的数据以及 Vcom 电压大小来确定的,用来显示各种不同灰阶,也就是实现彩色显示 GAMMA。Gamma 电压是用来控制显示器的灰阶的,一般情况下分为G0~G14,不同的 Gamma 电压与 Vcom 电压之间的压差造成液晶旋转角度不同从而形成亮度的差异,Vcom 电压最好的状况是位于 G0 和 G14 的中间值,这样液晶屏的闪烁状况会最好。调节 vcom 电压的方式,如果屏管脚有 vcom 管脚,直接调整相关电路,如果屏 driver IC 提供寄存器接口,可以通过寄存器接口来调整大小。

      1. 严格按照屏规定的上下电时序来对屏进行开关屏。许多极化残影现象并非长时间显示静止显示某个画面导致的,而是由于关机或者关屏时没有严格按照下电时序导致的,比如该关的电没关,或者延时不够。

      典型屏幕参数配置

      1024x600 RGB666 屏幕

      ;--------------------------------------------------
      ;Parallel RGB LCD
      ;--------------------------------------------------
      [lcd0]
      lcd_used            = 1
      
      lcd_driver_name     = "default_lcd"
      lcd_backlight       = 150
      lcd_if              = 0
      lcd_x               = 1024
      lcd_y               = 600
      lcd_width           = 150
      lcd_height          = 94
      lcd_rb_swap         = 0
      lcd_dclk_freq       = 48
      lcd_pwm_used        = 1
      lcd_pwm_ch          = 7
      lcd_pwm_freq        = 500000
      lcd_pwm_pol         = 1
      lcd_hbp             = 160
      lcd_ht              = 1344
      lcd_hspw            = 20
      lcd_vbp             = 20
      lcd_vt              = 635
      lcd_vspw            = 3
      lcd_lvds_if         = 0
      lcd_lvds_colordepth = 1
      lcd_lvds_mode       = 0
      lcd_frm             = 0
      lcd_io_phase        = 0x0000
      lcd_gamma_en        = 0
      lcd_bright_curve_en = 0
      lcd_cmap_en         = 0
      
      ;reset
      ;lcd_gpio_0               = port:GPIO_EXP15<1><0><3><1>
      ;cs
      ;lcd_gpio_1               = port:GPIO_EXP03<1><0><3><0>
      ;sdi
      ;lcd_gpio_2               = port:GPIO_EXP06<1><0><3><0>
      ;scl
      ;lcd_gpio_3               = port:GPIO_EXP07<1><0><3><0>
      
      ;LCD_D2-LCD_D7
      lcd_gpio_4               = port:PA00<8><0><3><0>
      lcd_gpio_5               = port:PA01<8><0><3><0>
      lcd_gpio_6               = port:PA02<8><0><3><0>
      lcd_gpio_7               = port:PA03<8><0><3><0>
      lcd_gpio_8               = port:PA04<8><0><3><0>
      lcd_gpio_9               = port:PA05<8><0><3><0>
      
      ;LCD_D10-LCD_D15
      lcd_gpio_10              = port:PA11<8><0><3><0>
      lcd_gpio_11              = port:PA10<8><0><3><0>
      lcd_gpio_12              = port:PA08<8><0><3><0>
      lcd_gpio_13              = port:PA07<8><0><3><0>
      lcd_gpio_14              = port:PA06<8><0><3><0>
      lcd_gpio_15              = port:PA09<8><0><3><0>
      
      ;LCD_D18-LCD_D23
      lcd_gpio_16              = port:PA12<8><0><3><0>
      lcd_gpio_17              = port:PA13<8><0><3><0>
      lcd_gpio_18              = port:PA14<8><0><3><0>
      lcd_gpio_19              = port:PA15<8><0><3><0>
      lcd_gpio_20              = port:PB03<8><0><3><0>
      lcd_gpio_21              = port:PB02<8><0><3><0>
      
      ;LCD_VSYNC, LCD_HSYNC, LCD_DCLK, LCD_DE
      lcd_gpio_0              = port:PA18<8><0><3><0>
      lcd_gpio_1              = port:PA19<8><0><3><0>
      lcd_gpio_2              = port:PA20<8><0><3><0>
      lcd_gpio_3              = port:PA21<8><0><3><0>
      

      320x480 ST7796 i8080 屏幕

      ;--------------------------------------------------
      ;MCU LCD
      ;--------------------------------------------------
      [lcd0]
      lcd_used            = 1
      
      lcd_driver_name     = "cl40bc1019_cpu"
      lcd_backlight       = 150
      lcd_if              = 1
      lcd_x               = 320
      lcd_y               = 480
      lcd_width           = 150
      lcd_height          = 94
      lcd_rb_swap         = 0
      
      lcd_pwm_used        = 1
      lcd_pwm_ch          = 7
      lcd_pwm_freq        = 5000
      lcd_pwm_pol         = 1
      
      lcd_cpu_mode        = 0
      lcd_cpu_te          = 0
      lcd_cpu_if          = 12
      
      lcd_dclk_freq       = 32
      lcd_hbp             = 75
      lcd_ht              = 1060
      lcd_hspw            = 40
      lcd_vbp             = 6
      lcd_vt              = 490
      lcd_vspw            = 2
      
      lcd_lvds_if         = 0
      lcd_lvds_colordepth = 1
      lcd_lvds_mode       = 0
      lcd_frm             = 0
      lcd_io_phase        = 0x0000
      lcd_gamma_en        = 0
      lcd_bright_curve_en = 0
      lcd_cmap_en         = 0
      
      deu_mode            = 0
      lcdgamma4iep        = 22
      smart_color         = 90
      
      ;reset pin
      lcd_gpio_0               = port:PB03<1><0><3><0>
      ;CS
      lcd_gpio_1               = port:PA12<1><0><3><0>
      
      ;LCD_D3-LCD_D7
      lcd_gpio_2               = port:PA01<8><0><3><0>
      lcd_gpio_3               = port:PA02<8><0><3><0>
      lcd_gpio_4               = port:PA03<8><0><3><0>
      lcd_gpio_5               = port:PA04<8><0><3><0>
      lcd_gpio_6               = port:PA05<8><0><3><0>
      
      ;LCD_D10-LCD_D12
      lcd_gpio_7               = port:PA11<8><0><3><0>
      lcd_gpio_8               = port:PA10<8><0><3><0>
      lcd_gpio_9               = port:PA08<8><0><3><0>
      
      ;WR
      lcd_gpio_10              = port:PA06<7><0><3><0>
      ;RD
      lcd_gpio_11              = port:PA07<7><0><3><0>
      ;RS
      lcd_gpio_12              = port:PA09<7><0><3><0>
      

      480x480 RGB 86 面板屏

      ;--------------------------------------------------
      ;Parallel RGB LCD
      ;--------------------------------------------------
      [lcd0]
      lcd_used            = 1
      
      lcd_driver_name     = "p0400060a"
      lcd_backlight       = 150
      lcd_if              = 0
      lcd_x               = 480
      lcd_y               = 480
      lcd_width           = 94
      lcd_height          = 94
      lcd_rb_swap         = 0
      lcd_dclk_freq       = 21
      lcd_hv_clk_phase    = 1
      lcd_pwm_used        = 1
      lcd_pwm_ch          = 6
      lcd_pwm_freq        = 5000
      lcd_pwm_pol         = 1
      lcd_hbp             = 80
      lcd_ht              = 648
      lcd_hspw            = 8
      lcd_vbp             = 10
      lcd_vt              = 522
      lcd_vspw            = 2
      lcd_lvds_if         = 0
      lcd_lvds_colordepth = 1
      lcd_lvds_mode       = 0
      lcd_frm             = 1
      lcd_io_phase        = 0x0000
      lcd_gamma_en        = 0
      lcd_bright_curve_en = 0
      lcd_cmap_en         = 0
      
      deu_mode            = 0
      lcdgamma4iep        = 22
      smart_color         = 90
      
      ;reset
      lcd_gpio_0               = port:PB01<1><0><3><1>
      ;cs
      lcd_gpio_1               = port:PA27<1><0><3><0>
      ;sdi
      lcd_gpio_2               = port:PA28<1><0><3><0>
      ;scl
      lcd_gpio_3               = port:PB00<1><0><3><0>
      
      
      ;LCD_D2-LCD_D7
      lcd_gpio_4               = port:PA00<8><0><3><0>
      lcd_gpio_5               = port:PA01<8><0><3><0>
      lcd_gpio_6               = port:PA02<8><0><3><0>
      lcd_gpio_7               = port:PA03<8><0><3><0>
      lcd_gpio_8               = port:PA04<8><0><3><0>
      lcd_gpio_9               = port:PA05<8><0><3><0>
      
      ;LCD_D10-LCD_D15
      lcd_gpio_10              = port:PA11<8><0><3><0>
      lcd_gpio_11              = port:PA10<8><0><3><0>
      lcd_gpio_12              = port:PA08<8><0><3><0>
      lcd_gpio_13              = port:PA07<8><0><3><0>
      lcd_gpio_14              = port:PA06<8><0><3><0>
      lcd_gpio_15              = port:PA09<8><0><3><0>
      
      ;LCD_D18-LCD_D23
      lcd_gpio_16              = port:PA12<8><0><3><0>
      lcd_gpio_17              = port:PA13<8><0><3><0>
      lcd_gpio_18              = port:PA14<8><0><3><0>
      lcd_gpio_19              = port:PA15<8><0><3><0>
      lcd_gpio_20              = port:PB03<8><0><3><0>
      lcd_gpio_21              = port:PB02<8><0><3><0>
      
      ;LCD_VSYNC, LCD_HSYNC, LCD_DCLK, LCD_DE
      lcd_gpio_22              = port:PA18<8><0><3><0>
      lcd_gpio_23              = port:PA19<8><0><3><0>
      lcd_gpio_24              = port:PA20<8><0><3><0>
      lcd_gpio_25              = port:PA21<8><0><3><0>
      

      320x320 i8080 86 面板屏

      [lcd0]
      lcd_used            = 1
      
      lcd_driver_name     = "d392t9390v0_cpu"
      lcd_backlight       = 200
      lcd_if              = 1
      lcd_x               = 320
      lcd_y               = 320
      lcd_width           = 78
      lcd_height          = 78
      lcd_rb_swap         = 1
      
      lcd_pwm_used        = 1
      lcd_pwm_ch          = 7
      lcd_pwm_freq        = 50000
      lcd_pwm_pol         = 1
      
      lcd_cpu_mode        = 0
      lcd_cpu_te          = 0
      lcd_cpu_if          = 12
      
      lcd_dclk_freq       = 32
      lcd_hbp             = 75
      lcd_ht              = 1060
      lcd_hspw            = 40
      lcd_vbp             = 6
      lcd_vt              = 490
      lcd_vspw            = 2
      
      lcd_lvds_if         = 0
      lcd_lvds_colordepth = 1
      lcd_lvds_mode       = 0
      
      lcd_frm             = 0
      lcd_io_phase        = 0x0000
      lcd_gamma_en        = 0
      lcd_bright_curve_en = 0
      lcd_cmap_en         = 0
      
      deu_mode            = 0
      lcdgamma4iep        = 22
      smart_color         = 90
      
      ;reset pin
      lcd_gpio_0               = port:PA12<1><0><3><0>
      
      ;LCD_D3-LCD_D7
      lcd_gpio_2               = port:PA01<8><0><3><0>
      lcd_gpio_3               = port:PA02<8><0><3><0>
      lcd_gpio_4               = port:PA03<8><0><3><0>
      lcd_gpio_5               = port:PA04<8><0><3><0>
      lcd_gpio_6               = port:PA05<8><0><3><0>
      
      ;LCD_D10-LCD_D12
      lcd_gpio_7               = port:PA11<8><0><3><0>
      lcd_gpio_8               = port:PA10<8><0><3><0>
      lcd_gpio_9               = port:PA08<8><0><3><0>
      
      ;WR
      lcd_gpio_10              = port:PA06<7><0><3><0>
      ;RD
      lcd_gpio_11              = port:PA07<7><0><3><0>
      ;RS
      lcd_gpio_12              = port:PA09<7><0><3><0>
      

      800x480 标准 40Pin RGB屏

      [lcd0]
      lcd_used            = 1
      
      lcd_driver_name     = "default_lcd"
      lcd_backlight       = 150
      lcd_if              = 0
      lcd_x               = 800
      lcd_y               = 480
      lcd_width           = 150
      lcd_height          = 94
      lcd_rb_swap         = 0
      lcd_dclk_freq       = 33
      lcd_pwm_used        = 1
      lcd_pwm_ch          = 6
      lcd_pwm_freq        = 5000
      lcd_pwm_pol         = 1
      lcd_hbp             = 46
      lcd_ht              = 1055
      lcd_hspw            = 0
      lcd_vbp             = 23
      lcd_vt              = 525
      lcd_vspw            = 0
      lcd_lvds_if         = 0
      lcd_lvds_colordepth = 1
      lcd_lvds_mode       = 0
      lcd_frm             = 0
      lcd_io_phase        = 0x0000
      lcd_gamma_en        = 0
      lcd_bright_curve_en = 0
      lcd_cmap_en         = 0
      
      deu_mode            = 0
      lcdgamma4iep        = 22
      smart_color         = 90
      
      ;LCD_D2-LCD_D7
      lcd_gpio_0               = port:PA00<8><0><3><0>
      lcd_gpio_1               = port:PA01<8><0><3><0>
      lcd_gpio_2               = port:PA02<8><0><3><0>
      lcd_gpio_3               = port:PA03<8><0><3><0>
      lcd_gpio_4               = port:PA04<8><0><3><0>
      lcd_gpio_5               = port:PA05<8><0><3><0>
      
      ;LCD_D10-LCD_D15
      lcd_gpio_6               = port:PA11<8><0><3><0>
      lcd_gpio_7               = port:PA10<8><0><3><0>
      lcd_gpio_8               = port:PA08<8><0><3><0>
      lcd_gpio_9               = port:PA07<8><0><3><0>
      lcd_gpio_10              = port:PA06<8><0><3><0>
      lcd_gpio_11              = port:PA09<8><0><3><0>
      
      ;LCD_D18-LCD_D23
      lcd_gpio_12              = port:PA12<8><0><3><0>
      lcd_gpio_13              = port:PA13<8><0><3><0>
      lcd_gpio_14              = port:PA14<8><0><3><0>
      lcd_gpio_15              = port:PA15<8><0><3><0>
      lcd_gpio_16              = port:PB03<8><0><3><0>
      lcd_gpio_17              = port:PB02<8><0><3><0>
      
      ;LCD_VSYNC, LCD_HSYNC, LCD_DCLK, LCD_DE
      lcd_gpio_18              = port:PA18<8><0><3><0>
      lcd_gpio_19              = port:PA19<8><0><3><0>
      lcd_gpio_20              = port:PA20<8><0><3><0>
      lcd_gpio_21              = port:PA21<8><0><3><0>
      
      发布在 A Series
      livpo
      livpo
    • 回复: T527的芯片资料和软件包在哪里下载啊

      @zero2024 卖家应该有提供的呀,tb客服问一下

      发布在 T Series
      livpo
      livpo
    • 技术帖 | 飞凌嵌入式T113-i开发板的休眠及唤醒操作

      飞凌嵌入式OK113i-S开发板支持两种休眠方式:freeze和mem。

      这两种方式可以通过/sys/power/state文件节点进行操作,用户可以通过在该文件节点写入freeze或mem来触发相应的休眠状态。

      在进行休眠之前,系统会配置唤醒源。一旦系统进入休眠状态,可以通过这些唤醒源(如按键、RTC等)在需要时唤醒系统。这种设计允许用户根据需要选择何时以及通过何种方式快速唤醒系统,实现了功耗最小化和快速恢复的平衡。这一机制使得系统在休眠状态下能够极大地减少功耗,同时保留了用户在唤醒后迅速使用系统的便利性。

      本篇内容小编会为大家介绍如何让飞凌嵌入式OK113i-S开发板进入休眠模式,以及如何通过RTC时钟实现定时唤醒。

      关于两种休眠模式

      • freeze

      冻结I/O设备,将它们置于低功耗状态,使处理器进入空闲状态,唤醒最快,耗电比其它方式高。实测OK113i-S开发板在只接串口线的情况下5V供电,电流约为0.112A。

      • mem

      挂起到内存,计算机将目前的运行状态等数据存放在内存,关闭硬盘、外设等设备,进入等待状态。此时内存仍然需要电力维持其数据,但整机耗电很少。恢复时计算机从内存读出数据,回到挂起前的状态,恢复速度较快。实测OK113i-S开发板在只接串口线情况下5V供电,电流约为0.076A。

      1、cat /sys/power/state可以看到OK113i-S开发板支持的模式有哪些:

      微信图片_20240116170509.png

      2、echo freeze > /sys/power/state 进入freeze模式:

      微信图片_20240116170522.png

      3、echo mem > /sys/power/state 进入mem模式:

      微信图片_20240116170524.png

      通过RTC定时唤醒

      注意:此处需要使用内部RTC,外部RTC不支持唤醒功能,后面我们还会提及。

      进入开发板的内核配置:

      root@ubuntu: /home/forlinx/work/linux/OK113i-linux-sdk# ./build.sh menuconfig
      

      根据下图框选进行功能选择:
      微信图片_20240116170527.png
      微信图片_20240116170529.png
      微信图片_20240116170531.png

      配置完成后保存,然后修改设备树文件,打开内部RTC功能。
      微信图片_20240116170535.png

      微信图片_20240116170537.png

      保存后进行编译:
      微信图片_20240116170539.png

      编译成功后打包成镜像,烧写完成后,我们在串口终端进行测试。

      进入串口终端进行测试:

      echo “+15”> /sys/class/rtc/rtc0/wakealarm
      

      此处为15秒定时,可自由设置时间,命令执行后就会生效,RTC会单独计时,如果是15秒后才进入休眠,不会触发唤醒。(注意此处需要使用内部RTC,外部RTC不支持唤醒功能)

      echo mem > /sys/power/state
      

      (这里两条指令输入时要紧凑,两条指令间,间隔太长就无效了)

      微信图片_20240116170542.png

      (这里需要注意,我们在未打开内部RTC时,我们的外部RTC默认节点是rtc0,修改后外部rtc设备节点会变更成rtc1。)

      到这里,我们就完成了在飞凌嵌入式OK113i-S开发板上实现休眠以及通过RTC定时唤醒的全部操作了,当然,不同的主控平台板卡的具体操作会有差异,但是整体思路是一样的,具体可以根据相对应的平台查看相关资料来确定具体步骤,希望本文提供的方法能够对屏幕前的工程师朋友们的项目开发有所帮助。

      发布在 飞凌嵌入式专区
      livpo
      livpo
    • 【R128】应用开发案例——DBI驱动ST7789V1.3寸LC

      基于R128-S2设计的全套开发板已上线淘宝百问网韦东山老师个人店进行售卖,包含黑色的DshanMCU-R128s2-R16N16模组和全套的DshanMCU-R128s2-DEVKIT。

      • DshanMCU-R128s2-R16N16模组:39.9元
      • DshanMCU-R128s2-DEVKIT开发板:59.9元

      R128开发板购买链接:https://item.taobao.com/item.htm?spm=a21n57.1.0.0.46b0523cMfarLo&id=736154682975&ns=1&abbucket=5#detail

      “主图_01”为智能对象-1.jpg

      DBI驱动ST7789V1.3寸LCD

      之前介绍了 R128 平台使用 SPI 驱动显示屏 ST7789V1.3寸 LCD,接下来介绍的是使用 DBI 接口驱动。

      R128 平台提供了 SPI DBI 的 SPI TFT 接口,具有如下特点:

      • Supports DBI Type C 3 Line/4 Line Interface Mode
      • Supports 2 Data Lane Interface Mode
      • Supports data source from CPU or DMA
      • Supports RGB111/444/565/666/888 video format
      • Maximum resolution of RGB666 240 x 320@30Hz with single data lane
      • Maximum resolution of RGB888 240 x 320@60Hz or 320 x 480@30Hz with dual data lane
      • Supports tearing effect
      • Supports software flexible control video frame rate

      同时,提供了 SPILCD 驱动框架以供 SPI 屏幕使用。

      此次适配的SPI屏为 ZJY130S0800TG01,使用的是 DBI 进行驱动。

      DBI接口的全称是 Display Bus Serial Interface ,在显示屏数据手册中,一般会说这是SPI接口,所以有人会误认为SPI屏可以使用 normal spi 去直接驱动。

      SPI 接口就是俗称的4线模式,这是因为发送数据时需要额外借助DC线来区分命令和数据,与sclk,cs和sda共四线。

      spi-16932054745452 (2).png

      DBI 分为多种接口,包括

      0:L3I1
      1:L3I2
      2:L4I1
      3:L4I2
      4:D2LI
      

      L3I1和L3I2是三线模式(不需要DC脚),区别是读时序,也就是是否需要额外脚来读寄存器。读写时序图如下:

      • L3I1写时序

      L3I1-16932054745453 (1).png

      • L3I1读时序

      L3I_RD-16932054745464 (1).png

      L4I1和L4I2是四线模式,与spi接口协议一样,区别是DC脚的控制是否自动化控制,另外I2和I1的区别是读时序,也就是否需要额外脚来读取寄存器。

      • L4I写时序

      spi-16932054745452-16987339859554.png

      • L4I读时序

      spi_read-16932054745465 (1).png

      D2LI是两data lane模式。发送命令部分时序与读时序与L3I1一致,下图是发送数据时的时序,不同像素格式时钟周期数量不一样。

      • D2LI写时序
        D2LI-16932054745466 (1).png

      可以知道,在3线模式时,发送命令前有1位A0用于指示当前发送的是数据,还是命令。而命令后面接着的数据就没有这个A0位了,代表 SPI 需要在 9 位和 8 位之间来回切换,而在读数据时,更是需要延时 dummy clock 才能读数据,normal spi 都很难,甚至无法实现。所以 normal spi 只能模拟 4 线的 DBI 的写操作。读操作只能通过模拟IO来实现。

      对于R128这类支持 DBI 接口的CPU,可以选择不去了解 SPI。直接选用 DBI 来驱动屏幕。由于不需要模拟延时和切换数据,屏幕驱动效率将有明显提升。

      image-20231023102211315 (2).png

      引脚配置如下:

      R128 Devkit TFT 模块
      PA12 CS
      PA13 SCL
      PA18 SDA
      PA9 BLK
      PA20 RES
      PA19 DC
      3V3 VCC
      GND GND

      载入方案

      我们使用的开发板是 R128-Devkit,需要开发 C906 核心的应用程序,所以载入方案选择 r128s2_module_c906

      $ source envsetup.sh 
      $ lunch_rtos 1
      

      image-20230802110150203 (9).png

      设置 DBI 驱动

      屏幕使用的是SPI驱动,所以需要勾选SPI驱动,运行 mrtos_menuconfig 进入配置页面。前往下列地址找到 SPI Devices

      Drivers Options  --->
          soc related device drivers  --->
              DBI Devices --->
              -*- enable dbi driver
      

      image-20231031143901502.png

      配置 SPI 引脚

      DBI同样使用 SPI 控制器,所以需要配置SPI的相关配置。打开你喜欢的编辑器,修改文件:board/r128s2/module/configs/sys_config.fex,在这里我们不需要用到SPI WP引脚,注释掉即可。SPI HOLD 需要作为 DC 脚接入LCD模块。

      ;----------------------------------------------------------------------------------
      ;SPI controller configuration
      ;----------------------------------------------------------------------------------
      ;Please config spi in dts
      [spi1]
      spi1_used       = 1
      spi1_cs_number  = 1
      spi1_cs_bitmap  = 1
      spi1_cs0        = port:PA12<6><0><3><default>
      spi1_sclk       = port:PA13<6><0><3><default>
      spi1_mosi       = port:PA18<6><0><3><default>
      spi1_miso       = port:PA21<6><0><3><default>
      spi1_hold       = port:PA19<6><0><2><default>
      ;spi1_wp         = port:PA20<6><0><2><default>
      

      image-20231031144020034.png

      设置 PWM 驱动

      屏幕背光使用的是PWM驱动,所以需要勾选PWM驱动,运行 mrtos_menuconfig 进入配置页面。前往下列地址找到 PWM Devices

      Drivers Options  --->
          soc related device drivers  --->
              PWM Devices --->
              -*- enable pwm driver
      

      image-20230825144408144 (4).png

      配置 PWM 引脚

      打开你喜欢的编辑器,修改文件:board/r128s2/module/configs/sys_config.fex,增加 PWM1 节点

      [pwm1]
      pwm_used        = 1
      pwm_positive    = port:PA9<4><0><3><default>
      

      image-20230825150128954 (4).png

      设置 SPI LCD 驱动

      SPI LCD 由专门的驱动管理。运行 mrtos_menuconfig 进入配置页面。前往下列地址找到 SPILCD Devices ,注意同时勾选 spilcd hal APIs test 方便测试使用。

      Drivers Options  --->
          soc related device drivers  --->
              [*] DISP Driver Support(spi_lcd)
              [*]   spilcd hal APIs test
      

      image-20230825150341879 (3).png

      编写 SPI LCD 显示屏驱动

      获取屏幕初始化序列

      首先询问屏厂提供驱动源码

      image-20231023102239928 (1).png

      找到 LCD 的初始化序列代码

      image-20231023102252129 (1).png

      找到屏幕初始化的源码

      image-20231023102333476 (1).png

      整理后的初始化代码如下:

      LCD_WR_REG(0x11); // Sleep out 
      delay_ms(120);    // Delay 120ms 
      //************* Start Initial Sequence **********// 
      LCD_WR_REG(0x36);
      LCD_WR_DATA8(0x00);
      
      LCD_WR_REG(0x3A);     
      LCD_WR_DATA8(0x05);   
      
      LCD_WR_REG(0xB2);     
      LCD_WR_DATA8(0x1F);   
      LCD_WR_DATA8(0x1F);   
      LCD_WR_DATA8(0x00);   
      LCD_WR_DATA8(0x33);   
      LCD_WR_DATA8(0x33);   
      
      LCD_WR_REG(0xB7);     
      LCD_WR_DATA8(0x35);   
      
      LCD_WR_REG(0xBB);     
      LCD_WR_DATA8(0x20);   // 2b
      
      LCD_WR_REG(0xC0);     
      LCD_WR_DATA8(0x2C);   
      
      LCD_WR_REG(0xC2);     
      LCD_WR_DATA8(0x01);   
      
      LCD_WR_REG(0xC3);     
      LCD_WR_DATA8(0x01);   
      
      LCD_WR_REG(0xC4);     
      LCD_WR_DATA8(0x18);   // VDV, 0x20:0v
      
      LCD_WR_REG(0xC6);     
      LCD_WR_DATA8(0x13);   // 0x13:60Hz   
      
      LCD_WR_REG(0xD0);     
      LCD_WR_DATA8(0xA4);   
      LCD_WR_DATA8(0xA1);   
      
      LCD_WR_REG(0xD6);     
      LCD_WR_DATA8(0xA1);   // sleep in后,gate输出为GND
      
      LCD_WR_REG(0xE0);     
      LCD_WR_DATA8(0xF0);   
      LCD_WR_DATA8(0x04);   
      LCD_WR_DATA8(0x07);   
      LCD_WR_DATA8(0x04);   
      LCD_WR_DATA8(0x04);   
      LCD_WR_DATA8(0x04);   
      LCD_WR_DATA8(0x25);   
      LCD_WR_DATA8(0x33);   
      LCD_WR_DATA8(0x3C);   
      LCD_WR_DATA8(0x36);   
      LCD_WR_DATA8(0x14);   
      LCD_WR_DATA8(0x12);   
      LCD_WR_DATA8(0x29);   
      LCD_WR_DATA8(0x30);   
      
      LCD_WR_REG(0xE1);     
      LCD_WR_DATA8(0xF0);   
      LCD_WR_DATA8(0x02);   
      LCD_WR_DATA8(0x04);   
      LCD_WR_DATA8(0x05);   
      LCD_WR_DATA8(0x05);   
      LCD_WR_DATA8(0x21);   
      LCD_WR_DATA8(0x25);   
      LCD_WR_DATA8(0x32);   
      LCD_WR_DATA8(0x3B);   
      LCD_WR_DATA8(0x38);   
      LCD_WR_DATA8(0x12);   
      LCD_WR_DATA8(0x14);   
      LCD_WR_DATA8(0x27);   
      LCD_WR_DATA8(0x31);   
      
      LCD_WR_REG(0xE4);     
      LCD_WR_DATA8(0x1D);   // 使用240根gate  (N+1)*8
      LCD_WR_DATA8(0x00);   // 设定gate起点位置
      LCD_WR_DATA8(0x00);   // 当gate没有用完时,bit4(TMG)设为0
      
      LCD_WR_REG(0x21);     
      
      LCD_WR_REG(0x29);     
      

      用现成驱动改写 SPI LCD 驱动

      选择一个现成的 SPI LCD 改写即可,这里选择 nv3029s.c 驱动来修改

      image-20231017104714827 (3).png

      复制这两个驱动,重命名为 st7789v.c

      image-20231017104740060 (3).png

      先编辑 st7789v.h 将 nv3029s 改成 st7789v

      image-20231017104851772 (3).png

      #ifndef _ST7789V_H
      #define _ST7789V_H
      
      #include "panels.h"
      
      struct __lcd_panel st7789v_panel;
      
      #endif /*End of file*/
      

      编辑 st7789v.c 将 nv3029s 改成 st7789v

      image-20231017104942286 (3).png

      编写初始化序列

      先删除 static void LCD_panel_init(unsigned int sel) 中的初始化函数。

      image-20231017105101421 (3).png

      然后将屏厂提供的初始化序列复制进来

      image-20231023102641761 (1).png

      然后按照 spi_lcd 框架的接口改写驱动接口,具体接口如下

      屏厂函数 SPILCD框架接口
      LCD_WR_REG sunxi_lcd_cmd_write
      LCD_WR_DATA8 sunxi_lcd_para_write
      delay_ms sunxi_lcd_delay_ms

      可以直接进行替换

      image-20231023095114146 (2).png

      完成后如下

      image-20231023095158411 (2).png

      然后对照屏厂提供的驱动修改 address 函数

      image-20231023102549896 (1).png

      做如下修改

      static void address(unsigned int sel, int x, int y, int width, int height)
      {
      	sunxi_lcd_cmd_write(sel, 0x2B); /* Set row address */
      	sunxi_lcd_para_write(sel, (y >> 8) & 0xff);
      	sunxi_lcd_para_write(sel, y & 0xff);
      	sunxi_lcd_para_write(sel, (height >> 8) & 0xff);
      	sunxi_lcd_para_write(sel, height & 0xff);
      	sunxi_lcd_cmd_write(sel, 0x2A); /* Set coloum address */
      	sunxi_lcd_para_write(sel, (x >> 8) & 0xff);
      	sunxi_lcd_para_write(sel, x & 0xff);
      	sunxi_lcd_para_write(sel, (width >> 8) & 0xff);
      	sunxi_lcd_para_write(sel, width & 0xff);
      	sunxi_lcd_cmd_write(sel, 0x2c);
      }
      

      完成驱动如下

      #include "st7789v.h"
      
      static void LCD_power_on(u32 sel);
      static void LCD_power_off(u32 sel);
      static void LCD_bl_open(u32 sel);
      static void LCD_bl_close(u32 sel);
      static void LCD_panel_init(u32 sel);
      static void LCD_panel_exit(u32 sel);
      #define RESET(s, v) sunxi_lcd_gpio_set_value(s, 0, v)
      #define power_en(sel, val) sunxi_lcd_gpio_set_value(sel, 0, val)
      
      static struct disp_panel_para info[LCD_FB_MAX];
      
      static void address(unsigned int sel, int x, int y, int width, int height)
      {
      	sunxi_lcd_cmd_write(sel, 0x2B); /* Set row address */
      	sunxi_lcd_para_write(sel, (y >> 8) & 0xff);
      	sunxi_lcd_para_write(sel, y & 0xff);
      	sunxi_lcd_para_write(sel, (height >> 8) & 0xff);
      	sunxi_lcd_para_write(sel, height & 0xff);
      	sunxi_lcd_cmd_write(sel, 0x2A); /* Set coloum address */
      	sunxi_lcd_para_write(sel, (x >> 8) & 0xff);
      	sunxi_lcd_para_write(sel, x & 0xff);
      	sunxi_lcd_para_write(sel, (width >> 8) & 0xff);
      	sunxi_lcd_para_write(sel, width & 0xff);
      	sunxi_lcd_cmd_write(sel, 0x2c);
      }
      
      static void LCD_panel_init(unsigned int sel)
      {
      	if (bsp_disp_get_panel_info(sel, &info[sel])) {
      		lcd_fb_wrn("get panel info fail!\n");
      		return;
      	}
      
      	sunxi_lcd_cmd_write(sel, 0x11); // Sleep out 
      	sunxi_lcd_delay_ms(120);    // Delay 120ms 
      	//************* Start Initial Sequence **********// 
      	sunxi_lcd_cmd_write(sel, 0x36);
      	sunxi_lcd_para_write(sel, 0x00);
      
      	sunxi_lcd_cmd_write(sel, 0x3A);     
      	sunxi_lcd_para_write(sel, 0x05);   
      
      	sunxi_lcd_cmd_write(sel, 0xB2);     
      	sunxi_lcd_para_write(sel, 0x1F);   
      	sunxi_lcd_para_write(sel, 0x1F);   
      	sunxi_lcd_para_write(sel, 0x00);   
      	sunxi_lcd_para_write(sel, 0x33);   
      	sunxi_lcd_para_write(sel, 0x33);   
      
      	sunxi_lcd_cmd_write(sel, 0xB7);     
      	sunxi_lcd_para_write(sel, 0x35);   
      
      	sunxi_lcd_cmd_write(sel, 0xBB);     
      	sunxi_lcd_para_write(sel, 0x20);   // 2b
      
      	sunxi_lcd_cmd_write(sel, 0xC0);     
      	sunxi_lcd_para_write(sel, 0x2C);   
      
      	sunxi_lcd_cmd_write(sel, 0xC2);     
      	sunxi_lcd_para_write(sel, 0x01);   
      
      	sunxi_lcd_cmd_write(sel, 0xC3);     
      	sunxi_lcd_para_write(sel, 0x01);   
      
      	sunxi_lcd_cmd_write(sel, 0xC4);     
      	sunxi_lcd_para_write(sel, 0x18);   // VDV, 0x20:0v
      
      	sunxi_lcd_cmd_write(sel, 0xC6);     
      	sunxi_lcd_para_write(sel, 0x13);   // 0x13:60Hz   
      
      	sunxi_lcd_cmd_write(sel, 0xD0);     
      	sunxi_lcd_para_write(sel, 0xA4);   
      	sunxi_lcd_para_write(sel, 0xA1);   
      
      	sunxi_lcd_cmd_write(sel, 0xD6);     
      	sunxi_lcd_para_write(sel, 0xA1);   // sleep in后,gate输出为GND
      
      	sunxi_lcd_cmd_write(sel, 0xE0);     
      	sunxi_lcd_para_write(sel, 0xF0);   
      	sunxi_lcd_para_write(sel, 0x04);   
      	sunxi_lcd_para_write(sel, 0x07);   
      	sunxi_lcd_para_write(sel, 0x04);   
      	sunxi_lcd_para_write(sel, 0x04);   
      	sunxi_lcd_para_write(sel, 0x04);   
      	sunxi_lcd_para_write(sel, 0x25);   
      	sunxi_lcd_para_write(sel, 0x33);   
      	sunxi_lcd_para_write(sel, 0x3C);   
      	sunxi_lcd_para_write(sel, 0x36);   
      	sunxi_lcd_para_write(sel, 0x14);   
      	sunxi_lcd_para_write(sel, 0x12);   
      	sunxi_lcd_para_write(sel, 0x29);   
      	sunxi_lcd_para_write(sel, 0x30);   
      
      	sunxi_lcd_cmd_write(sel, 0xE1);     
      	sunxi_lcd_para_write(sel, 0xF0);   
      	sunxi_lcd_para_write(sel, 0x02);   
      	sunxi_lcd_para_write(sel, 0x04);   
      	sunxi_lcd_para_write(sel, 0x05);   
      	sunxi_lcd_para_write(sel, 0x05);   
      	sunxi_lcd_para_write(sel, 0x21);   
      	sunxi_lcd_para_write(sel, 0x25);   
      	sunxi_lcd_para_write(sel, 0x32);   
      	sunxi_lcd_para_write(sel, 0x3B);   
      	sunxi_lcd_para_write(sel, 0x38);   
      	sunxi_lcd_para_write(sel, 0x12);   
      	sunxi_lcd_para_write(sel, 0x14);   
      	sunxi_lcd_para_write(sel, 0x27);   
      	sunxi_lcd_para_write(sel, 0x31);   
      
      	sunxi_lcd_cmd_write(sel, 0xE4);     
      	sunxi_lcd_para_write(sel, 0x1D);   // 使用240根gate  (N+1)*8
      	sunxi_lcd_para_write(sel, 0x00);   // 设定gate起点位置
      	sunxi_lcd_para_write(sel, 0x00);   // 当gate没有用完时,bit4(TMG)设为0
      
      	sunxi_lcd_cmd_write(sel, 0x21);     
      
      	sunxi_lcd_cmd_write(sel, 0x29);   
      
      	if (info[sel].lcd_x < info[sel].lcd_y)
      		address(sel, 0, 0, info[sel].lcd_x - 1, info[sel].lcd_y - 1);
      	else
      		address(sel, 0, 0, info[sel].lcd_y - 1, info[sel].lcd_x - 1);
      }
      
      static void LCD_panel_exit(unsigned int sel)
      {
      	sunxi_lcd_cmd_write(sel, 0x28);
      	sunxi_lcd_delay_ms(20);
      	sunxi_lcd_cmd_write(sel, 0x10);
      	sunxi_lcd_delay_ms(20);
      	sunxi_lcd_pin_cfg(sel, 0);
      }
      
      static s32 LCD_open_flow(u32 sel)
      {
      	lcd_fb_here;
      	/* open lcd power, and delay 50ms */
      	LCD_OPEN_FUNC(sel, LCD_power_on, 50);
      	/* open lcd power, than delay 200ms */
      	LCD_OPEN_FUNC(sel, LCD_panel_init, 200);
      
      	LCD_OPEN_FUNC(sel, lcd_fb_black_screen, 50);
      	/* open lcd backlight, and delay 0ms */
      	LCD_OPEN_FUNC(sel, LCD_bl_open, 0);
      
      	return 0;
      }
      
      static s32 LCD_close_flow(u32 sel)
      {
      	lcd_fb_here;
      	/* close lcd backlight, and delay 0ms */
      	LCD_CLOSE_FUNC(sel, LCD_bl_close, 50);
      	/* open lcd power, than delay 200ms */
      	LCD_CLOSE_FUNC(sel, LCD_panel_exit, 10);
      	/* close lcd power, and delay 500ms */
      	LCD_CLOSE_FUNC(sel, LCD_power_off, 10);
      
      	return 0;
      }
      
      static void LCD_power_on(u32 sel)
      {
      	/* config lcd_power pin to open lcd power0 */
      	lcd_fb_here;
      	power_en(sel, 1);
      
      	sunxi_lcd_power_enable(sel, 0);
      
      	sunxi_lcd_pin_cfg(sel, 1);
      	RESET(sel, 1);
      	sunxi_lcd_delay_ms(100);
      	RESET(sel, 0);
      	sunxi_lcd_delay_ms(100);
      	RESET(sel, 1);
      }
      
      static void LCD_power_off(u32 sel)
      {
      	lcd_fb_here;
      	/* config lcd_power pin to close lcd power0 */
      	sunxi_lcd_power_disable(sel, 0);
      	power_en(sel, 0);
      }
      
      static void LCD_bl_open(u32 sel)
      {
      	sunxi_lcd_pwm_enable(sel);
      	/* config lcd_bl_en pin to open lcd backlight */
      	sunxi_lcd_backlight_enable(sel);
      	lcd_fb_here;
      }
      
      static void LCD_bl_close(u32 sel)
      {
      	/* config lcd_bl_en pin to close lcd backlight */
      	sunxi_lcd_backlight_disable(sel);
      	sunxi_lcd_pwm_disable(sel);
      	lcd_fb_here;
      }
      
      
      /* sel: 0:lcd0; 1:lcd1 */
      static s32 LCD_user_defined_func(u32 sel, u32 para1, u32 para2, u32 para3)
      {
      	lcd_fb_here;
      	return 0;
      }
      
      static int lcd_set_var(unsigned int sel, struct fb_info *p_info)
      {
      	return 0;
      }
      
      static int lcd_set_addr_win(unsigned int sel, int x, int y, int width, int height)
      {
      	address(sel, x, y, width, height);
      	return 0;
      }
      
      static int lcd_blank(unsigned int sel, unsigned int en)
      {
      	return 0;
      }
      
      struct __lcd_panel st7789v_panel = {
          /* panel driver name, must mach the name of lcd_drv_name in sys_config.fex
             */
      	.name = "st7789v",
      	.func = {
      		.cfg_open_flow = LCD_open_flow,
      		.cfg_close_flow = LCD_close_flow,
      		.lcd_user_defined_func = LCD_user_defined_func,
      		.blank = lcd_blank,
      		.set_var = lcd_set_var,
      		.set_addr_win = lcd_set_addr_win,
      	},
      };
      

      对接驱动框架

      完成了屏幕驱动的编写,接下来需要对接到 SPILCD 驱动框架。首先编辑 Kconfig

      image-20231017105738155 (3).png

      增加 st7789v 的配置

      image-20231017105814814 (3).png

      config LCD_SUPPORT_ST7789V
          bool "LCD support st7789v panel"
          default n
          ---help---
              If you want to support st7789v panel for display driver, select it.
      

      然后编辑 panels.c 在 panel_array 里增加 st7789 驱动的引用

      image-20231017105948156 (3).png

      如下图

      image-20231017105919628 (3).png

      #ifdef CONFIG_LCD_SUPPORT_ST7789V
          &st7789v_panel,
      #endif
      

      之后编辑 panels.h 同样增加引用

      image-20231017110043805 (3).png

      如下图

      image-20231017110122397 (3).png

      #ifdef CONFIG_LCD_SUPPORT_ST7789V
      extern struct __lcd_panel st7789v_panel;
      #endif
      

      最后编辑外层的 Makefile 增加编译选项

      image-20231017110204681 (3).png

      如下所示

      image-20231017110242997 (3).png

      obj-${CONFIG_LCD_SUPPORT_ST7789V} += panels/st7789v.o
      

      选择 ST7789V 驱动

      在 SPILCD 驱动选择界面可以看到 LCD_FB panels select 选择 SPI 屏幕的驱动

      进入 LCD_FB panels select 选项

      image-20230825150812435 (4).png

      选择并勾选 [*] LCD support st7789v panel

      image-20231017110344277 (3).png

      配置 SPI LCD 引脚

      这里是重点部分:打开你喜欢的编辑器,修改文件:board/r128s2/module/configs/sys_config.fex

      [lcd_fb0]
      lcd_used            = 1   
      lcd_model_name      = "spilcd"   
      lcd_driver_name     = "st7789v" 
      ; 屏幕规格配置
      lcd_x               = 240
      lcd_y               = 240  
      lcd_width           = 48
      lcd_height          = 48
      ; SPI 速率
      lcd_data_speed      = 50
      ; PWM 背光配置项
      lcd_pwm_used        = 1
      lcd_pwm_ch          = 1
      lcd_pwm_freq        = 5000 
      lcd_pwm_pol         = 0 
      lcd_backlight       = 100
      ; 配置 lcd_if = 1 为 DBI 模式,双缓冲
      lcd_if              = 1
      fb_buffer_num       = 2
      ; 配置屏幕传输数据的像素格式,这里是 LCDFB_FORMAT_RGB_565
      lcd_pixel_fmt       = 10
      ; 配置 DBI 接口像素格式,这里是 RGB565
      lcd_dbi_fmt         = 2
      ; 配置 DBI 时钟的行为模式,这里是自动停止模式。有数据就有时钟,没发数据就没有时钟
      lcd_dbi_clk_mode    = 0
      ; 屏幕没有 TE 脚,配置 TE 为 0
      lcd_dbi_te          = 0
      ; 配置屏幕 DBI 格式 L4I1
      lcd_dbi_if          = 2
      ; 输入图像数据 RGB 顺序识别设置,这里配置是 RGB 格式
      lcd_rgb_order       = 0
      ; 设置屏的刷新率,单位Hz。当lcd_dbi_te使能时,这个值设置无效。
      lcd_fps             = 60
      ; 使用 SPI1 作为通讯接口
      lcd_spi_bus_num     = 1
      lcd_frm             = 2
      lcd_gamma_en        = 1
      
      lcd_power_num       = 0
      lcd_gpio_regu_num   = 0
      lcd_bl_percent_num  = 0
      
      ;RESET Pin
      lcd_gpio_0          = port:PA20<1><0><2><0>
      

      编译打包

      运行命令 mp 编译打包,可以看到编译了 st7789v.o

      image-20231017111015362 (3).png

      测试

      烧录启动之后,屏幕背光启动,但是屏幕全黑。

      image-20231023103606375 (1).png

      输入 test_spilcd ,屏幕显示蓝色。

      image-20231031144435046.png

      输入 lv_examples 1 可以显示 lvgl 界面

      image-20231023103710014 (1).png

      常见问题

      LVGL 颜色异常

      这是由于 LVGL 配置的 LV_COLOR_DEPTH 为 32,但是 SPI 屏配置为16位。请修改 lv_conf.h,也请注意 LV_COLOR_16_SWAP 仅有 SPI 需要设置为 1,在使用 DBI 驱动的时候不需要配置为 1。

      image-20231031144514859.png

      出现部分花屏

      image-20231023103039467 (2).png

      • 检查 address 函数是否正确
      • 检查 sys_config.fex 屏幕配置分辨率是否正确

      SPI LCD 颜色相关问题

      首先,得先确定显示屏使用的是SPI接口,还是DBI接口,不同的接口,输入数据的解析方式是不一样的。

      DBI接口的全称是 Display Bus Serial Interface ,在显示屏数据手册中,一般会说这是SPI接口,所以有人会误认为SPI屏可以使用 normal spi 去直接驱动。

      阅读lcd_dbi_if部分的介绍可以知道,在3线模式时,发送命令前有1位A0用于指示当前发送的是数据,还是命令。而命令后面接着的数据就没有这个A0位了,代表SPI需要在9位和8位之间来回切换,而在读数据时,更是需要延时 dummy clock 才能读数据,normal spi 都很难,甚至无法实现。所以 normal spi 只能模拟4 线的DBI的写操作。

      对于R128这类支持DBI接口的CPU,可以选择不去了解SPI。如果需要用到SPI去驱动显示屏,必须把显示屏设置成小端。

      RGB565和RGB666

      SPI显示屏一般支持RGB444,RGB565和RGB666,RGB444使用的比较少,所以只讨论RGB565和RGB666.

      RGB565代表一个点的颜色由2字节组成,也就是R(红色)用5位表示,G(绿色)用6位表示,B(蓝色)用5位表示,如下图所示:

      image-20231016100553340 (1).png

      RGB666一个点的颜色由3字节组成,每个字节代表一个颜色,其中每个字节的低2位会无视,如下图所示:

      image-20231016100620890 (1).png

      SPI 接口

      因为SPI接口的通讯效率不高,所以建议使用RGB565的显示,以 jlt35031c 显示屏为例,他的显示驱动芯片是 ST7789v,设置显示格式的方式是往 3a 寄存器写入0x55(RGB565)或者 0x66(RGB666),在 R128SDK 中,已经把 jlt35031c 的通讯格式写死为 0x55,lcd_pixel_fmt配置选项无效:

      sunxi_lcd_cmd_write(sel, 0x3a);
      sunxi_lcd_para_write(sel, 0x55);
      

      在例程中,输入的数据是 0xff,0x00,0xff,0x00,对于SPI接口,是按字节发送。实际上,例程只需要每次发送2字节即可,因为前后发送的都是相同的ff 00,所以没有看出问题。

      根据对 565 的数据解析,我们拆分 ff 00 就可以得到红色分量是 0b11111,也就是 31,绿色是0b111000,也就是 56,,蓝色是 0.我们等效转换成 RGB888,有:

      R = 31/31*255 = 255
      G = 56/63*255 = 226
      

      在调色板输入对应颜色,就可以得到黄色

      image-20231016100913213 (1).png

      因为 DBI 通讯效率较高,所以可以使用 RGB565 或者 RGB666,使用 DBI 接口,也就是 lcd_if 设置为1时,驱动会根据 lcd_pixel_fmt 配置寄存器,以 SDK 中的 kld2844b.c 为例,这显示屏的显示驱动也是 ST7789,但是不同的屏幕,厂家封装时已经限制了通讯方式,所以即使是能使用 DBI 接口的驱动芯片的屏幕,或许也用不了DBI。

      sunxi_lcd_cmd_write(sel, 0x3A); /* Interface Pixel Format */
      /* 55----RGB565;66---RGB666 */
      if (info[sel].lcd_pixel_fmt == LCDFB_FORMAT_RGB_565 ||
          info[sel].lcd_pixel_fmt == LCDFB_FORMAT_BGR_565) {
          sunxi_lcd_para_write(sel, 0x55);
          if (info[sel].lcd_pixel_fmt == LCDFB_FORMAT_RGB_565)
              rotate &= 0xf7;
          else
              rotate |= 0x08;
      } else if (info[sel].lcd_pixel_fmt < LCDFB_FORMAT_RGB_888) {
          sunxi_lcd_para_write(sel, 0x66);
          if (info[sel].lcd_pixel_fmt == LCDFB_FORMAT_BGRA_8888 ||
              info[sel].lcd_pixel_fmt == LCDFB_FORMAT_BGRX_8888 ||
              info[sel].lcd_pixel_fmt == LCDFB_FORMAT_ABGR_8888 ||
              info[sel].lcd_pixel_fmt == LCDFB_FORMAT_XBGR_8888) {
              rotate |= 0x08;
          }
      } else {
          sunxi_lcd_para_write(sel, 0x66);
      }
      

      对于 DBI 格式,不再是以字节的形式去解析,而是以字的方式去解析,为了统一,软件已经规定了,RGB565 格式时,字大小是2字节,也就是16位,而 RGB666 格式时,字大小是4字节,也就是32位。

      对于 RGB565 格式,同样是设置为 0xff,0x00。因为屏幕是大端,而芯片存储方式是小端,所以芯片的 DBI 模块,会自动把数据从新排列,也就是实际上 DBI 发送数据时,会先发送0x00,再发送0xff,也就是红色分量为0,绿色分量为 0b000111,也就是7,蓝色分量是 0x11111,也就是31,我们同样转换成RGB888

      G = 7/63*255 = 28
      B= 31/31*255 = 255
      

      在调色板上输入,可以得到蓝色。

      image-20231016101233907 (1).png

      如果是 RGB666,虽然占用的是3个字节,但是没有CPU是3字节对齐的,所以需要一次性输入4字节,然后 DBI 硬件模块,会自动舍弃1个字节,软件同意舍弃了最后一个字节。

      依旧以例程为例,例程输入了 0xff,0x00,0xff,0x00,为了方便说明,标准为 0xff(1),0x00(1),0xff(2),0x00(2),其中 0x00(2)会被舍弃掉,然后发送顺序是0xff(2),0x00(1),0xff(1),也就是 0xff(2) 是红色分量,0xff(1) 是蓝色分量,混合可以得到紫色。

      image-20231016101308346 (1).png

      发布在 MR Series
      livpo
      livpo
    • 【R128】应用开发案例——按键输入

      基于R128-S2设计的全套开发板已上线淘宝百问网韦东山老师个人店进行售卖,包含黑色的DshanMCU-R128s2-R16N16模组和全套的DshanMCU-R128s2-DEVKIT。

      • DshanMCU-R128s2-R16N16模组:39.9元
      • DshanMCU-R128s2-DEVKIT开发板:59.9元

      R128开发板购买链接:https://item.taobao.com/item.htm?spm=a21n57.1.0.0.46b0523cMfarLo&id=736154682975&ns=1&abbucket=5#detail

      “主图_01”为智能对象-1.jpg

      按键输入

      本文案例代码 下载地址
      按键输入案例代码 https://www.aw-ol.com/downloads?cat=24

      首先我们搭建电路,如下:

      image-20230815135935193.png

      引脚 按键
      PA25 按键1脚
      GND 按键3脚

      载入方案

      我们使用的开发板是 R128-Devkit,需要开发 C906 核心的应用程序,所以载入方案选择r128s2_module_c906

      $ source envsetup.sh 
      $ lunch_rtos 1
      

      72a7c89591f87b8b108e761882986c3c50a99f29.png

      勾选 GPIO 驱动

      mrtos_menuconfig 找到下列驱动

      Drivers Options  --->
          soc related device drivers  --->
                  GPIO devices --->
                      [*] enable GPIO driver
                      [*] enbale GPIO hal APIs Test command
      

      2023-08-15-11-09-04-image-20230803111740921.png

      编写程序

      打开你喜欢的编辑器,修改文件:lichee/rtos/projects/r128s2/module_c906/src/main.c

      93f23b7b43c2b88b07d690ac1438e9008f840886.png

      引入头文件

      #include <hal_gpio.h>
      

      7b5cb3a89bcbe28c4f4ed5ae9b64bd5b0a8aaa71.png

      使用 GPIO 配置引脚

      配置 GPIO 的上下拉状态

      使用 hal_gpio_set_pull(gpio_pin_t pin, gpio_pull_status_t pull); 来设置。这里我们设置 PA25 引脚为默认上拉状态。

      hal_gpio_set_pull(GPIOA(25), GPIO_PULL_UP);
      

      配置 GPIO 输入输出模式

      使用 hal_gpio_set_direction(gpio_pin_t pin, gpio_direction_t direction); 来设置 GPIO 的输入输出模式,这里配置为输入模式。

      hal_gpio_set_direction(GPIOA(25), GPIO_DIRECTION_INPUT);
      

      配置 GPIO 的 MUX 功能

      GPIO 通常有多种功能,需要配置 MUX 选择需要的功能,使用 hal_gpio_pinmux_set_function(gpio_pin_t pin, gpio_muxsel_t function_index); 来设置 GPIO 的复用功能,这里配置为GPIO 输入模式(GPIO_MUXSEL_IN)

      hal_gpio_pinmux_set_function(GPIOA(25), GPIO_MUXSEL_IN);
      

      获取 GPIO 的电平

      使用 int hal_gpio_get_data(gpio_pin_t pin, gpio_data_t *data); 来获取 GPIO 的电平,这里获取 A25 的电平状态。

      gpio_data_t gpio_data;
      hal_gpio_get_data(GPIOA(25), &gpio_data);
      

      完整的配置 GPIO

      gpio_data_t gpio_data;
      hal_gpio_set_pull(GPIOA(25), GPIO_PULL_UP);
      hal_gpio_set_direction(GPIOA(25), GPIO_DIRECTION_INPUT);
      hal_gpio_pinmux_set_function(GPIOA(25), GPIO_MUXSEL_IN);
      
      while(1){
          hal_gpio_get_data(GPIOA(25), &gpio_data);
          if(gpio_data == GPIO_DATA_LOW){
              printf("Key Pressed!\n");
          }
      }
      

      2023-08-15-11-20-04-image.png

      结果

      按下按键,串口会输出 Key Pressed!

      2023-08-15-11-20-50-image.png

      发布在 A Series
      livpo
      livpo
    • 全志T527芯片详解【一】:计算性能

      高效架构 性能稳健

      内置8*ARM Cortex-A55,8核主频可运行至1.8GHz,提供更稳健强劲的处理能力

      • Octa-core ARM Cortex-A55 in a DynamlQ big.LITTLE configuration, up to 1.8 GHz
      • 32KB L1 I-cache and 32KB L1 D-cache per A55 core
      • Optional 64KB L2 cache per“LITTLE”core
      • Optional 128KB L2 cache per“big”core

      lQLPJxXEUm9PONXNAlPNA_Ow1ZQPgrg4oj0GMZ3UVwB2AA_1011_595.png

      图形显示 清晰流畅

      集成ARM Mail-G57 GPU图形显示能力更上一层楼

      • ARM G57 MC1 GPU
      • Supports OpenGL ES 3.2/2.0/1.1, Vulkan 1.1/1.2/1.3, and OpenCL2.2
      • Anti-aliasing algorithm
      • High memory bandwidth and low power consumption in 3D graphics processing

      lQLPJx1du0D2IlXNAj7NA_SwUIKjgzilbv4GMZ3gyhDIAA_1012_574.png

      边缘计算 AI赋能

      集成2Tops NPU为端侧语音、自然语言处理、图像处理及画质增强等AI应用提供性能支持

      • 2 TOPS NPU
      • Embedded 512KB internal buffer
      • Supports deep learning frameworks:TensorFlow, Pytorch, Caffe, Onnx NN, TFLite.......

      lQLPKdTGOXhy8FXNAkHNA_awjroyLjZnBmkGMZ31JeCzAA_1014_577.png

      声音算法 浑然天成

      内置HiFi4 DSP,频率可达600MHz,广泛应用于图像、音频及数字信号处理的专用领域为 影音娱乐、工业生产提供专属算力

      • HiFi4 Audio DSP
      • 32KB I-cache +32KB D-cache

      lQLPJw8v8DVfN9XNAffNA_awW1u4j03nK8oGMZ3_6eMBAA_1014_503.png

      独立MCU 控制更实时

      内置RISC-V架构MCU,主频可达200MHz独立运行RTOS系统,为工业及机器人系统上的实时处理、高速响应及工业级的稳定运行提供重要保障

      • RISC-V CPU, up to 200 MHz
      • 16 KB I-cache and 16 KB D-cache·RV32IMAFC instructions

      lQLPJxSpeBhmbtXNAjvNA_WwxLr62XkfU8kGMZ4NnCj8AA_1013_571.png

      更多全志T527芯片详解系列专题内容即将发布

      联系我们

      微信公众号: Allwinnertech
      官方邮箱:service@allwinnertech.com

      lQLPJxu6dDomTRXNAfPNA_SwH1ib7dGKKPgGMZ4g_zEhAA_1012_499.png

      发布在 T Series
      livpo
      livpo
    • 回复: (送开发板~)看!是芒果派,他好像在玩一种很新的开发板

      真的好想要芒果派呀

      linux1.psd

      发布在 公告
      livpo
      livpo
    • 【R128】应用开发案例——SPI驱动ST7789V1.3寸LCD

      基于R128-S2设计的全套开发板已上线淘宝百问网韦东山老师个人店进行售卖,包含黑色的DshanMCU-R128s2-R16N16模组和全套的DshanMCU-R128s2-DEVKIT。

      • DshanMCU-R128s2-R16N16模组:39.9元
      • DshanMCU-R128s2-DEVKIT开发板:59.9元

      R128开发板购买链接:https://item.taobao.com/item.htm?spm=a21n57.1.0.0.46b0523cMfarLo&id=736154682975&ns=1&abbucket=5#detail

      “主图_01”为智能对象-1.jpg

      SPI驱动ST7789V1.3寸LCD

      R128 平台提供了 SPI DBI 的 SPI TFT 接口,具有如下特点:

      • Supports DBI Type C 3 Line/4 Line Interface Mode
      • Supports 2 Data Lane Interface Mode
      • Supports data source from CPU or DMA
      • Supports RGB111/444/565/666/888 video format
      • Maximum resolution of RGB666 240 x 320@30Hz with single data lane
      • Maximum resolution of RGB888 240 x 320@60Hz or 320 x 480@30Hz with dual data lane
      • Supports tearing effect
      • Supports software flexible control video frame rate

      同时,提供了 SPILCD 驱动框架以供 SPI 屏幕使用。

      此次适配的SPI屏为 ZJY130S0800TG01,使用的是 SPI 进行驱动。

      image-20231023102211315.png

      引脚配置如下:

      R128 Devkit TFT 模块
      PA12 CS
      PA13 SCL
      PA18 SDA
      PA9 BLK
      PA20 RES
      PA19 DC
      3V3 VCC
      GND GND

      载入方案

      我们使用的开发板是 R128-Devkit,需要开发 C906 核心的应用程序,所以载入方案选择 r128s2_module_c906

      $ source envsetup.sh 
      $ lunch_rtos 1
      

      image-20230802110150203 (8).png

      设置 SPI 驱动

      屏幕使用的是SPI驱动,所以需要勾选SPI驱动,运行 mrtos_menuconfig 进入配置页面。前往下列地址找到 SPI Devices

      Drivers Options  --->
          soc related device drivers  --->
              SPI Devices --->
              -*- enable spi driver
      

      image-20230825144134701 (4).png

      配置 SPI 引脚

      打开你喜欢的编辑器,修改文件:board/r128s2/module/configs/sys_config.fex,在这里我们不需要用到 SPI HOLD与SPI WP引脚,注释掉即可。

      ;----------------------------------------------------------------------------------
      ;SPI controller configuration
      ;----------------------------------------------------------------------------------
      ;Please config spi in dts
      [spi1]
      spi1_used       = 1
      spi1_cs_number  = 1
      spi1_cs_bitmap  = 1
      spi1_cs0        = port:PA12<6><0><3><default>
      spi1_sclk       = port:PA13<6><0><3><default>
      spi1_mosi       = port:PA18<6><0><3><default>
      spi1_miso       = port:PA21<6><0><3><default>
      ;spi1_hold       = port:PA19<6><0><2><default>
      ;spi1_wp         = port:PA20<6><0><2><default>
      

      image-20230825144205551 (3).png

      设置 PWM 驱动

      屏幕背光使用的是PWM驱动,所以需要勾选PWM驱动,运行 mrtos_menuconfig 进入配置页面。前往下列地址找到 PWM Devices

      Drivers Options  --->
          soc related device drivers  --->
              PWM Devices --->
              -*- enable pwm driver
      

      image-20230825144408144 (3).png

      配置 PWM 引脚

      打开你喜欢的编辑器,修改文件:board/r128s2/module/configs/sys_config.fex,增加 PWM1 节点

      [pwm1]
      pwm_used        = 1
      pwm_positive    = port:PA9<4><0><3><default>
      

      image-20230825150128954 (3).png

      设置 SPI LCD 驱动

      SPI LCD 由专门的驱动管理。运行 mrtos_menuconfig 进入配置页面。前往下列地址找到 SPILCD Devices ,注意同时勾选 spilcd hal APIs test 方便测试使用。

      Drivers Options  --->
          soc related device drivers  --->
              [*] DISP Driver Support(spi_lcd)
              [*]   spilcd hal APIs test
      

      image-20230825150341879 (2).png

      编写 SPI LCD 显示屏驱动

      获取屏幕初始化序列

      首先询问屏厂提供驱动源码

      image-20231023102239928.png

      找到 LCD 的初始化序列代码

      image-20231023102252129.png

      找到屏幕初始化的源码

      image-20231023102333476.png

      整理后的初始化代码如下:

      LCD_WR_REG(0x11); // Sleep out 
      delay_ms(120);    // Delay 120ms 
      //************* Start Initial Sequence **********// 
      LCD_WR_REG(0x36);
      LCD_WR_DATA8(0x00);
      
      LCD_WR_REG(0x3A);     
      LCD_WR_DATA8(0x05);   
      
      LCD_WR_REG(0xB2);     
      LCD_WR_DATA8(0x1F);   
      LCD_WR_DATA8(0x1F);   
      LCD_WR_DATA8(0x00);   
      LCD_WR_DATA8(0x33);   
      LCD_WR_DATA8(0x33);   
      
      LCD_WR_REG(0xB7);     
      LCD_WR_DATA8(0x35);   
      
      LCD_WR_REG(0xBB);     
      LCD_WR_DATA8(0x20);   // 2b
      
      LCD_WR_REG(0xC0);     
      LCD_WR_DATA8(0x2C);   
      
      LCD_WR_REG(0xC2);     
      LCD_WR_DATA8(0x01);   
      
      LCD_WR_REG(0xC3);     
      LCD_WR_DATA8(0x01);   
      
      LCD_WR_REG(0xC4);     
      LCD_WR_DATA8(0x18);   // VDV, 0x20:0v
      
      LCD_WR_REG(0xC6);     
      LCD_WR_DATA8(0x13);   // 0x13:60Hz   
      
      LCD_WR_REG(0xD0);     
      LCD_WR_DATA8(0xA4);   
      LCD_WR_DATA8(0xA1);   
      
      LCD_WR_REG(0xD6);     
      LCD_WR_DATA8(0xA1);   // sleep in后,gate输出为GND
      
      LCD_WR_REG(0xE0);     
      LCD_WR_DATA8(0xF0);   
      LCD_WR_DATA8(0x04);   
      LCD_WR_DATA8(0x07);   
      LCD_WR_DATA8(0x04);   
      LCD_WR_DATA8(0x04);   
      LCD_WR_DATA8(0x04);   
      LCD_WR_DATA8(0x25);   
      LCD_WR_DATA8(0x33);   
      LCD_WR_DATA8(0x3C);   
      LCD_WR_DATA8(0x36);   
      LCD_WR_DATA8(0x14);   
      LCD_WR_DATA8(0x12);   
      LCD_WR_DATA8(0x29);   
      LCD_WR_DATA8(0x30);   
      
      LCD_WR_REG(0xE1);     
      LCD_WR_DATA8(0xF0);   
      LCD_WR_DATA8(0x02);   
      LCD_WR_DATA8(0x04);   
      LCD_WR_DATA8(0x05);   
      LCD_WR_DATA8(0x05);   
      LCD_WR_DATA8(0x21);   
      LCD_WR_DATA8(0x25);   
      LCD_WR_DATA8(0x32);   
      LCD_WR_DATA8(0x3B);   
      LCD_WR_DATA8(0x38);   
      LCD_WR_DATA8(0x12);   
      LCD_WR_DATA8(0x14);   
      LCD_WR_DATA8(0x27);   
      LCD_WR_DATA8(0x31);   
      
      LCD_WR_REG(0xE4);     
      LCD_WR_DATA8(0x1D);   // 使用240根gate  (N+1)*8
      LCD_WR_DATA8(0x00);   // 设定gate起点位置
      LCD_WR_DATA8(0x00);   // 当gate没有用完时,bit4(TMG)设为0
      
      LCD_WR_REG(0x21);     
      
      LCD_WR_REG(0x29);     
      

      用现成驱动改写 SPI LCD 驱动

      选择一个现成的 SPI LCD 改写即可,这里选择 nv3029s.c 驱动来修改

      image-20231017104714827 (2).png

      复制这两个驱动,重命名为 st7789v.c

      image-20231017104740060 (2).png

      先编辑 st7789v.h 将 nv3029s 改成 st7789v

      image-20231017104851772 (2).png

      #ifndef _ST7789V_H
      #define _ST7789V_H
      
      #include "panels.h"
      
      struct __lcd_panel st7789v_panel;
      
      #endif /*End of file*/
      

      编辑 st7789v.c 将 nv3029s 改成 st7789v

      image-20231017104942286 (2).png

      编写初始化序列

      先删除 static void LCD_panel_init(unsigned int sel) 中的初始化函数。

      image-20231017105101421 (2).png

      然后将屏厂提供的初始化序列复制进来

      image-20231023102641761.png

      然后按照 spi_lcd 框架的接口改写驱动接口,具体接口如下

      屏厂函数 SPILCD框架接口
      LCD_WR_REG sunxi_lcd_cmd_write
      LCD_WR_DATA8 sunxi_lcd_para_write
      delay_ms sunxi_lcd_delay_ms

      可以直接进行替换

      image-20231023095114146 (1).png

      完成后如下

      image-20231023095158411 (1).png

      然后对照屏厂提供的驱动修改 address 函数

      image-20231017105738155 (2).png

      做如下修改

      static void address(unsigned int sel, int x, int y, int width, int height)
      {
      	sunxi_lcd_cmd_write(sel, 0x2B); /* Set row address */
      	sunxi_lcd_para_write(sel, (y >> 8) & 0xff);
      	sunxi_lcd_para_write(sel, y & 0xff);
      	sunxi_lcd_para_write(sel, (height >> 8) & 0xff);
      	sunxi_lcd_para_write(sel, height & 0xff);
      	sunxi_lcd_cmd_write(sel, 0x2A); /* Set coloum address */
      	sunxi_lcd_para_write(sel, (x >> 8) & 0xff);
      	sunxi_lcd_para_write(sel, x & 0xff);
      	sunxi_lcd_para_write(sel, (width >> 8) & 0xff);
      	sunxi_lcd_para_write(sel, width & 0xff);
      	sunxi_lcd_cmd_write(sel, 0x2c);
      }
      

      完成驱动如下

      #include "st7789v.h"
      
      static void LCD_power_on(u32 sel);
      static void LCD_power_off(u32 sel);
      static void LCD_bl_open(u32 sel);
      static void LCD_bl_close(u32 sel);
      static void LCD_panel_init(u32 sel);
      static void LCD_panel_exit(u32 sel);
      #define RESET(s, v) sunxi_lcd_gpio_set_value(s, 0, v)
      #define power_en(sel, val) sunxi_lcd_gpio_set_value(sel, 0, val)
      
      static struct disp_panel_para info[LCD_FB_MAX];
      
      static void address(unsigned int sel, int x, int y, int width, int height)
      {
      	sunxi_lcd_cmd_write(sel, 0x2B); /* Set row address */
      	sunxi_lcd_para_write(sel, (y >> 8) & 0xff);
      	sunxi_lcd_para_write(sel, y & 0xff);
      	sunxi_lcd_para_write(sel, (height >> 8) & 0xff);
      	sunxi_lcd_para_write(sel, height & 0xff);
      	sunxi_lcd_cmd_write(sel, 0x2A); /* Set coloum address */
      	sunxi_lcd_para_write(sel, (x >> 8) & 0xff);
      	sunxi_lcd_para_write(sel, x & 0xff);
      	sunxi_lcd_para_write(sel, (width >> 8) & 0xff);
      	sunxi_lcd_para_write(sel, width & 0xff);
      	sunxi_lcd_cmd_write(sel, 0x2c);
      }
      
      static void LCD_panel_init(unsigned int sel)
      {
      	if (bsp_disp_get_panel_info(sel, &info[sel])) {
      		lcd_fb_wrn("get panel info fail!\n");
      		return;
      	}
      
      	sunxi_lcd_cmd_write(sel, 0x11); // Sleep out 
      	sunxi_lcd_delay_ms(120);    // Delay 120ms 
      	//************* Start Initial Sequence **********// 
      	sunxi_lcd_cmd_write(sel, 0x36);
      	sunxi_lcd_para_write(sel, 0x00);
      
      	sunxi_lcd_cmd_write(sel, 0x3A);     
      	sunxi_lcd_para_write(sel, 0x05);   
      
      	sunxi_lcd_cmd_write(sel, 0xB2);     
      	sunxi_lcd_para_write(sel, 0x1F);   
      	sunxi_lcd_para_write(sel, 0x1F);   
      	sunxi_lcd_para_write(sel, 0x00);   
      	sunxi_lcd_para_write(sel, 0x33);   
      	sunxi_lcd_para_write(sel, 0x33);   
      
      	sunxi_lcd_cmd_write(sel, 0xB7);     
      	sunxi_lcd_para_write(sel, 0x35);   
      
      	sunxi_lcd_cmd_write(sel, 0xBB);     
      	sunxi_lcd_para_write(sel, 0x20);   // 2b
      
      	sunxi_lcd_cmd_write(sel, 0xC0);     
      	sunxi_lcd_para_write(sel, 0x2C);   
      
      	sunxi_lcd_cmd_write(sel, 0xC2);     
      	sunxi_lcd_para_write(sel, 0x01);   
      
      	sunxi_lcd_cmd_write(sel, 0xC3);     
      	sunxi_lcd_para_write(sel, 0x01);   
      
      	sunxi_lcd_cmd_write(sel, 0xC4);     
      	sunxi_lcd_para_write(sel, 0x18);   // VDV, 0x20:0v
      
      	sunxi_lcd_cmd_write(sel, 0xC6);     
      	sunxi_lcd_para_write(sel, 0x13);   // 0x13:60Hz   
      
      	sunxi_lcd_cmd_write(sel, 0xD0);     
      	sunxi_lcd_para_write(sel, 0xA4);   
      	sunxi_lcd_para_write(sel, 0xA1);   
      
      	sunxi_lcd_cmd_write(sel, 0xD6);     
      	sunxi_lcd_para_write(sel, 0xA1);   // sleep in后,gate输出为GND
      
      	sunxi_lcd_cmd_write(sel, 0xE0);     
      	sunxi_lcd_para_write(sel, 0xF0);   
      	sunxi_lcd_para_write(sel, 0x04);   
      	sunxi_lcd_para_write(sel, 0x07);   
      	sunxi_lcd_para_write(sel, 0x04);   
      	sunxi_lcd_para_write(sel, 0x04);   
      	sunxi_lcd_para_write(sel, 0x04);   
      	sunxi_lcd_para_write(sel, 0x25);   
      	sunxi_lcd_para_write(sel, 0x33);   
      	sunxi_lcd_para_write(sel, 0x3C);   
      	sunxi_lcd_para_write(sel, 0x36);   
      	sunxi_lcd_para_write(sel, 0x14);   
      	sunxi_lcd_para_write(sel, 0x12);   
      	sunxi_lcd_para_write(sel, 0x29);   
      	sunxi_lcd_para_write(sel, 0x30);   
      
      	sunxi_lcd_cmd_write(sel, 0xE1);     
      	sunxi_lcd_para_write(sel, 0xF0);   
      	sunxi_lcd_para_write(sel, 0x02);   
      	sunxi_lcd_para_write(sel, 0x04);   
      	sunxi_lcd_para_write(sel, 0x05);   
      	sunxi_lcd_para_write(sel, 0x05);   
      	sunxi_lcd_para_write(sel, 0x21);   
      	sunxi_lcd_para_write(sel, 0x25);   
      	sunxi_lcd_para_write(sel, 0x32);   
      	sunxi_lcd_para_write(sel, 0x3B);   
      	sunxi_lcd_para_write(sel, 0x38);   
      	sunxi_lcd_para_write(sel, 0x12);   
      	sunxi_lcd_para_write(sel, 0x14);   
      	sunxi_lcd_para_write(sel, 0x27);   
      	sunxi_lcd_para_write(sel, 0x31);   
      
      	sunxi_lcd_cmd_write(sel, 0xE4);     
      	sunxi_lcd_para_write(sel, 0x1D);   // 使用240根gate  (N+1)*8
      	sunxi_lcd_para_write(sel, 0x00);   // 设定gate起点位置
      	sunxi_lcd_para_write(sel, 0x00);   // 当gate没有用完时,bit4(TMG)设为0
      
      	sunxi_lcd_cmd_write(sel, 0x21);     
      
      	sunxi_lcd_cmd_write(sel, 0x29);   
      
      	if (info[sel].lcd_x < info[sel].lcd_y)
      		address(sel, 0, 0, info[sel].lcd_x - 1, info[sel].lcd_y - 1);
      	else
      		address(sel, 0, 0, info[sel].lcd_y - 1, info[sel].lcd_x - 1);
      }
      
      static void LCD_panel_exit(unsigned int sel)
      {
      	sunxi_lcd_cmd_write(sel, 0x28);
      	sunxi_lcd_delay_ms(20);
      	sunxi_lcd_cmd_write(sel, 0x10);
      	sunxi_lcd_delay_ms(20);
      	sunxi_lcd_pin_cfg(sel, 0);
      }
      
      static s32 LCD_open_flow(u32 sel)
      {
      	lcd_fb_here;
      	/* open lcd power, and delay 50ms */
      	LCD_OPEN_FUNC(sel, LCD_power_on, 50);
      	/* open lcd power, than delay 200ms */
      	LCD_OPEN_FUNC(sel, LCD_panel_init, 200);
      
      	LCD_OPEN_FUNC(sel, lcd_fb_black_screen, 50);
      	/* open lcd backlight, and delay 0ms */
      	LCD_OPEN_FUNC(sel, LCD_bl_open, 0);
      
      	return 0;
      }
      
      static s32 LCD_close_flow(u32 sel)
      {
      	lcd_fb_here;
      	/* close lcd backlight, and delay 0ms */
      	LCD_CLOSE_FUNC(sel, LCD_bl_close, 50);
      	/* open lcd power, than delay 200ms */
      	LCD_CLOSE_FUNC(sel, LCD_panel_exit, 10);
      	/* close lcd power, and delay 500ms */
      	LCD_CLOSE_FUNC(sel, LCD_power_off, 10);
      
      	return 0;
      }
      
      static void LCD_power_on(u32 sel)
      {
      	/* config lcd_power pin to open lcd power0 */
      	lcd_fb_here;
      	power_en(sel, 1);
      
      	sunxi_lcd_power_enable(sel, 0);
      
      	sunxi_lcd_pin_cfg(sel, 1);
      	RESET(sel, 1);
      	sunxi_lcd_delay_ms(100);
      	RESET(sel, 0);
      	sunxi_lcd_delay_ms(100);
      	RESET(sel, 1);
      }
      
      static void LCD_power_off(u32 sel)
      {
      	lcd_fb_here;
      	/* config lcd_power pin to close lcd power0 */
      	sunxi_lcd_power_disable(sel, 0);
      	power_en(sel, 0);
      }
      
      static void LCD_bl_open(u32 sel)
      {
      	sunxi_lcd_pwm_enable(sel);
      	/* config lcd_bl_en pin to open lcd backlight */
      	sunxi_lcd_backlight_enable(sel);
      	lcd_fb_here;
      }
      
      static void LCD_bl_close(u32 sel)
      {
      	/* config lcd_bl_en pin to close lcd backlight */
      	sunxi_lcd_backlight_disable(sel);
      	sunxi_lcd_pwm_disable(sel);
      	lcd_fb_here;
      }
      
      
      /* sel: 0:lcd0; 1:lcd1 */
      static s32 LCD_user_defined_func(u32 sel, u32 para1, u32 para2, u32 para3)
      {
      	lcd_fb_here;
      	return 0;
      }
      
      static int lcd_set_var(unsigned int sel, struct fb_info *p_info)
      {
      	return 0;
      }
      
      static int lcd_set_addr_win(unsigned int sel, int x, int y, int width, int height)
      {
      	address(sel, x, y, width, height);
      	return 0;
      }
      
      static int lcd_blank(unsigned int sel, unsigned int en)
      {
      	return 0;
      }
      
      struct __lcd_panel st7789v_panel = {
          /* panel driver name, must mach the name of lcd_drv_name in sys_config.fex
             */
      	.name = "st7789v",
      	.func = {
      		.cfg_open_flow = LCD_open_flow,
      		.cfg_close_flow = LCD_close_flow,
      		.lcd_user_defined_func = LCD_user_defined_func,
      		.blank = lcd_blank,
      		.set_var = lcd_set_var,
      		.set_addr_win = lcd_set_addr_win,
      	},
      };
      

      对接驱动框架

      完成了屏幕驱动的编写,接下来需要对接到 SPILCD 驱动框架。首先编辑 Kconfig

      image-20231017105738155 (2).png

      增加 st7789v 的配置

      image-20231017105814814 (2).png

      config LCD_SUPPORT_ST7789V
          bool "LCD support st7789v panel"
          default n
          ---help---
              If you want to support st7789v panel for display driver, select it.
      

      然后编辑 panels.c 在 panel_array 里增加 st7789 驱动的引用

      image-20231017105948156 (2).png

      如下图

      image-20231017105919628 (2).png

      #ifdef CONFIG_LCD_SUPPORT_ST7789V
          &st7789v_panel,
      #endif
      

      之后编辑 panels.h 同样增加引用

      image-20231017110043805 (2).png

      如下图

      image-20231017110122397 (2).png

      #ifdef CONFIG_LCD_SUPPORT_ST7789V
      extern struct __lcd_panel st7789v_panel;
      #endif
      

      最后编辑外层的 Makefile 增加编译选项

      image-20231017110204681 (2).png

      如下所示

      image-20231017110242997 (2).png

      obj-${CONFIG_LCD_SUPPORT_ST7789V} += panels/st7789v.o
      

      选择 ST7789V 驱动

      在 SPILCD 驱动选择界面可以看到 LCD_FB panels select 选择 SPI 屏幕的驱动

      进入 LCD_FB panels select 选项

      image-20230825150812435 (3).png

      选择并勾选 [*] LCD support st7789v panel

      image-20231017110344277 (2).png

      配置 SPI LCD 引脚

      打开你喜欢的编辑器,修改文件:board/r128s2/module/configs/sys_config.fex

      [lcd_fb0]
      lcd_used            = 1   
      lcd_model_name      = "spilcd"   
      lcd_driver_name     = "st7789v" 
      ; 屏幕规格配置
      lcd_x               = 240
      lcd_y               = 240  
      lcd_width           = 32  
      lcd_height          = 32  
      ; SPI 速率
      lcd_data_speed      = 50
      ; PWM 背光配置项
      lcd_pwm_used        = 1
      lcd_pwm_ch          = 1
      lcd_pwm_freq        = 5000 
      lcd_pwm_pol         = 0 
      lcd_backlight       = 100
      ; 配置 lcd_if = 1 为 SPI 模式,双缓冲
      lcd_if              = 0
      fb_buffer_num       = 2
      ; SPI 模式下以下配置无效
      lcd_pixel_fmt       = 11 
      lcd_dbi_fmt         = 2
      lcd_dbi_clk_mode    = 1
      lcd_dbi_te          = 1
      lcd_dbi_if          = 4
      lcd_rgb_order       = 0
      lcd_fps             = 60
      ; 使用 SPI1 作为通讯接口
      lcd_spi_bus_num     = 1
      lcd_frm             = 2
      lcd_gamma_en        = 1
      
      lcd_power_num       = 0
      lcd_gpio_regu_num   = 0
      lcd_bl_percent_num  = 0
      
      lcd_spi_dc_pin      = port:PA19<1><0><3><0>
      ;RESET Pin
      lcd_gpio_0          = port:PA20<1><0><2><0>
      

      编译打包

      运行命令 mp 编译打包,可以看到编译了 st7789v.o

      image-20231017111015362 (2).png

      测试

      烧录启动之后,屏幕背光启动,但是屏幕全黑。

      image-20231023102211315 (1).png

      输入 test_spilcd ,屏幕显示黄色。

      image-20231023103606375.png

      image-20231023103636509.png

      输入 lv_examples 1 可以显示 lvgl 界面

      image-20231023103710014.png

      常见问题

      LVGL 出现 DMA Over Size

      image-20231023101154351 (2).png

      这是由于 LVGL 配置的 LV_COLOR_DEPTH 为 32,但是 SPI 屏配置为16位。请修改 lv_conf.h

      image-20231023101317634 (2).png

      出现部分花屏

      image-20231023103039467 (1).png

      • 检查 address 函数是否正确
      • 检查 sys_config.fex 屏幕配置分辨率是否正确
      发布在 A Series
      livpo
      livpo
    • 百问网Avaota A1 T527开发板

      全志科技T527采用多核异构设计,集成了CPU、GPU、NPU、DSP、MCU等各种计算单元,为各种复杂场景的多任务处理、专用数据处理以及算法应用提供了高效且灵活的解决方案,为生成式AI、人工智能算法的场景化落地提供了坚实的硬件基础。

      c46ddd2c-083e-4103-bfba-1ec9739fbb52-image.png

      为了方便开发者进行产品预研和项目开发,百问网推出了搭载全志T527芯片的AvaotaA1开发板。AvaotaA1是一款硬件+软件全开源的卡片电脑,提供全面开放的软硬件资料,小巧的板型搭载了8核处理器并引出了丰富的外部资源,还支持Avaota OS、Android13、Armbian等多种操作系统。

      3ec4108e-dff9-4e47-a9a5-2dd531169699-image.png
      43438e71-0024-48cd-a847-4530a9dc7e29-image.png

      硬件资源

      AvaotaA1开发板采用98mm x 73mm的接口丰富易用SBC板型设计,可以很好的兼容目前市面上的绝大多数主流开发板,在足够的板型尺寸下,开发板板载了500+元器件、14个外设接口以及40个拓展GPIO口。

      28ef36ff-6d61-4a30-95e9-ce237ed03c1d-image.png

      9e876b60-c2e9-4ef0-b045-d1a4c730e362-image.png

      • CPU: 八核 Cortex-A55,最高可达 1.8GHz
      • GPU: Mali G57,支持 OpenGL ES 3.2/2.0/1.1,Vulkan 1.1/1.2/1.3,OpenCL 2.2
      • RCPU: XuanTie E906 RISC-V @ 200MHz
      • DSP: HIFI4 DSP @ 600MHz
      • NPU: 可选最高可达 2TOPS 的计算能力
      • 内存芯片: 支持 LPDDR4,可选 1/2/4GB
      • 存储芯片: 支持 eMMC 5.1,可选 16/32/64/128GB
      • 相机: MIPI CSI 接口,支持 1300 万像素,4 路 MIPI CSI
      • 网络通信接口: 2 x 千兆以太网,WIFI6 2.4G + 5G,BT5.4
      • USB 接口: 包括 USB OTG,USB3.0 OTG,USB2.0 HOST
      • 音频接口: 3.5mm 音频接口,具有录音功能
      • 显示接口: 包括 RGB 接口,MIPI DSI 接口,DP 接口,HDMI2.0 接口,LVDS 接口,MIPI DBI 接口
      • 其他接口模块: 包括调试串口,TF 卡槽,CAN 接口,RGB LED,红外(IR),UART,IIC,I2S,SPI,PWM

      e36c2538-4ccc-4e75-a735-14b364e4d030-image.png

      50cb25f2-1f7a-4367-a6e8-388af4784f04-image.png

      在显示接口方面,AvaotaA1板载了一块240x135@60fps的1.14寸IPS屏,可用于显示LVGL UI、显示输出、显示系统性能等基础功能实现。

      4e0b195f-33ec-4011-8be5-566aa962db00-image.png

      结合板载的HDMI、DP、MIPI/RGB/LVDS显示接口,AvaotaA1能够轻松实现4K60以及4K+1080P双屏异显的功能场景,结合NPU以及全志自研的新一代画质引擎,可支持AI超分等画质增强算法,提供更佳的显示体验。

      97f31ed3-5bd8-4dee-a432-cae6a46aab5e-image.png

      a1969d40-4260-467f-a83c-04e442059f48-image.png

      软件系统支持

      AvaotaA1支持专为Avaota SBC系列所打造的Avaota OS,Avaota OS是基于Ubuntu的特殊定制版,借助于Avaota SBC提供的性能和功能,Avaota OS在Ubuntu的基础上进行了深度优化,为用户提供了一体化的操作系统体验。

      3fb6177c-daf1-48c9-a115-a92d1507f99f-image.png

      Avaota OS不仅继承了Ubuntu的强大功能和稳定性,还增加了许多独特的特色功能。这些功能包括针对Avaota硬件的优化,如支持特定的传感器和接口,以及预先配置的软件包,以满足各种应用场景的需求。并通过对系统内核、资源管理和功耗控制的调整,以提供优异的性能和稳定性。

      除了百问网支持的专属Avaota OS,AvaotaA1也支持Android13、Buildroot、Armbian、Openwrt等多种适用于不同应用领域的操作系统,并附上了详尽的移植教程。

      9f046c45-1759-4b95-8a63-f4ec7a5a92fa-image.png

      开发板购买&资料获取

      目前R128 Devkit全套开发板已上线淘宝百问网韦东山老师个人店进行售卖,包含2+16和4+32两种裸板配置的绿色AvaotaA1 SBC,并可自行选购Type-C USB调试器:

      • 2GB+16GB裸板:249元
      • 4GB+32GB裸板:349元
      • Type-C USB调试器:49.9元

      另外关于AvaotaA1开发板的硬件设计、数据手册、软件系统源码等所有资料都在Avaota官方网站上开源,并配置了详尽的开发文档供开发者学习,感兴趣的小伙伴可以复制以下链接或者点击【阅读原文】获取更多详细资料。

      AvaotaA1开发板文档:
      https://docs.avaota.fun/avaota-sbc/

      f8a5ee84-07f8-496b-809f-aa8f81b62fca-image.png

      c8474885-6f54-4122-b348-ee63c8e84b7e-image.png

      发布在 T Series
      livpo
      livpo
    • 我DIY了一个可以声控刷抖音的神器,这下连手指都解放了

      用一块板两个模块三根线就可以爆改手机,实现语音控制刷抖音,上下滑动视频、点赞那都是基础操作,关键是还可以一键睡觉,懒人福音!

      9594899291f647b5bf93ade007916db5-ezgif.com-optimize.gif

      所谓一块板两个模块三根线,一块开发板就是负责接收和控制的香橙派H616开发板,两个模块分别是负责发送的语音识别模块SU-03T和一个mic,所以除了电源外,只需要将语音识别模块的TX(B7)接到香橙派的RX就可以,算上电源共三根线:

      e23c04ae769049c189c662f98db11991.jpeg

      b305beb98cf9470d8c441030a9afe63c.png

      项目需求只是简单的通过语音指令来控制安卓手机刷抖音,以实现视频切换和点赞等功能:

      1、开机播报“你好,我是你的刷抖音助手”
      2、当说出“你好抖音助手"可以唤醒模块,模块回复“抖音助手在”
      3、当超过10s没有指令或说出“退下”时,模块会进入休眠模式,并回复“有需要再叫我”
      4、当说出“下一个视频”或“这个不好看”时,模块回复“切换至下一个视频”,并划到下一个视频
      5、当说出“上一个视频”或“刚刚那个挺好看”时,模块回复“切换至上一个视频”,并划回上一个视频
      6、当说出“点个赞”或“这个视频不错”时,模块回复“已为您点赞”,并点赞当前视频
      7、当说出“不想看了”时,模块回复“已为您关闭屏幕”,并关闭手机屏幕,一键入睡!

      语音识别模块配置

      项目中所需要的是一个非特定语音识别,也就是不识别特定人类的声音的模块。所以SU-03T这个模块的操作相较于其他语音识别模块更简单,不需要编程或二次开发,只需要通过厂家给的网站配置后即可使用。

      创建产品

      cef30e83cf7c4bfba78c7d0612212d37.png

      设置PIN脚为串口模式

      对于语音识别模块,串口的RX和TX分别对应B6和B7,并设置相应的波特率

      1e1f4ab2bef64ca98fcc603ebed1c181.png

      设置唤醒词

      dfa213de176e492aae58f5f83c8b9308.png

      设置指令语句

      d168b89efe024fe88937f417d8ffa35a.png

      设置控制详情

      参数的设置就是行为的名字,大写字母 ,16进制ASCII码,已空格分开

      • next -> 4E 45 58 54
      • pre -> 50 52 45
      • zan -> 5A 41 4E
      • guan -> 47 55 41 4E

      dc2dafa537a7495db5c5875ad6fd591e.png

      测试

      在完成以上步骤,并下载SDK并烧写进入语音识别模块后,可以打开串口助手来测试一下,分别说出对应的指令,看看语音识别模块是否会向串口发送对应的字符。

      分别说出了四条指令,可见串口输出正确!

      629e75656d314e44ab7eaecb2e97fccf.png

      编写香橙派代码框架

      语音识别模块设置完成后,就可以将语音识别模块接到香橙派并进行Linux部分的代码编写,连接方式如下图所示:

      e23c04ae769049c189c662f98db11991.jpeg

      新创建一个“douyin”文件夹,将语音刷抖音项目的代码放在这里面:

      76c0a9593c3a4dfe9f505c93852ac56c.png

      然后将一些代码拷贝进来方便修改:

      2c259338d7304e40b05f540b6a851955.png
      重新编译一下,并说出4条控制语句:

      509c7e1178784bfbbb3d30983f5748be.png

      可见,语音识别模块成功识别了指令,香橙派成功的接收了语音识别模块通过串口打印的字符!

      现在,就需要修改serial_douyin.c中接收的代码,添加数据处理的部分,先简单写一个数据处理的框架

      
      void *write_serial(void *arg)
      {
          char readbuf[32] = {'\0'};
          while(1){
              while(serialDataAvail (*((int *)arg))){
                  serialGetstring (*((int *)arg),readbuf) ;
       
                  if(strcmp(readbuf,"NEXT") == 0 ){
                      printf("收到下一条视频指令\n");
                  }else if(strcmp(readbuf,"PRE") == 0){
                      printf("收到上一条视频指令\n");
                  }else if(strcmp(readbuf,"ZAN") == 0){
                      printf("收到点赞指令\n");
                  }else if(strcmp(readbuf,"GUAN") == 0){
                      printf("收到关闭指令\n");
                  }else{
                      printf("未知指令\n");
                  }
       
                  memset(readbuf,'\0',32);
              }
          }
       
          pthread_exit(NULL);
      }
      

      重新编译一下,并说出4条控制语句:

      17a1a4a6a98f45babb4f5fed054a7ac0.png

      可见,函数框架正确,接下来只需要将printf替换成真正的操作手机的代码就可以了。

      将手机接入香橙派

      将我破旧的小米5C再次拿出哈哈哈,然后通过 TYPE-C -- USB 连接到香橙派:

      9a20875f3287492f8b85171eaf5265bc.jpeg

      香橙派输入dmesg指令查看手机接入情况,可见已经成功识别

      f4d2e9b5a4d74933a85d31406cf2fc22.png

      由于安卓手机的底层也是用Linux系统来操作的,所以可以通过香橙派来直接进入控制手机shell的界面,但需要先安装adb工具,adb是做安卓开发中常用的工具

      sudo apt-get insall adb
      

      安装完之后,执行”adb devices“指令,发现好像权限不太对,因此需要在安卓手机上设置权限

      a85c1c74cfe2404b9c7c97b2ed3158d7.png

      报错的本质原因是香橙派系统还不支持USB设备的热拔插和UDEV的机制。

      什么是UDEV

      udev是一个设备管理工具,udev以守护进程的形式运行,通过侦听内核发出来的uevent来管理/dev目录下的设备文件。

      udev在用户空间运行,而不在内核空间 运行。它能够根据系统中的硬 件设备的状态动态更新设备文件,包括设备文件的创建,删除等。设备文件通常放在/dev目录下。使用udev后,在/dev目录下就只包含系统中真正存在的设备。

      eeb5eeb604e44a929142c51584f00604.png

      解决办法:在 /etc/udev/rules.d 文件夹下创建规则文件:

      cd /etc/udev/rules.d/
      sudo vim 51-android.rules
      

      然后在文件中添加内容:

      cd /etc/udev/rules.d/
      sudo vim 51-android.rules
      

      9473527b5d9c434cb0a9242fdbdca65c.png

      97de761f071b4aa09f197d9c614750d1.png

      然后重新拔插手机,再次执行”adb devices“指令

      8dfa7627b23743eeb1faeb2665319120.png

      没有任何报错了,此时”adb shell“指令会显示连接成功,此时如果“ls”一下,可以看到很多文件没有权限,因为没有root,不过不重要,到这里手机端的配置就好了

      c48d24dc29364423939744a7bd8a2812.jpeg

      现在可以成功的连入手机内部的系统,关键就在于对于滑动或点击屏幕的指令模拟了,

      • adb shell input swipe <起始x坐标> <起始y坐标> <结束x坐标> <结束y坐标> <滑动持续时间ms>
      • adb shell input keyevent <按键事件的常量>
      adb shell input swipe 540 1300 540 500 500 //下滑
      adb shell input swipe 540 500 540 1300 500 //上滑
      adb shell "seq 3 | while read i;do input tap 350 1050 & input tap 350 1050 &sleep 0.01;done;" //点赞
      adb shell input keyevent 26 //锁屏
      

      最终实现效果

      现在有了基本的代码模型,和控制手机的具体指令,接下来的工作就是在数据处理的部分,执行adb指令了,显然,使用system函数就可以:

      int main ()
      {
        int fd ;
       
        int ret;
        pthread_t read_thread;
        pthread_t write_thread;
       
        if ((fd = myserialOpen ("/dev/ttyS5", 115200)) < 0) //打开驱动文件,配置波特率
        {
          fprintf (stderr, "Unable to open serial device: %s\n", strerror (errno)) ;
          return 1 ;
        }
       
        /*  if (wiringPiSetup () == -1)
          {
          fprintf (stdout, "Unable to start wiringPi: %s\n", strerror (errno)) ;
          return 1 ;
          }*/
       
        ret = pthread_create(&read_thread,NULL,read_serial,(void *)&fd);
        if(ret != 0){
          printf("read_serial create error\n");
          return 1;
        }
        ret = pthread_create(&write_thread,NULL,write_serial,(void *)&fd);
        if(ret != 0){
          printf("write_serial create error\n");
          return 1;
        }
       
        pthread_join(read_thread,NULL);
        pthread_join(write_thread,NULL);
       
        return 0 ;
      }
      

      和项目需求一致,可见,我的手并没有碰到手机屏幕,只是说出了对应的指令,手机就会有所反应:

      9594899291f647b5bf93ade007916db5.gif

      并且在香橙派终端也可以看到指令历史:

      71b6d91d02354456874df96b7fd67f0f.png

      发布在 H/F/TV Series
      livpo
      livpo
    • PhoenixSuit烧录工具四种烧录模式说明

      如图所示,分四种烧录模式分别是:单或多分区下载、保留数据升级、分区擦除升级和全盘擦除升级。

      下面对四种烧录模式进行说明:

      • 单或多分区下载:用户加载固件之后,会从固件信息读取到MBR表的分区信息,然后将每个分区名字显示在UI上提供可选择单或多个分区烧录,若全部不选择,则默认下载物理分区的boot0和boot1。
      • 保留数据升级:此模式仅擦除sys_partition配置的分区,但不包含udisk或user_data分区的数据。
      • 分区擦除升级:此模式仅擦除sys_partition配置的分区,与“保留数据升级”相反,擦除包含udisk或user_data。
      • 全盘擦除升级:顾名思义,就是将flash进行初始化,所有的数据将会被擦除

      a0d87316ca1242fb85d34709b0548588.jfif

      发布在 其它全志芯片讨论区
      livpo
      livpo
    • 实测14us,Linux-RT实时性能及开发案例分享—基于全志T507-H国产平台

      本文带来的是基于全志T507-H(硬件平台:创龙科技TLT507-EVM评估板),Linux-RT内核的硬件GPIO输入和输出实时性测试及应用开发案例的分享。本次演示的开发环境如下:

      • Windows开发环境:Windows 7 64bit、Windows 10 64bit
      • Linux开发环境:Ubuntu18.04.4 64bit
      • 虚拟机:VMware16.2.5
      • U-Boot:U-Boot 2018
      • Kernel:Linux-RT-4.9.170
      • SDK:LinuxSDK-V2.0
      • GPIO: LED(PI13)、KEY3(PH4)
      • 分享案例:rt_gpio_ctrl、rt_input案例
      • 测试工具:示波器

      测试数据汇总

      基于全志T507-H(硬件平台:创龙科技TLT507-EVM评估板),按照创龙科技提供的案例用户手册进行操作,得出如下测试结果。

      备注:测试数据与实际测试环境有关,仅供参考。

      测试结果如下表所示:

      1374E095-7650-480f-8B90-44007DAF51E7.png

      (1)GPIO输入延时:通过使用示波器测量按键事件触发LED电平翻转的实际耗时结合系统延时与GPIO输出延时得出数据;

      (2)系统延迟:根据Linux-RT性能测试平均值得出数据;

      (3)GPIO输出延时:通过使用示波器测量LED电平翻转的实际耗时得出数据。

      40615E6F-6167-4516-A271-ABDAF39EC9F5.png F6C621CF-C958-4e15-94B5-277A166589A3.png

      根据不隔离CPU核心、隔离CPU核心三种状态的测试结果可知:当程序指定至隔离的CPU3核心上运行时,Linux系统延迟最低,可有效提高系统实时性。故推荐对实时性要求较高的程序(功能)指定至T507-H隔离的CPU核心运行。

      Linux-RT实时性测试

      本次测试是使用Cyclictest延迟检测工具测试Linux系统实时性。Cyclictest是rt-tests测试套件下的测试工具,也是rt-tests下使用最广泛的测试工具,一般主要用来测试内核的延迟,从而判断内核的实时性。Cyclictest主要通过反复测量并精确统计线程的实际唤醒时间,以提供有关系统的延迟信息。它可测量由硬件、固件和操作系统引起的实时系统的延迟。

      使用Cyclictest测试系统实时性

      基于全志T507-H(硬件平台:创龙科技TLT507-EVM评估板),按照创龙科技提供的案例用户手册进行操作,使用Cyclictest程序测试系统实时性,得出如下测试结果。

      7FB735F4-BA86-4fc4-BA41-361C193B961A.png B7ABDFF9-A291-45b0-98CF-8E41ADB06EEE.png

      对比测试数据,可看到基于Linux-RT-4.9.170内核的系统的延时更加稳定,最大延时更低,系统实时性更佳。

      T507-H核心板典型应用场景

      781D9442-B1CF-4ef0-9974-0721DF80D2D2.png

      Linux-RT应用案例的分享

      rt_gpio_ctrl案例

      通过创建一个基本的实时线程,在线程内触发LED的电平翻转,同时程序统计实时线程的调度延时,并通过示波器测出LED电平两次翻转的时间间隔。由于程序默认以最高优先级运行,为避免CPU资源被程序完全占用,导致系统被挂起,因此在程序中增加100us的延时。程序原理大致如下:

      (1)在Linux-RT内核上创建、使用实时线程。

      (2)实时线程中,计算出触发LED电平翻转的系统调度延时。

      将可执行文件拷贝至评估板文件系统,并执行如下命令运行测试程序,再按"Ctrl + C"退出测试,串口终端将打印程序统计的延时数据,如下图所示。

      Target# ./rt_gpio_ctrl 100
      

      DC03B7F9-7FE8-4378-B49F-08285035AAB9.png

      同时使用示波器捕捉LED两次电平翻转之间的间隔就对应上线程调度的延迟。算出电平两次翻转的时间间隔为∆x = 114us,如下图所示。由于程序中默认增加了100us的时间延时。因此,实际延时应为:114us-100us = 14us,与程序统计打印的Latency results平均值相近。

      9F75550E-F4B6-4002-8806-A8E3118E4746.png

      rt_input案例

      通过创建一个基本的实时线程,在线程内打开input设备,并对按键事件进行监听,然后触发LED的电平翻转,再通过示波器测量按键触发到LED电平翻转期间的实际耗时。程序原理大致如下:

      (1)在Linux-RT内核上创建、使用实时线程。

      (2)实时线程中对打开的input设备节点进行按键事件监听,通过判断监听得到的按键事件来触发LED的电平翻转。

      将可执行文件拷贝至评估板文件系统,并执行如下命令运行测试程序,程序运行后按下KEY3用户按键点亮LED,松开按键后LED熄灭,再按"Ctrl + C"退出测试程序。

      Target#./rt_input /dev/input/event8
      

      6BEF7392-F543-4c69-ACCD-574A384CDBC1.png

      分别使用示波器探头1测量按键KEY3引脚1,使用示波器探头2测量LED。

      从按键下降沿触发的开始(下图黄线)到LED上升沿触发的完成(下图蓝线)的时间间隔,即为系统实时捕获按键输入时间并响应触发LED电平翻转的时间∆x,从图中可看到∆x = 76us。

      59F2A6BA-20C6-4acb-91AD-0B4E8F138FAA.png

      发布在 创龙科技专区
      livpo
      livpo
    • 回复: OrangePi 4A也发布了,T527 SBC 小爆发啊

      我来帮你上图

      bf1c7c61-f168-4c58-9475-a4cf169eeb9c-image.png

      1d5822a8-e4ca-411c-9d46-7737983aa617-image.png

      d96e6d5a-1ace-4f39-99f5-c94a05df0cdb-image.png

      0e798492-01ea-405f-b6a7-2bac632eec9e-image.png

      发布在 T Series
      livpo
      livpo
    • 回复: YuzukiChameleon,xr829扫卡失败[XRADIO_ERR] mac address not found in efuse.

      看看这个帖子有没有帮助:
      【FAQ】全志D1芯片 XR829扫卡失败问题排查
      https://bbs.aw-ol.com/topic/757/share/1

      发布在 H/F/TV Series
      livpo
      livpo
    • T113在内核中采用硬解jpeg方式实现开机动画

      参考几位大神的logo替换方法以及相关问题:

      1. https://bbs.aw-ol.com/topic/2217/
      2. https://bbs.aw-ol.com/topic/2546/
      3. .......

      主要实现思路:在kernel中,将jpg图片通过VE解码,连续显示形成动画。先将视频按帧截取成jpg图片,打包成特定格式的二进制文件。把资源包放入到一个指定分区中,在uboot阶段加载资源包,并告知kernel将资源包的内存区域保留出来。

      资源包制作

      int test_pic(int argc, char **argv)
      {
          FILE *fp_in;
          FILE *fp_out;
          int rc;
          char bmpfile[1024] = {0};
          char path_out[1024] = {0};
          int i = 0;
          char buffer[4096];
          unsigned int length = 0;
          int pic_num = 100;
          if (argc > 3)
          {
              pic_num = atoi(argv[3]);
          }
          printf("pic_num:%d\n", pic_num);
          sprintf(path_out, "%s", argv[2]);
          printf("output:%s\n", path_out);
      
          fp_out = fopen(path_out, "wb+");
          if (fp_out == NULL)
          {
              printf("Open file %s error\n", bmpfile);
              return (-1);
          }
          fwrite(&pic_num, 4, 1, fp_out);
          for (i = 0; i < pic_num; i++)
          {
              sprintf(bmpfile, "%s/1 (%d).jpg", argv[1], i + 1);
              fp_in = fopen(bmpfile, "rb");
              if (fp_in == NULL)
              {
                  printf("Open file %s error\n", bmpfile);
                  return (-1);
              }
              length = get_file_size(bmpfile);
              if (length < 1)
              {
                  printf(" file %s error\n", bmpfile);
                  return (-1);
              }
              fwrite(&length, 4, 1, fp_out);
      
              while (!feof(fp_in))
              {
                  rc = fread(buffer, 1, 1024, fp_in);
                  if (rc < 1)
                      printf("file %s error\n", bmpfile);
                  fwrite(buffer, 1, rc, fp_out);
              }
              fclose(fp_in);
          }
          fclose(fp_out);
          return 0;
      }
      

      将所有图片打包成一份二进制文件,并命名为animation.fex,文件内容格式如下:

      图片总数量 第一张图片大小(int) 第一张图片内容 第二张图片大小(int) 第二张图片内容 依此类推
      int int char 数组 int char 数组 。。。

      使用方法:

      ./bin/demo [图片路径] [资源包路径] [图片数量]
      

      如:

      ./bin/demo bin/Capture100/ bin/animation.fex 90
      

      log输出:

      pic_num:90
      output:bin/animation.fex
      

      资源包的制作和内核中读取的格式相对应,如有需要可自行拓展。

      资源包存放

      新建一个分区

      diff --git a/configs/demo2.0/longan/sys_partition.fex b/configs/demo2.0/longan/sys_partition.fex
      index c67aca3..1e12607 100755
      --- a/configs/demo2.0/longan/sys_partition.fex
      +++ b/configs/demo2.0/longan/sys_partition.fex
      @@ -68,9 +68,9 @@ size = 16384
      
       ;------------------------------>mmcblk0p6/nand0p6
       [partition]
      -    name         = recovery
      -    size         = 231072
      -    ;downloadfile = "recovery.fex"
      +    name         = animation
      +    size         = 102400
      +    downloadfile = "animation.fex"
           user_type    = 0x8000
      
       ;------------------------------>mmcblk0p7/nand0p7
      diff --git a/pack b/pack
      index d57362b..77e7b1f 100755
      --- a/pack
      +++ b/pack
      @@ -164,6 +164,7 @@ ${LICHEE_COMMON_CONFIG_DIR}/tools/cardscript.fex
       ${LICHEE_COMMON_CONFIG_DIR}/tools/cardscript_secure.fex
       ${LICHEE_CHIP_CONFIG_DIR}/tools/cardscript.fex
       ${LICHEE_CHIP_CONFIG_DIR}/tools/cardscript_secure.fex
      +${LICHEE_CHIP_CONFIG_DIR}/tools/animation.fex
       ${LICHEE_COMMON_CONFIG_DIR}/tools/cardtool.fex
       ${LICHEE_CHIP_CONFIG_DIR}/tools/cardtool.fex
       ${LICHEE_COMMON_CONFIG_DIR}/tools/usbtool.fex
      

      uboot修改

      合入补丁

      通过

      CONFIG_ANIMATION_MEM_RESERVE
      

      控制功能的开启关闭。

      从emmc读取整个分区,分区越大耗时越久,可适当减少分区大小。

      kernel修改
      合入补丁

      通过

      CONFIG_ANIMATION_MEM_RESERVE
      

      宏开启

      注意编解码VE和显示DE驱动代码的初始化的先后顺序

      T113_animation.7z

      发布在 其它全志芯片讨论区
      livpo
      livpo
    • 交叉编译NumCpp到ARM上的方法(以全志V853为例)

      前段时间,在电脑上安装了Ubuntu/Windows系统双系统,现在拿Ubuntu系统试试水。

      NumCpp是类似Numpy的C++版本,可以方便的进行一些矩阵的操作,现在我们来把它交叉编译到ARM上去。

      1. 获取源码

      要交叉编译NumCpp,首先要获取依赖库boost源代码以及NumCpp的源代码。

      在这里我使用的是boost1.68,可以从下面这个网址下载。
      https://www.boost.org/

      Boost C++ Libraries
      https://www.boost.org/
      

      NumCpp可以从GitHub上clone下来
      https://github.com/dpilger26/NumCpp

      GitHub - dpilger26/NumCpp: C++ implementation of the Python Numpy library
      C++ implementation of the Python Numpy library. Contribute to dpilger26/NumCpp development by creating an account on GitHub.
      https://github.com/dpilger26/NumCpp
      

      2. 交叉编译boost

      将下载好的压缩包解压。

      cd boost_1_68_0
      ./bootstrap.sh --with-libraries=all --with-toolset=gcc
      

      其中,--with-libraries可以选择自己想要安装的库。

      执行完这一步之后,打开新生成的project-config.jam,修改下面这几行中的gcc部分。一定要注意空格!(每一个空格缺一不可),其中的arm-openwrt-linux-gcc的路径可以换成你的交叉编译器的路径

      if ! gcc in [ feature.values <toolset> ]
      {
          using gcc : arm : /home/xinzhe/toolchains/rootfsbuilt/arm/toolchain-sunxi-musl-gcc-830/toolchain/bin/arm-openwrt-linux-gcc ; 
      }
      

      之后执行

      ./b2
      

      检查输出的log中的编译器是否已经是交叉编译器,然后再执行

      ./b2 install --prefix=/home/xinzhe/Documents/boost_arm
      

      其中--prefix后面是你想要的boost库安装路径。

      安装完成后,检查安装的boost库是否为ARM版本。

      3.交叉编译NumCpp

      Clone完成NumCpp之后,进入NumCpp文件夹。

      由于我之前编译过x86-64版本的NumCpp,因此在编译ARM版本的时候,需要修改CMakeLists.txt,让cmake找到ARM版本的boost库。

      在CMakeLists.txt中寻找Boost有关代码的上方加入

      set(CMAKE_FIND_ROOT_PATH /home/xinzhe/Documents/boost_arm)
      
      set(CMAKE_FIND_ROOT_PATH /home/xinzhe/Documents/boost_arm)
       
       
       
      if(NUMCPP_NO_USE_BOOST)
          target_compile_definitions(${ALL_INTERFACE_TARGET} INTERFACE -DNUMCPP_NO_USE_BOOST)
      else()
          find_package(Boost 1.68.0 REQUIRED 
              COMPONENTS 
              date_time
          )
          target_link_libraries(${ALL_INTERFACE_TARGET} INTERFACE 
              Boost::boost
              $<$<OR:$<CXX_COMPILER_ID:Clang>,$<CXX_COMPILER_ID:GNU>>:Boost::date_time>
          )
      endif()
      

      同时,在NumCpp文件夹里,创建一个Compile.cmake文件,来指定交叉编译器。内容如下:

      set(CMAKE_SYSTEM_NAME Linux)
      set(TOOLCHAIN_PATH /home/xinzhe/toolchains/rootfsbuilt/arm/toolchain-sunxi-musl-gcc-830/toolchain/arm-openwrt-linux-muslgnueabi)
      set(CMAKE_C_COMPILER /home/xinzhe/toolchains/rootfsbuilt/arm/toolchain-sunxi-musl-gcc-830/toolchain/bin/arm-openwrt-linux-gcc)
      set(CMAKE_CXX_COMPILER /home/xinzhe/toolchains/rootfsbuilt/arm/toolchain-sunxi-musl-gcc-830/toolchain/bin/arm-openwrt-linux-g++)
      

      其中的TOOLCHAIN_PATH、CMAKE_C_COMPILER、CMAKE_CXX_COMPILER替换成自己的交叉编译工具。

      然后在NumCpp文件夹下执行下面的操作

      mkdir build
      cd build
      cmake .. -DCMAKE_TOOLCHAIN_FILE=../Compile.cmake
      

      然后检查输出的log中,C编译工具是否为交叉编译工具,以及找到的boost库是否为我们刚刚创建的ARM版本的。

      检查完毕后,修改/NumCpp/build目录下的cmake_install.cmake,修改其中的CMAKE_INSTALL_PREFIX为自己想要安装的目录。

      # Install script for directory: /home/xinzhe/Documents/NumCpp
       
      # Set the install prefix
      if(NOT DEFINED CMAKE_INSTALL_PREFIX)
        set(CMAKE_INSTALL_PREFIX "/home/xinzhe/Documents/boost_arm")
      endif()
      string(REGEX REPLACE "/$" "" CMAKE_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}")
      

      然后执行:

      cmake --build . --target install
      

      我们就在boost_arm下得到了ARM版本的boost和ARM版本的NumCpp的编译结果文件

      4. 编译自己的代码

      创建NumCpp_arm文件夹,放入自己的main.cpp,并创建Compile.cmake(同上),CMakeLists.txt。

      其中CMakeLists.txt代码如下:

      cmake_minimum_required(VERSION 3.20)
       
      project("HelloWorld" CXX)
       
      add_executable(${PROJECT_NAME} main.cpp)
      set(CMAKE_FIND_ROOT_PATH /home/xinzhe/Documents/boost_arm)
      set(CMAKE_CXX_STANDARD 17)
      set(CMAKE_CXX_FLAGS "-g  -std=c++17 -lstdc++fs")
      find_package(NumCpp 2.10.1 REQUIRED)
      target_link_libraries(${PROJECT_NAME}
          NumCpp::NumCpp
          stdc++fs
      )
      

      其中的CMAKE_CXX_FLAGS的设置,以及stdc++fs添加到target_link_libraries里,是为了防止一下的报错。

      xinzhe@xinzhe-G3-3590:~/Documents/NumCpp/examples/NumCpp_arm/build$ make
      Consolidate compiler generated dependencies of target HelloWorld
      [ 50%] Linking CXX executable HelloWorld
      CMakeFiles/HelloWorld.dir/main.cpp.o: In function `std::filesystem::exists(std::filesystem::__cxx11::path const&)':
      main.cpp:(.text._ZNSt10filesystem6existsERKNS_7__cxx114pathE[_ZNSt10filesystem6existsERKNS_7__cxx114pathE]+0x1c): undefined reference to `std::filesystem::status(std::filesystem::__cxx11::path const&)'
      CMakeFiles/HelloWorld.dir/main.cpp.o: In function `std::filesystem::__cxx11::path::path<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::filesystem::__cxx11::path>(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::filesystem::__cxx11::path::format)':
      main.cpp:(.text._ZNSt10filesystem7__cxx114pathC2INSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEES1_EERKT_NS1_6formatE[_ZNSt10filesystem7__cxx114pathC5INSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEES1_EERKT_NS1_6formatE]+0x60): undefined reference to `std::filesystem::__cxx11::path::_M_split_cmpts()'
      collect2: error: ld returned 1 exit status
      make[2]: *** [CMakeFiles/HelloWorld.dir/build.make:98: HelloWorld] Error 1
      make[1]: *** [CMakeFiles/Makefile2:83: CMakeFiles/HelloWorld.dir/all] Error 2
      make: *** [Makefile:91: all] Error 2
      

      然后,我们执行一下操作

      mkdir build
      cd build
      cmake .. -DCMAKE_TOOLCHAIN_FILE=../Compile.cmake
      

      检查输出的log,交叉编译工具对不对,boost与NumCpp是否为ARM版本。

      最后执行

      make
      

      下面的是我的编译结果,可以看到编译成功

      xinzhe@xinzhe-G3-3590:~/Documents/NumCpp/examples/NumCpp_arm/build$ make
      [ 50%] Building CXX object CMakeFiles/HelloWorld.dir/main.cpp.o
      [100%] Linking CXX executable HelloWorld
      [100%] Built target HelloWorld
      

      检查一下生成的可执行文件是够为ARM版本。

      xinzhe@xinzhe-G3-3590:~/Documents/NumCpp/examples/NumCpp_arm/build$ file HelloWorld
      HelloWorld: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), dynamically linked, interpreter /lib/ld-musl-armhf.so.1, with debug_info, not stripped
      

      5.移植到V853上

      将HelloWorld文件和boost_arm整个文件夹push到板子上

      xinzhe@xinzhe-G3-3590:~/Documents/NumCpp/examples/NumCpp_arm/build$ adb push HelloWorld /root
      HelloWorld: 1 file pushed. 3.6 MB/s (2607428 bytes in 0.684s)
      

      然后到HelloWorld所在目录下执行

      root@TinaLinux:~# export LD_LIBRARY_PATH=/root/boost_arm/lib:$LD_LIBRARY_PATH
      

      可以看到我的HelloWorld正常执行

      root@TinaLinux:~# ./HelloWorld
      [[0.452916, 0.119087, -0.459488, -0.733428, 0.374606, 0.244954, -0.237323, -0.109258, 0.907443, -0.413274, -0.157849, 0.158918, 0.0474297, -0.519952, 1.38254, -0.174767, -0.911914, -0.812675, -0.506005, -0.222257, -0.192745, -1.04207, 0.424627, 0.217617, 0.277484, -0.0261248, -0.0865481, -0.371074, -0.275607, 0.937942,
      

      到这里,交叉编译就完成啦,希望各位小伙伴也能顺利进行!

      原文链接:https://blog.csdn.net/where_are_u/article/details/129863901

      发布在 V Series
      livpo
      livpo
    • 回复: t113每次重启网卡的mac地址都是随机的

      @abcdef 在 kernel menuconfig下面
      QQ图片20220916140833.jpg QQ图片20220916140837.jpg

      发布在 MR Series
      livpo
      livpo
    • 【R128】外设模块配置——USB外设功能配置

      基于R128-S2设计的全套开发板已上线淘宝百问网韦东山老师个人店进行售卖,包含黑色的DshanMCU-R128s2-R16N16模组和全套的DshanMCU-R128s2-DEVKIT。

      • DshanMCU-R128s2-R16N16模组:39.9元
      • DshanMCU-R128s2-DEVKIT开发板:59.9元

      R128开发板购买链接:https://item.taobao.com/item.htm?spm=a21n57.1.0.0.46b0523cMfarLo&id=736154682975&ns=1&abbucket=5#detail

      “主图_01”为智能对象-1.jpg

      USB 外设功能配置

      USB 功能简介

      USB 功能模块包括了USB Host,USB Device 和OTG 功能。

      USB Host 目前已经支持上的功能有:Mass Storage,UVC。

      USB Device 目前已经支持上的功能有:ADB,UAC。

      OTG 主要用作Host 与Device 的切换,如当板子通过 USB 线连接到 USB 主机 (PC) 上时,
      此时 OTG 是加载成 USB Device;若当前板子是通过 OTG 线连接一个USB 设备,此时 OTG 则加载
      成 USB Host。

      USB 外设特性

      • Complies with USB 2.0 Specification
      • Supports High-Speed (HS, 480-Mbps), Full-Speed (FS, 12-Mbps), and Low-Speed (LS, 1.5-Mbps) in Host mode
      • Supports High-Speed (HS, 480 Mbps), Full-Speed (FS, 12 Mbps) in Device mode
      • Supports the UTMI+ Level 3 interface. The 8-bit bidirectional data buses are used
      • Supports bi-directional endpoint0 for Control transfer
      • Supports up to 8 User-Configurable Endpoints for Bulk, Isochronous and Interrupt bi-directional transfers (Endpoint1, Endpoint2, Endpoint3, Endpoint4)
      • Supports up to (4KB+64Bytes) FIFO for EPs (Including EP0)
      • Supports High-Bandwidth Isochronous & Interrupt transfers
      • Automated splitting/combining of packets for Bulk transfers
      • Supports point-to-point and point-to-multipoint transfer in both Host and Peripheral mode
      • Includes automatic ping capabilities
      • Soft connect/disconnect function
      • Performs all transaction scheduling in hardware
      • Power Optimization and Power Management capabilities
      • Includes interface to an external Normal DMA controller for every Eps

      USB 配置介绍

      sys_config.fex 配置说明

      sys_config.fex 中主要是对 OTG 功能进行配置,各个配置的含义可如下所示:

      Key Value
      [usbc0] 控制器0的配置。
      usb_used: USB使能标志。置1,表示系统中USB模块可用,置0,则表示系统USB禁用。
      usb_port_type USB端口的使用情况。 0: device only;1: host only;2: OTG;usb_detect_type: USB
      usb_detect_mode USB端口的检查方式。0: 线程轮询;1: id中断触发
      usb_id_gpio USB ID pin脚配置。具体请参考gpio配置说明。
      usb_det_vbus_gpio USB DET_VBUS pin脚配置。具体请参考gpio配置说明。
      usb_drv_vbus_gpio USB DRY_VBUS pin脚配置。具体请参考gpio配置说明。
      usb_drv_vbus_type vbus设置方式。0: 无; 1: gpio; 2: axp。
      usb_det_vbus_gpio "axp_ctrl",表示axp 提供。
      usbh_driver_level usb驱动能力等级
      usbh_irq_flag usb中断标志

      示例:

      ;--------------------------------
      ;---       USB0控制标志
      ;--------------------------------
      [usbc0]
      usb_used                = 1
      usb_port_type           = 2
      usb_detect_type         = 1
      usb_detect_mode         = 0
      usb_id_gpio             = port:PB04<0><0><default><default>
      usb_det_vbus_gpio       = port:PA24<0><0><default><default>
      usb_drv_vbus_gpio       = port:PA29<1><0><default><default>
      usb_drv_vbus_type       = 1
      usbh_driver_level       = 5
      usbh_irq_flag           = 0
      

      rtos menuconfig 配置说明

      • 使能USB 驱动
      ‑> Drivers Options
          ‑> soc related device drivers
              ‑> USB Drivers
                  [*] USB
      
      • 使能OTG
      ‑> Drivers Options
          ‑> soc related device drivers
              ‑> USB Drivers
                  [*] USB_MANAGER
      
      • 使能USB Host

      使能完USB Host 之后,还需要选择:OHCI 与EHCI(一共有0 和1 两组,对于R128 来说,只需要使能USB0)。另外,还要选择功能驱动(Mass Storage,UVC),不然只是使能USB Host 则无法正常运行USB Host 的功能。

      USB Host 控制器驱动配置如下:

      USB Host
      ‑> Drivers Options
          ‑> soc related device drivers
              ‑> USB Drivers
                  [*] USB_HOST
      
      OHCI
      ‑> Drivers Options
          ‑> soc related device drivers
              ‑> USB Drivers
                  ‑> USB HOST
                      [*] USB_OHCI_0
      
      EHCI
      ‑> Drivers Options
          ‑> soc related device drivers
              ‑> USB Drivers
                  ‑> USB HOST
                      [*] USB_EHCI_0
      
      • 使能USB Device

      USB Device 除了UDC 的使能之外,也需要选择对应的功能驱动Gadget 以及功能驱动对应的上层应用。

      USB Device 控制器驱动配置如下:

      USB Device
      ‑> Drivers Options
          ‑> soc related device drivers
              ‑> USB Drivers
                  [*] USB_DEVICE
      
      USB Device使能dma通信
      ‑> Drivers Options
          ‑> soc related device drivers
              ‑> USB Drivers
                  ‑> USB DEVICE
                      [*] UDC_USE_DMA
      

      USB 源码结构

      lichee/rtos‑hal/hal/source/usb
      ├── common/
      ├── core/
      ├── gadget/
      │ ├── function/
      ├── hid/
      │ ├── Class/
      │ ├── Client/
      │ │ ├── KeyBoard/
      │ │ └── Mouse/
      │ ├── Include/
      ├── host/
      ├── include/
      ├── manager/
      ├── platform/
      ├── storage/
      │ ├── Class/
      │ ├── Disk/
      │ ├── include/
      │ └── Misc/
      ├── udc/
      └── uvc/
      ├── Class/
      ├── drv_webcam/
      │ ├── dev_cfg/
      │ └── webcam_core/
      ├── Include/
      ├── Misc/
      └── Webcam/
      
      • common: USB 驱动初始化公用文件。
      • core: USB Host 驱动框架层文件。
      • gadget: USB Deivce 功能驱动gadget 驱动总入口文件,function 则是各个功能驱动的驱动文件。
      • hid: USB Host HID 协议及驱动文件。
      • host: USB Host 硬件控制器驱动。
      • include: USB 公用头文件。
      • manager: USB OTG 驱动。
      • platform: 不同平台的配置文件。
      • storage: USB Host Mass Storage 协议及驱动文件。
      • udc: USB Deivce 硬件控制器驱动。
      • uvc: USB Host UVC 协议及驱动文件。

      详细说明请见:HAL USB

      USB 常用功能说明

      配置OTG 功能

      OTG 功能下,需要根据USB ID 脚去进行Device/Host 模式的切换;如果需要支持NULL 模式(既不加载Device 也不加载Host 驱动), 那么还需要VBUS 状态检测引脚。

      涉及到的主要改动点:

      在sys_config.fex,修改如下配置:
      usb_port_type配置为2,即OTG模式。
      usb_id_gpio配置对应的USB ID引脚。
      usb_det_vbus_gpio, 需要根据实际情况进行配置:
      
      1.如果需要检测VBUS状态,则按下面情况进行配置:
      配置成对应的gpio即可。
      
      2.如果不需要检测VBUS状态(缺少NULL模式)
      那么直接填写USB ID的gpio配置(也就是VBUS与ID状态一致)。
      

      USB OTG 驱动,会根据ID 和VBUS 的状态,自动切换成对应的模式。ID 和VBUS 的对应关系如下表:

      ID VBUS 模式
      0 0 Host
      1 0 Null
      0 1 Host
      1 1 Device
      • ID 脚一般情况下为高电平,只有接入OTG 线时会拉低;
      • VBUS 为1 表示micro USB 口有接入外部电源;
      • 一般不会出现ID 为0,VBUS 为1 的情况。这表示接入OTG 线,但是还检测到VBUS;
      • 如果没有VBUS 检测,ID 只有0 和1 的两种情况, 也就是说要么加载device 驱动,要么加载host 驱动; 这会带来一些影响:usb 相关时钟一直被打开,导致有一定功耗,以及硬件射频。

      USB Gadget 功能配置

      USB Gadget 支持众多功能,它们的配置方法比较类似,只需要在mrtos_menuconfig 中选上对应的Gadget 功能驱动即可在系统初始化时自动加载。与Linux 不一样的是,RTOS 的gadget 功能配置全部hardcode 在功能驱动里,无需像Linux 一样需要在应用层手动进行gadget 配置。

      另外,目前RTOS 的USB 驱动还不支持composite gadget,因此只能支持加载单一的gadget 功能驱动,无法同时多个功能。

      ADB 功能

      adb 的全称为Android Debug Bridge,就是起到调试桥的作用。通过ADB,可以直接在PC 上通过命令行控制小机端的控制台;也可以通过ADB 进行文件传输。

      menuconfig 驱动相关配置:

      ‑> Drivers Options
          ‑> soc related device drivers
              ‑> USB Drivers
                  ‑> USB DEVICE
                      [*] DRIVERS_USB_GADGET_ADB
      

      menuconfig ADBD 应用相关配置:

      ‑> System components
          ‑> aw components
              ‑> USB Components Support
                  ‑> USB Gadget Support
                      [*] adbd service
      

      在RTOS 的USB 框架中,一旦加载了adb gadget,就会自动启用adbd 服务,直接连上PC 就可以使用了。

      adb 正常启动的相关log:

      [usb0] insmod device driver!
      adbd version:AW‑V1.1.6, compiled on: Apr 11 2023 10:33:24
      adbd service init successful
      

      PC 运行效果图如下图所示:

      image-20230725112925370.png

      UAC 功能

      UAC 全称为USB Audio Class,USB 音频类。

      通过UAC,可以实现实时获取音频设备的音频数据,并且通过UAC 实现操控设备音量,采样率,等参数。UAC 实现对外接音频操作,从用户功能来说,主要包括USB 麦克风、USB 声卡和其它音频设备的功能控制和接口标准。

      menuconfig 驱动相关配置:

      ‑> Drivers Options
          ‑> soc related device drivers
              ‑> USB Drivers
                  ‑> USB DEVICE
                      [*] DRIVERS_USB_GADGET_UAC
      

      menuconfig UACD 应用相关配置:

      ‑> System components
          ‑> aw components
              ‑> USB Components Support
                  ‑> USB Gadget Support
                      [*] uacd service
      
      ‑> System components
          ‑> aw components
              ‑> USB Components Support
                  ‑> USB Gadget Support
                      ‑> uacd audio function
                          [*] AudioSystem local audio
      

      uacd 正常启动的相关log:

      [usb0] insmod device driver!
      uacd version:AW‑V0.5, compiled on: Apr 27 2023 10:44:02
      [UACD‑INFO][u_audio_init] line:167 stream=1, rate=48000, ch=2, bits=16, audio_buf_size=192
      [UACD‑INFO][u_audio_init] line:167 stream=0, rate=16000, ch=2, bits=16, audio_buf_size=64
      [UACD‑INFO][u_audio_stop_capture] line:320
      [UACD‑INFO][u_audio_stop_playback] line:457
      [UACD‑INFO][u_audio_stop_capture] line:320
      [UACD‑INFO][u_audio_stop_playback] line:457
      

      启动成功之后,能够在PC 端看到新增了一个音频输入和输出的设备,如下图:

      image-20230725113103304.png

      USB Host 功能配置

      接入OTG 线后,成功切换成 USB Host 的log 可参考如下:

      [ehci‑usb0] insmod host driver!
      calibration finish, val:0x19, usbc_no:0
      ehci insmod status = 1
      [usbh core]: add gen_dev SW USB2.0 'Enhanced' Host Controller (EHCI) Driver
      [D(rv.)] devops: register dev(sunxi_timer) ok
      USB 0.0 started, EHCI 1.00
      [usbh core]: adding sub dev (config #1, interface 0)
      usb match id suceessfull
      [hub]: usb hub probe
      [hub]: 1 port detected
      [usbh hub]: local power source is good
      [E(rv.)] pm device sunxi_ehci0(00000000082AE1D0) has already registered
      [ohci‑usb0] insmod host driver!
      calibration finish, val:0x19, usbc_no:0
      [usbh core]: add gen_dev SW USB2.0 'Open' Host Controller (OHCI) Driver
      [usbh core]: adding sub dev (config #1, interface 0)
      usb match id suceessfull
      [hub]: usb hub probe
      [hub]: 1 port detected
      [usbh hub]: local power source is good
      [D(rv.)] devops: register dev(sunxi_ohci0) ok
      

      U 盘功能

      选上以下配置:

      Mass Storage
      ‑> Drivers Options
          ‑> soc related device drivers
              ‑> USB Drivers
                  ‑> USB HOST
                      [*] Mass Storage support
                      [*] USB CD support	
      

      等待U 盘挂载成功,可以发现根目录下多了一个usb_msc 的文件夹,这个即是U 盘挂载的文件夹。可以通过该文件夹与U 盘进行读取/传输文件。

      • 目前R128 只支持fat32 文件系统的U 盘,其他文件系统U 盘会挂载失败。
      • 只支持挂载单分区的U 盘,如果U 盘被分解成了多个分区的话,只能挂载上第一个分区。

      接入U 盘后,系统开始识别U 盘。成功识别到U 盘的log 信息如下:

      ehci_irq: highspeed device connect
      port debounce 0...
      port debounce 0...
      port debounce 25...
      port debounce 50...
      port debounce 75...
      hub_port_init: udev address = 0
      [hub_port_init]: new high speed USB device address 0
      usb hub set new address(2)
      [usbh core]: adding sub dev (config #1, interface 0)
      usb match id suceessfull
      mscDevProbe begin
      [msc]: GetMaxLUN successful, max lun is 0
      begin mscLunAdd
      disk, send last lun msg.........
      mscLun‑>LunNo=0
      mscLun‑>mscDev‑>MaxLun=1
      BlkDev‑>last_lun=1
      Wrn: short transfer, urb_state(0), want_len(192), real_len(70)
      Wrn: short transfer, urb_state(0), want_len(192), real_len(24)
      ‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑Disk Information‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑
      WriteProtect = 0
      MediaPresent = 1
      WCE = 0
      RCD = 0
      capacity = 29532M, sector number = 60481536
      sector_size = 512
      DevNo = 0
      ClassName =
      DevName = SCSI_DISK_000
      ‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑
      hub_port = 1
      cnt = 0
      Classname = DISK
      DevName = SCSI_DISK_000
      DeviceName = SCSI_DISK_000
      Vender = TOSHIBA USB FLASH DRIVE PMAP
      Product = USB FLASH DRIVE PMAP
      Serial = PMAP
      HubPortNo = 1
      DeviceType = 2
      DiskRead: block(0, 1) is adjacence max capacity(39ae000), can't use special write
      mount usb mass storage successull!!
      ..............................................................................
      [USB Disk]: Register new device, class = [DISK], dev = [SCSI_DISK_000]
      ..............................................................................
      end mscLunAdd
      mscDevScanThread end...
      

      然后通过ls 查看usb_msc,可以看到U 盘里的文件:

      c906>ls
      dev data usb_msc
      
      c906>ls usb_msc
      System Volume Information 3.txt
      

      USB 摄像头

      选上以下配置:

      UVC
      ‑> Drivers Options
          ‑> soc related device drivers
              ‑> USB Drivers
                  ‑> USB HOST
                      [*] USB_CAMERA
      

      接上USB 摄像头后,成功识别出摄像头后会出现以下log:

      ehci_irq: highspeed device connect
      port debounce 0...
      port debounce 0...
      port debounce 25...
      port debounce 50...
      port debounce 75...
      hub_port_init: udev address = 0
      [hub_port_init]: new high speed USB device address 0
      usb hub set new address(3)
      [hub] :skipped 1 descriptor after configuration
      skipped 6 descriptors after interface
      skipped 1 descriptor after endpoint
      skipped 26 descriptors after interface
      num_ep:0
      skipped 1 descriptor after endpoint
      skipped 4 descriptors after interface
      num_ep:0
      num_ep:0
      skipped 2 descriptors after interface
      skipped 1 descriptor after endpoint
      [usbh core]: adding sub dev (config #1, interface 0)
      usb match id suceessfull
      UVCDevProbe begin
      Probing generic UVC device
      device quirks 0x0
      Found format MJPEG.
      ‑ 1920x1080 (30.0 fps)
      ‑ 1280x720 (30.0 fps)
      ‑ 640x480 (30.0 fps)
      ‑ 640x360 (30.0 fps)
      ‑ 352x288 (30.0 fps)
      ‑ 320x240 (30.0 fps)
      ‑ 320x180 (30.0 fps)
      ‑ 176x144 (30.0 fps)
      ‑ 160x120 (30.0 fps)
      ‑ 1920x1080(30.0 fps)
      Found format YUV 4:2:2 (YUYV).
      ‑ 1920x1080 (5.0 fps)
      ‑ 1280x720 (10.0 fps)
      ‑ 640x480 (30.0 fps)
      ‑ 640x360 (30.0 fps)
      ‑ 352x288 (30.0 fps)
      ‑ 320x240 (30.0 fps)
      ‑ 320x180 (30.0 fps)
      ‑ 176x144 (30.0 fps)
      ‑ 160x120 (30.0 fps)
      ‑ 1920x1080 (5.0 fps)
      Found UVC 1.00 device USB 2.0 Camera (0c45:6366)
      num_altsetting=7
      UVC device initialized.
      DRV_WEBCAM_MInit
      webcam plug in message...
      [usbh core]: adding sub dev (config #1, interface 1)
      [usbh core]: adding sub dev (config #1, interface 2)
      [usbh core]: adding sub dev (config #1, interface 3)
      

      通过log 信息能够看到,在识别出USB 摄像头后,会打印出该摄像头支持的格式以及分辨率。

      接着通过uvc 测试命令,对UVC 功能进行测试:

      usb uvc_test
      

      测试命令调用完成之后,会在/data 目录下生成5 张名为/data/source_frame_x.jpg 的图片。通过adb pull 将图片拉到PC 端,然后在PC 端打开图片即可看到USB 摄像头拍下的照片。

      USB 调试方法

      USB OTG 功能调试

      除了OTG 的自动切换功能,还可以进行手动的切换,需要使用到USB 命令。

      USB Device/Host 的手动切换方法:

      输入usb ‑h 能够看见usb 的全部命令。

      • USB Device 的相关命令
      usb udc {‑i|‑r} [<port>]
      ‑i:指的是指定需要进行切换的USB口。目前R128芯片只有USB0是支持Device模式的。
      ‑r:指的是remove,注销当前的Device模式。
      
      比如说将USB0切换成Device模式,则运行:
      usb udc ‑i 0
      
      • USB Host 的相关命令
      usb hci {‑i|‑r} [<port>]
      ‑i:指的是指定需要进行切换的USB口。
      ‑r:指的是remove,注销当前的Host模式。
      
      比如说将USB0切换成Host模式,则运行:
      usb hci ‑i 0
      

      USB 相关工具

      ADB

      ADB 功能是从Android 移植过来的,设备端会运行adbd 服务,而Host 端(一般为PC) 通过adb工具进行调试,如adb shell, adb push/pull 等。

      ADB 功能说明

      adb shell 功能

      PC端执行adb shell,可进入控制台。

      PC端执行adb shell + command, 例如adb shell ls /可以直接将结果输出到终端。

      image-20230725113602003.png

      adb push/pull 功能

      推送文件到小机端:
      adb push test.bin /data
      
      从小机端拉取文件:
      adb pull /data/test.bin .
      

      image-20230725113838662.png

      adb 网络连接

      如果需要用此功能,需要额外进行menuconfig 的配置,配置方法如下:

      ‑> System components
          ‑> aw components
              ‑> USB Components Support
                  ‑> USB Gadget Support
                      [*] adb local transport
                      (5555) adb local transport port
      

      adb local transport port 是用于配置端口号的,用于在adb 网络连接时匹配端口的,默认为5555。

      在进行网络adb 连接之前,需要保证PC 和小机端在同一个局域网中,并且可以ping 通。

      小机端运行ifconfig,查看当前小机端的IP,假设IP为192.168.1.101。

      则PC端执行adb connect 192.168.1.101建立连接,然后就可以开始执行adb shell等命令了。

      9eba885aef3d44df8e70084fdb83a7f7.png

      发布在 A Series
      livpo
      livpo
    • 在V853上进行Opencv库的编译步骤

      OpenCV(Open Source Computer Vision)是一个开放源代码的计算机视觉库,它提供了一系列函数和算法,用于处理图像和视频。通过使用OpenCV,您可以进行各种计算机视觉任务,例如图像处理、对象识别、目标追踪、人脸检测和机器学习等。它提供了底层图像处理功能,以及高级功能和模块,如特征提取、边缘检测、图像分割和物体测量等。

      当V853需要进行图像的预处理操作时,涉及到使用opencv库中的相关接口和函数。所以如果要基于853进行模型的输入预处理或者输出后处理操作时,就不可避免的要依赖到opencv库。

      先获取GitHub中的opencv源码:
      https://github.com/opencv/opencv
      https://github.com/opencv/opencv_contrib

      进入目录下,创建build目录:

      mkdir build
      cd build/
      

      运行cmake指令配置参数生成Makefile文件:

      cmake -D CMAKE_BUILD_TYPE=Release -D CMAKE_INSTALL_PREFIX=/usr/local -D OPENCV_EXTRA_MODULES_PATH=/xxxxx/opencv/opencv_contrib/modules ..
      

      当然,如果需要将库外置不编入环境的话可以修改CMAKE_INSTALL_PREFIX的值,直接改为对应路径即可。
      选择库的相关指令(指定编译/不编译某些库,优化opencv库大小):

      指定编译opencv_core库:

      -DBUILD_opencv_core=ON
      

      指定不编译opencv_hdf库:

      -DBUILD_opencv_hdf=OFF
      

      使用make指令进行编译:

      make -j4
      

      make完成并不代表结束,还需要生成opencv库使用。

      编译完成后,加上install命令,将其加载/注册到usr文件夹中,这样每次编译 就不用使用一整个opencv库/包了。

      make install
      

      当编译可执行文件需要链接opencv库时,可以编写一个cmake来编译文件:

      # 声明要求的 cmake 最低版本
      cmake_minimum_required( VERSION 2.8 )
      
      # 声明一个 cmake 工程
      project( test )
      
      #添加OPENMP库
      FIND_PACKAGE( OpenMP REQUIRED)
      if(OPENMP_FOUND)
      message("OPENMP FOUND")
      set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS}")
      set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}")
      set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${OpenMP_EXE_LINKER_FLAGS}")
      endif()
      
      #添加OPENCV库
      #指定OpenCV版本,代码如下
      ##find_package(OpenCV 3.3 REQUIRED)
      #如果不需要指定OpenCV版本,代码如下
      set(OpenCV_DIR ${CMAKE_SOURCE_DIR}/usr/lib/cmake/opencv4)
      find_package(OpenCV REQUIRED)
      
      #添加OpenCV头文件
      include_directories(./ ${OpenCV_INCLUDE_DIRS})
      
      # 添加一个可执行程序
      # 语法:add_executable( 程序名 源代码文件 )
      add_executable(main ${DIRSRCS} ${NPULIB_DIRSRCS})
      
      # 将库文件链接到可执行程序上
      target_link_libraries(main ${OpenCV_LIBS} ${VIP_LIBS} -lstdc++ -lpthread -lrt -lm -ldl)
      

      可以看到,该cmake链接了opencv库,

      set(OpenCV_DIR ${CMAKE_SOURCE_DIR}/usr/lib/cmake/opencv4)
      

      该方法通过链接opencv库中的cmake文件然后来找到对应的opencv包,保证主程序能够顺利调用。

      可以在github上下载yolov7处理代码,链接库进行验证。如若cmake生成makefile正常,make编译也正常,成功执行例程过后,opencv库成功生成。

      发布在 V Series
      livpo
      livpo
    • 回复: 新 SDK 平台下载 D1-H/D1s SDK

      @zyfl88 这个问题上面不是写得很清楚了吗😧

      发布在 MR Series
      livpo
      livpo
    • 【R128】软件配置——RTOS 软件包配置

      基于R128-S2设计的全套开发板已上线淘宝百问网韦东山老师个人店进行售卖,包含黑色的DshanMCU-R128s2-R16N16模组和全套的DshanMCU-R128s2-DEVKIT。

      • DshanMCU-R128s2-R16N16模组:39.9元
      • DshanMCU-R128s2-DEVKIT开发板:59.9元

      R128开发板购买链接:https://item.taobao.com/item.htm?spm=a21n57.1.0.0.46b0523cMfarLo&id=736154682975&ns=1&abbucket=5#detail

      “主图_01”为智能对象-1.jpg

      RTOS 软件包配置

      本文将介绍 RTOS 软件包、地址,内核配置等。

      Kconfig 简介

      有过 linux 内核开发经验的人,对 menuconfig 不会陌生。对于各类内核,只要是支持 menuconfig 配置界面,都是使用 Kconfig。

      换言之:

      • menuconfig:支持配置内核的图形化界面。
      • Kconfig:生成 menuconfig 界面的脚本语言。

      menuconfig 操作简介

      我们运行 mrtos_menuconfig 之后,会打开如下图所示的界面

      image-20230719161928649.png

      整个显示界面大致分为三部分

      上方是操作的说明和图例说明。

      image-20230719162115665.png

      • <Enter> 按下Enter键进入子菜单。
      • <Y> 按下Y键,选中这项功能。
      • <N> 按下N键,排除这项功能 。
      • <M> 按下M键,以模块的形式选择。
      • <Esc><Esc> 按两下Esc键,返回上一级菜单
      • <?> 按下?键,查看高亮功能的帮助信息
      • </> 按下/键,搜索
      • [*] 被选中的项目
      • [ ] 未被选择的项目
      • <M> 以模块形式被选择的项目
      • < > 未被选择的模块

      中间部分就是我们要选择的各项功能了,使用上下两个方向键进行选择,也可以使用关键字进行跳转。

      image-20230719162128144.png

      最下方是功能选择按钮,功能与案件基本重合。使用左右方向键选择,Enter键选中。

      image-20230719162151941.png

      • <Select> 进入下级菜单
      • < Exit > 退回上一级菜单,在最顶层菜单就是退出menuconfig
      • < Help > 显示当前选项的帮助信息
      • < Save > 手动保存配置文件
      • < Load > 加载指定的配置文件,默认加载的是.config

      RTOS menuconfig 说明

      了解了menuconfig的基本操作,我们再来了解一下 RTOS 的 menuconfig 具体都有是么内容。

      Build target sunxi arch

      分别选择芯片的系列,对于R128平台,使用的是 sun20iw2p1 平台

      image-20230719162924415.png

      Build OS or Library

      选择编译为 Library 还是 OS,这里我们使用的是 OS 所以设置为编译 OS

      image-20230719163017332.png

      Build System

      这部分配置 RTOS 的配套编译脚手架,配置编译使用的编译工具链的路径,libc的类型等等。

      image-20230719163257466.png

      Architecture Options

      架构配置,配置CPU的架构,入口地址,分配的内存长度,RV核心的地址,DSP核心的地址。这里也也配置默认启动的核心,可以关闭 C906 和 DSP 核心的启动。

      image-20230719163436490.png

      Kernel Options

      FreeRTOS 内核相关的配置,一般不需要修改。

      image-20230719163539609.png

      Drivers Options

      驱动配置,分为 soc related device drivers 和 other drivers

      image-20230719164020498.png

      soc related device drivers

      包括各种外设驱动,与驱动的单元测试。

      image-20230719164108461.png

      other drivers

      包括 WiFi 驱动,蓝牙驱动等等

      image-20230719164139803.png

      System components

      组件选择,软件包,协议栈等相关选择

      image-20230719164229711.png

      aw components

      全志提供的相关组件,核间通信组件,多媒体组件等。

      image-20230719164317839.png

      thirdparty components

      第三方提供的组件,lvgl,协议栈等等

      image-20230719164416278.png

      Projects Options

      项目配置,选择项目方案

      image-20230719164451026.png

      menuconfig 配置保存

      完成配置之后,我们可以选择使用左右方向键选择 < save > 并按下Enter键。

      如果修改配置文件名称的话,就是将当前的配置另外保存到指定文件;不修改的话默认保存在.config中, 然后选择 ok 确认,就可以继续进行配置了修改了。如果不想保存,那么可以按两下 Esc 键返回。

      image-20230719163114729.png

      当然也可以不使用上面的操作,在全部修改都完成之后,连按 Esc 键,就会退出menuconfig。 在退出时会提醒是否保存配置文件,选择 Yes 即可保存配置,如果不想保存可以选择 No , 如果还想继续修改,可以按两下 Esc 键返回 menuconfig 继续修改。

      image-20230719163133763.png

      发布在 A Series
      livpo
      livpo
    • 回复: V851S拉取tina SDK的版本命令

      @minjialong 不能直接拉
      可以拉了这样打补丁 https://bbs.aw-ol.com/topic/2513

      发布在 V Series
      livpo
      livpo
    • 又进化了!T113智能家居86盒圆屏版(圆屏加一体化驱动板+CNC外壳+炫酷LVGL UI)

      PCB产品发车QQ群号:787148555 ——繁花T113板(太极派)交流群

      PCB产品发车QQ群号:787148555 ——繁花T113板(太极派)交流群

      PCB产品发车QQ群号:787148555 ——繁花T113板(太极派)交流群

      文末有详细PCB发车介绍

      流畅到要设置帧率限制的神之眼/符玄个性时钟

      CLOCK2-ezgif.com-optimize.gif

      巨丝滑的高清高帧率视频播放

      1-ezgif.com-optimize.gif

      如上,你所见到的这款圆滚滚的小玩意,就是魔改了一款在前几期的文章中我们所介绍过的,一款方形的T113-S3智能家居86盒,原项目因其验证过的原理图和PCB(√),可初始化的屏幕(√),详尽的SDK和保姆级的部署教程(√),从而受到了不少开发者的关注,其中也包括一些爱整活的小伙伴。

      于是萨纳兰的黄昏在86盒的原作者FanHuaCloud大佬加持下,又给86盒挖了个新坑,为了解决之前ESP32所驱动圆屏只能播放MJPEG并且帧率较低的尴尬问题,集圆屏加一体化驱动板+外壳+炫酷LVGL UI于一身的圆形86盒横空出世,并命名其为——T113太极派。

      ezgif.com-video-to-gif-converter.gif

      硬件设计简介

      T113太极派可以简单理解为是方形86盒的圆形改版,两者在硬件的设计上大体相同,圆屏由于面积较小,元件排列比方形的困难许多,调整了很久的布局,只是调整了背光芯片以及其它部件的PCB布局走线。

      01、全志T113-S3主控,双核A7,内存128M,支持硬件解码,和D1s Pin to Pin,支持相互替换
      02、7701S 2.1寸SPI RGB接口圆屏,带触摸
      03、RTL8723 WIFI模块,用于连接网络
      04、128MB SPI Nand,支持从SPI Nand启动系统
      05、双USB接口,其中一个支持Host/Device,另一个仅支持Host
      06、板载麦克风和一个耳机口
      07、支持Typec口接DAC小尾巴输出音频

      640 (9).jpeg

      微信图片_20240410113945.png

      目前的版本是插电使用的超薄版本 ,后续可能会增加带扩展版的支持电池的充电版本,其最主要的特点是作者为这款圆形的太极派专门给它建模做了一个极其轻薄的CNC外壳,触摸屏直径是71.8mm,外壳直径为74mm。树脂版外壳厚度为13mm,CNC版外壳厚度为10mm。

      40093917bd9a1f25d989701a16277441_raw-ezgif.com-optimize.gif

      软件系统及UI界面适配

      APP是设备的灵魂,肯定不能只跑个lvgl demo看看那些图形蹦来蹦去最后得个数字。前面做的这一切,都是为了将之前的代码移植过来。本APP使用LVGL 8.3.11编写。目前有以下功能(方屏版和圆屏板都可用,相同的功能在不同形状的屏幕上会有不同的表现形式)

      AIDA64无线副屏

      原理很简单,电脑端开启AIDA64,新建一个Remote Panel,将指定的设备信息设置为指定格式后放上去,本设备在联网后就会定时获取信息,并动态展示。目前该界面还是为方屏设计的,圆屏还没有特别好的创意,等有了再改。

      AIDA-ezgif.com-optimize.gif

      音乐频谱节拍器

      同样针对方屏和圆屏做了两套不同的UI,不但UI不同,而且频谱样式也不同,方屏是条形频谱,圆形是放射形渐变频谱。频谱的实现是由全志的Tplayer的音频回调函数将PCM信息推送给对应的FFT进程,然后FFT计算后推送给页面展示来实现的。另:全志的tplayer有bug,如果设置单曲循环,且播放的音乐为flac格式,在音乐播放完,跳转会开头的时候会出错。为解决这个问题,APP在遇到flac音乐单曲循环的时候,会重新初始化该音乐。

      又是频谱,没错我就是频谱星人。拾音频谱做了专属样式,实现是通过alsa接口读取麦克风PCM数据,然后同样推送给FFT进程。圆形版本的拾音频谱叠加了视频背景(视频背景由b站up主渣渣一块钱4个制作)。

      UI-ezgif.com-optimize.gif

      2-ezgif.com-optimize.gif

      动态天气时钟

      以之前HTC手机的那个动态天气动画作为背景的时钟,根据不同的天气加载不同的动态背景,使用了高德天气API。

      ezgif.com-optimize (1).gif

      神之眼/符玄个性时钟

      移植了之前做的一些个性主题时钟,比如随机星座时钟等,比较二次元,结合全志硬解视频功能,支持mp4视频播放作为动态背景 有兴趣的可以看我相关的视频展示。效果见文首视频连接

      CLOCK2-ezgif.com-optimize.gif

      CLOCK-ezgif.com-video-to-gif-converter.gif

      视频/相册播放器

      针对方屏和圆屏做了两套不同的UI。除了按钮功能外,在屏幕右侧区域上下滑是调节音量,左侧区域上下滑调节亮度,中间区域左右滑调节进度。总之,将全志t113的硬件解码功能全部解锁出来,并配上lvgl播放器UI~

      111-ezgif.com-video-to-gif-converter.gif

      1-ezgif.com-optimize.gif

      ezgif.com-optimize.gif

      SDK系统

      修掉了很多问题,直接编译可用,后续提供一键编译的虚拟机(不含视频演示ui相关),可以很好解决初学者反复踩坑环境搭建问题。

      SDK中已经集成了我的APP所用到的一切库,如

      • fftw3:FFT库
      • Libjpeg:改为了最新的版本
      • Taglib:读取音乐文件里面的图像

      开源资料获取

      本文所有内有内容均转载自原作者本人的B站视频账号及立创开源硬件平台的工程页面,文章内所提到的圆形86盒硬件设计不开源,可以参考方形的86盒PCB设计,可以直接使用的SDK已开源在帖子内,感兴趣的小伙伴可以复制下方链接或者戳文末的“阅读原文”获取。

      • 作者萨纳兰的黄昏个人网站:https://pressf5.run/
      • B站视频介绍:https://www.bilibili.com/video/BV1ip421m77K
      • 方形PCB参考及开源SDK获取:https://oshwhub.com/planevina/t11386-box-revision

      关于发车:QQ群号:787148555

      目前圆屏pcb添加了一个接喇叭的功放电路,正在打样验证,预计下周初即验证完,然后就会准备smt事项。具体一个板子多少钱会根据到时候贴的数量来看,多就会便宜点少就会贵点。

      • 程序:买了板子的免费激活视频中展示的程序。
      • 外壳:样品已用fdm打印验证过,树脂版的已下单三维猴验证,同样下周可验证完。会在群内直接提供stl文件供自己打印(也有可能统一下单三维猴树脂打印)。我个人非常喜欢cnc壳,看到时候有没有人一起凑一凑去加工一些。
      • sdk:可使用繁花在立创上开源的那个,后面也会再整理一套
      发布在 爱搞机专区
      livpo
      livpo
    • 【R128】应用开发案例——LVGL 与 SPI TFT GUI

      基于R128-S2设计的全套开发板已上线淘宝百问网韦东山老师个人店进行售卖,包含黑色的DshanMCU-R128s2-R16N16模组和全套的DshanMCU-R128s2-DEVKIT。

      • DshanMCU-R128s2-R16N16模组:39.9元
      • DshanMCU-R128s2-DEVKIT开发板:59.9元

      R128开发板购买链接:https://item.taobao.com/item.htm?spm=a21n57.1.0.0.46b0523cMfarLo&id=736154682975&ns=1&abbucket=5#detail

      “主图_01”为智能对象-1.jpg

      LVGL 与 SPI TFT GUI

      本次使用的是 Dshan_Display Module,如下图:

      O1CN01EnnPfh1adisHYyi4d_!!0-item_pic.jpg_Q75.jpg_.png

      引脚配置如下:

      R128 Devkit TFT 模块
      PA12 CS
      PA13 SCK
      PA18 MOSI
      PA9 PWM
      PA20 RESET
      PA19 RS
      3V3 3.3V
      GND GND

      image-20230825143934156 (1).png

      载入方案

      我们使用的开发板是 R128-Devkit,需要开发 C906 核心的应用程序,所以载入方案选择 r128s2_module_c906

      $ source envsetup.sh 
      $ lunch_rtos 1
      

      image-20230802110150203 (3).png

      配置 SPI LCD 驱动

      驱动配置请参照 《SPI 驱动 TFT LCD 屏》文章进行配置。

      配置 LVGL 软件包

      运行 mrtos_menuconfig 进入配置页面。在如下地址找到勾选相关软件包。

      System components  --->
      	thirdparty components  --->
      		[*] Littlevgl-8  --->
      			[*]   lvgl examples
      			[*]   lvgl-8.1.0 use sunxifb double buffer
      			[*]   lvgl-8.1.0 use sunxifb cache
      

      image-20230825153504625.png

      注意,lv_examples 与 lv_g2d_test 不能同时勾选,否则会报错重复定义错误。

      论坛提问:想给R128移植上LVGL按照教程结果失败

      image-20231016111517049.png

      错误如下:

      image-20231016111544273.png

      如果出现 update_mbr_failed,请参照常见问题修改分区表增大分区容量

      修改 LVGL 显示配置

      进入 lichee/rtos-components/thirdparty/littlevgl-8/lv_examples/src/lv_conf.h 找到 LV_COLOR_DEPTH 并修改为 16 位,LV_COLOR_16_SWAP 置为 1 进行红蓝颜色交换。

      image-20230825153629754.png

      测试

      编译打包刷写后,在控制台输入 lv_examples 可以查看相关命令。

      image-20230825153901829.png

      运行 lv_examples 1 即可在屏幕显示 LVGL 界面。

      image-20230825153927629.png

      image-20230825154036301.png

      发布在 A Series
      livpo
      livpo
    • 回复: 想问问怎么获取全志客户服务平台的资源

      @lyl20050926 用公司账户在全志客户服务平台上按流程注册就可以获取相关资料了

      发布在 飞凌嵌入式专区
      livpo
      livpo
    • 回复: TinaLinux 无法创建 swap 分区吗?

      可以的,
      【FAQ】全志V853芯片 swap功能简介与tina上swap分区使用方法
      https://bbs.aw-ol.com/topic/1626/share/1

      发布在 V Series
      livpo
      livpo
    • 回复: A133/R818 Tina Linux Qt 平板电脑方案 解决方案 ❀

      @rayzhang 不放点demo出来秀一秀吗

      发布在 A Series
      livpo
      livpo
    • 回复: 搭建开发环境,出现问了了,总是报fatal: cannot obtain manifest https://sdk.aw-ol.com/git_repo/V853Tina_Open/manifest.git

      @aaa0557li 在 搭建开发环境,出现问了了,总是报fatal: cannot obtain manifest https://sdk.aw-ol.com/git_repo/V853Tina_Open/manifest.git 中说:

      git config --global user.email "you@example.com"
      git config --global user.name "Your Name"

      发布在 V Series
      livpo
      livpo
    • 【R128】基础组件开发指南——SPI LCD 显示驱动

      SPI LCD 显示驱动

      简介

      R128 平台提供了 SPI DBI 的 SPI TFT 接口,具有如下特点:

      • Supports DBI Type C 3 Line/4 Line Interface Mode
      • Supports 2 Data Lane Interface Mode
      • Supports data source from CPU or DMA
      • Supports RGB111/444/565/666/888 video format
      • Maximum resolution of RGB666 240 x 320@30Hz with single data lane
      • Maximum resolution of RGB888 240 x 320@60Hz or 320 x 480@30Hz with dual data lane
      • Supports tearing effect
      • Supports software flexible control video frame rate

      同时,提供了SPILCD驱动框架以供 SPI 屏幕使用。

      模块驱动

      menuconfig配置说明

      SPILCD 模块 menuconfig 的配置如下(以选择kld2844b屏为例):

      Drivers Options  --->
          soc related device drivers  --->
              SPILCD Devices  --->
                  [*] DISP Driver Support(spi_lcd)
                  [*]   spilcd hal APIs test           //spilcd模块测试用例
                  LCD_FB panels select  --->           //spilcd屏驱动配置
                      [*] LCD support kld2844B panel
                      [ ] LCD support kld35512 panel
                  Board select  --->
                      [ ] board kld2844b support       //板级显示使用显示驱动私有方式的配置项,而使用sys_config.fex方式不用配置
      

      另外可能需依赖的配置项有:

      • DRIVERS_SPI
      • DRIVERS_DBI
      • DRIVERS_PWM

      源码结构介绍

      源码结构及主要驱动文件如下:

      spilcd/
      ├── lcd_fb/
      │   ├── dev_lcd_fb.c				# spilcd driver 层
      │   ├── disp_display.c
      │   ├── disp_lcd.c
      │   ├── lcd_fb_intf.c
      │   └── panels/					    # lcd驱动相关
      │       ├── kld2844b.c
      │       ├── lcd_source.c
      │       ├── panels.c
      │       └── panels.h
      └── soc/
          ├── disp_board_config.c			# 板级配置解析
          └── kld2844b_config.c			# 显示私有方式的板级配置文件
      
      

      模块参数配置

      当前板级显示支持两种配置方法,一是使用 sys_config.fex 的方式进行配置,二是在不支持sys_config.fex 情况下,可以通过显示驱动私有的方式进行配置。下面分别对两种方式进行说明。

      使用 sys_config.fex 的方式进行配置

      FreeRTOS系统路径:board/芯片名/方案名/configs/sys_config.fex

      配置文件具体要看芯片方案所实际使用的,也可能使用的配置文件名称为sys_config_xxx.fex(xx是存储方案的标识,例如sys_config_nor.cfg、sys_config_nand.cfg)

      具体配置举例如下:

      ;----------------------------------------------------------------------------------
      ;lcd_fb0 configuration
      ;----------------------------------------------------------------------------------
      [lcd_fb0]
      lcd_used            = 1              ; 使用显示屏
      lcd_model_name      = "spilcd"       ; 模型:spilcd
      lcd_driver_name     = "jlt35031c"    ; 屏幕驱动:jlt35031c
      lcd_x               = 320            ; 屏幕宽分辨率
      lcd_y               = 480            ; 屏幕高分辨率
      lcd_width           = 49             ; 屏幕物理宽度
      lcd_height          = 74             ; 屏幕物理高度
      lcd_data_speed      = 60             ; SPI 驱动频率 60MHz
      lcd_pwm_used        = 1              ; lcd使用pwm背光
      lcd_pwm_ch          = 1              ; lcd使用pwm背光通道1
      lcd_pwm_freq        = 5000           ; lcd使用pwm背光频率5000Hz
      lcd_pwm_pol         = 0              ; lcd使用pwm背光相位0
      lcd_if              = 0              ; lcd使用spi接口,0-spi, 1-dbi
      lcd_pixel_fmt       = 11             
      lcd_dbi_fmt         = 2
      lcd_dbi_clk_mode    = 1
      lcd_dbi_te          = 1
      fb_buffer_num       = 2
      lcd_dbi_if          = 4
      lcd_rgb_order       = 0
      lcd_fps             = 60
      lcd_spi_bus_num     = 1
      lcd_frm             = 2
      lcd_gamma_en        = 1
      lcd_backlight       = 100
      
      lcd_power_num       = 0
      lcd_gpio_regu_num   = 0
      lcd_bl_percent_num  = 0
      
      lcd_spi_dc_pin      = port:PA19<1><0><3><0> ; DC脚
      ;RESET Pin
      lcd_gpio_0          = port:PA20<1><0><2><0> ; 复位脚
      

      lcd_driver_name

      Lcd屏驱动的名字(字符串),必须与屏驱动中strcut __lcd_panel变量的name成员一致。

      lcd_model_name

      Lcd屏模型名字,非必须,可以用于同个屏驱动中进一步区分不同屏。

      lcd_if

      Lcd Interface

      设置相应值的对应含义为:

      0:spi接口
      1:dbi接口
      

      spi接口就是俗称的4线模式,这是因为发送数据时需要额外借助DC线来区分命令和数据,与sclk,cs和sda共四线。

      spi-16932054745452.png

      如果设置了dbi接口,那么还需要进一步区分dbi接口,需要设置 lcd_dbi_if

      lcd_dbi_if

      Lcd dbi 接口设置。

      这个参数只有在lcd_if=1时才有效。

      设置相应值的对应含义为:

      0:L3I1
      1:L3I2
      2:L4I1
      3:L4I2
      4:D2LI
      

      所有模式在发送数据时每个周期的比特数量根据不同像素格式不同而不同。

      L3I1和L3I2是三线模式(不需要DC脚),区别是读时序,也就是是否需要额外脚来读寄存器。读写时序图如下:

      L3I1-16932054745453.png

      L3I_RD-16932054745464.png

      L4I1和L4I2是四线模式,与spi接口协议一样,区别是DC脚的控制是否自动化控制,另外I2和I1的区别是读时序,也就是否需要额外脚来读取寄存器。

      spi-16932054745452 (1).png

      spi_read-16932054745465.png

      D2LI是两data lane模式。发送命令部分时序与读时序与L3I1一致,下图是发送数据时的时序,不同像素格式时钟周期数量不一样。

      D2LI-16932054745466.png

      lcd_dbi_fmt

      DBI接口像素格式。

      0:RGB111
      1:RGB444
      2:RGB565
      3:RGB666
      4:RGB888
      

      选择的依据是接收端屏Driver IC的支持情况,请查看Driver IC手册或询问屏厂。

      然后必须配合lcd_pixel_fmt的选择,比如说选RGB565时,lcd_pixel_fmt也要选565格式。

      lcd_dbi_te

      使能te触发。

      te即(Tearing effect),也就是撕裂的意思,由于读写不同导致撕裂现象,te脚的功能就是用于同步读写,te脚的频率也就是屏的刷新率,所以te脚也可以看做vsync脚(垂直同步脚)

      0: 禁止te
      1: 下降沿触发
      2: 上升沿触发
      

      查看带te脚的屏进一步说明。

      lcd_dbi_clk_mode

      选择dbi时钟的行为模式。

      0:自动停止。有数据就有时钟,没发数据就没有
      1:一直保持。无论发不发数据都有时钟
      

      注意上面的选项关系屏兼容性。

      lcd_rgb_order

      输入图像数据rgb顺序识别设置,仅当lcd_if=1时有效。

      0:RGB
      1:RBG
      2:GRB
      3:GBR
      4:BRG
      5:BGR
      6:G_1RBG_0
      7:G_0RBG_1
      8:G_1BRG_0
      9:G_0BRG_1
      

      非RGB565格式用0到5即可。

      针对rgb565格式说明如下:

      rgb565格式会遇到大小端问题,arm平台和PC平台存储都是小端(little endian,低字节放在低地址,高字节放在高地址),但是许多spi屏都是默认大端(Big Endian)。

      也就是存储的字节顺序和发送的字节顺序不对应。

      这个时候选择6以下,DBI接口就会自动将小端转成大端。

      如果遇到默认是小端的spi屏,则需要选择6以上,DBI接口会自动用回小端方式。

      6以上格式这样解释:

      R是5比特,G是6比特,B是5比特,再把G拆成高3位(G_1)和低3位(G_0)
      所以以下两种顺序:

      rgb565-16932054745467.png

      1. R-G_1-G_0-B,大端。
      2. G_0-B-R-G_1,对应上面的9,小端。

      lcd_x

      显示屏的水平像素数量,注意如果屏支持横竖旋转,那么lcd_x和lcd_y也要对调。

      lcd_y

      显示屏的行数,注意如果屏支持横竖旋转,那么lcd_x和lcd_y也要对调。

      lcd_data_speed

      用于设置spi/dbi接口时钟的速率,单位MHz。

      1. 发送端(SOC)的最大限制是100MHz。
      2. 接收端(屏Driver IC)的限制,请查看对应Driver IC手册或者询问屏厂支持。
      3. 超出以上限制都有可能导致显示异常。

      lcd_fps

      设置屏的刷新率,单位Hz。当lcd_dbi_te使能时,这个值设置无效。

      lcd_pwm_used

      是否使用pwm。

      此参数标识是否使用pwm用以背光亮度的控制。

      lcd_pwm_ch

      Pwm channel used

      此参数标识使用的Pwm通道。

      lcd_pwm_freq

      Lcd backlight PWM Frequency

      这个参数配置PWM信号的频率,单位为Hz。

      lcd_pwm_pol

      Lcd backlight PWM Polarity

      这个参数配置PWM信号的占空比的极性。设置相应值对应含义为:

      0:active high
      1:active low
      

      lcd_pwm_max_limit

      Lcd backlight PWM

      最高限制,以亮度值表示。

      比如150,则表示背光最高只能调到150,0~255范围内的亮度值将会被线性映射到0~150范围内。用于控制最高背光亮度,节省功耗。

      lcd_backlight

      默认背光值,取值范围0到255,值越大越亮。

      lcd_bl_en

      背光使能脚定义

      lcd_spi_dc_pin

      指定作为DC的管脚,用于spi接口时。

      lcd_gpio_x

      x表示数字。如果有多个gpio脚需要控制,则定义lcd_gpio_0,lcd_gpio_1等。

      lcd_spi_bus_num

      选择spi总线id,只有spi1支持DBI协议,所以这里一般选择1。

      取值范围:0到1。

      lcd_pixel_fmt

      选择传输数据的像素格式。

      可选值如下,当你更换RGB分量顺序的时候,也得相应修改lcd_rgb_order,或者修改屏驱动的rgb分量顺序(一般是3Ah寄存器)。

      DBI接口只支持RGB32和RGB16的情况。

      SPI接口只支持RGB16的情况。

      		enum lcdfb_pixel_format {
      			LCDFB_FORMAT_ARGB_8888 = 0x00,	// MSB  A-R-G-B  LSB
      			LCDFB_FORMAT_ABGR_8888 = 0x01,
      			LCDFB_FORMAT_RGBA_8888 = 0x02,
      			LCDFB_FORMAT_BGRA_8888 = 0x03,
      			LCDFB_FORMAT_XRGB_8888 = 0x04,
      			LCDFB_FORMAT_XBGR_8888 = 0x05,
      			LCDFB_FORMAT_RGBX_8888 = 0x06,
      			LCDFB_FORMAT_BGRX_8888 = 0x07,
      			LCDFB_FORMAT_RGB_888 = 0x08,
      			LCDFB_FORMAT_BGR_888 = 0x09,
      			LCDFB_FORMAT_RGB_565 = 0x0a,
      			LCDFB_FORMAT_BGR_565 = 0x0b,
      			LCDFB_FORMAT_ARGB_4444 = 0x0c,
      			LCDFB_FORMAT_ABGR_4444 = 0x0d,
      			LCDFB_FORMAT_RGBA_4444 = 0x0e,
      			LCDFB_FORMAT_BGRA_4444 = 0x0f,
      			LCDFB_FORMAT_ARGB_1555 = 0x10,
      			LCDFB_FORMAT_ABGR_1555 = 0x11,
      			LCDFB_FORMAT_RGBA_5551 = 0x12,
      			LCDFB_FORMAT_BGRA_5551 = 0x13,
      		};
      

      fb_buffer_num

      显示framebuffer数量,为了平滑显示,这里一般是2个,为了省内存也可以改成1。

      模块 sys_config.fex 配置案例

      典型2 data lane配置

      一些屏支持双数据线传输以加快数据传输速度,此时需要走DBI协议,典型配置如下:

      [lcd_fb0]
      lcd_used          = 1
      lcd_driver_name   = "kld2844b"
      lcd_if            = 1
      lcd_dbi_if        = 4
      lcd_data_speed    = 60
      lcd_spi_bus_num   = 1
      lcd_x             = 320
      lcd_y             = 240
      lcd_pwm_used      = 1
      lcd_pwm_ch        = 4
      lcd_pwm_freq      = 5000
      lcd_pwm_pol       = 0
      lcd_pixel_fmt     = 0
      lcd_dbi_fmt       = 3
      lcd_rgb_order     = 0
      fb_buffer_num     = 2
      lcd_backlight     = 200
      lcd_fps           = 60
      lcd_dbi_te        = 0
      lcd_bl_en         = port:PB04<1><0><default><1>
      lcd_gpio_0        = port:PB02<1><0><default><1>
      
      1. 硬件连接上,第二根数据脚连接到原来1 data lane的DC脚,可以这样理解:2数据线在传输数据时就自带D/C(Data/Commend)信息了,所以原来的DC脚就可以空出来作为第二根数据线了。
      2. 屏驱动上,需要使能2 data lane模式,具体寄存器查看对应driverIC手册或者询问屏厂。
      3. 这里的针对对2 data lane的关键参数是lcd_if,lcd_dbi_if,lcd_dbi_fmt和lcd_spi_bus_num。
      4. lcd_x和lcd_y是屏分辨率。如果屏支持旋转(横竖旋转),这里也需要对调。
      5. lcd_pwm开头,lcd_backlight和lcd_bl_en的是背光相关设置,如果有相关硬件连接的话。
      6. lcd_pixel_fmt和fb_buffer_num是显示framebuffer的设置。
      7. lcd_gpio_开头的是自定义gpio的设置(比如复位脚)。
      8. lcd_fps和lcd_dbi_te是刷新方式相关的设置。

      原SPI接口屏配置

      如果IC支持DBI接口,那么就没有必要用SPI接口,DBI接口其协议能覆盖所有情况。

      一些IC不支持DBI,那么只能用spi接口(通过设置lcd_if),如果使用spi接口,它有一些限制。

      1. 不支持2 data lane。
      2. 必须指定DC脚。这是由于spi协议不会自动控制DC脚来区分数据命令,通过设置lcd_spi_dc_pin可以完成这个目的,这跟管脚不必用spi里面的脚。
      3. 只支持rgb565的像素格式。由于只有单data lane,速度过慢,rgb565以上格式都不现实。
      [lcd_fb0]
      lcd_used          = 1
      lcd_driver_name   = "kld2844b"
      lcd_if            = 0
      lcd_data_speed    = 60
      lcd_spi_bus_num   = 1
      lcd_x             = 320
      lcd_y             = 240
      lcd_pwm_used      = 1
      lcd_pwm_ch        = 4
      lcd_pwm_freq      = 5000
      lcd_pwm_pol       = 0
      lcd_pixel_fmt     = 10
      lcd_rgb_order     = 0
      fb_buffer_num     = 2
      lcd_backlight     = 200
      lcd_fps           = 60
      lcd_dbi_te        = 0
      lcd_bl_en         = port:PB04<1><0><default><1>
      lcd_gpio_0        = port:PB02<1><0><default><1>
      lcd_spi_dc_pin    = port:PA19<1><0><3><1>
      

      带te脚的屏

      te即(Tearing effect),也就是撕裂的意思,是由于读写不同步导致撕裂现象,te脚的功能就是用于同步读写,te脚的频率也就是屏的刷新率,所以te脚也可以看做vsync脚(垂直同步脚)。

      1. 硬件设计阶段,需要将屏的te脚连接到IC的DBI接口的te脚。
      2. 配置上接口使用dbi接口。
      3. 然后使能lcd_dbi_te。
      4. 屏驱动使能te功能,寄存器一般是35h,详情看屏对应的driver IC手册。
      5. 屏驱动设置帧率,根据屏能接受的传输速度选择合理的帧率(比如ST7789H2里面是通过c6h来设置te频率)。
      [lcd_fb0]
      lcd_used          = 1
      lcd_driver_name   = "kld2844b"
      lcd_if            = 1
      lcd_dbi_if        = 4
      lcd_data_speed    = 60
      lcd_spi_bus_num   = 1
      lcd_x             = 320
      lcd_y             = 240
      lcd_pwm_used      = 1
      lcd_pwm_ch        = 4
      lcd_pwm_freq      = 5000
      lcd_pwm_pol       = 0
      lcd_pixel_fmt     = 0
      lcd_dbi_fmt       = 3
      lcd_rgb_order     = 0
      fb_buffer_num     = 2
      lcd_backlight     = 200
      lcd_fps           = 60
      lcd_dbi_te        = 1
      lcd_bl_en         = port:PB04<1><0><default><1>
      lcd_gpio_0        = port:PB02<1><0><default><1>
      

      横竖屏旋转

      1. 平台没有硬件旋转功能,软件旋转太慢而且耗费CPU。
      2. 不少spi屏支持内部旋转,需要在屏驱动初始化的时候进行设置,一般是36h寄存器。
      // 转成横屏
      sunxi_lcd_cmd_write(sel, 0x36);
      sunxi_lcd_para_write(sel, 0xa0);
      
      1. lcd_fb0的配置中需要将lcd_x和lcd_y的值对调,此时软件将屏视为横屏。
      2. 屏内部旋转出图效果可能会变差,建议选屏的时候直接选好方向。

      帧率控制

      屏的刷新率受限于多方面:

      1. SPI/DBI硬件传输速度,也就是时钟脚的频率。设置lcd_data_speed可以设置硬件传输速度,最大不超过100MHz。如果屏能正常接收,这个值自然是越大越好。
      2. 屏driver IC接收能力。Driver IC手册中会提到屏的能接受的最大sclk周期。
      3. 使用2 data lane还是1 data lane,理论上2 data lane的速度会翻倍。见典型2 data lane配置。
      4. 像素格式。像素格式决定需要传输的数据量,颜色数量越小的像素格式,帧率越高,但是效果越差。
      5. 带te脚的屏一节中我们知道,te相关设置直接影响到屏刷新率。
      6. 如果不支持te,可以通过设置lcd_fps来控制帧率,你需要根据第一点和第二点选择一个合适的值。

      背光控制

      1. 硬件需要支持pwm背光电路。
      2. 驱动支持pwm背光调节,只需要配置好lcd_pwm开头,lcd_backlight和lcd_bl_en等背光相关配置即可。

      像素格式相关

      1. lcd_pixel_fmt,这个设置项用于设置fbdev的像素格式。
      2. lcd_dbi_fmt,这个用于设置DBI接口发送的像素格式。

      SPI/DBI发送数据的时候没有必要发送alpha通道,但是应用层却有对应的alpha通道,比如ARGB8888格式。

      这个时候硬件会自动帮我们处理好alpha通道,所以lcd_pixel_fmt选择有alpha通道的格式时,lcd_dbi_fmt可以选rgb666或者rgb888,不用和它一样。

      电源配置

      有多个电源的话,就用lcd_power1,lcd_power2......然后屏驱动里面调用sunxi_lcd_power_enable接口即可。

      GPIO配置说明

      lcd_bl_en、lcd_spi_dc_pin以及lcd_gpio_x都是属于GPIO属性类型。

      下面以lcd_spi_dc_pin为例,具体说明GPIO属性的参数值含义:

      lcd_spi_dc_pin   = port:PA19<1><0><3><1>
      

      引脚说明:port: 端口 < 复用功能 >< 上下拉 >< 驱动能力 >< 输出值 >

      等式右边的从左到右5个字段代表的具体含义如下:

      • PA19:端口,表示GPIO管脚。PA表示PA组管脚,19表示第19根管脚;即PA19管脚。
      • <1>:复用功能,表示GPIO复用类型。1表示将PA19选择为通用GPIO功能,0为输入,1为输出。
      • <0>:上下拉,表示内置电阻。使用0的话,表示内部电阻高阻态,如果是1则是内部电阻上拉,2就代表内部电阻下拉。其它数据无效。
      • <3>:表示驱动能力。1是默认等级,数字越大驱动能力越强,最大是3。
      • <1>:表示默认输出电平值。0为低电平,1为高电平。

      多个显示

      1. 确定硬件有没有多余的spi/dbi接口。
      2. 需要在sys_config.fex里面新增lcd_fb1,配置方式与lcd_fb0一样(或者在显示私有方式的板级配置文件里面新增g_lcd1_config,配置方式与g_lcd0_config一样),其中lcd_spi_bus_num不能一样。

      依赖驱动配置

      spilcd模块依赖spi,dbi,pwm等驱动。

      使用显示私有方式进行配置

      路径:rtos-hal/hal/source/spilcd/soc/

      具体板级显示配置文件可参考该路径下的 kld2844b_config.c 配置文件。

      该文件模仿 sys_config.fex来配置一些板级相关的资源,源文件中需要定义4个全局变量:g_lcd0_config、g_lcd1_config、g_lcd0_config_len和g_lcd1_config_len,且变量名字固定,不能修改。

      具体说明如下:

      1. g_lcd0_config:第一个屏的配置变量,struct property_t数据类型。
      2. g_lcd1_config:第二个屏的配置变量,struct property_t数据类型。
      3. g_lcd0_config_len和g_lcd1_config_len是对应上面两个数组变量的长度,照搬即可。

      struct property_t数据类型,用于定义一个属性的信息:

      1. 属性名字。对应其成员name,字符串。
      2. 属性类型。对应其成员type,看enum proerty_type的定义,共有整型,字符串,GPIO,专用pin和电源。
      3. 属性值。根据上面的属性类型来选择union中成员来赋值。

      对于上述常用的属性类型举例如下:

      整型:

      	{
      		.name = "lcd_used",
      		.type = PROPERTY_INTGER,
      		.v.value = 1,
      	},
      

      字符串:

      	{
      		.name = "lcd_driver_name",
      		.type = PROPERTY_STRING,
      		.v.str = "kld2844b",
      	},
      

      GPIO:

      	{
      		.name = "lcd_spi_dc_pin",
      		.type = PROPERTY_GPIO,
      		.v.gpio_list = {
      			.gpio = GPIOA(19),
      			.mul_sel = GPIO_DIRECTION_OUTPUT,
      			.pull = 0,
      			.drv_level = 3,
      			.data = 1,
      		},
      	},
      

      g_lcd0_config的含义与前述的使用sys_config.fex方式中的lcd_fb0一致。

      编写屏驱动

      屏驱动源码位置:

      rtos-hal/hal/source/spilcd/lcd_fb/panels
      
      1. 在屏驱动源码位置下拷贝现有一个屏驱动,包括头文件和源文件,然后将文件名改成有意义的名字,比如屏型号。

      2. 修改源文件中的strcut __lcd_panel变量的名字,以及这个变量成员name的名字,这个名字必须和 sys_config.fex 中 [lcd_fb0](或板级配置文件中g_lcd0_config)的lcd_driver_name一致。

      3. 在屏驱动目录下修改panel.c和panel.h。在全局结构体变量panel_array中新增刚才添加strcut __lcd_panel的变量指针。panel.h中新增strcut __lcd_panel的声明。并用宏括起来。

      4. 修改rtos-hal/hal/source/spilcd/lcd_fb/panels/Kconfig,新增一个config,与第三点提到的宏对应。

      5. 修改rtos-hal/hal/source/spilcd/lcd_fb/路径下的Makefile文件。给lcd_fb-obj变量新增刚才加入的源文件对应.o。

      6. 根据本手册以及屏手册,Driver IC手册修改sys_config.fex中的[lcd_fb0]节点(或显示私有方式的板级配置文件中的g_lcd0_config配置变量)下面的属性

      7. 实现屏源文件中的LCD_open_flow,LCD_close_flow,LCD_panel_init,LCD_power_on等函数

      开关屏流程函数解析

      开关屏的操作流程如下图所示。

      其中,LCD_open_flow和LCD_close_flow称为开关屏流程函数,该函数利用LCD_OPEN_FUNC进行注册回调函数,先注册先执行,可以注册多个,不限制数量。

      lcd_flow.png

      LCD_open_flow

      功能:初始化开屏的步骤流程。

      原型:

      static __s32 LCD_open_flow(__u32 sel)
      

      函数常用内容为:

      static __s32 LCD_open_flow(__u32 sel)
      {
      	LCD_OPEN_FUNC(sel, LCD_power_on,10);
      	LCD_OPEN_FUNC(sel, LCD_panel_init, 50);
      	LCD_OPEN_FUNC(sel, lcd_fb_black_screen, 100); 
      	LCD_OPEN_FUNC(sel, LCD_bl_open, 0);
      	return 0;
      }
      

      如上,初始化整个开屏的流程步骤为四个:

      1. 打开LCD电源,再延迟10ms。
      2. 初始化屏,再延迟50ms;(不需要初始化的屏,可省掉此步骤)。
      3. 向屏发送全黑的数据。这一步骤是必须的,而且需要在开背光之前。
      4. 打开背光,再延迟0ms。

      LCD_open_flow 函数只会系统初始化的时候调用一次,执行每个 LCD_OPEN_FUNC 即是把对应的开屏步骤函数进行注册,并没有立即执行该开屏步骤函数。LCD_open_flow函数的内容必须统一用 LCD_OPEN_FUNC(sel, function, delay_time) 进行函数注册的形式,确保正常注册到开屏步骤中。

      LCD_OPEN_FUNC的第二个参数是前后两个步骤的延时长度,单位ms,注意这里的数值请按照屏手册规定去填,乱填可能导致屏初始化异常或者开关屏时间过长,影响用户体验。

      LCD_close_flow

      功能:初始化关屏的步骤流程。

      原型:

      static __s32 LCD_close_flow(__u32 sel)
      

      函数常用内容为:

      static __s32 LCD_close_flow(__u32 sel)
      {
      	LCD_CLOSE_FUNC(sel, LCD_bl_close, 50);
      	LCD_CLOSE_FUNC(sel, LCD_panel_exit, 10);
      	LCD_CLOSE_FUNC(sel, LCD_power_off, 10);
      	return 0;
      }
      
      1. LCD_bl_close,是关背光,关完背光在处理其它事情,不会影响用户视觉。
      2. LCD_panel_exit,发送命令让屏退出工作状态。
      3. 关电复位,让屏彻底关闭。

      LCD_OPEN_FUNC

      功能:注册开屏步骤函数到开屏流程中,记住这里是注册不是执行!

      原型:

      void LCD_OPEN_FUNC(__u32 sel, LCD_FUNC func, __u32 delay)
      

      参数说明:

      func是一个函数指针,其类型是:void (*LCD_FUNC) (__u32 sel),用户自己定义的函数必须也要用统一的形式。比如:

      void user_defined_func(__u32 sel)
      {
      	//do something
      }
      

      delay是执行该步骤后,再延迟的时间,时间单位是毫秒。

      LCD_power_on

      这是开屏流程中第一步,一般在这个函数使用sunxi_lcd_gpio_set_value进行GPIO控制,用sunxi_lcd_power_enable函数进行电源开关。

      参考屏手册里面的上电时序(Power on sequence)。

      LCD_panel_init

      这是开屏流程第二步,一般使用sunxi_lcd_cmd_write和sunxi_lcd_para_write对屏寄存器进行初始化。

      请向屏厂索要初始化寄存器代码或者自行研究屏Driver IC手册。

      lcd_fb_black_screen

      向屏传输全黑数据的接口,是必须的,否则打开背光后,呈现的将是雪花屏。

      LCD_bl_open

      这是背光使能,固定调用。

      1. sunxi_lcd_backlight_enable, 打开lcd_bl_en脚。
      2. sunxi_lcd_pwm_enable, 使能pwm。

      LCD_bl_close

      这是关闭背光。固定调用下面两个函数,分别是:

      1. sunxi_lcd_backlight_disable,lcd_bl_en关闭
      2. sunxi_lcd_pwm_disable, 关闭pwm。

      LCD_power_off

      这是关屏流程中最后一步,一般在这个函数使用sunxi_lcd_gpio_set_value进行GPIO控制,用sunxi_lcd_power_enable函数进行电源开关。

      参考屏手册里面的下电时序(Power off sequence)。

      sunxi_lcd_delay_ms

      函数:sunxi_lcd_delay_ms/sunxi_lcd_delay_us

      功能:延时函数,分别是毫秒级别/微秒级别的延时。

      原型:s32 sunxi_lcd_delay_ms(u32 ms); / s32 sunxi_lcd_delay_us(u32 us);

      sunxi_lcd_backlight_enable

      函数:sunxi_lcd_backlight_enable/ sunxi_lcd_backlight_disable

      功能:打开/关闭背光,操作的是lcd_bl_en。

      原型:

      • void sunxi_lcd_backlight_enable(u32 screen_id);

      • void sunxi_lcd_backlight_disable(u32 screen_id);

      sunxi_lcd_pwm_enable

      函数:sunxi_lcd_pwm_enable / sunxi_lcd_pwm_disable

      功能:打开/关闭pwm控制器,打开时pwm将往外输出pwm波形。对应的是lcd_pwm_ch所对应的那一路pwm。

      原型:

      • s32 sunxi_lcd_pwm_enable(u32 screen_id);

      • s32 sunxi_lcd_pwm_disable(u32 screen_id);

      sunxi_lcd_power_enable

      函数:sunxi_lcd_power_enable / sunxi_lcd_power_disable

      功能:打开/关闭Lcd电源,操作的是板级配置文件中的lcd_power/lcd_power1/lcd_power2。(pwr_id标识电源索引)。

      原型:

      • void sunxi_lcd_power_enable(u32 screen_id, u32 pwr_id);

      • void sunxi_lcd_power_disable(u32 screen_id, u32 pwr_id);

      1. pwr_id = 0:对应于配置文件中的lcd_power。
      2. pwr_id = 1:对应于配置文件中的lcd_power1。
      3. pwr_id = 2:对应于配置文件中的lcd_power2。
      4. pwr_id = 3:对应于配置文件中的lcd_power3。

      sunxi_lcd_cmd_write

      函数:sunxi_lcd_cmd_write

      功能:使用spi/dbi发送命令。

      原型:s32 sunxi_lcd_cmd_write(u32 screen_id, u8 cmd);

      sunxi_lcd_para_write

      函数:sunxi_lcd_para_write

      功能:使用spi/dbi发送参数。

      原型:s32 sunxi_lcd_para_write(u32 screen_id, u8 para);

      sunxi_lcd_gpio_set_value

      函数:sunxi_lcd_gpio_set_value

      功能:LCD_GPIO PIN脚上输出高电平或低电平。

      原型:s32 sunxi_lcd_gpio_set_value(u32 screen_id, u32 io_index, u32 value);

      参数说明:

      • io_index = 0:对应于配置文件中的lcd_gpio_0。
      • io_index = 1:对应于配置文件中的lcd_gpio_1。
      • io_index = 2:对应于配置文件中的lcd_gpio_2。
      • io_index = 3:对应于配置文件中的lcd_gpio_3。
      • value = 0:对应IO输出低电平。
      • Value = 1:对应IO输出高电平。

      只用于该GPIO定义为输出的情形。

      sunxi_lcd_gpio_set_direction

      函数:sunxi_lcd_gpio_set_direction

      功能:设置LCD_GPIO PIN脚为输入或输出模式。

      原型:

      s32 sunxi_lcd_gpio_set_direction(u32 screen_id, u32 io_index, u32 direction);
      

      参数说明:

      • io_index = 0:对应于配置文件中的lcd_gpio_0。
      • io_index = 1:对应于配置文件中的lcd_gpio_1。
      • io_index = 2:对应于配置文件中的lcd_gpio_2。
      • io_index = 3:对应于配置文件中的lcd_gpio_3。
      • direction = 0:对应IO设置为输入。
      • direction = 1:对应IO设置为输出。

      模块测试用例

      #include <stdio.h>
      #include <stdlib.h>
      #include <stdint.h>
      #include <string.h>
      #include <unistd.h>
      
      #include <hal_cache.h>
      #include <hal_mem.h>
      #include <hal_log.h>
      #include <hal_cmd.h>
      #include <hal_lcd_fb.h>
      
      static uint32_t width;
      static uint32_t height;
      static long int screensize = 0;
      static char *fbsmem_start = 0;
      
      static void lcdfb_fb_init(uint32_t yoffset, struct fb_info *p_info)
      {
          p_info->screen_base = fbsmem_start;
          p_info->var.xres = width;
          p_info->var.yres = height;
          p_info->var.xoffset = 0;
          p_info->var.yoffset = yoffset;
      }
      
      int show_rgb(unsigned int sel)
      {
          int i = 0, ret = -1;
          struct fb_info fb_info;
          int bpp = 4;
          unsigned char color[4] = {0xff,0x0,0xff,0x0};
      
          width = bsp_disp_get_screen_width(sel);
          height = bsp_disp_get_screen_height(sel);
      
          screensize = width * bpp * height;
          fbsmem_start = hal_malloc_coherent(screensize);
      
          hal_log_info("width = %d, height = %d, screensize = %d, fbsmem_start = %x\n",
                  width, height, screensize, fbsmem_start);
      
          memset(fbsmem_start, 0, screensize);
          for (i = 0; i < screensize / bpp; ++i) {
              memcpy(fbsmem_start+i*bpp, color, bpp);
          }
      
          memset(&fb_info, 0, sizeof(struct fb_info));
          lcdfb_fb_init(0, &fb_info);
          hal_dcache_clean((unsigned long)fbsmem_start, screensize);
          bsp_disp_lcd_set_layer(sel, &fb_info);
      
          hal_free_coherent(fbsmem_start);
          return ret;
      }
      
      static int cmd_test_spilcd(int argc, char **argv)
      {
          uint8_t ret;
      
          hal_log_info("Run spilcd hal layer test case\n");
      
          ret = show_rgb(0);
      
          hal_log_info("spilcd test finish\n");
      
          return ret;
      }
      
      FINSH_FUNCTION_EXPORT_CMD(cmd_test_spilcd, test_spilcd, spilcd hal APIs tests)
      

      测试命令:

      test_spilcd
      

      测试结果:

      1.如果LCD是ARGB8888像素格式输入,执行命令后会显示一帧紫色画面。
      2.如果LCD是RGB565像素格式输入,执行命令后会显示一帧蓝色画面(SPI 协议是黄色画面(没有RB Swap))
      

      FAQ

      怎么判断屏初始化成功

      屏初始化成功,一般呈现的现象是雪花屏。因为屏驱动里面,在 LCD_open_flow 中添加了lcd_fb_black_screen的注册,故正常情况下开机是有背光的黑屏画面。

      黑屏-无背光

      一般是电源或者pwm相关配置没有配置好。参考lcd_pwm开头的相关配置。

      送图无显示

      排除步骤:

      1. 首先执行spilcd模块测试命令test_spilcd,如果能正常显示颜色画面,说明显示通路正常,只是应用未能正确配置送图接口。如果执行测试用例无法正常显示颜色画面,接着下面步骤。
      2. 屏驱动里面,在LCD_open_flow中删除lcd_fb_black_screen的注册,启动后,如果屏初始化成功应该是花屏状态(大部分屏如此)。
      3. 如果屏没有初始化成功,请检查屏电源,复位脚状态。
      4. 如果屏初始化成功,但是发数据时又没法显示,那么需要检查是不是帧率过快,查看帧率控制。
      5. 如果电源复位脚正常,请检查配置,lcd_dbi_if, lcd_dbi_fmt是否正确,屏是否支持, 如果支持,在屏驱动里面是否有对应上。
      6. 尝试修改lcd_dbi_clk_mode。

      闪屏

      非常有可能是速度跑太快,参考帧率控制一小节。

      画面偏移

      画面随着数据的发送偏移越来越大。

      尝试修改lcd_dbi_clk_mode。

      屏幕白屏

      屏幕白屏,但是背光亮起

      image-20231017113731873 (1).png

      白屏是因为屏幕没有初始化,需要检查屏幕初始化序列或者初始化数据是否正确。

      屏幕花屏

      屏幕花屏,无法控制

      image-20231017113841569 (1).png

      花屏一般是因为屏幕初始化后没有正确设置 addrwin,或者初始化序列错误。

      LVGL 屏幕颜色不正确

      出现反色,颜色异常

      image-20231017113620580 (1).png

      请配置 LVGL LV_COLOR_DEPTH 参数为 16,LV_COLOR_16_SWAP 为 1,这是由 SPI LCD 的特性决定的。

      image-20231017113520621 (1).png

      显示反色

      运行 test_spilcd ,屏幕显示蓝色。

      这是由于屏幕启动了 RB SWAP,一般是 0x36 寄存器修改

      正常显示

      sunxi_lcd_cmd_write(sel, 0X36);
      sunxi_lcd_para_write(sel, 0x00);
      

      反色显示

      sunxi_lcd_cmd_write(sel, 0X36);
      sunxi_lcd_para_write(sel, 0x08);
      

      LVGL 出现 DMA Over Size

      image-20231023101154351.png

      这是由于 LVGL 配置的 LV_COLOR_DEPTH 为 32,但是 SPI 屏配置为16位。请修改 lv_conf.h

      image-20231023101317634.png

      出现部分花屏

      image-20231023103039467.png

      • 检查 address 函数是否正确
      • 检查 sys_config.fex 屏幕配置分辨率是否正确

      总结

      调试LCD显示屏实际上就是调试发送端芯片(全志SOC)和接收端芯片(LCD屏上的driver IC)的一个过程:

      1. 添加屏驱动请看编写屏驱动
      2. 仔细阅读屏手册以及driver IC手册(有的话)。
      3. 仔细阅读板级显示配置参数详解。
      4. 确保LCD所需要的各路电源管脚正常。

      SPI LCD 颜色相关问题

      首先,得先确定显示屏使用的是SPI接口,还是DBI接口,不同的接口,输入数据的解析方式是不一样的。

      DBI接口的全称是 Display Bus Serial Interface ,在显示屏数据手册中,一般会说这是SPI接口,所以有人会误认为SPI屏可以使用 normal spi 去直接驱动。

      阅读lcd_dbi_if部分的介绍可以知道,在3线模式时,发送命令前有1位A0用于指示当前发送的是数据,还是命令。而命令后面接着的数据就没有这个A0位了,代表SPI需要在9位和8位之间来回切换,而在读数据时,更是需要延时 dummy clock 才能读数据,normal spi 都很难,甚至无法实现。所以 normal spi 只能模拟4 线的DBI的写操作。

      对于R128这类支持DBI接口的CPU,可以选择不去了解SPI。如果需要用到SPI去驱动显示屏,必须把显示屏设置成小端。

      RGB565和RGB666

      SPI显示屏一般支持RGB444,RGB565和RGB666,RGB444使用的比较少,所以只讨论RGB565和RGB666.

      RGB565代表一个点的颜色由2字节组成,也就是R(红色)用5位表示,G(绿色)用6位表示,B(蓝色)用5位表示,如下图所示:

      image-20231016100553340.png
      RGB666一个点的颜色由3字节组成,每个字节代表一个颜色,其中每个字节的低2位会无视,如下图所示:

      image-20231016100620890.png

      SPI 接口

      因为SPI接口的通讯效率不高,所以建议使用RGB565的显示,以 jlt35031c 显示屏为例,他的显示驱动芯片是 ST7789,设置显示格式的方式是往 3a 寄存器写入0x55(RGB565)或者 0x66(RGB666),在 R128SDK 中,已经把 jlt35031c 的通讯格式写死为 0x55,lcd_pixel_fmt配置选项无效:

      sunxi_lcd_cmd_write(sel, 0x3a);
      sunxi_lcd_para_write(sel, 0x55);
      

      在例程中,输入的数据是 0xff,0x00,0xff,0x00,对于SPI接口,是按字节发送。实际上,例程只需要每次发送2字节即可,因为前后发送的都是相同的ff 00,所以没有看出问题。

      根据对 565 的数据解析,我们拆分 ff 00 就可以得到红色分量是 0b11111,也就是 31,绿色是0b111000,也就是 56,,蓝色是 0.我们等效转换成 RGB888,有:

      R = 31/31*255 = 255
      G = 56/63*255 = 226
      

      在调色板输入对应颜色,就可以得到黄色

      image-20231016100913213.png

      DBI 接口

      因为 DBI 通讯效率较高,所以可以使用 RGB565 或者 RGB666,使用 DBI 接口,也就是 lcd_if 设置为1时,驱动会根据 lcd_pixel_fmt 配置寄存器,以 SDK 中的 kld2844b.c 为例,这显示屏的显示驱动也是 ST7789,但是不同的屏幕,厂家封装时已经限制了通讯方式,所以即使是能使用 DBI 接口的驱动芯片的屏幕,或许也用不了DBI。

      sunxi_lcd_cmd_write(sel, 0x3A); /* Interface Pixel Format */
      /* 55----RGB565;66---RGB666 */
      if (info[sel].lcd_pixel_fmt == LCDFB_FORMAT_RGB_565 ||
      	info[sel].lcd_pixel_fmt == LCDFB_FORMAT_BGR_565) {
      	sunxi_lcd_para_write(sel, 0x55);
      	if (info[sel].lcd_pixel_fmt == LCDFB_FORMAT_RGB_565)
      		rotate &= 0xf7;
      	else
      		rotate |= 0x08;
      } else if (info[sel].lcd_pixel_fmt < LCDFB_FORMAT_RGB_888) {
      	sunxi_lcd_para_write(sel, 0x66);
      	if (info[sel].lcd_pixel_fmt == LCDFB_FORMAT_BGRA_8888 ||
      		info[sel].lcd_pixel_fmt == LCDFB_FORMAT_BGRX_8888 ||
      		info[sel].lcd_pixel_fmt == LCDFB_FORMAT_ABGR_8888 ||
      		info[sel].lcd_pixel_fmt == LCDFB_FORMAT_XBGR_8888) {
      		rotate |= 0x08;
      	}
      } else {
      	sunxi_lcd_para_write(sel, 0x66);
      }
      

      对于 DBI 格式,不再是以字节的形式去解析,而是以字的方式去解析,为了统一,软件已经规定了,RGB565 格式时,字大小是2字节,也就是16位,而 RGB666 格式时,字大小是4字节,也就是32位。

      对于 RGB565 格式,同样是设置为 0xff,0x00。因为屏幕是大端,而芯片存储方式是小端,所以芯片的 DBI 模块,会自动把数据从新排列,也就是实际上 DBI 发送数据时,会先发送0x00,再发送0xff,也就是红色分量为0,绿色分量为 0b000111,也就是7,蓝色分量是 0x11111,也就是31,我们同样转换成RGB888

      G = 7/63*255 = 28
      B= 31/31*255 = 255
      

      在调色板上输入,可以得到蓝色。

      image-20231016101233907.png

      如果是 RGB666,虽然占用的是3个字节,但是没有CPU是3字节对齐的,所以需要一次性输入4字节,然后 DBI 硬件模块,会自动舍弃1个字节,软件同意舍弃了最后一个字节。

      依旧以例程为例,例程输入了 0xff,0x00,0xff,0x00,为了方便说明,标准为 0xff(1),0x00(1),0xff(2),0x00(2),其中 0x00(2)会被舍弃掉,然后发送顺序是0xff(2),0x00(1),0xff(1),也就是 0xff(2) 是红色分量,0xff(1) 是蓝色分量,混合可以得到紫色。

      image-20231016101308346.png

      发布在 A Series
      livpo
      livpo
    • 1 / 1