【鸿蒙OH 5.0】OpenHarmony标准系统方案之瑞芯微RK3568移植案例(一)

article/2025/7/19 10:51:49

📝往期推文全新看点(文中附带最新·鸿蒙全栈学习笔记)

🚩 鸿蒙(HarmonyOS)北向开发知识点记录~

🚩 鸿蒙(OpenHarmony)南向开发保姆级知识点汇总~

🚩 鸿蒙应用开发与鸿蒙系统开发哪个更有前景?

🚩 嵌入式开发适不适合做鸿蒙南向开发?看完这篇你就了解了~

🚩 对于大前端开发来说,转鸿蒙开发究竟是福还是祸?

🚩 鸿蒙岗位需求突增!移动端、PC端、IoT到底该怎么选?

🚩 记录一场鸿蒙开发岗位面试经历~

📃 持续更新中……


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

产品配置和目录规划

产品配置

在产品 //productdefine/common/device 目录下创建以 rk3568 名字命名的 json 文件,并指定 CPU 的架构。//productdefine/common/device/rk3568.json 配置如下:

{"device_name": "rk3568","device_company": "rockchip","target_os": "ohos","target_cpu": "arm","kernel_version": "","device_build_path": "device/board/hihope/rk3568","enable_ramdisk": true,   //是否支持ramdisk二级启动"build_selinux": true    // 是否支持selinux权限管理
}

在 //productdefine/common/products 目录下创建以产品名命名的 rk3568.json 文件。该文件用于描述产品所使用的 SOC 以及所需的子系统。配置如下

{"product_name": "rk3568","product_company" : "hihope","product_device": "rk3568","version": "2.0","type": "standard","parts":{"ace:ace_engine_standard":{},"ace:napi":{},..."xts:phone_tests":{}}
}

主要的配置内容包括:

  1. product_device:配置所使用的 SOC。
  2. type:配置系统的级别, 这里直接 standard 即可。
  3. parts:系统需要启用的子系统。子系统可以简单理解为一块独立构建的功能块。
    已定义的子系统可以在 //build/subsystem_config.json 中找到。当然你也可以定制子系统。
    这里建议先拷贝 Hi3516DV300 开发板的配置文件,删除掉 hisilicon_products 这个子系统。这个子系统为 Hi3516DV300 SOC 编译内核,不适合 rk3568。

目录规划

参考  Board 和 SoC 解耦的设计思路 ,并把芯片适配目录规划为:

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

vendor
└── hihope			└── rk3568         			 --- 产品名字:产品、hcs以及demo相关

内核启动

二级启动

二级启动简单来说就是将之前直接挂载 sytem,从 system 下的 init 启动,改成先挂载 ramdsik,从 ramdsik 中的 init 启动,做些必要的初始化动作,如挂载 system,vendor 等分区,然后切到 system 下的 init 。
Rk3568 适配主要是将主线编译出来的 ramdisk 打包到 boot_linux.img 中,主要有以下工作:

1.使能二级启动
在 productdefine/common/device/rk3568.json 中使能 enable_ramdisk。

{"device_name": "rk3568","device_company": "hihope","target_os": "ohos","target_cpu": "arm","kernel_version": "","device_build_path": "device/hihope/build","enable_ramdisk": true,"build_selinux": true
}

2.把主线编译出来的 ramdsik.img 打包到 boot_linux.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

INIT 配置

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

音频

RK3568 Audio 总体结构图

ADM 适配方案介绍

RK3568 平台适配 ADM 框架图

  1. ADM Drivers adapter
    主要完成 Codec/DMA/I2S 驱动注册,使得 ADM 可以加载驱动节点;并注册 ADM 与 Drivers 交互的接口函数
  2. ADM Drivers impl
    主要完成 ADM Drivers adapter 接口函数的实现,以及 Codec_config.hcs/dai_config.hcs 等配置信息的获取,并注册到对应的设备
  3. Linux Drivers
    ADM Drivers impl 可以直接阅读硬件手册,完成驱动端到端的配置;也可以借用 Linux 原生驱动实现与接口,减少开发者工作量。
