OpenHarmony实战:瑞芯微RK3566移植案例(上)

news2024/11/25 18:36:21

本文章是基于瑞芯微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

目录规划

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:

rk3568 适配二级启动 · Pull Request !569 · OpenHarmony/build - Gitee.com

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);

  2. 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,

    }; HDF_INIT(g_daiDriverEntry);

  2. 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);

  2. 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,

    };

  3. 完成 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驱动模型层次关系

640

当前驱动模型主要部署在内核态中,向上对接到 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
  2. 关闭 fondation进程

    service_control stop fondation
  3. 运行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显示屏幕上的变化

代码路径:/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",
  ]
}

内容篇幅过长,下篇文章继续~~~~

最后

有很多小伙伴不知道学习哪些鸿蒙开发技术?不知道需要重点掌握哪些鸿蒙应用开发知识点?而且学习时频繁踩坑,最终浪费大量时间。所以有一份实用的鸿蒙(HarmonyOS NEXT)资料用来跟着学习是非常有必要的。 

这份鸿蒙(HarmonyOS NEXT)资料包含了鸿蒙开发必掌握的核心知识要点,内容包含了ArkTS、ArkUI开发组件、Stage模型、多端部署、分布式应用开发、音频、视频、WebGL、OpenHarmony多媒体技术、Napi组件、OpenHarmony内核、Harmony南向开发、鸿蒙项目实战等等)鸿蒙(HarmonyOS NEXT)技术知识点。

希望这一份鸿蒙学习资料能够给大家带来帮助,有需要的小伙伴自行领取,限时开源,先到先得~无套路领取!!

获取这份完整版高清学习路线,请点击→纯血版全套鸿蒙HarmonyOS学习资料

鸿蒙(HarmonyOS NEXT)最新学习路线

  •  HarmonOS基础技能

  • HarmonOS就业必备技能 
  •  HarmonOS多媒体技术

  • 鸿蒙NaPi组件进阶

  • HarmonOS高级技能

  • 初识HarmonOS内核 
  • 实战就业级设备开发

有了路线图,怎么能没有学习资料呢,小编也准备了一份联合鸿蒙官方发布笔记整理收纳的一套系统性的鸿蒙(OpenHarmony )学习手册(共计1236页)鸿蒙(OpenHarmony )开发入门教学视频,内容包含:ArkTS、ArkUI、Web开发、应用模型、资源分类…等知识点。

获取以上完整版高清学习路线,请点击→纯血版全套鸿蒙HarmonyOS学习资料

《鸿蒙 (OpenHarmony)开发入门教学视频》

《鸿蒙生态应用开发V2.0白皮书》

图片

《鸿蒙 (OpenHarmony)开发基础到实战手册》

OpenHarmony北向、南向开发环境搭建

图片

 《鸿蒙开发基础》

  • ArkTS语言
  • 安装DevEco Studio
  • 运用你的第一个ArkTS应用
  • ArkUI声明式UI开发
  • .……

图片

 《鸿蒙开发进阶》

  • Stage模型入门
  • 网络管理
  • 数据管理
  • 电话服务
  • 分布式应用开发
  • 通知与窗口管理
  • 多媒体技术
  • 安全技能
  • 任务管理
  • WebGL
  • 国际化开发
  • 应用测试
  • DFX面向未来设计
  • 鸿蒙系统移植和裁剪定制
  • ……

图片

《鸿蒙进阶实战》

  • ArkTS实践
  • UIAbility应用
  • 网络案例
  • ……

图片

 获取以上完整鸿蒙HarmonyOS学习资料,请点击→纯血版全套鸿蒙HarmonyOS学习资料

总结

总的来说,华为鸿蒙不再兼容安卓,对中年程序员来说是一个挑战,也是一个机会。只有积极应对变化,不断学习和提升自己,他们才能在这个变革的时代中立于不败之地。 

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

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

相关文章

中科院发布大模型想象增强法IAG,无需外部资源,想想就能变强

