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

news/2024/10/8 20:46:30/

往期知识点记录:

  • 鸿蒙(HarmonyOS)应用层开发(北向)知识点汇总
  • 鸿蒙(OpenHarmony)南向开发保姆级知识点汇总~
  • 持续更新中……

本文章是基于瑞芯微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=$1uart=$2image=$3echo "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}" ]; thenecho "    initrd /extlinux/ramdisk.img" >> ${EXTLINUX_CONF}ficmdline="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_RK3566bool "Enable HDF Audio Codec driver"default ndepends on DRIVERS_HDF_AUDIOhelpAnswer 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/pwmplatform/fe6e0000.pwm, 1 PWM devicepwm-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_composerrga_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 = truedeps = [ "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_pathsources = [ "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.imginstall_images = [ chipset_base_dir ]relative_install_dir = "chipsetsdk"subsystem_name = "rockchip_products"part_name = "rockchip_products"install_enable = truesymlink_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 TypeApowerSequence {/* [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.cstatic 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.cstatic 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.……

鸿蒙开发面试真题(含参考答案):

在这里插入图片描述

OpenHarmony_1634">OpenHarmony源码解析》:

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

图片

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

图片
在这里插入图片描述


http://www.ppmy.cn/news/1536286.html

相关文章

系统架构设计师-英文翻译题(2022年下半年)

Micro-service is a software development technology, which advocates dividing a single application into a group of small services, which coordinates and cooperates with each other to provide ultimate value for users. 微服务是一种软件开发技术&#xff0c;它主…

【JavaEE】http/https 超级详解

&#x1f525;个人主页&#xff1a; 中草药 &#x1f525;专栏&#xff1a;【Java】登神长阶 史诗般的Java成神之路 &#x1f98a;一.定义 HTTP&#xff08;HyperText Transfer Protocol&#xff09;即超文本传输协议&#xff0c;他是应用非常广泛的应用层协议&#xff0c;是…

「Java开发指南」如何用MyEclipse为iPhone搭建Spring应用程序?

本教程将引导您完成在iPhone上使用的软件组件生成&#xff0c;这就产生了一个完全实现的可运行iPhone应用程序&#xff0c;可以立即用于验证和测试生成的iPhone组件。在本教程中&#xff0c;您将学习如何&#xff1a; 从数据库表搭建到现有项目部署应用程序 MyEclipse v2024.…

使用Scikit-image进行图像处理入门

简介 在数据科学的广阔领域中&#xff0c;图像处理占据了重要的一席之地&#xff0c;为分析和处理视觉数据提供了各种工具和技术。Python 拥有丰富的库生态系统&#xff0c;为图像处理提供了多种选择&#xff0c;其中&#xff0c;scikit-image 凭借其强大且易用的功能脱颖而出…

【设计模式】设计模式介绍和常见设计模式代码示例

文章目录 设计模式分类创建型模式结构型模式行为型模式 设计模式详解单例模式&#xff08;Singleton Pattern&#xff09;懒汉模式饿汉模式 工厂模式&#xff08;Factory Pattern&#xff09;简单工厂模式工厂方法模式抽象工厂模式 装饰模式&#xff08;Decorator Pattern&…

基于 Seq2Seq 的中英文翻译项目(pytorch)

项目简介 本项目旨在使用 PyTorch 构建一个基于 Seq2Seq&#xff08;编码器-解码器架构&#xff09;的中英文翻译模型。我们将使用双语句子对的数据进行训练&#xff0c;最终实现一个能够将英文句子翻译为中文的模型。项目的主要步骤包括&#xff1a; 数据预处理&#xff1a;…

SpringMVC2~~~

数据格式化 提交数据(比如表单)&#xff0c;对提交的数据进行转换和处理 基本数据类型可以和字符串自动转换 <a href"<%request.getContextPath()%>/addMonsterUI">添加妖怪</a> Controller Scope(value "prototype") public class …

搭建安全的分析环境

分析恶意软件&#xff0c;动态行为分析是必不可少的一步&#xff0c;所有样本都必须在虚拟环境中执行&#xff0c;不可在主机上执行。因此&#xff0c;我们要需要搭建供动态分析的环境以及防止在主机运行的环境。 动态分析环境搭建 工具 虚拟机 虚拟机软件有很多&#xff0…