目录结构
./device/board/hihope/rk3568/audio_drivers
├── codec
│   └── rk809_codec
│       ├── include
│       │   ├── rk809_codec_impl.h
│       │   └── rk817_codec.h
│       └── src
│           ├── rk809_codec_adapter.c
│           ├── rk809_codec_linux_driver.c
│           └── rk809_codec_ops.c
├── dai
│   ├── include
│   │   ├── rk3568_dai_linux.h
│   │   └── rk3568_dai_ops.h
│   └── src
│       ├── rk3568_dai_adapter.c
│       ├── rk3568_dai_linux_driver.c
│       └── rk3568_dai_ops.c
├── dsp
│   ├── include
│   │   └── rk3568_dsp_ops.h
│   └── src
│       ├── rk3568_dsp_adapter.c
│       └── rk3568_dsp_ops.c
├── include
│   ├── audio_device_log.h
│   └── rk3568_audio_common.h
└── soc├── include│   └── rk3568_dma_ops.h└── src├── rk3568_dma_adapter.c└── rk3568_dma_ops.c

RK3568 适配 ADM 详细过程

梳理平台 Audio 框架

梳理目标平台的 Audio 结构,明确数据流与控制流通路。

  1. 针对 RK3568 平台,Audio 的结构相对简单见 RK3568 Audio 总体结构图,Codec 作为一个独立设备。I2C 完成对设备的控制,I2S 完成 Codec 设备与 CPU 之间的交互。
  2. 结合原理图整理 I2S 通道号,对应的引脚编号;I2C 的通道号,地址等硬件信息。
  3. 获取 Codec 对应的 datasheet,以及 RK3568 平台的 Datasheet(包含 I2S/DMA 通道等寄存器的介绍)。
熟悉并了解 ADM 结构

ADM 结构框图如下,Audio Peripheral Drivers 和 Platform Drivers 为平台适配需要完成的工作。

结合第 1 步梳理出来的 Audio 结构分析,Audio Peripheral Drivers 包含 Rk809 的驱动,Platform Drivers 包含 DMA 驱动和 I2S 驱动。

需要适配的驱动ADM 对应模块接口文件路径
RK809 驱动Accessorydrivers/framework/include/audio/audio_accessory_if.h
DMA 驱动platformdrivers/framework/include/audio/audio_platform_if.h
I2S 驱动DAIdrivers/framework/include/audio/audio_dai_if.h.h
搭建驱动代码框架
配置 HCS 文件

在 device_info.hcs 文件中 Audio 下注册驱动节点

        audio :: host {hostName = "audio_host";priority = 60;device_dai0 :: device {device0 :: deviceNode {policy = 1;priority = 50;preload = 0;permission = 0666;moduleName = "DAI_RK3568";serviceName = "dai_service";deviceMatchAttr = "hdf_dai_driver";}}device_codec :: device {device0 :: deviceNode {policy = 1;priority = 50;preload = 0;permission = 0666;moduleName = "CODEC_RK809";serviceName = "codec_service_0";deviceMatchAttr = "hdf_codec_driver";}}device_codec_ex :: device {device0 :: deviceNode {policy = 1;priority = 50;preload = 0;permission = 0666;moduleName = "CODEC_RK817";serviceName = "codec_service_1";deviceMatchAttr = "hdf_codec_driver_ex";}}device_dsp :: device {device0 :: deviceNode {policy = 1;priority = 50;preload = 0;permission = 0666;moduleName = "DSP_RK3568";serviceName = "dsp_service_0";deviceMatchAttr = "hdf_dsp_driver";}}device_dma :: device {device0 :: deviceNode {policy = 1;priority = 50;preload = 0;permission = 0666;moduleName = "DMA_RK3568";serviceName = "dma_service_0";deviceMatchAttr = "hdf_dma_driver";}}......}

根据接入的设备,选择 Codec 节点还是 Accessory 节点,配置硬件设备对应的私有属性(包含寄存器首地址,相关 control 寄存器地址)涉及 Codec_config.hcs 和 DAI_config.hcs 配置相关介绍见  Audio hcs 配置章节以及 ADM 框架的 audio_parse 模块代码。

