天天看點

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一次開發,多端部署的展現。

繼續閱讀