OpenHarmony(鸿蒙南向开发)——标准系统方案之瑞芯微RK3566移植案例(上)

news2024/11/11 6:58:24

往期知识点记录:

  • 鸿蒙(HarmonyOS)应用层开发(北向)知识点汇总
  • 鸿蒙(OpenHarmony)南向开发保姆级知识点汇总~
  • OpenHarmony(鸿蒙南向开发)——轻量系统STM32F407芯片移植案例
  • OpenHarmony(鸿蒙南向开发)——Combo解决方案之W800芯片移植案例
  • OpenHarmony(鸿蒙南向开发)——小型系统STM32MP1芯片移植案例
  • OpenHarmony(鸿蒙南向开发)——标准系统方案之瑞芯微RK3568移植案例(上)
  • OpenHarmony(鸿蒙南向开发)——标准系统方案之瑞芯微RK3568移植案例(下)
  • OpenHarmony(鸿蒙南向开发)——标准系统方案之瑞芯微RK3566移植案例(上)
  • 持续更新中……

本文章是基于瑞芯微RK3566芯片的khdvk_3566b开发板,进行标准系统相关功能的移植,主要包括产品配置添加,内核启动、升级,音频ADM化,Camera,TP,LCD,WIFI,BT,vibrator、sensor、图形显示模块的适配案例总结,以及相关功能的适配。

产品配置和目录规划

产品配置

在产品//vendor/目录下创建以kaihong名字命名的文件夹,并在kaihong文件夹下面新建产品命的文件夹khdvk_3566b。

//vendor/kaihong/khdvk_3566b目录下创建config.json文件。该文件用于描述产品所使用的SOC以及所需的子系统。配置如下

{
  "product_name": "khdvk_3566b",
  "device_company": "kaihong",
  "device_build_path": "device/board/kaihong/build",
  "target_cpu": "arm",
  "type": "standard",
  "version": "3.0",
  "board": "khdvk_3566b",
  "enable_ramdisk": true,//是否支持ramdisk二级启动
  "build_selinux": true,// 是否支持selinux权限管理
  "subsystems": [
    {
      "subsystem": "arkui",
      "components": [
        {
          "component": "ace_engine_standard",
          "features": []
        },
        {
          "component": "napi",
          "features": []
        }
      ]
    },
    .
    .
    .
    {
      "subsystem": "thirdparty",
      "components": [
        {
          "component": "musl",
          "features": []
        }
      ]
    }
  ]
}

主要的配置内容包括:

  1. product_device:配置所使用的SOC。
  2. type:配置系统的级别,这里直接standard即可。
  3. subsystems:系统需要启用的子系统。子系统可以简单理解为一块独立构建的功能块。

已定义的子系统可以在//build/subsystem_config.json中找到。当然你也可以定制子系统。

这里建议先拷贝Hi3516DV300开发板的配置文件,删除掉hisilicon_products这个子系统。这个子系统为Hi3516DV300 SOC编译内核,不适合rk3566

目录规划

参考https://gitee.com/openharmony-sig/sig-content/blob/master/devboard/docs/board-soc-arch-design.md,并把芯片适配目录规划为:

device
├── board                                --- 单板厂商目录
│   └── kaihong                          --- 单板厂商名字:
│       └── khdvk_3566b                  --- 单板名:khdvk_3566b,主要放置开发板相关的驱动业务代码
└── soc                                  --- SoC厂商目录
    └── rockchip                         --- SoC厂商名字:rockchip
        └── rk3566                       --- SoC Series名:rk3566,主要为芯片原厂提供的一些方案,以及闭源库等


vendor
└── kaihong                              --- 开发产品样例厂商目录
    └── khdvk_3566b                      --- 产品名字:产品、hcs以及demo相关

内核启动

二级启动

二级启动简单来说就是将之前直接挂载sytem,从system下的init启动,改成先挂载ramdsik,从ramdsik中的init 启动,做些必要的初始化动作,如挂载system,vendor等分区,然后切到system下的init。

Rk3566适配主要是将主线编译出来的ramdisk打包到boot.img中,主要有以下工作:

1.使能二级启动

在//vendor/kaihong/khdvk_3566b/config.json中使能enable_ramdisk。

