OpenHarmony PWM Core 原创 精华
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变量,能有效的防止互斥访问的问题。