codec/accessory 模块
  1. 将驱动注册到 HDF 框架中,代码片段如下,启动 moduleName 与 HCS 文件的中 moduleName 一致
    struct HdfDriverEntry g_codecDriverEntry = {.moduleVersion = 1,.moduleName = "CODEC_HI3516",.Bind = CodecDriverBind,.Init = CodecDriverInit,.Release = CodecDriverRelease,};HDF_INIT(g_codecDriverEntry);
  1. Codec 模块需要填充:
    g_codecData:codec 设备的操作函数集和私有数据集。
    g_codecDaiDeviceOps:codecDai 的操作函数集,包括启动传输和参数配置等函数接口。
    g_codecDaiData:codec 的数字音频接口的操作函数集和私有数据集。
  2. 完成 bind、init 和 release 函数的实现
  3. 验证
    在 bind 和 init 函数加调试日志,编译版本并获取系统系统日志:
[    1.548624] [E/"rk809_codec_adapter"]  [Rk809DriverBind][line:258]: enter
[    1.548635] [E/"rk809_codec_adapter"]  [Rk809DriverBind][line:260]: success
[    1.548655] [E/"rk809_codec_adapter"]  [Rk809DriverInit][line:270]: enter
[    1.549050] [E/"rk809_codec_adapter"]  [GetServiceName][line:226]: enter
[    1.549061] [E/"rk809_codec_adapter"]  [GetServiceName][line:250]: success
[    1.549072] [E/"rk809_codec_adapter"]  [Rk809DriverInit][line:316]: g_chip->accessory.drvAccessoryName = codec_service_1
[    1.549085] [E/audio_core]  [AudioSocRegisterDai][line:86]: Register [accessory_dai] success.
[    1.549096] [E/audio_core]  [AudioRegisterAccessory][line:120]: Register [codec_service_1] success.
[    1.549107] [E/"rk809_codec_adapter"]  [Rk809DriverInit][line:323]: success!
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);
  1. 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. 完成 bind、init 和 release 函数的实现
  2. 验证
    在 bind/init 函数加调试日志,编译版本并获取系统系统日志
    [    1.549193] [I/device_node] launch devnode dai_service[    1.549204] [E/HDF_LOG_TAG]  [DaiDriverBind][line:38]: entry![    1.549216] [E/HDF_LOG_TAG]  [DaiDriverBind][line:55]: success![    1.549504] [E/audio_core]  [AudioSocRegisterDai][line:86]: Register [dai_service] success.[    1.549515] [E/HDF_LOG_TAG]  [DaiDriverInit][line:116]: success.
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,.DmaBufFree = Rk3568DmaBufFree,.DmaRequestChannel = Rk3568DmaRequestChannel,.DmaConfigChannel = Rk3568DmaConfigChannel,.DmaPrep = Rk3568DmaPrep,.DmaSubmit = Rk3568DmaSubmit,.DmaPending = Rk3568DmaPending,.DmaPause = Rk3568DmaPause,.DmaResume = Rk3568DmaResume,.DmaPointer = Rk3568PcmPointer,};struct PlatformData g_platformData = {.PlatformInit = AudioDmaDeviceInit,.ops = &g_dmaDeviceOps,};
  1. 完成 bind、init 和 release 函数的实现
  2. 验证
    在 bind 和 init 函数加调试日志,编译版本并获取系统系统日志
    [    1.548469] [E/rk3568_platform_adapter]  [PlatformDriverBind][line:42]: entry![    1.548481] [E/rk3568_platform_adapter]  [PlatformDriverBind][line:58]: success![    1.548492] [E/rk3568_platform_adapter]  [PlatformDriverInit][line:100]: entry. [    1.548504] [E/rk3568_platform_adapter]  [PlatformGetServiceName][line:67]: entry![    1.548515] [E/rk3568_platform_adapter]  [PlatformGetServiceName][line:91]: success![    1.548528] [E/audio_core]  [AudioSocRegisterPlatform][line:63]: Register [dma_service_0] success.[    1.548536] [E/rk3568_platform_adapter]  [PlatformDriverInit][line:119]: success.
驱动适配

code/accessory 模块

  1. 读取 DTS 文件,获取到对应设备节点,使用 Linux 原生的驱动注册函数,获取到对应 device。