{
  "product_name": "khdvk_3566b",
  "device_company": "kaihong",
  "device_build_path": "device/board/kaihong/build",
  "target_cpu": "arm",
  "type": "standard",
  "version": "3.0",
  "board": "khdvk_3566b",
  "enable_ramdisk": true,//是否支持ramdisk二级启动
  "build_selinux": true,// 是否支持selinux权限管理

2.把主线编译出来的ramdsik.img 打包到boot.img

配置:

由于rk 启动uboot 支持从ramdisk 启动,只需要在打包boot_linux.img 的配置文件中增加ramdisk.img,因此没有使用主线的its格式,具体配置就是在内核编译脚本make-ohos.sh中增加:

function make_extlinux_conf()
{
    dtb_path=$1
    uart=$2
    image=$3

    echo "label rockchip-kernel-5.10" > ${EXTLINUX_CONF}
    echo "    kernel /extlinux/${image}" >> ${EXTLINUX_CONF}
    echo "    fdt /extlinux/${TOYBRICK_DTB}" >> ${EXTLINUX_CONF}
    if [ "enable_ramdisk" == "${ramdisk_flag}" ]; then
        echo "    initrd /extlinux/ramdisk.img" >> ${EXTLINUX_CONF}
    fi
    cmdline="append earlycon=uart8250,mmio32,${uart} root=PARTUUID=614e0000-0000-4b53-8000-1d28000054a9 rw rootwait rootfstype=ext4"
    echo "  ${cmdline}" >> ${EXTLINUX_CONF}
}

打包

增加了打包boot镜像的脚本make-boot.sh,供编译完ramdisk,打包boot 镜像时调用,主要内容:

genext2fs -B ${blocks} -b ${block_size} -d boot_linux -i 8192 -U boot_linux.img

调用make-boot.sh的修改可以参考如下pr:

https://gitee.com/openharmony/build/pulls/569/files

INIT配置

init相关配置请参考 启动子系统的规范要求 即可

音频

khdvk_3566b Audio硬件结构图

khdvk_3566b平台Audio驱动框架图

  1. HDI adapter

实现Audio HAL层驱动(HDI接口适配),给Audio服务(frameworks)提供所需的音频硬件驱动能力接口。包含 Audio Manager、Audio Adapter、Audio Control、Audio Capture、Audio Render等接口对象。

  1. Audio Interface Lib

配合内核中的Audio Driver Model使用,实现音频硬件的控制、录音数据的读取、播放数据的写入。它里面包括Stream_ctrl_common 通用层,主要是为了和上层的audio HDI adapter层进行对接。

  1. ADM(Audio Driver Model)

音频驱动框架模型,向上服务于多媒体音频子系统,便于系统开发者能够更便捷的根据场景来开发应用。向下服务于具体的设备厂商,对于Codec和DSP设备厂商来说,可根据ADM模块提供的向下统一接口适配各自的驱动代码,就可以实现快速开发和适配OpenHarmony系统。

  1. Audio Control Dispatch

接收lib层的控制指令并将控制指令分发到驱动层。

  1. Audio Stream Dispatch

接收lib层的数据并将数据分发到驱动层

  1. Card Manager

多声卡管理模块,每个声卡含有Dai、Platform、Codec、Accessory、Dsp、SAPM模块。

  1. Platform Drivers

驱动适配层。

  1. SAPM(Smart Audio Power Manager)

电源管理模块,对整个ADM电源进行功耗策略优化。

Audio 驱动开发

这里以khdvk_3566b为例,讲述Audio驱动开发,其涉及到的模块驱动主要有:Codec驱动、platform驱动、Dai驱动。
相关代码路径如下:

device/board/kaihong/khdvk_3566b/audio_drivers/codec/rk809_codec/
device/board/kaihong/khdvk_3566b/audio_drivers/codec/dai/
device/board/kaihong/khdvk_3566b/audio_drivers/codec/soc/

HDF HCS配置路径如下:

vendor/kaihong/khdvk_3566b/hdf_config/khdf/device_info/
vendor/kaihong/khdvk_3566b/hdf_config/khdf/audio/ 

Audio 驱动开发流程:

step1:配置各个模块的HCS
step2:修改各个模块的编译文件
step3:配置各个模块的函数操作集
step4:进行功能调试
Audio驱动开发实例
codec驱动开发实例

代码路径:
device/board/kaihong/khdvk_3566b/audio_drivers/codec/rk809_codec/

  1. 将codec注册绑定到HDF框架中,moduleName与device_info.hcs中的moduleName匹配

    struct HdfDriverEntry g_Rk809DriverEntry = {

 .moduleVersion = 1,
 .moduleName = "CODEC_RK809",
 .Bind = Rk809DriverBind,
 .Init = Rk809DriverInit,
 .Release = RK809DriverRelease,
};

HDF_INIT(g_Rk809DriverEntry);
  1. Codec模块需要填充下面三个结构体:

g_codecData:codec设备的操作函数集和私有数据集。

g_codecDaiDeviceOps:codecDai的操作函数集,包括启动传输和参数配置等函数接口。

g_codecDaiData:codec的数字音频接口的操作函数集和私有数据集。

struct CodecData g_rk809Data = {
    .Init = Rk809DeviceInit,
    .Read = RK809CodecReadReg,
    .Write = Rk809CodecWriteReg,
};

struct AudioDaiOps g_rk809DaiDeviceOps = {
    .Startup = Rk809DaiStartup,
    .HwParams = Rk809DaiHwParams,
    .Trigger = Rk809NormalTrigger,
};

struct DaiData g_rk809DaiData = {
    .DaiInit = Rk809DaiDeviceInit,
    .ops = &g_rk809DaiDeviceOps,
};

1> CodecData结构体操作函数的实现

int32_t Rk809DeviceInit(struct AudioCard *audioCard, const struct CodecDevice *device)
{
     ......  
    //get和set功能注册 
    if (CodecSetCtlFunc(device->devData, RK809GetCtrlOps, RK809SetCtrlOps) != HDF_SUCCESS) {
       AUDIO_DRIVER_LOG_ERR("AudioCodecSetCtlFunc failed.");
       return HDF_FAILURE;
    }
   //codec默认寄存器的初始化
   ret = RK809RegDefaultInit(device->devData->regCfgGroup);
   ......
   if (AudioAddControls(audioCard, device->devData->controls, device->devData->numControls) != HDF_SUCCESS) {
       AUDIO_DRIVER_LOG_ERR("add controls failed.");
       return HDF_FAILURE;
   }
   ......
}
/*读寄存器接口*/
int32_t RK809CodecReadReg(const struct CodecDevice *codec, uint32_t reg, uint32_t *val)
{
    ......
    if (Rk809DeviceRegRead(reg, val)) {
        AUDIO_DRIVER_LOG_ERR("read register fail: [%04x]", reg);
        return HDF_FAILURE;
    }

   return HDF_SUCCESS;
}
 /*写寄存器接口*/
int32_t Rk809CodecWriteReg(const struct CodecDevice *codec, uint32_t reg, uint32_t value)
{
    if (Rk809DeviceRegWrite(reg, value)) {
        AUDIO_DRIVER_LOG_ERR("write register fail: [%04x] = %04x", reg, value);
        return HDF_FAILURE;
    }

    return HDF_SUCCESS;
}

2> g_rk809DaiDeviceOps结构体的具体实现

/*Rk809DaiStartup为启动时的一些设置*/
int32_t Rk809DaiStartup(const struct AudioCard *card, const struct DaiDevice *device)
{
    ......
    ret = RK809WorkStatusEnable(device->devData->regCfgGroup);
    ......
}
/*Rk809DaiHwParams为参数配置,包括采样率、位宽等。*/
int32_t Rk809DaiHwParams(const struct AudioCard *card, const struct AudioPcmHwParams *param)
{
    ......
    ret = AudioFormatToBitWidth(param->format, &bitWidth); 
    codecDaiParamsVal.frequencyVal = param->rate;
    codecDaiParamsVal.DataWidthVal = bitWidth;

    ret =  RK809DaiParamsUpdate(card->rtd->codecDai->devData->regCfgGroup, codecDaiParamsVal);
    ......
}
/*PCM流控制寄存器相关配置*/
int32_t Rk809NormalTrigger(const struct AudioCard *card, int cmd, const struct DaiDevice *device)
{
    g_cuurentcmd = cmd;
    switch (cmd) {
        case AUDIO_DRV_PCM_IOCTL_RENDER_START:
        case AUDIO_DRV_PCM_IOCTL_RENDER_RESUME:
            RK809DeviceRegConfig(rk817_render_start_regmap_config);
        break;

       case AUDIO_DRV_PCM_IOCTL_RENDER_STOP:
       case AUDIO_DRV_PCM_IOCTL_RENDER_PAUSE:
           RK809DeviceRegConfig(rk817_render_stop_regmap_config);
           break;

       case AUDIO_DRV_PCM_IOCTL_CAPTURE_START:
       case AUDIO_DRV_PCM_IOCTL_CAPTURE_RESUME:
          RK809DeviceRegConfig(rk817_capture_start_regmap_config);
          break;

       case AUDIO_DRV_PCM_IOCTL_CAPTURE_STOP:
       case AUDIO_DRV_PCM_IOCTL_CAPTURE_PAUSE:
            RK809DeviceRegConfig(rk817_capture_stop_regmap_config);
         break;

       default:
         break;
  }

  return HDF_SUCCESS;
}
  1. 完成 bind、init和release函数的实现

HdfDriverEntry结构体的具体填充:

/*获取codec service,以及注册codec*/
static int32_t Rk809DriverInit(struct HdfDeviceObject *device)
{
   ......
   CodecGetConfigInfo(device, &(g_chip->codec)) 
   CodecSetConfigInfo(&(g_chip->codec),  &(g_chip->dai)
   GetServiceName(device)
   CodecGetDaiName(device,  &(g_chip->dai.drvDaiName)
   OsalMutexInit(&g_rk809Data.mutex);
   AudioRegisterCodec(device, &(g_chip->codec), &(g_chip->dai)
   ......
}   
/*将codec service绑定到HDF*/
static int32_t Rk809DriverBind(struct HdfDeviceObject *device)
{
    struct CodecHost *codecHost;
    ......
    codecHost = (struct CodecHost *)OsalMemCalloc(sizeof(*codecHost));
    ......
    codecHost->device = device;
    device->service = &codecHost->service;
   return HDF_SUCCESS;
}
/*释放资源*/
static void RK809DriverRelease(struct HdfDeviceObject *device)
{
   struct CodecHost *codecHost;
   ......
   codecHost = (struct CodecHost *)device->service;
   if (codecHost == NULL) {
       HDF_LOGE("CodecDriverRelease: codecHost is NULL");
       return;
   }
   OsalMemFree(codecHost);
}
  1. 配置codec hcs文件

    1> vendor/kaihong/khdvk_3566b/hdf_config/khdf/device_info/device_info.hcs

相关配置如下:

device_codec :: device {
            device0 :: deviceNode {
                policy = 1;
                priority = 50;
                preload = 0;
                permission = 0666;
                moduleName = "CODEC_RK809";
                serviceName = "codec_service_0";
                deviceMatchAttr = "hdf_codec_driver";
            }
}

2> vendor/kaihong/khdvk_3566b/hdf_config/khdf/audio/codec_config.hcs

该文件涉及音量、静音模式、mic、通道模式等相关寄存器配置

DAI驱动开发实例

代码路径:

device/board/kaihong/khdvk_3566b/audio_drivers/codec/dai/
  1. 将I2S驱动注册绑定到HDF框架中,代码片段如下,启动moduleName与HCS文件的中moduleName一致

    struct HdfDriverEntry g_daiDriverEntry = {

 .moduleVersion = 1,
 .moduleName = "DAI_RK3568",
 .Bind = DaiDriverBind,
 .Init = DaiDriverInit,
 .Release = DaiDriverRelease,
  1. DAI模块需要填充下面两个结构体

g_daiData:dai设备私有配置,其中包含dai设备的初始化、读写寄存器、操作函数。

g_daiDeviceOps:dai设备操作函数集,包含了dai的参数设置、触发、启动。

struct AudioDaiOps g_daiDeviceOps = {
    .Startup = Rk3568DaiStartup,
    .HwParams = Rk3568DaiHwParams,
    .Trigger = Rk3568NormalTrigger,
};

struct DaiData g_daiData = {
    .Read = Rk3568DeviceReadReg,
    .Write = Rk3568DeviceWriteReg,
    .DaiInit = Rk3568DaiDeviceInit,
    .ops = &g_daiDeviceOps,
};

1> AudioDaiOps结构体的具体填充

/*Rk3568DaiHwParams中主要完成一些pcm流信息的设置*/
int32_t Rk3568DaiHwParams(const struct AudioCard *card, const struct AudioPcmHwParams *param)
{
     ......  
     data->pcmInfo.channels = param->channels;

     if (AudioFormatToBitWidth(param->format, &bitWidth) != HDF_SUCCESS) {
         AUDIO_DEVICE_LOG_ERR("AudioFormatToBitWidth error");
         return HDF_FAILURE;
     }

     data->pcmInfo.bitWidth = bitWidth;
     data->pcmInfo.rate = param->rate;
     data->pcmInfo.streamType = param->streamType;

     i2sTdm = dev_get_drvdata(&platformdev->dev);
     ret = RK3568I2sTdmSetSysClk(i2sTdm, param);
     if (ret != HDF_SUCCESS) {
         AUDIO_DEVICE_LOG_ERR("RK3568I2sTdmSetSysClk error");
         return HDF_FAILURE;
     }
     ret = RK3568I2sTdmSetMclk(i2sTdm, &mclk, param);
     if (ret != HDF_SUCCESS) {
         AUDIO_DEVICE_LOG_ERR("RK3568I2sTdmSetMclk error");
         return HDF_FAILURE;
     }
     AUDIO_DEVICE_LOG_DEBUG("success");
     return HDF_SUCCESS;
}
int32_t Rk3568NormalTrigger(const struct AudioCard *card, int cmd, const struct DaiDevice *device)
{
    ......
    Rk3568TxAndRxSetReg(i2sTdm, streamType, triggerFlag);
    ......
}

2> DaiData结构体的具体填充

/*封装linux内核的读寄存器接口*/
int32_t Rk3568DeviceReadReg(const struct DaiDevice *dai, uint32_t reg, uint32_t *val)
{
    ......
    if (regmap_read(i2sTdm->regmap, reg, val)) {
    ......
}
/*封装linux内核的写寄存器接口*/  
int32_t Rk3568DeviceWriteReg(const struct DaiDevice *dai, uint32_t reg, uint32_t value)
{
    ......
    if (regmap_write(i2sTdm->regmap, reg, value)) {
    ......
}
/*dai 设备的初始化*/
int32_t Rk3568DaiDeviceInit(struct AudioCard *card, const struct DaiDevice *dai)
  1. 完成 bind、init和release函数的实现

HdfDriverEntry结构体中的bind、init、release具体填充:

static int32_t DaiDriverInit(struct HdfDeviceObject *device)
{
    ......
    DaiGetConfigInfo(device, &g_daiData)
    DaiGetServiceName(device)
    AudioSocRegisterDai(device, (void *)&g_daiData);
    ......
}
static int32_t DaiDriverBind(struct HdfDeviceObject *device)
{
    ......
    daiHost->device = device;
    device->service = &daiHost->service;
    g_daiData.daiInitFlag = false;
    ......
}
static void DaiDriverRelease(struct HdfDeviceObject *device)
{
    ......
    OsalMutexDestroy(&g_daiData.mutex);
    daiHost = (struct DaiHost *)device->service;
    OsalMemFree(daiHost);
    ......
}

4.配置dai hcs文件

1> vendor/kaihong/khdvk_3566b/hdf_config/khdf/device_info/device_info.hcs

device_dai0 :: device {
    device0 :: deviceNode {
        policy = 1;
        priority = 50;
        preload = 0;
        permission = 0666;
        moduleName = "DAI_RK3568";
        serviceName = "dai_service";
        deviceMatchAttr = "hdf_dai_driver";
    }
}

2> vendor/kaihong/khdvk_3566b/hdf_config/khdf/audio/dai_config.hcs

该文件涉及I2S时序、配置参数以及rk809使能等相关寄存器配置

Platform驱动开发实例
  1. 将DMA驱动注册到HDF框架中,代码片段如下,启动moduleName与HCS文件的中moduleName一致
struct HdfDriverEntry g_platformDriverEntry = {

 .moduleVersion = 1,
 .moduleName = "DMA_RK3568",
 .Bind = PlatformDriverBind,
 .Init = PlatformDriverInit,
 .Release = PlatformDriverRelease,
}; HDF_INIT(g_platformDriverEntry);
  1. DMA模块需要填充下面两个结构体
struct AudioDmaOps g_dmaDeviceOps = {

 .DmaBufAlloc = Rk3568DmaBufAlloc, //dma内存申请函数接口
 .DmaBufFree = Rk3568DmaBufFree,   // dma内存释放函数接口
 .DmaRequestChannel = Rk3568DmaRequestChannel,  // dma申请通道函数接口
 .DmaConfigChannel = Rk3568DmaConfigChannel,    // dma通道配置函数接口
 .DmaPrep = Rk3568DmaPrep,             // dma准备函数接口
 .DmaSubmit = Rk3568DmaSubmit,         // dma submit函数接口
 .DmaPending = Rk3568DmaPending,       // dma pending函数接口
 .DmaPause = Rk3568DmaPause,           // dma暂停、停止函数接口
 .DmaResume = Rk3568DmaResume,         // dma恢复函数接口
 .DmaPointer = Rk3568PcmPointer,       // dma获取当前播放或录音位置函数接口
};

struct PlatformData g_platformData = {

 .PlatformInit = AudioDmaDeviceInit,   // dma设备初始化接口
 .ops = &g_dmaDeviceOps,
};
  1. 完成 bind、init和release函数的实现

HdfDriverEntry结构体中的bind、init、release具体填充:

static int32_t PlatformDriverInit(struct HdfDeviceObject *device)
{
    ......
    PlatformGetServiceName(device);
    AudioSocRegisterPlatform(device, &g_platformData)
    ......
}
static int32_t PlatformDriverBind(struct HdfDeviceObject *device)
{
    ......
    platformHost->device = device;
    device->service = &platformHost->service;
    ......
}
static void PlatformDriverRelease(struct HdfDeviceObject *device)
{
   ......
   platformHost = (struct PlatformHost *)device->service;
   OsalMemFree(platformHost);
   ......
}
  1. 配置dma hcs文件

1> vendor/kaihong/khdvk_3566b/hdf_config/khdf/device_info/device_info.hcs

相关配置如下:

 device_dma :: device {
     device0 :: deviceNode {
         policy = 1;
         priority = 50;
         preload = 0;
         permission = 0666;
         moduleName = "DMA_RK3568";
         serviceName = "dma_service_0";
         deviceMatchAttr = "hdf_dma_driver";
     }
 }

2> vendor/kaihong/khdvk_3566b/hdf_config/khdf/audio/dma_config.hcs

没有特殊参数需要配置,一般情况下不需改动。

Makefile和Kconfig配置文件

文件路径:

drivers/adapter/khdf/linux/model/audio

Makefile文件相关内容:

obj-$(CONFIG_DRIVERS_HDF_AUDIO_RK3566) += \
      $(KHDF_AUDIO_RK3566_DIR)/codec/rk809_codec/src/rk809_codec_adapter.o \
      $(KHDF_AUDIO_RK3566_DIR)/codec/rk809_codec/src/rk809_codec_impl.o \
      $(KHDF_AUDIO_RK3566_DIR)/codec/rk809_codec/src/rk809_codec_linux_driver.o \
      $(KHDF_AUDIO_RK3566_DIR)/dsp/src/rk3568_dsp_adapter.o \
      $(KHDF_AUDIO_RK3566_DIR)/dsp/src/rk3568_dsp_ops.o \
      $(KHDF_AUDIO_RK3566_DIR)/dai/src/rk3568_dai_adapter.o \
      $(KHDF_AUDIO_RK3566_DIR)/dai/src/rk3568_dai_ops.o \
      $(KHDF_AUDIO_RK3566_DIR)/dai/src/rk3568_dai_linux_driver.o \
      $(KHDF_AUDIO_RK3566_DIR)/soc/src/rk3568_dma_adapter.o \
      $(KHDF_AUDIO_RK3566_DIR)/soc/src/rk3568_dma_ops.o

Kconfig相关内容:

config DRIVERS_HDF_AUDIO_RK3566
    bool "Enable HDF Audio Codec driver"
    default n
    depends on DRIVERS_HDF_AUDIO
    help
       Answer Y to choice HDF Audio Codec driver.

LCD

khdvk_3566b平台默认支持一个mipi接口的lcd屏幕

LCD的适配主要依赖于HDF显示模型,显示驱动模型基于 HDF 驱动框架、Platform 接口及 OSAL 接口开发,可以屏蔽不同内核形态(LiteOS、Linux)差异,适用于不同芯片平台,为显示屏器件提供统一的驱动平台。

如图为 HDF Display驱动模型层次关系

当前驱动模型主要部署在内核态中,向上对接到 Display 公共 hal 层,辅助 HDI 的实现。显示驱动通过 Display-HDI 层对图形服务暴露显示屏驱动能力;向下对接显示屏 panel 器件,驱动屏幕正常工作,自上而下打通显示全流程通路。

所以LCD的适配主要在于LCD panel器件驱动的适配

器件驱动的适配分为2部分:panel驱动和hcs配置

涉及的文件有:

drivers/framework/model/display/driver/panel
vendor/kaihong/khdvk_3566b/hdf_config/khdf/device_info
vendor/kaihong/khdvk_3566b/hdf_config/khdf/input

panel驱动

器件驱动主要围绕如下接口展开:

struct PanelData {
    struct HdfDeviceObject *object;
    int32_t (*init)(struct PanelData *panel);
    int32_t (*on)(struct PanelData *panel);
    int32_t (*off)(struct PanelData *panel);
    int32_t (*prepare)(struct PanelData *panel);
    int32_t (*unprepare)(struct PanelData *panel);
    struct PanelInfo *info;
    enum PowerStatus powerStatus;
    struct PanelEsd *esd;
    struct BacklightDev *blDev;
    void *priv;
};

驱动中在初始化接口中实例化该结构体:

panelSimpleDev->panel.init = PanelSimpleInit;
panelSimpleDev->panel.on = PanelSimpleOn;
panelSimpleDev->panel.off = PanelSimpleOff;
panelSimpleDev->panel.prepare = PanelSimplePrepare;
panelSimpleDev->panel.unprepare = PanelSimpleUnprepare;

static void PanelResInit(struct panel_jdi_gt911_dev *panel_dev)
{
   ......   
   panel_dev->panel.info = &g_panelInfo;
   panel_dev->panel.init = PanelInit;
   panel_dev->panel.on = PanelOn;
   panel_dev->panel.off = PanelOff;
   panel_dev->panel.prepare = PanelPrepare;
   panel_dev->panel.unprepare = PanelUnprepare;
   ...... 
}

g_panelInfo配置panel基础参数

PanelInit负责panel的软件初始化

PanelOn负责亮屏

PanelOff负责灭屏

PanelPrepare负责亮屏的硬件时序初始化

PanelUnprepare负责灭屏的硬件时序初始化

实例化后使用RegisterPanel接口向display模型注册该panel驱动即可

需要说明的是,khdvk_3566b上的这款lcd使用的时候DRM显示框架

hcs配置

device3 :: deviceNode {
       policy = 0;
       priority = 100;
       preload = 0;
       moduleName = "LCD_MIPI_JDI_GT911";
}

背光

背光控制分为原生linux内核框架下背光驱动以及基于HDF框架开发的背光驱动模型。

rk3566背光是通过pwm控制占空比实现的,具体使用的是pwm4

linux背光驱动代码路径:

linux-5.10/drivers/video/backlight/pwm_bl.c
linux-5.10/drivers/video/backlight/backlight.c
linux-5.10/drivers/pwm/pwm-rockchip.c

使用HDF框架下的背光驱动,需要关闭原生驱动

    # CONFIG_BACKLIGHT_PWM is not set

HDF实现

基于HDF框架开发的背光驱动模型,如下图:

代码路径:

drivers/framework/model/display/driver/backlight/hdf_bl.c

HDF BL入口函数:

    static int32_t BacklightInit(struct HdfDeviceObject *object)
    {
         if (object == NULL) {
         HDF_LOGE("%s: object is null!", __func__);
         return HDF_FAILURE;
         }
    
         HDF_LOGI("%s success", __func__);
         return HDF_SUCCESS;
    }
    
    struct HdfDriverEntry g_blDevEntry = {
        .moduleVersion = 1,
        .moduleName = "HDF_BL",
        .Init = BacklightInit,
        .Bind = BacklightBind,
    };
    
    HDF_INIT(g_blDevEntry);

代码路径:

drivers/framework/model/display/driver/backlight/pwm_bl.c

HDF PWM入口函数:

    struct HdfDriverEntry g_pwmBlDevEntry = {
    .moduleVersion = 1,
    .moduleName = "PWM_BL",
    .Init = BlPwmEntryInit,
    };
    HDF_INIT(g_pwmBlDevEntry);

具体控制背光的接口:

    static int32_t BlPwmUpdateBrightness(struct BacklightDev *blDev, uint32_t brightness)
    {
        int32_t ret;
        uint32_t duty;
        struct BlPwmDev *blPwmDev = NULL;
    
        blPwmDev = ToBlDevPriv(blDev);
        if (blPwmDev == NULL) {
            HDF_LOGE("%s blPwmDev is null", __func__);
            return HDF_FAILURE;
        }
    
        if (blPwmDev->props.maxBrightness == 0) {
            HDF_LOGE("%s maxBrightness is 0", __func__);
            return HDF_FAILURE;
        }
    
        if (brightness == 0) {
             return PwmDisable(blPwmDev->pwmHandle);
        }
    
        duty = (brightness * blPwmDev->config.period) / blPwmDev->props.maxBrightness;
        ret = PwmSetDuty(blPwmDev->pwmHandle, duty);
        if (ret != HDF_SUCCESS) {
            HDF_LOGE("%s: PwmSetDuty failed, ret %d", __func__, ret);
            return HDF_FAILURE;
        }
        return PwmEnable(blPwmDev->pwmHandle);
    }
    
    static struct BacklightOps g_blDevOps = {
         .updateBrightness = BlPwmUpdateBrightness,
    };

HDF PWM实现的调用的就是内核pwm的接口。

代码路径:

drivers/framework/model/display/driver/panel/mipi_jdi_gt911.c

在LCD HDF器件驱动注册背光:

    panel_dev->panel.blDev = GetBacklightDev("hdf_pwm");
    if (panel_dev->panel.blDev == NULL) {
        HDF_LOGE("%s GetBacklightDev fail", __func__);
        goto FAIL;
    }

HCS配置

驱动hcs配置:

    device_pwm_bl :: device {
        device0 :: deviceNode {
            policy = 0;
            priority = 95;
            preload = 0;
            moduleName = "PWM_BL";
            deviceMatchAttr = "pwm_bl_dev";
        }
    }
    device_backlight :: device {
        device0 :: deviceNode {
            policy = 2;
            priority = 90;
            preload = 0;
            permission = 0660;
            moduleName = "HDF_BL";
            serviceName = "hdf_bl";
        }
    }

pwm背光的hcs配置:

    root {
        backlightConfig {
            pwmBacklightConfig {
                match_attr = "pwm_bl_dev";
                pwmDevNum = 1;
                pwmMaxPeroid = 25000;
                backlightDevName = "hdf_pwm";
                minBrightness = 0;
                defBrightness = 127;
                maxBrightness = 255;
           }
       }
    }

测试

cat /sys/kernel/debug/pwm 来查看hdf pwm是否申请到pwm4

申请成功有如下结果:

requested 代表申请成功

enabled 代表pwm4使能成功

    # cat /sys/kernel/debug/pwm
    platform/fe6e0000.pwm, 1 PWM device
    pwm-0   (backlight           ): requested period: 25000 ns duty: 0 ns polarity: normal

显示适配

显示适配需要完成的工作:图形服务HDI接口适配、GPU适配、mipi dsi驱动适配

显示HDI

显示HDI 对图形服务提供显示驱动能力,包括显示图层的管理、显示内存的管理及硬件加速等。 显示HDI需要适配两部分:gralloc 和 display_device。

OpenHarmony提供了使用与Hi3516DV300参考实现,厂商可根据实际情况参考适配,khdvk_3566b display适配是在//device/soc/rockchip/hardware/display目录下,仓名为 device_soc_rockchip 。

display gralloc适配

gralloc模块提供显示内存管理功能,该实现基于drm开发。

drm设备节点定义在//device/soc/rockchip/hardware/display/src/display_gralloc/display_gralloc_gbm.c文件中,根据khdvk_3566b实际情况修改了drm文件节点。

const char *g_drmFileNode = "/dev/dri/renderD128";
display device适配

display device模块提供显示设备管理、layer管理、硬件加速等功能。

  1. display drm设备节点初始化,根据khdvk_3566b实际情况修改了drm设备名称。
//device/soc/rockchip/hardware/display/src/display_device/drm/drm_device.cpp
std::shared_ptr<HdiDeviceInterface> DrmDevice::Create()
{
    DISPLAY_DEBUGLOG();
    if (mDrmFd == nullptr) {
        const std::string name("rockchip");    // 将drm驱动设备名称修改为“rockchip”
        int drmFd = open("/dev/dri/card0", O_RDWR | O_CLOEXEC);  // 将drm驱动设备文件句柄修改为"/dev/dri/card0"
        if (drmFd < 0) {
            DISPLAY_LOGE("drm file:%{public}s open failed %{public}s", name.c_str(), strerror(errno));
            return nullptr;
        }
        DISPLAY_DEBUGLOG("the drm fd is %{public}d", drmFd);
        mDrmFd = std::make_shared<HdiFd>(drmFd);
    }
    if (mInstance == nullptr) {
        mInstance = std::make_shared<DrmDevice>();
    }
    return mInstance;
}
  1. display硬件合成的修改
//device/soc/rockchip/hardware/display/src/display_gfx/display_gfx.c

硬件合成文件添加了颜色空间的支持模式

RgaSURF_FORMAT colorSpaceModeChange(PixelFormat color, uint8_t *isYuv)
{
    RgaSURF_FORMAT rkFormat;
    switch (color) {
        case PIXEL_FMT_RGB_565:          /**< RGB565 format */
            rkFormat = RK_FORMAT_RGB_565;
            *isYuv = 0;
            break;
        case PIXEL_FMT_RGBA_4444:        /**< RGBA4444 format */
            rkFormat = RK_FORMAT_RGBA_4444;
            *isYuv = 0;
            break;
        case PIXEL_FMT_RGBA_5551:        /**< RGBA5551 format */
            rkFormat = RK_FORMAT_RGBA_5551;
            *isYuv = 0;
            break;
        case PIXEL_FMT_RGBX_8888:        /**< RGBX8888 format */
            rkFormat = RK_FORMAT_RGBX_8888;
            *isYuv = 0;
            break;
        case PIXEL_FMT_RGBA_8888:        /**< RGBA8888 format */
            rkFormat = RK_FORMAT_RGBA_8888;
            *isYuv = 0;
            break;
        case PIXEL_FMT_RGB_888:          /**< RGB888 format */
            rkFormat = RK_FORMAT_RGB_888;
            *isYuv = 0;
            break;
        case PIXEL_FMT_BGR_565:          /**< BGR565 format */
            rkFormat = RK_FORMAT_BGR_565;
            *isYuv = 0;
            break;
        case PIXEL_FMT_BGRA_4444:        /**< BGRA4444 format */
            rkFormat = RK_FORMAT_BGRA_4444;
            *isYuv = 0;
            break;
        case PIXEL_FMT_BGRA_5551:        /**< BGRA5551 format */
            rkFormat = RK_FORMAT_BGRA_5551;
            *isYuv = 0;
            break;
        case PIXEL_FMT_BGRX_8888:        /**< BGRX8888 format */
            rkFormat = RK_FORMAT_BGRX_8888;
            *isYuv = 0;
            break;
        case PIXEL_FMT_BGRA_8888:        /**< BGRA8888 format */
            rkFormat = RK_FORMAT_BGRA_8888;
            *isYuv = 0;
            break;
        case PIXEL_FMT_YCBCR_422_SP:     /**< YCBCR422 semi-planar format */
            rkFormat = RK_FORMAT_YCbCr_420_SP;
            *isYuv = 1;
            break;
        case PIXEL_FMT_YCRCB_422_SP:     /**< YCRCB422 semi-planar format */
            rkFormat = RK_FORMAT_YCrCb_422_SP;
            *isYuv = 1;
            break;
        case PIXEL_FMT_YCBCR_420_SP:     /**< YCBCR420 semi-planar format */
            rkFormat = RK_FORMAT_YCbCr_420_SP;
            *isYuv = 1;
            break;
        case PIXEL_FMT_YCRCB_420_SP:     /**< YCRCB420 semi-planar format */
            rkFormat = RK_FORMAT_YCrCb_420_SP;
            *isYuv = 1;
            break;
        case PIXEL_FMT_YCBCR_422_P:      /**< YCBCR422 planar format */
            rkFormat = RK_FORMAT_YCbCr_422_P;
            *isYuv = 1;
            break;
        case PIXEL_FMT_YCRCB_422_P:      /**< YCRCB422 planar format */
            rkFormat = RK_FORMAT_YCrCb_422_P;
            *isYuv = 1;
            break;
        case PIXEL_FMT_YCBCR_420_P:      /**< YCBCR420 planar format */
            rkFormat = RK_FORMAT_YCbCr_420_P;
            *isYuv = 1;
            break;
        case PIXEL_FMT_YCRCB_420_P:      /**< YCRCB420 planar format */
            rkFormat = RK_FORMAT_YCrCb_420_P;
            *isYuv = 1;
            break;
        case PIXEL_FMT_YUYV_422_PKG:     /**< YUYV422 packed format */
            rkFormat = RK_FORMAT_YUYV_422;
            *isYuv = 1;
            break;
        case PIXEL_FMT_UYVY_422_PKG:     /**< UYVY422 packed format */
            rkFormat = RK_FORMAT_UYVY_422;
            *isYuv = 1;
            break;
        case PIXEL_FMT_YVYU_422_PKG:     /**< YVYU422 packed format */
            rkFormat = RK_FORMAT_YUYV_422;
            *isYuv = 1;
            break;
        case PIXEL_FMT_VYUY_422_PKG:     /**< VYUY422 packed format */
            rkFormat = RK_FORMAT_VYUY_422;
            *isYuv = 1;
            break;
        default:
            rkFormat = RK_FORMAT_UNKNOWN;
            break;
    }
    return rkFormat;
}

在合成时增加了旋转90、180、270度

int32_t TransformTypeChange(TransformType type)
{
    int32_t rkRotateType;
    switch (type) {
        case ROTATE_90:            /**< Rotation by 90 degrees */
            rkRotateType = IM_HAL_TRANSFORM_ROT_90;
            break;
        case ROTATE_180:             /**< Rotation by 180 degrees */
            rkRotateType = IM_HAL_TRANSFORM_ROT_180;
            break;
        case ROTATE_270:             /**< Rotation by 270 degrees */
            rkRotateType = IM_HAL_TRANSFORM_ROT_270;
            break;
        default:
            rkRotateType = 0;        /**< No rotation */
            break;
    }
    return rkRotateType;
}
测试验证

hello_composer 测试模块:Rosen图形框架提供的测试程序,主要显示流程,HDI接口等功能是否正常,默认随系统编译。

代码路径:

foundation/graphic/graphic_2d/rosen/samples/composer/
├── BUILD.gn
├── hello_composer.cpp
├── hello_composer.h
├── layer_context.cpp
├── layer_context.h
└── main.cpp

具体验证如下:

  1. 关闭render service
   service_control stop render_service
  1. 关闭 fondation进程
   service_control stop fondation
  1. 运行hello_composer测试相关接口
    切换到/system/bin目录下,运行hello_composer测试命令
   #cd /system/bin
   #./hello_composer
   rga_api version 1.3.0_[1] (df26244 build: 2021-09-01 11:23:31 base: )

查看mipi显示屏幕上的变化

https://gitee.com/openharmony/drivers_peripheral/tree/master/display/test/unittest/standard单元测试:HDI显示模块提供的测试模块,主要测试HDI接口、显示buffer、驱动等能力,测试时也需要关闭render service和fondation进程。

代码路径:/drivers/peripheral/display/test/unittest/standard

├── BUILD.gn
├── common
│   ├── display_test.h
│   ├── display_test_utils.cpp
│   └── display_test_utils.h
├── display_device
│   ├── hdi_composition_check.cpp
│   ├── hdi_composition_check.h
│   ├── hdi_device_test.cpp
│   ├── hdi_device_test.h
│   ├── hdi_test_device_common.h
│   ├── hdi_test_device.cpp
│   ├── hdi_test_device.h
│   ├── hdi_test_display.cpp
│   ├── hdi_test_display.h
│   ├── hdi_test_layer.cpp
│   ├── hdi_test_layer.h
│   ├── hdi_test_render_utils.cpp
│   └── hdi_test_render_utils.h
│── display_gfx
│   │── display_gfx_test.cpp
│   │── display_gfx_test.h
│   │── soft_blit.cpp
│   │── soft_blit.h
└── display_gralloc
    ├── display_gralloc_test.cpp
    └── display_gralloc_test.h

具体验证如下:

  1. 添加编译模块
    修改drivers/peripheral/display/test/BUILD.gn
  group("hdf_test_display") {
    testonly = true
    deps = [ 
    "fuzztest:hdf_display_fuzztest",
    "unittest/standard:hdf_unittest_display",        //添加display单元测试
    ]
  }
  1. 添加缺失的文件包含
    修改drivers/peripheral/display/test/unittest/standard/BUILD.gn
    在第63行处,添加包含目录//device/soc/rockchip/hardware/display/src/display_gralloc,如果不修改此处有可能编译报错。
ohos_unittest("gralloctest") {
  module_out_path = module_output_path
  sources = [ "display_gralloc/display_gralloc_test.cpp" ]
  deps = [
    "//drivers/peripheral/display/hal:hdi_display_gralloc",
    "//third_party/googletest:gtest_main",
  ]
  include_dirs = [
    "common",
    "//drivers/peripheral/display/hal/default_standard/include",
    "//drivers/peripheral/display/hal/default_standard/src/display_gralloc",
    "//device/soc/rockchip/hardware/display/src/display_gralloc",        //添加这行,将display_gralloc包含进编译
    "//drivers/peripheral/display/interfaces/include",
    "//drivers/peripheral/base",
    "//drivers/peripheral/display/interfaces/include",
    "//foundation/graphic/standard/utils/include",
  ]
  external_deps = [
    "device_driver_framework:libhdf_utils",
    "utils_base:utils",
  ]
}
  1. 编译命令
    编译hdf_test_display的命令如下:
   ./build.sh --product-name khdvk_3566b --build-target hdf_test_display
  1. 编译结果
    编译结果路径在out/khdvk_3566b/tests/unittest/hdf/display目录下,该目录下有三个可执行文件devicetest、gfxtest、gralloctest,可将这三文件通过hdc发送到khdvk_3566b开发板上运行测试。

  2. 运行测试
    通过hdc下载到开发板/system/bin/目录下,并修改测试程序的可执行属性,在终端下输入如下命令

hdc_std.exe file send D:\hdc\devicetest /system/bin/
hdc_std.exe file send D:\hdc\gfxtest /system/bin/
hdc_std.exe file send D:\hdc\gralloctest /system/bin/

进入hdc命令hdc_std.exe shell后

先关闭render service和foundation:

service_control stop render_service
service_control stop fondation

再分别执行命令,查看mipi屏显示结果:

cd /system/bin/

执行devicetest

chmod -R 777 devicetest
devicetest

执行gfxtest

chmod -R 777 gfxtest
gfxtest

执行gralloctest

chmod -R 777 gralloctest
gralloctest

GPU

GPU图形处理器, khdvk_3566b GPU适配是在//device/soc/rockchip/hardware/gpu目录下,目前采用的是rockchip提供闭源的bifrost gpu方案。

目录结构:

├── BUILD.gn
├── lib64
│   └── libmali-bifrost-g52-g2p0-ohos.so
├── lib
│   └── libmali-bifrost-g52-g2p0-ohos.so
└── include
    └── gbm.h

gpu编译的内容,我们来看下BUILD.gn的内容,其中我们预编译了libmali-bifrost-g52-g2p0-ohos.so动态库,khdvk_3566b是arm64位的,所以编译了lib64目录下的libmali-bifrost-g52-g2p0-ohos.so动态库。其中gup模块符号链接libEGL.so、libGLESv1.so、libGLESv2.so、libGLESv3.so、libmali.so.0、libmali.so.1动态库的符号。

import("//build/ohos.gni")
import("//build/ohos/ndk/ndk.gni")

config("libmali-bifrost-g52-g2p0-ohos") {
  include_dirs = [ "include" ]
  cflags = [
    "-Wno-incompatible-pointer-types",
    "-Werror",
    "-Wimplicit-function-declaration",
    "-Wno-error=unused-variable",
  ]
  cflags = []
}

ohos_prebuilt_shared_library("mali-bifrost-g52-g2p0-ohos") {
  if (target_cpu == "arm") {
    source = "lib/libmali-bifrost-g52-g2p0-ohos.so"
  } else if (target_cpu == "arm64") {
    source = "lib64/libmali-bifrost-g52-g2p0-ohos.so"
  }

  # decoupling system.img and vendor.img
  install_images = [ chipset_base_dir ]
  relative_install_dir = "chipsetsdk"
  subsystem_name = "rockchip_products"
  part_name = "rockchip_products"
  install_enable = true
  symlink_target_name = [
    "libEGL.so",
    "libGLESv1.so",
    "libGLESv2.so",
    "libGLESv3.so",
    "libmali.so.0",
    "libmali.so.1",
  ]
}

TOUCH PANEL

常见的INPUT设备有键盘、鼠标、游戏杆、Touch Screen等。Touch 设备与主机通讯采用标准 I2C 总线,触屏 IC 提供中断支持,提高了触屏数据的实时性。本项目的触摸屏器件IC 为 GT911。

驱动框架模型

INPUT驱动模型

INPUT 驱动模型核心部分由设备管理层、公共驱动层、器件驱动层组成。

(1)设备管理层:为各类输入设备驱动提供input设备的注册、注销接口,同时统一管理 input 设备列表;

(2)平台驱动层:指各类input设备的公共抽象驱动(例如触摸屏的公共驱动),负责对板级硬件进行初始化、硬件中断处理、向manager注册input设备等;

(3)器件驱动层:指各器件厂家的差异化驱动,通过适配平台驱动预留的差异化接口,实现器件驱动开发量最小化。

HDI接口层框架图

INPUT驱动提供给系统服务Input Service可直接调用的驱动能力接口,按照属性分类三类:input设备管理模块、input数据上报模块、input业务控制模块,HDI接口主要包括如下三大类:

  • input设备管理模块:管理输入设备,包括输入设备的打开、关闭、设备列表信息获取等;
  • input数据上报模块:负责输入事件的上报,包括注册、注销数据上报回调函数等;
  • input业务控制模块:提供input设备的业务控制接口,包括获取器件信息及设备类型、设置电源状态等。

HDF驱动适配

HCS配置

配置设备描述信息,在device_info.hcs中添加device_touch_chip:

 input :: host {
            hostName = "input_host";
            priority = 100;
            device_input_manager :: device {   // Input管理层设备描述信息
                device0 :: deviceNode {
                    policy = 2;
                    priority = 100;
                    preload = 0;
                    permission = 0660;
                    moduleName = "HDF_INPUT_MANAGER";
                    serviceName = "hdf_input_host";
                    deviceMatchAttr = "";
                }
            }
            device_hdf_touch :: device {      // Input公共驱动层设备描述信息
                device0 :: deviceNode {
                    policy = 2;
                    priority = 120;
                    preload = 0;
                    permission = 0660;
                    moduleName = "HDF_TOUCH";
                    serviceName = "hdf_input_event1";
                    deviceMatchAttr = "touch_device1";
                }
            }
            device_touch_chip :: device {     // Input器件驱动层信息
                device0 :: deviceNode {
                    policy = 0;
                    priority = 130;
                    preload = 0;
                    permission = 0660;
                    moduleName = "HDF_TOUCH_GT911";
                    serviceName = "hdf_touch_gt911_service";
                    deviceMatchAttr = "zsj_gt911_5p5";
                }
            }
            device_hdf_hid :: device {
                device0 :: deviceNode {
                    policy = 2;
                    priority = 200;
                    preload = 0;
                    permission = 0660;
                    moduleName = "HDF_HID";
                }
            }
        }

配置Touch器件信息,在input_config.hcs中添加器件的特性:

 chipConfig {
                    template touchChip {
                        match_attr = "";
                        chipName = "gt911";
                        vendorName = "zsj";
                        chipInfo = "AAAA11222";  
                        busType = 0;
                        deviceAddr = 0x5D;                       
                        irqFlag = 2;
                        maxSpeed = 400;
                        chipVersion = 0; //parse Coord TypeA
                        powerSequence {
                            /* [type, status, dir , delay]
                                <type> 0:none 1:vcc-1.8v 2:vci-3.3v 3:reset 4:int
                                <status> 0:off or low  1:on or high  2:no ops
                                <dir> 0:input  1:output  2:no ops
                                <delay> meanings delay xms, 20: delay 20ms
                             */
                            powerOnSeq = [4, 0, 1, 5,
                                         3, 0, 1, 10,
                                         3, 1, 1, 60,
                                         4, 2, 0, 50];
                            suspendSeq = [3, 0, 2, 10];
                            resumeSeq = [3, 1, 2, 10];
                            powerOffSeq = [3, 0, 2, 10,
                                           1, 0, 2, 20];
                        }
                    }

                    chip0 :: touchChip {
                        match_attr = "zsj_gt911_5p5";
                        chipInfo = "ZIDN45100";  
                        chipVersion = 0; 
                    }     
                }
适配文件

Touch驱动适配涉及的文件及目录:

1、 编辑 Makefile 文件:./drivers/adapter/khdf/linux/model/input/Makefile

2、 公共配置文件:./vendor/kaihong/khdvk_3566b/hdf_config/khdf/device_info/device_info.hcs

3、 私有配置文件:./vendor/kaihong/khdvk_3566b/hdf_config/khdf/input/input_config.hcs

4、 驱动:drivers\framework\model\input\driver\touchscreen

HDF驱动模型高度抽象集成,TP驱动的适配主要是器件驱动层的适配,首先需要明确TP所需要的软硬件资源。

TP模组需要主机上的如下硬件资源:

1.中断引脚

2.Reset引脚

3.使用的哪一组i2c,从设备的地址是什么

4.TP的初始化固件(通常由IC厂商提供)

5.触摸屏的分辨率

TP模组需要依赖主机上的如下软件资源:

1.Hdf gpio子系统 用于设置gpio pin脚以及一些中断资源

2.Hdf i2c 子系统 用于进行i2c通信

3.Input模型

器件差异化接口适配,示例代码路径:

./drivers/framework/model/input/driver/touchscreen/Touch_gdi_gt911.c

static struct TouchChipOps g_gt911ChipOps = {     // 器件IC接口
    .Init = ChipInit,                             // 初始化
    .Detect = ChipDetect,                         // 器件检测
    .Resume = ChipResume,                         // 唤醒
    .Suspend = ChipSuspend,                       // 休眠
    .DataHandle = ChipDataHandle,                 // 器件数据读取
    .UpdateFirmware = UpdateFirmware,             // 固件升级
    .SetAbility = SetAbility,                     // 配置
};

器件驱动初始化及HDF注册,示例代码路径:

./drivers/framework/model/input/driver/touchscreen/touch_jdi_gt911.c

static int32_t HdfGoodixChipInit(struct HdfDeviceObject *device)
{
    ...
    /* 器件配置结构体内存申请、配置信息解析及挂载 */
    chipCfg = ChipConfigInstance(device);
    ...
    /* 器件实例化 */
    chipDev = ChipDeviceInstance();
    ...
    /* 器件信息挂载及器件私有操作挂载 */
    chipDev->chipCfg = chipCfg;
    chipDev->ops = &g_gt911ChipOps;
    ...
    /* 注册器件驱动至平台驱动 */
    RegisterChipDevice(chipDev);
    ...
}

struct HdfDriverEntry g_touchGoodixChipEntry = {
    .moduleVersion = 1,
    .moduleName = "HDF_TOUCH_GT911",
    .Init = HdfGoodixChipInit,          // 器件驱动初始化函数
    .Release = HdfGoodixChipRelease,
};
HDF_INIT(g_touchGoodixChipEntry);       // 注册器件驱动至HDF框架
代码分布
/drivers/peripheral/input
/drivers/framework/model/input

经常有很多小伙伴抱怨说:不知道学习鸿蒙开发哪些技术?不知道需要重点掌握哪些鸿蒙应用开发知识点?

为了能够帮助到大家能够有规划的学习,这里特别整理了一套纯血版鸿蒙(HarmonyOS Next)全栈开发技术的学习路线,包含了鸿蒙开发必掌握的核心知识要点,内容有(ArkTS、ArkUI开发组件、Stage模型、多端部署、分布式应用开发、WebGL、元服务、OpenHarmony多媒体技术、Napi组件、OpenHarmony内核、OpenHarmony驱动开发、系统定制移植等等)鸿蒙(HarmonyOS NEXT)技术知识点。

在这里插入图片描述

《鸿蒙 (Harmony OS)开发学习手册》(共计892页):https://gitcode.com/HarmonyOS_MN/733GH/overview

如何快速入门?

1.基本概念
2.构建第一个ArkTS应用
3.……

开发基础知识:

1.应用基础知识
2.配置文件
3.应用数据管理
4.应用安全管理
5.应用隐私保护
6.三方应用调用管控机制
7.资源分类与访问
8.学习ArkTS语言
9.……

在这里插入图片描述

基于ArkTS 开发

1.Ability开发
2.UI开发
3.公共事件与通知
4.窗口管理
5.媒体
6.安全
7.网络与链接
8.电话服务
9.数据管理
10.后台任务(Background Task)管理
11.设备管理
12.设备使用信息统计
13.DFX
14.国际化开发
15.折叠屏系列
16.……

在这里插入图片描述

鸿蒙开发面试真题(含参考答案):https://gitcode.com/HarmonyOS_MN/733GH/overview

在这里插入图片描述

OpenHarmony 开发环境搭建

图片

《OpenHarmony源码解析》:https://gitcode.com/HarmonyOS_MN/733GH/overview

  • 搭建开发环境
  • Windows 开发环境的搭建
  • Ubuntu 开发环境搭建
  • Linux 与 Windows 之间的文件共享
  • ……
  • 系统架构分析
  • 构建子系统
  • 启动流程
  • 子系统
  • 分布式任务调度子系统
  • 分布式通信子系统
  • 驱动子系统
  • ……

图片

OpenHarmony 设备开发学习手册:https://gitcode.com/HarmonyOS_MN/733GH/overview

图片
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2146831.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Eclipse离线安装Tomcat插件

Eclipse离线安装Tomcat插件 最近的自己在对低版本的代码的进行维护补丁,不得不采用Eclipse 来进行跑项目,真的是折磨 其中遇到一个问题就是打开Eclipse的2021版,安装Tomcat的插件,发现好家伙,就是死活在线安装失败 (喵的,真的是让我抓耳挠腮!!哈哈哈) 无奈,只好采用离线安装,特…

C# 携手 7-Zip 命令行:大文件压缩的终极武器?

前言 嗨&#xff0c;大家好&#xff01; 今天咱们来聊聊如何用 C# 调用 7-Zip 命令行来压缩大文件&#xff0c;这是个既高效又稳定的好办法&#xff0c;亲测有效&#xff01; 在实际工作中&#xff0c;压缩文件几乎是家常便饭&#xff0c;但可惜的是&#xff0c;许多常用的方…

VBS学习2:问题解决(文件中含义中文运行报错或者中文乱码)

文件中含义中文运行报错或者中文乱码 问题 msgbox"fdsfdsf大蘇打撒旦dsfsdffsdfsd发斯蒂芬斯蒂芬"解决 文件编码修改成GB2312

JAVA开源项目 校园美食分享平台 计算机毕业设计

本文项目编号 T 033 &#xff0c;文末自助获取源码 \color{red}{T033&#xff0c;文末自助获取源码} T033&#xff0c;文末自助获取源码 目录 一、系统介绍二、演示录屏三、启动教程四、功能截图五、文案资料5.1 选题背景5.2 国内外研究现状5.3 可行性分析 六、核心代码6.1 查…

springboot通过tomcat部署项目(包含jar、war两种方式,迄今为止全网最详细!2024更新..建议收藏,教学!万字长文!)

本博客参考的所有文章均已在结尾声明&#xff01;&#xff01;&#xff01; 在 Spring Boot 项目中&#xff0c;有两种常见的部署方式&#xff1a; 1、使用 Spring Boot 自带的 内置 Tomcat&#xff0c;将项目打包为 jar 并直接运行。 2、使用 外置 Tomcat&#xff0c;将项目打…

DHCP服务(relay中继)实验简述

一、DHCP服务&#xff08;relay中继模式&#xff09; 一、配置接入交换机LSW 2 The device is running! sys Enter system view, return user view with CtrlZ. [Huawei]undo info-center enable Info: Information center is disabled. [Huawei]sysname LSW2 [LSW2]vlan batc…

记录可编辑表格(未完整)

每一行都独立 <el-table-column label"操作" width"220" fixed"right"><template #default"{ row, $index }"><el-buttonv-if"!row.tableEditFlag"type"primary"size"small"click"…

中间件:maxwell、canal

文章目录 1、底层原理&#xff1a;基于mysql的bin log日志实现的&#xff1a;把自己伪装成slave2、bin log 日志有三种模式&#xff1a;2.1、statement模式&#xff1a;2.2、row模式&#xff1a;2.3、mixed模式&#xff1a; 3、maxwell只支持 row 模式&#xff1a;4、maxwell介…

Vue 自定义指令实战

引言 Vue自定义指令是Vue.js框架中强大而灵活的功能之一&#xff0c;它允许开发者根据具体需求创建自定义的指令&#xff0c;以实现更加精细化的交互和数据绑定效果。本文将带你深入探索Vue自定义指令的使用方法、原理和实战&#xff0c;为你打开前端开发的新视野。 我们前面…

2024年汉字小达人区级自由报名备考冲刺:今年官方模拟题练一练

2024年第十一届汉字小达人的区级活动的时间9月25-30日正式开赛&#xff0c;还有一周就开始了。 有一些孩子刚刚被老师通知可以参加这个比赛&#xff0c;担心现在准备汉字小达人比赛是否来得及。根据我的经验&#xff0c;精准备考还是有希望的&#xff0c;也可以把这个比赛当做…

视频怎么提取音频?一键音频提取,视频内容轻松听!

视频怎么提取音频&#xff1f;一键解锁音频世界&#xff0c;让视频精彩不再静默&#xff01;无论您是忙碌于日常工作的上班族&#xff0c;还是热衷于学习的求知者&#xff0c;亦或是享受闲暇时光的聆听者&#xff0c;一键提取音频功能让视频内容瞬间转化为耳畔的温柔低语&#…

51单片机——直流电机驱动

1、直流电机介绍 直流电机是一种将电能转换为机械能的装置。一般的直流电机有两个电极&#xff0c;当电极正接时&#xff0c;电机正转&#xff0c;当电极反接时&#xff0c;电机反转。 直流电机主要由永磁体&#xff08;定子&#xff09;、线圈&#xff08;转子&#xff09;和…

Java双端队列ArrayDeque

概述 双端队列ArrayDeque是Java集合框架中的一种数据结构&#xff0c;它实现了Deque接口&#xff0c;因此支持在两端进行添加和移除元素。通过名称也能看出&#xff0c;ArrayDeque是基于数组实现的&#xff0c;ArrayDeque内部使用一个可动态调整大小的环形数组来存储元素。当Ar…

函数递归(上)

目录 1.递归是什么&#xff1f; 1.1 递归的思想&#xff1a; 2.顺序打印⼀个整数的每⼀位 ​编辑3 迭代 4.求第n个斐波那契数 1.递归是什么&#xff1f; 递归其实是⼀种解决问题的⽅法&#xff0c;在C语⾔中&#xff0c;递归就是函数⾃⼰调⽤⾃⼰。 #include <stdio.h…

【JAVA开源】基于Vue和SpringBoot的购物商城网站

本文项目编号 T 032 &#xff0c;文末自助获取源码 \color{red}{T032&#xff0c;文末自助获取源码} T032&#xff0c;文末自助获取源码 目录 一、系统介绍二、演示录屏三、启动教程四、功能截图五、文案资料5.1 选题背景5.2 国内外研究现状5.3 可行性分析 六、核心代码6.1 查…

鸿蒙OpenHarmony【轻量系统芯片移植案例】标准系统方案之扬帆移植案例

标准系统方案之扬帆移植案例 ​ 本文章是基于瑞芯微RK3399芯片的yangfan开发板&#xff0c;进行标准系统相关功能的移植&#xff0c;主要包括产品配置添加&#xff0c;内核启动、升级&#xff0c;音频ADM化&#xff0c;Camera&#xff0c;TP&#xff0c;LCD&#xff0c;WIFI&a…

高效财税自动化软件如何提升企业财务工作的效率与准确性

在当今企业运营中&#xff0c;财务管理发挥着核心作用。它不仅涉及企业正常运转和市场决策&#xff0c;还是推动企业向高质量发展迈进的关键动力。面对激烈的市场竞争与科技革新的双重挑战&#xff0c;财务管理亟需进行持续的转型与提升&#xff0c;为企业高质量发展目标的实现…

vue2.0+ts注册全局函数和几个递归查找

vue2.0ts注册全局函数和几个递归查找 一、main.ts 一、main.ts // 定义你的全局函数,判断是否有按钮权限 interface Item {label: string;checked: number;[k: string]: any; } // 获取按钮时候权限 function globalLable(arr: Item[], label: string): boolean {for (const i…

为大模型提供服务需要多少 GPU 显存?

在几乎所有的 LLM 面试中&#xff0c;有一个问题总是会被提及&#xff1a;“为大模型提供服务需要多少 GPU 显存&#xff1f;” 这不仅仅是一个随机的问题——它是一个关键指标&#xff0c;反映了你对这些强大模型在生产环境中部署和可扩展性的理解程度。 当你使用 GPT、LLaM…

在QSciscintilla编辑器源码中新增关键词分组的方法

由于项目需要&#xff0c;得在QSciscintilla的底层源码中增加几组关键词分组。在网上找了很久&#xff0c;一般都是说利用重写keywords(int set)接口的方式来增加&#xff0c;但这种方法不适用于我的应用场景。因为这种方法只能增加有限个关键词&#xff0c;且都是直接写死的方…