在人工智能领域&#xff0c;尤其是自然语言处理&#xff08;NLP&#xff09;的子领域——问答系统&#xff08;QA&#xff09;中&#xff0c;知识的获取和利用一直是推动技术进步的核心问题。近年来&#xff0c;大语言模型&#xff08;LLMs&#xff09;在各种任务中展现出了惊人…

【C语言】扫雷【附源码】

欢迎来到英杰社区https://bbs.csdn.net/topics/617804998 一、扫雷游戏规则 尽快找到雷区中的所有不是地雷的格子,而不许踩到地雷。点开的数字是几&#xff0c;则说明该数字旁边的8个位置中有几个雷&#xff0c;如果挖开的是地雷&#xff0c;则会输掉游戏。 二、代码思路&…

Laravel 项目如何运行

如有一个 Laravel 项目&#xff0c;在配置好 PHP 版本和运行环境后&#xff0c;可以直接在项目下直接运行&#xff1a; php artisan serve 来启动你的项目。 通过浏览器查看 当项目运行后&#xff0c;默认的启动端口为 8000&#xff0c;可以通过浏览器来进行查看运行的 Larav…

C++性能测试工具

使用示例main.cpp // g-13 -O3 -stdc17 main.cpp profile.cpp #include <iostream> #include <chrono> #include <stdint.h> #include <mutex> // std::mutex#include "profile.h" #include "profile_rdtsc.h"std::mut…

GlusterFS分布式文件系统