static int rk817_platform_probe(struct platform_device *pdev) {rk817_pdev = pdev;dev_info(&pdev->dev, "got rk817-codec platform_device");return 0;
}
static struct platform_driver rk817_codec_driver = {.driver = {.name = "rk817-codec",                     // codec node in dts file.of_match_table = rk817_codec_dt_ids,},.probe = rk817_platform_probe,.remove = rk817_platform_remove,
};

2.读写寄存器函数封装
根据上述获取到的 device, 使用 Linux 的 regmap 函数,开发者不需要获取模块的基地址
获取 rk817 的 regmap 代码段

g_chip = devm_kzalloc(&rk817_pdev->dev, sizeof(struct Rk809ChipData), GFP_KERNEL);if (!g_chip) {AUDIO_DEVICE_LOG_ERR("no memory");return HDF_ERR_MALLOC_FAIL;}g_chip->pdev = rk817_pdev;struct rk808 *rk808 = dev_get_drvdata(g_chip->pdev->dev.parent);if (!rk808) {AUDIO_DEVICE_LOG_ERR("%s: rk808 is NULL\n", __func__);ret = HDF_FAILURE;RK809ChipRelease();return ret;}g_chip->regmap = devm_regmap_init_i2c(rk808->i2c,&rk817_codec_regmap_config);if (IS_ERR(g_chip->regmap)) {AUDIO_DEVICE_LOG_ERR("failed to allocate regmap: %ld\n", PTR_ERR(g_chip->regmap));RK809ChipRelease();return ret;}

寄存器读写函数代码段

int32_t Rk809DeviceRegRead(uint32_t reg, uint32_t *val) {if (regmap_read(g_chip->regmap, reg, val)) {AUDIO_DRIVER_LOG_ERR("read register fail: [%04x]", reg);return HDF_FAILURE;}return HDF_SUCCESS;}int32_t Rk809DeviceRegWrite(uint32_t reg, uint32_t value) {if (regmap_write(g_chip->regmap, reg, value)) {AUDIO_DRIVER_LOG_ERR("write register fail: [%04x] = %04x", reg, value);return HDF_FAILURE;}return HDF_SUCCESS;}int32_t Rk809DeviceRegUpdatebits(uint32_t reg, uint32_t mask, uint32_t value) {if (regmap_update_bits(g_chip->regmap, reg, mask, value)) {AUDIO_DRIVER_LOG_ERR("update register bits fail: [%04x] = %04x", reg, value);return HDF_FAILURE;}return HDF_SUCCESS;}

3.寄存器初始化函数
因为使用 Linux 的 regmap 函数,所以需要自行定义 RegDefaultInit 函数,读取 hcs 中 initSeqConfig 的寄存器以及数值来进行配置
RK809RegDefaultInit 代码段

int32_t RK809RegDefaultInit(struct AudioRegCfgGroupNode **regCfgGroup)
{int32_t i;struct AudioAddrConfig *regAttr = NULL;if (regCfgGroup == NULL || regCfgGroup[AUDIO_INIT_GROUP] == NULL ||regCfgGroup[AUDIO_INIT_GROUP]->addrCfgItem == NULL || regCfgGroup[AUDIO_INIT_GROUP]->itemNum <= 0) {AUDIO_DEVICE_LOG_ERR("input invalid parameter.");return HDF_ERR_INVALID_PARAM;}regAttr = regCfgGroup[AUDIO_INIT_GROUP]->addrCfgItem;for (i = 0; i < regCfgGroup[AUDIO_INIT_GROUP]->itemNum; i++) {Rk809DeviceRegWrite(regAttr[i].addr, regAttr[i].value);}return HDF_SUCCESS;
}

4.封装控制接口的读写函数
设置控制读写函数为 RK809CodecReadReg 和 RK809CodecWriteReg

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,
};

封装控制接口的读写函数
因为原来的读写原型,涉及三个参数(unsigned long virtualAddress,uint32_t reg, uint32_t *val),其中 virtualAddress 我们并不需要用到,所以封装个接口即可,封装如下

