OpenHarmony PWM Core 原创 精华

杀手来过
发布于 2022-4-9 09:25
浏览
0收藏

PWM Core

在上一篇中中,我们使用到了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变量,能有效的防止互斥访问的问题。

©著作权归作者所有,如需转载,请注明出处,否则将追究法律责任
收藏
回复
举报
回复
    相关推荐