> 文档中心 > OpenHarmony HDF PWM Core 学习

OpenHarmony HDF PWM Core 学习

文章目录

    • PWM Core
      • 一、PwmDev
      • 二、PWM 核心层接口
      • 三、PWM Core的优势

PWM Core

在PWM开发 基于STM32MP1中,我们使用到了pwm_if.h,将我们实现的pwm驱动注册到pwm核心层,本文就来介绍这个pwm core是如何实现的,以及这个核心层的作用。

一、PwmDev

在pwm core中,定义了一个PwmDev,它是对所有pwm外设的一个抽象的描述:

struct PwmDev {    struct IDeviceIoService service;    //驱动服务    struct HdfDeviceObject *device;     //驱动对象    struct PwmConfig cfg; //pwm配置    struct PwmMethod *method;    //底层pwm驱动实现的方法    bool busy;     //忙标志    uint32_t num;  //pwm设备编号    OsalSpinlock lock;    //自旋锁    void *priv;    //私有数据};

该对象在pwm core中起到承上启下的作用,可以通过PwmMethod对象来调用底层的pwm驱动,实现方波的配置。对于上层,则在驱动的基础上,增加了busy标志以及自旋锁,并封装了对底层驱动服务的订阅操作,简化了内核驱动使用pwm的步骤。(省去了订阅驱动服务)

二、PWM 核心层接口

在上一篇中,我们会调用PwmDeviceAdd()函数,将我们定义的PWM驱动注册到核心层,其实现是非常简单的:

主要的工作就是初始化自旋锁,以及将pwmdev对象和驱动服务互相绑定。

//添加PWM驱动到核心层int32_t PwmDeviceAdd(struct HdfDeviceObject *obj, struct PwmDev *pwm){    //检查参数    if (obj == NULL || pwm == NULL) { HDF_LOGE("%s: invalid parameter", __func__); return HDF_ERR_INVALID_PARAM;    }    if (pwm->method == NULL || pwm->method->setConfig == NULL) { HDF_LOGE("%s: setConfig is null", __func__); return HDF_ERR_INVALID_PARAM;    }    //初始化pwm的锁,pwm驱动在同一时间只能被一个应用使用    if (OsalSpinInit(&(pwm->lock)) != HDF_SUCCESS) { HDF_LOGE("%s: init spinlock fail", __func__); return HDF_FAILURE;    }    //绑定驱动服务和pwmdev,PwmOpen()会用到    pwm->device = obj;    obj->service = &(pwm->service);    return HDF_SUCCESS;}

还记得核心层如何和底层pwm驱动通信吗?我们实现了一个PwmMethod的对象,这个对象会在核心层被频繁的调用,使用该对象里的函数。上一篇中,我们实现了setConfig方法,我们来看在核心层中,它是如何被使用的。具体而言,是在pwm_core.c中的PwmSetConfig():

//更新pwm的配置int32_t PwmSetConfig(DevHandle handle, struct PwmConfig *config){    int32_t ret;    struct PwmDev *pwm = NULL;    //检查参数    if (handle == NULL) { HDF_LOGE("%s: handle is NULL", __func__); return HDF_ERR_INVALID_OBJECT;    }    if (config == NULL) { HDF_LOGE("%s: config is NULL", __func__); return HDF_ERR_INVALID_PARAM;    }    //获取pwmdev    pwm = (struct PwmDev *)handle;    //比较pwm->cfg和config的内容是否相同    if (memcmp(config, &(pwm->cfg), sizeof(*config)) == 0) { //配置相同,说明不需要更新,直接返回 HDF_LOGE("%s: do not need to set config", __func__); return HDF_SUCCESS;    }    //否则,需要更新pwm    //检查method对象,由底层实现的    if (pwm->method == NULL || pwm->method->setConfig == NULL) { HDF_LOGE("%s: setConfig is not support", __func__); return HDF_ERR_NOT_SUPPORT;    }    //调用底层驱动,实现设置pwm    ret = pwm->method->setConfig(pwm, config);    if (ret != HDF_SUCCESS) { HDF_LOGE("%s: failed, ret %d", __func__, ret); return ret;    }    pwm->cfg = *config;    return HDF_SUCCESS;}

核心层的其他函数最终也是调用PwmSetConfig()来完成与底层驱动的沟通。

在对上层应用的处理上,核心层引入了PwmOpen()和PwmClose()来限制进程对pwm驱动的并发访问。当一个应用打算使用某一个pwm驱动时,它必须要先调用PwmOpen()来获取pwm驱动的使用权,在使用完成pwm驱动后,应该及时的调用PwmClose()来释放pwm驱动,以免造成资源的长期独占。

这么做的目的是,pwm的使用往往是有严格的时间要求,应用程序必须在这个时间内对pwm完全占有其使用权,否则pwm产生的方波就有可能出现噪声,导致应用出错。

PwmOpen()是如何实现对pwm驱动的独占呢,下面注释给出答案:

通过busy标志,来实现对pwm驱动的互斥使用。这里的自旋锁,是防止在多线程同时访问pwm->busy变量,导致程序出现严重错误。

//打开PWM,进程会占有pwm的驱动DevHandle PwmOpen(uint32_t num){    int32_t ret;    //通过num获取到pwmdev对象    struct PwmDev *pwm = PwmGetDevByNum(num);    if (pwm == NULL) { HDF_LOGE("%s: dev is null", __func__); return NULL;    }    //进入自旋锁    (void)OsalSpinLock(&(pwm->lock));    //如果pwm的busy被置1,则表示pwm当前被其他进程使用中,返回失败    if (pwm->busy) { (void)OsalSpinUnlock(&(pwm->lock)); HDF_LOGE("%s: pwm%u is busy", __func__, num); return NULL;    }    //否则,调用底层的open函数    if (pwm->method != NULL && pwm->method->open != NULL) { ret = pwm->method->open(pwm);  if (ret != HDF_SUCCESS) {     (void)OsalSpinUnlock(&(pwm->lock));     HDF_LOGE("%s: open failed, ret %d", __func__, ret);     return NULL; }    }    //设置pwm的busy标志为1,表示对pwm驱动的占有    pwm->busy = true;    //退出自旋锁    (void)OsalSpinUnlock(&(pwm->lock));    return (DevHandle)pwm;}

PwmGetDevByNum()函数实现了对pwm驱动服务的获取。我们在实现芯片的pwm底层驱动时,在配置中,必须将pwm驱动服务的名称设置为“HDF_PLATFORM_PWM_x”,x=0,1,2,3…,这样我们才能正确的接入pwm核心层。通过pwm驱动服务,我们就能获取到PwmDev对象。

static struct PwmDev *PwmGetDevByNum(uint32_t num){    int ret;    char *name = NULL;    struct PwmDev *pwm = NULL;    //申请字符串内存    name = (char *)OsalMemCalloc(PWM_NAME_LEN + 1);    if (name == NULL) { return NULL;    }    //根据num拼接字符串:如HDF_PLATFORM_PWM_1    ret = snprintf_s(name, PWM_NAME_LEN + 1, PWM_NAME_LEN, "HDF_PLATFORM_PWM_%u", num);    if (ret < 0) { HDF_LOGE("%s: snprintf_s failed", __func__); OsalMemFree(name); return NULL;    }    //通过名称来获取PWM驱动服务    pwm = (struct PwmDev *)DevSvcManagerClntGetService(name);    OsalMemFree(name);    return pwm;}

三、PWM Core的优势

以往的stm32编程中,都是直接使用pwm 库函数,为何OpenHarmony要引入一个核心层呢?我们来思考加入这个东西后,有什么优势。

首先就是芯片驱动和设备驱动的解耦。假设我写了一个呼吸灯的驱动程序,它会调用pwm_if.h中的函数,在某一个芯片平台上,它能够正常的运行。这时我要换一个芯片平台,因为有pwm核心层对芯片驱动的解耦,我不需要重新写一个呼吸灯的驱动程序。因为对于呼吸灯驱动来说,它调用的接口都是pwm_if.h,这个文件在所有OpenHarmony系统上都是相同的。只要新的芯片平台实现了底层的pwm驱动,那么我的呼吸灯驱动就能完美的运行。这也是OpenHarmony一次开发,多端部署的体现。

其次是加入了对PWM的互斥访问。单片机程序往往不需要考虑多进程共享访问的问题,而OpenHarmony是一个面向众多设备的操作系统,其有运行环境可能是多线程的。pwm核心层加入互斥锁以及busy变量,能有效的防止互斥访问的问题。