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