一、GlusterFS简介 GlusterFS 是一个开源的分布式文件系统。由存储服务器、客户端以及NFS/Samba存储网关(可选&#xff0c;根据需要选择使用)组成。没有元数据服务器组件&#xff0c;这有助于提升整个系统的性能、可靠性和稳定性 二、GlusterFS特点 2.1 扩展性和高性能 Glu…

Tensorboard以及Transforms初步学习

一.前情提要 1.本文是代码结合知识点&#xff0c;注释即为知识点 2.主要详细讲解Tensorboard以及Transforms代码以及基础知识 3.若想深入学习&#xff0c;建议阅读 P3. Python学习中的两大法宝函数&#xff08;当然也可以用在PyTorch&#xff09;_哔哩哔哩_bilibili 二.简述…

Scala中如何使用Jsoup库处理HTML文档?

在当今互联网时代&#xff0c;数据是互联网应用程序的核心。对于开发者来说&#xff0c;获取并处理数据是日常工作中的重要一环。本文将介绍如何利用Scala中强大的Jsoup库进行网络请求和HTML解析&#xff0c;从而实现爬取京东网站的数据&#xff0c;让我们一起来探索吧&#xf…

云智慧发布对象关系型数据库CloudPanguDB,打破传统技术壁垒

近日&#xff0c;云智慧推出关系型数据库CloudPanguDB&#xff08;中文名称&#xff1a;盘古数据库&#xff09;&#xff0c;旨在通过高兼容性能和创新技术架构&#xff0c;降低企业项目整体运营成本。 无论是处理海量复杂数据&#xff0c;还是构建清晰有序的数据结构关系&…

c++ 谷歌glog日志库使用

效果如图&#xff1a; 本次使用qt环境演示&#xff0c;相关库文件和头文件下载链接&#xff1a;https://download.csdn.net/download/bangtanhui/89108477 将相关库文件和头文件&#xff0c;丢到工程目录下 然后需要在工程pro文件当中引入库文件和头文件&#xff1a; …

创建型模式--2.简单工厂模式【人造恶魔果实工厂1】

1. 工厂模式的特点 在海贼王中&#xff0c;作为原王下七武海之一的多弗朗明哥&#xff0c;可以说是新世界最大的流氓头子&#xff0c;拥有无上的权利和无尽的财富。他既是德雷斯罗萨国王又是地下世界的中介&#xff0c;控制着世界各地的诸多产业&#xff0c;人造恶魔果实工厂就…

Java Web这一路走来

大部分Java应用都是Web或网络应用&#xff0c;MVC框架在Java框架中有着举足轻重的地位&#xff0c;一开始的Web应用并不现在这样子的&#xff0c;一步一步走来&#xff0c;每一步都经历了无数的血和泪的教训&#xff0c;以史为镜可以知兴替。 1. 草莽时代 早期的Java服务端技…

使用ollydbg还原密码程序的密码

前言&#xff1a;我们上次用ida pro破解了程序密码这次我们换个工具来还原程序密码 第一步 和上次一样把找到的程序拖到ollydbg里面 第二步 右键点击查找所有引用字符串 第三步 找到报错代码双击进入 第五步 找到上面的jnz双击将其改成jz 第六步 将所修改的保存点击空白…

FPGA(Verilog)实现uart传输协议传输数据(含仿真)

目录 实现功能&#xff1a; 1.接收uart串行数据&#xff0c;输出并行数据(1byte)。 2.输入并行数据(1byte)&#xff0c;输出uart串行数据。 3.完成uart传输的1次环回。 uart协议的1帧数据传输 模块封装-port设置 Verilog代码实现 1.uart接收模块:接收串行数据,输出并行数…

基于Java+SpringBoot+Vue企业员工管理系统(源码+文档+部署+讲解)

一.系统概述 随着社会的发展&#xff0c;系统的管理形势越来越严峻。越来越多的用户利用互联网获得信息&#xff0c;但各种信息鱼龙混杂&#xff0c;信息真假难以辨别。为了方便用户更好的获得信息&#xff0c;因此&#xff0c;设计一种安全高效的员工管理系统极为重要。 为设计…

swiftui macOS实现加载本地html文件

import SwiftUI import WebKitstruct ContentView: View {var body: some View {VStack {Text("测试")HTMLView(htmlFileName: "localfile") // 假设你的本地 HTML 文件名为 index.html.frame(minWidth: 100, minHeight: 100) // 设置 HTMLView 的最小尺寸…

【攻防世界】php_rce (ThinkPHP5)

进入题目环境&#xff0c;查看页面信息&#xff1a; 页面提示 ThinkPHP V5&#xff0c;猜测存在ThinkPHP5 版本框架的漏洞&#xff0c;于是查找 ThinkPHP5 的攻击POC。 构造 payload: http://61.147.171.105:50126/?sindex/think\app/invokefunction&functioncall_user_f…

续写Groq

这章写点Groq干货,理性的分析。 首先是Articical Analysis的关于Mixtral8*7B的吞吐比较 上图是有Mixtral 8*7BPaaS服务的AI服务商,Mistral自己居然排倒数第三 ,Groq是真的遥遥领先啊。 另外这个图是比较每100万tokens的cost,无论是推理速度还是cost,Groq都是遥遥领先…

如何把1G多的视频压缩到500兆以内?3个方法轻松减小文件内存~

微信已经成为了我们上班交流沟通时必不可少的通讯工具之一&#xff0c;在使用微信时&#xff0c;常常会遇到系统提示发送的word、ppt、pdf文件、视频、压缩包等文件超过1G&#xff0c;无法发送。有没有什么办法可以缩小文件的体积呢&#xff1f;今天给大家介绍几款可以用于视频…

Python中Python-docx 包的run介绍

先对run做一个简单地介绍。每个paragraph对象都包含一个run对象的列表。举例&#xff1a; 这是一个简短的段落。 from docx import Document doc Document("1.docx") #上面这段话保存在1.docx中 print("这一段的run个数是&#xff1a;",len(doc.paragr…

leetcode热题100.划分字母区间

Problem: 763. 划分字母区间 文章目录 题目思路复杂度Code 题目 给你一个字符串 s 。我们要把这个字符串划分为尽可能多的片段&#xff0c;同一字母最多出现在一个片段中。 注意&#xff0c;划分结果需要满足&#xff1a;将所有划分结果按顺序连接&#xff0c;得到的字符串仍…