int32_t RK809CodecReadReg(unsigned long virtualAddress,uint32_t reg, uint32_t *val)
{if (val == NULL) {AUDIO_DRIVER_LOG_ERR("param val is null.");return HDF_FAILURE;}if (Rk809DeviceRegRead(reg, val)) {AUDIO_DRIVER_LOG_ERR("read register fail: [%04x]", reg);return HDF_FAILURE;}ADM_LOG_ERR("read reg 0x[%02x] = 0x[%02x]",reg,*val);return HDF_SUCCESS;
}
int32_t RK809CodecWriteReg(unsigned long virtualAddress,uint32_t reg, uint32_t value)
{if (Rk809DeviceRegWrite(reg, value)) {AUDIO_DRIVER_LOG_ERR("write register fail: [%04x] = %04x", reg, value);return HDF_FAILURE;}  ADM_LOG_ERR("write reg 0x[%02x] = 0x[%02x]",reg,value);return HDF_SUCCESS;
}

5.其他 ops 函数

  • Rk809DeviceInit,读取 hcs 文件,初始化 Codec 寄存器,同时将对应的 control 配置(/* reg, rreg, shift, rshift, min, max, mask, invert, value */添加到 kcontrol,便于 dispatch contro 进行控制
  • Rk809DaiStartup, 读取 hcs 文件,配置可选设备(codec/accessory)的控制寄存器
  • Rk809DaiHwParams, 根据 hal 下发的 audio attrs(采样率、format、channel 等),配置对应的寄存器
  • RK809NormalTrigger,根据 hal 下发的操作命令码,操作对应的寄存器,实现 Codec 的启动停止、录音和放音的切换等

DAI(i2s)模块
1.读写寄存器函数
思路与 Codec 模块的一致,读取 Linux DTS 文件,使用 Linux 的 regmap 函数完成寄存器的读写操作

int32_t Rk3568DeviceReadReg(unsigned long regBase, uint32_t reg, uint32_t *val){AUDIO_DEVICE_LOG_ERR("entry");(void)regBase;struct device_node *dmaOfNode = of_find_node_by_path("/i2s@

2.其他 ops 函数

  • Rk3568DaiDeviceInit
    原始框架,主要完成 DAI_config.hcs 参数列表的读取,与 HwParams 结合,完成参数的设置。

  • Rk3568DaiHwParams
    主要完成 I2S MCLK/BCLK/LRCLK 时钟配置。

    • 根据不同采样率计算 MCLK
    int32_t RK3568I2sTdmSetSysClk(struct rk3568_i2s_tdm_dev *i2s_tdm, const struct AudioPcmHwParams *param){/* Put set mclk rate into rockchip_i2s_tdm_set_mclk() */uint32_t sampleRate = param->rate;uint32_t mclk_parent_freq = 0;switch (sampleRate) {case AUDIO_DEVICE_SAMPLE_RATE_8000:case AUDIO_DEVICE_SAMPLE_RATE_16000:case AUDIO_DEVICE_SAMPLE_RATE_24000:case AUDIO_DEVICE_SAMPLE_RATE_32000:case AUDIO_DEVICE_SAMPLE_RATE_48000:case AUDIO_DEVICE_SAMPLE_RATE_64000:case AUDIO_DEVICE_SAMPLE_RATE_96000:mclk_parent_freq = i2s_tdm->bclk_fs * AUDIO_DEVICE_SAMPLE_RATE_192000;break;case AUDIO_DEVICE_SAMPLE_RATE_11025:case AUDIO_DEVICE_SAMPLE_RATE_22050:case AUDIO_DEVICE_SAMPLE_RATE_44100:mclk_parent_freq = i2s_tdm->bclk_fs * AUDIO_DEVICE_SAMPLE_RATE_176400;break;default:AUDIO_DEVICE_LOG_ERR("Invalid LRCK freq: %u Hz\n", sampleRate);return HDF_FAILURE;}i2s_tdm->mclk_tx_freq = mclk_parent_freq;i2s_tdm->mclk_rx_freq = mclk_parent_freq;return HDF_SUCCESS;}
  • 根据获取的 mclk,计算 BCLK/LRclk 分频系数

  • Rk3568NormalTrigger 根据输入输出类型,以及 cmd(启动/停止/暂停/恢复),完成一系列配置:

  • mclk 的启停

  • DMA 搬运的启停

  • 传输的启停

详细实现见代码,参考 Linux 原生 I2s 驱动对应接口函数

    // 启动/恢复流程if (streamType == AUDIO_RENDER_STREAM) {clk_prepar
Platform(DMA)模块

ops 函数相关函数

1.Rk3568DmaBufAlloc/Rk3568DmaBufFree
获取 DMA 设备节点,参考 I2s 设备获取方式,使用系统函数 dma_alloc_wc/dma_free_wc,完成 DMA 虚拟内存与物理内存的申请/释放
2.Rk3568DmaRequestChannel
使用 Linux DMA 原生接口函数获取 DMA 传输通道,

dmaRtd->dmaChn[streamType] = dma_request_slave_channel(dmaDevice, dmaChannelNames[streamType]);

3.Rk3568DmaConfigChannel

   //设置通道配置参数// 放音通道参数配置slave_config.direction = DMA_MEM_TO_DEV;slave_config.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;slave_config.dst_addr = I2S1_ADDR + I2S_TXDR;slave_config.dst_maxburst = 8;// 录音通道参数配置slave_config.direction = DMA_DEV_TO_MEM;slave_config.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;slave_config.src_addr = I2S1_ADDR + I2S_RXDR;slave_config.src_maxburst = 8;//使用Linux DMA原生接口函数完成DMA通道配置ret = dmaengine_slave_config(dmaChan, &slave_config);if (ret != 0) {AUDIO_DEVICE_LOG_ERR("dmaengine_slave_config failed");return HDF_FAILURE;}

4.Rk3568DmaSubmit/Rk3568DmaPending
使用 Linux DMA 原生接口函数 dmaengine_prep_dma_cyclic,初始化一个具体的周期性的 DMA 传输描述符 dmaengine_submit 接口将该描述符放到传输队列上,然后调用 dma_async_issue_pending 接口,启动传输。

5.Rk3568PcmPointer
第 4 步完成之后,ADM 框架调用 Rk3568PcmPointer,循环写 cirBuf,计算 pointer

   dma_chn = dmaRtd->dmaChn[DMA_TX_CHANNEL];buf_size = data->renderBufInfo.cirBufSize;dmaengine_tx_status(dma_chn, dmaRtd->cookie[DMA_TX_CHANNEL], &dma_state);if (dma_state.residue) {currentPointer = buf_size - dma_state.residue;*pointer = BytesToFrames(data->pcmInfo.frameSize, currentPointer);} else {*pointer = 0;}

6.Rk3568DmaPause
使用 Linux DMA 原生接口函数 dmaengine_terminate_async,停止 DMA 传输

 dmaengine_terminate_async(dmaChan);

7.Rk3568DmaResume
暂停使用的 DMA 停止函数,对应恢复,相当于重启 DMA 传输,执行 Rk3568DmaSubmit/Rk3568DmaPending 相关操作即可完成

适配中遇到问题与解决方案

1.播放一段时间后,停止播放,持续有尖锐的很小的声音
问题原因:播放停止后,Codec 相关器件没有下电
解决方案:注册 Codec 的 trigger 函数,当接收到 Cmd 为 Stop 时,对 Codec 进行下电

2.播放一段时间后,停止播放,然后重新播放没有声音
问题原因:DMA 驱动的 PAUSE 接口函数,并未停止 DMA 传输
解决方案:暂停状态不再使用 DMA 的 PAUSE 函数,而是使用 DAM 传输停止接口; 相对应的,恢复函数的业务逻辑相当于重启 DMA 传输,执行 Rk3568DmaSubmit/Rk3568DmaPending 相关操作即可完成

3.播放存在杂音
问题原因:DMA 数据搬运 pointer 位置不正确
解决方案:Rk3568PcmPointer 函数返回值为 DMA 搬运的内存位置,用缓存区 buf 与 dma_state.residue 的差值计算

4.可以放音,但 Mclk 引脚没有时钟信号
问题原因:DTS 文件 pin-ctrl 没有配置 mclk 的引脚
解决方案:修改 DTS 文件


http://www.hkcw.cn/article/cJQVWQAIZq.shtml

相关文章

【错误记录】Windows 中 DevEco Studio 真机调试无法连接设备 ( 低版本的 HarmonyOS 4.2.0 华为手机无法在 DevEco Studio 5.0.2 上真机调试 )

文章目录 一、错误记录二、问题排查三、解决方案 参考文档 : hdc&#xff08;HarmonyOS Device Connector&#xff09;文档设备连接后&#xff0c;无法识别设备的处理指导真机调试流程 一、错误记录 手机 使用的是 HarmonyOS 4.2.0 系统 ; 使用 HarmonyOS 的 hdc 工具 , 执行 …

原生鸿蒙应用市场开发者服务的技术解析:从集成到应用发布的完整体验

文章目录 引言一、鸿蒙原生应用的高效开发二、用户隐私保护&#xff1a;安全访问管理三、开发者实用工具&#xff1a;应用分析与A/B测试四、应用审核与分发&#xff1a;快速上线4.1 应用加密&#xff1a;保护代码安全4.2 自动化测试与检测前移&#xff1a;提升应用质量 五、结语…

迪士尼打架是否互殴还需细心调查 因拍照起争执

5月31日,有网友发布视频称,在上海迪士尼发生了一起冲突事件,一对情侣和一家三口发生了争执并动手。视频中可以看到双方在现场扭打,周围的人纷纷上前劝阻。6月1日,当地相关部门透露,这起事件发生在5月31日下午,地点是迪士尼疯狂动物城的一处拍照打卡点。双方因拍照问题产…

从奥运会到法网,郑钦文在“福地”取得跨年10连胜! 职业生涯首进法网八强

郑钦文在法网比赛中首次跻身八强,这是她第四次闯进大满贯八强。她在罗兰加洛斯取得了个人10连胜,也是中国选手时隔14年再次进入法网八强。比赛结束后,郑钦文躺倒在地庆祝这场历时2小时47分钟的胜利。比赛从首盘开始便陷入胶着,郑钦文与萨姆索诺娃展开了一场发球与接发球大战…

没有假球全是世仇 比赛第一,友谊第十四

“友谊第一,比赛第二”这句话在江苏省首届城市足球联赛中被玩出了新花样。“友谊第一,比赛第十四!”这样的口号让观众们捧腹大笑。这个被称为“苏超”的足球联赛最近爆火出圈,盐城现场吸引了22613名观众,网友纷纷表示这上座率堪比世界杯。“苏超”的观众数量已经超过了同期…

陈梦9岁开始领工资 妈妈管理至今

在最新一期《是女儿是妈妈》节目中,陈梦妈妈的一番话引起了网友们的广泛关注。陈梦9岁进入省队就开始领工资,而她的工资卡至今仍由妈妈保管。此外,陈梦妈妈还提到,陈梦每天的日常生活除了训练、吃饭和睡觉外,几乎没有其他时间,更没有精力去谈恋爱。陈梦是一位乒乓球世界冠…

记录下载安装sqlite3的过程

sqlite是一个数据库管理软件&#xff0c;今天用到了&#xff0c;记录下载安装的过程。 本地环境&#xff1a;Windows 10 家庭中文版。 下载网址&#xff1a;SQLite Download Page &#xff08;一&#xff09;下载下图中的两个文件&#xff1a; &#xff08;二&#xff09;自…

HarmonyOS NEXT 鸿蒙ArkTS 视频相关 视频播放、直播视频、XComponent和typeNode多方案实现画中画功能开发

一、简单的视频播放、直播播放 1. 使用meida中的avPlayer结合XComponent进行视频播放 如果是音频只需要一个路径就差不多了&#xff0c;这是音频HDI显示HDI&#xff0c;所以需要做以下几点&#xff1a; 应用从XComponent组件获取窗口SurfaceID&#xff0c;获取方式参考XCompon…

res-downloader-视频号下载,网络视频资源嗅探下载器

适用系统&#xff1a;Windows(含Win7)、macOS 和 Linux 系统 一、核心功能与特性 全平台资源支持 支持微信视频号、抖音、快手、小红书等短视频平台的无水印视频下载&#xff0c;同时兼容酷狗音乐、QQ音乐、微信小程序等音频和多媒体资源 1 8。覆盖视频、音频、图片、m3u8流媒体…

通义万相2.1:开启视频生成新时代

文章摘要&#xff1a;通义万相 2.1 是一款在人工智能视频生成领域具有里程碑意义的工具&#xff0c;它通过核心技术的升级和创新&#xff0c;为创作者提供了更强大、更智能的创作能力。本文详细介绍了通义万相 2.1 的背景、核心技术、功能特性、性能评测、用户反馈以及应用场景…

计算机视觉——基于树莓派的YOLO11模型优化与实时目标检测、跟踪及计数的实践

概述 设想一下&#xff0c;你在多地拥有多个仓库&#xff0c;要同时监控每个仓库的实时状况&#xff0c;这对于时间和精力而言&#xff0c;都构成了一项艰巨挑战。从成本和可靠性的层面考量&#xff0c;大规模部署计算设备也并非可行之策。一方面&#xff0c;大量计算设备的购…

AI赋能视频创作:蓝耘MaaS与海螺AI技术的深度融合

云边有个稻草人-CSDN博客 目录 一、蓝耘MaaS平台概述 &#xff08;1&#xff09;平台的模块化设计 &#xff08;2&#xff09;蓝耘MaaS的灵活性与扩展性 &#xff08;3&#xff09;蓝耘MaaS的安全性与隐私保护 二、海螺AI视频模型简介 &#xff08;1&#xff09;海螺AI的…

6条视频涨粉千万 心中之城回应质疑 新IP崛起之路

DY平台再次见证了一个涨粉神话。一部剧,六条视频,让账号「心中之城」在短短时间内涨粉1000万。从四月发布第一条视频至今,该账号已跃居DY热榜榜首。各大媒体纷纷报道这一现象。「心中之城」通过解说英剧《豺狼的日子》吸引了大量关注。该账号的运营者曾是电影圈知名账号毒舌…

外卖员不用办健康证了?网友争论 食品安全引热议

点外卖已成为很多人的生活习惯,而网络订餐配送过程中的食品安全问题也备受关注。去年底,四川省卫生健康委与市场监管局联合发布新规,明确外卖送餐人员及预包装食品销售从业者无须办理传统健康证即可上岗,并要求体检机构停止为外卖员提供健康证服务。这一消息受到不少外卖小…

ubuntu系统更换镜像源

目录 前言 一 查看操作系统版本 二 备份镜像源 三 镜像源站点官网 四 修改配置文件 前言 ubuntu系统默认官方源&#xff08;archive.ubuntu.com&#xff09;通常位于国外&#xff0c;国内用户访问时网络延迟高、带宽受限&#xff0c;导致下载软件速度很慢或者直接遇到更新失…

南京“以债换房”可置换月供?假 网传信息为谣言

近日,一些账号如“南京二手房零首付李经理”、“合肥瑶珺房地产代理有限公司”、“中墅地产唐广君”、“清、静”、“杨哥”、“南京免首付房产管家”等发布帖子称,南京开放了“以债换房”政策,可以将网络债务直接置换为房子的月供,无需支付首付。经南京市房产部门和人行江…

QT入门学习(一)---新建工程与、信号与槽

一: 新建QT项目 二:QT文件构成 2.1 first.pro 项目管理文件&#xff0c;下面来看代码解析 QT core guigreaterThan(QT_MAJOR_VERSION, 4): QT widgetsCONFIG c11TARGET main# The following define makes your compiler emit warnings if you use # any Qt feature …

自己烧水喝是否比买桶装水更健康 减少微塑料摄入

水是构成人体的重要物质,对维持正常的身体活动和认知能力至关重要。尤其是在天气炎热时,及时补充水分尤为重要。在日常生活中,有人习惯自己烧水喝,也有人因担心自来水水质问题而选择桶装水或瓶装水。那么,这两种饮水方式哪种更健康呢?一项发表在《美国国家科学院院刊》上…

PCB设计教程【强化篇】——USB拓展坞PCB布线

前言 本教程基于B站Expert电子实验室的PCB设计教学的整理&#xff0c;为个人学习记录&#xff0c;旨在帮助PCB设计新手入门。所有内容仅作学习交流使用&#xff0c;无任何商业目的。若涉及侵权&#xff0c;请随时联系&#xff0c;将会立即处理 目录 前言 一、前期准备与规则…

1.RV1126-OPENCV 交叉编译

一.下载opencv-3.4.16.zip到自己想装的目录下 二.解压并且打开 opencv 目录 先用 unzip opencv-3.4.16.zip 来解压 opencv 的压缩包&#xff0c;并且进入 opencv 目录(cd opencv-3.4.16) 三. 修改 opencv 的 cmake 脚本的内容 先 cd platforms/linux 然后修改 arm-gnueabi.to…