0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

Liteos-a内核工作队列的实现原理分析及经验总结——芯海科技PPG芯片CS1262接入OpenHarmony实战

芯海科技(深圳)股份有限公司 2022-04-26 09:26 次阅读

摘要

OpenHarmony系统中使用了liteos-m、liteos-a、linux三种内核,工作队列是linux内核引入的一种异步处理机制。本文对liteos-a内核下工作队列的实现原理进行分析,并对芯海科技的PPG芯片CS1262接入OpenHarmony过程中对工作队列的使用方法进行总结分享。

工作队列工作队列(Workqueue)是linux内核引入的一种异步进程调用的机制,允许内核代码请求在将来某个时间调用一个函数。在内核源码的documentation中也有对workqueue的说明,路径为Documentation/core-api/workqueue.rst。网络上也有很多对工作队列机制的总结文章可以学习,《Linux workqueue工作原理》讲解的就比较详细。简单理解就是先创建一个队列用于存放work,并创建一个处理线程叫worker;用户进程通过调用接口往队列里面添加work驱动着worker来处理,如下:


fa12291e-c4bc-11ec-8521-dac502259ad0.png

而OpenHarmony系统根据不同的使用场景使用了liteos-m/liteos-a/linux三种内核。

这里通过CS1262驱动的实现,对liteos-a内核中工作队列的使用及实现做一下分析。


工作队列的使用方法

Sensor设备作为外接设备重要组成模块,Sensor驱动模型为上层Sensor服务系统提供稳定的Sensor基础能力接口,包括Sensor列表查询、Sensor启停、Sensor订阅及去订阅,Sensor参数配置等功能。传感器驱动模型总体框架如图1所示。

OpenHarmony系统对workqueue提供了几个接口,以方便用户的使用。

1. 调用HdfWorkQueueInit,传入queue名称,创建并初始化一个workqueue

2. 调用HdfWorkInit,可以理解为初始化一个work的模板,主要记录处理这个queue里面work的回调函数func以及参数para信息,类似于linux的work_struct

3. 通过调用HdfAddWork往workqueue中添加work,触发调用与此queue关联的回调函数func


步骤1~2可以在CS1262驱动HdfDriverEntry对象的Init接口中看到

int32_t InitPpgDriver(struct HdfDeviceObject *device)
{
    CHECK_NULL_PTR_RETURN_VALUE(device, HDF_ERR_INVALID_PARAM);
    struct PpgDrvData *drvData = (struct PpgDrvData *)device->service;
    CHECK_NULL_PTR_RETURN_VALUE(drvData, HDF_ERR_INVALID_PARAM);

    if (HdfWorkQueueInit(&drvData->ppgWorkQueue, HDF_PPG_WORK_QUEUE) != HDF_SUCCESS) {
        HDF_LOGE("%s: Ppg init work queue failed", __func__);
        return HDF_FAILURE;
    }

    if (HdfWorkInit(&drvData->ppgWork, PpgDataWorkEntry, drvData) != HDF_SUCCESS) {
        HDF_LOGE("%s: Ppg create thread failed", __func__);
        return HDF_FAILURE;
    }

    drvData->initStatus = true;
    drvData->enable = false;
    drvData->detectFlag = false;

    HDF_LOGI("%s: init Ppg driver success", __func__);
    return HDF_SUCCESS;
}

步骤3在CS1262的中断处理函数中,有数据需要上报时会产生一个中断,中断处理中添加一个work通过工作队列机制来实现数据的上报。

static int32_t PpgReadInt(uint16_t gpio, void *data)
{
    struct PpgDrvData *drvData = PpgGetDrvData();
    CHECK_PPG_INIT_RETURN_VALUE(drvData, HDF_ERR_NOT_SUPPORT);

    if (!drvData->enable) {
        HDF_LOGE("%s: ppg not enabled", __func__);
        return HDF_SUCCESS;
    }

    if (!HdfAddWork(&drvData->ppgWorkQueue, &drvData->ppgWork)) {
        HDF_LOGE("%s: Ppg add work queue failed", __func__);
        return HDF_FAILURE;
    }

    return HDF_SUCCESS;
}

CS1262工作对列中work的回调接口实现,两个主要功能:获取数据,数据上报。

static void PpgDataWorkEntry(void *arg)
{
    int32_t ret;
    struct PpgDrvData *drvData = (struct PpgDrvData *)arg;
    uint16_t readLen = 0;

    CHECK_NULL_PTR_RETURN(drvData);
    CHECK_NULL_PTR_RETURN(drvData->chipData.opsCall.ReadData);

    ret = drvData->chipData.opsCall.ReadData(g_fifoBuf, sizeof(g_fifoBuf), &readLen);
    if ((ret != HDF_SUCCESS) || (readLen > sizeof(g_fifoBuf))) {
        HDF_LOGE("%s: Ppg read data failed", __func__);
        return;
    }

    if (PpgReportInt(g_fifoBuf, readLen) != HDF_SUCCESS) {
        HDF_LOGE("%s: Cs1262ReportInt fail", __func__);
    }
}

说明:当前CS1262驱动已提交PR,还未正式上库


Liteos-a内核中工作队列的实现

1.HdfWorkQueueInit接口

(1)在liteos-a系统源码中搜索,接口定义如下:

源文件:drivers/adapter/khdf/liteos/osal/src/osal_workqueue.c
代码如下:
int32_t HdfWorkQueueInit(HdfWorkQueue *queue, char *name)
{
......

    queue->realWorkQueue = create_singlethread_workqueue(name);

......

    return HDF_SUCCESS;
}

(2)create_singlethread_workqueue接口实现如下

源文件:kernel/liteos_a/bsd/compat/linuxkpi/include/linux/workqueue.h

宏定义:
#define create_singlethread_workqueue(name) \
    linux_create_singlethread_workqueue(name)


源文件:kernel/liteos_a/bsd/compat/linuxkpi/src/linux_workqueue.c
代码如下:
struct workqueue_struct *linux_create_singlethread_workqueue(char *name)
{
    return __create_workqueue_key(name, 1, 0, 0, NULL, NULL);
}

(3)__create_workqueue_key中初始化化了一个event后面会用;创建一个workqueueThread线程用来处理这个workqueue里的所有work

源文件:kernel/liteos_a/bsd/compat/linuxkpi/src/linux_workqueue.c
代码如下:
struct workqueue_struct *__create_workqueue_key(char *name,
                                                int singleThread,
                                                int freezeable,
                                                int rt,
                                                struct lock_class_key *key,
                                                const char *lockName)
{
......

    (VOID)LOS_EventInit(&wq->wq_event);

    if (singleThread) {
        cwq = InitCpuWorkqueue(wq, singleThread);
        ret = CreateWorkqueueThread(cwq, singleThread);
    } else {
        LOS_MemFree(m_aucSysMem0, wq->cpu_wq);
        LOS_MemFree(m_aucSysMem0, wq);
        return NULL;
    }

    if (ret) {
        destroy_workqueue(wq);
        wq = NULL;
    }

    return wq;
}

(4)LOS_EventInit就是liteos系统的task之间通信的事件机制实现

(5)CreateWorkqueueThread就是调用的liteos的LOS_TaskCreate来创建一个Task(也即thread),处理函数为WorkerThread,如下

源文件:kernel/liteos_a/bsd/compat/linuxkpi/src/linux_workqueue.c
代码如下:
STATIC UINT32 CreateWorkqueueThread(cpu_workqueue_struct *cwq, INT32 cpu)
{
    ......

    taskInitParam.pfnTaskEntry = (TSK_ENTRY_FUNC)WorkerThread;

    ......

    ret = LOS_TaskCreate(&cwq->wq->wq_id, &taskInitParam);

    ......

    return LOS_OK;
}

STATIC VOID WorkerThread(cpu_workqueue_struct *cwqParam)
{
    cpu_workqueue_struct *cwq = cwqParam;

    for (;;) {
        if (WorkqueueIsEmpty(cwq)) {
(VOID)LOS_EventRead(&(cwq->wq->wq_event),0x01,LOS_WAITMODE_OR|LOS_WAITMODE_CLR,LOS_WAIT_FOREVER);
        }
        RunWorkqueue(cwq);
    }
}

线程处理函数里面就是一个死循环,当workqueue中为空时,代码会阻塞在LOS_EventRead处,读一个还未发生的事件时,代码就会在此处一直阻塞,直到事件发生;

(6)在事件发生(有work可以处理)时,就会调用真正的处理接口RunWorkqueue,对work调用回调函数(例如上面CS1262中的PpgDataWorkEntry)

源文件:kernel/liteos_a/bsd/compat/linuxkpi/src/linux_workqueue.c
代码如下:
STATIC VOID RunWorkqueue(cpu_workqueue_struct *cwq)
{
    ......
    if (!WorkqueueIsEmpty(cwq)) {
        ......
        func = work->func;
        func(work);
        ......
    }
    LOS_SpinUnlockRestore(&g_workqueueSpin, intSave);
}

2.HdfWorkInit接口

(1)接口实现如下

源文件:drivers/adapter/khdf/liteos/osal/src/osal_workqueue.c
代码如下:
int32_t HdfWorkInit(HdfWork *work, HdfWorkFunc func, void *para)
{
    ......

    wrapper = (struct WorkWrapper *)OsalMemCalloc(sizeof(*wrapper));
    if (wrapper == NULL) {
        HDF_LOGE("%s malloc fail", __func__);
        return HDF_ERR_MALLOC_FAIL;
    }
    realWork = &(wrapper->work.work);
    wrapper->workFunc = func;
    wrapper->para = para;

    INIT_WORK(realWork, WorkEntry);
    work->realWork = wrapper;

    return HDF_SUCCESS;
}

(2)从这里看这个处理函数func赋值给了wrapper->workFunc,而在INIT_WORK中将WorkEntry接口赋值给了work的func

源文件:drivers/adapter/khdf/liteos/osal/src/osal_workqueue.c
代码如下:
#ifdef WORKQUEUE_SUPPORT_PRIORITY
#define INIT_WORK(work, callbackFunc)  do {      \
    INIT_LIST_HEAD(&((work)->entry));            \
    (work)->func = (callbackFunc);               \
    (work)->data = (atomic_long_t)(0);           \
    (work)->work_status = 0;                     \
    (work)->work_pri = OS_WORK_PRIORITY_DEFAULT; \
} while (0)
#else
#define INIT_WORK(work, callbackFunc)  do { \
    INIT_LIST_HEAD(&((work)->entry));       \
    (work)->func = (callbackFunc);          \
    (work)->data = (atomic_long_t)(0);      \
    (work)->work_status = 0;                \
} while (0)
#endif

(3)从前面分析,有work需要处理时调用的就是这个(work)->func;而这里看这个接口的值是WorkEntry,怎么跟驱动侧输入的处理接口联系的呢?就是这个WorkEntry实现的

源文件:drivers/adapter/khdf/liteos/osal/src/osal_workqueue.c
代码如下:
static void WorkEntry(struct work_struct *work)
{
    struct WorkWrapper *wrapper = NULL;
    if (work != NULL) {
        wrapper = (struct WorkWrapper *)work;
        if (wrapper->workFunc != NULL) {
            wrapper->workFunc(wrapper->para);
        } else {
            HDF_LOGE("%s routine null", __func__);
        }
    } else {
        HDF_LOGE("%s work null", __func__);
    }
}

从这里看就是调用的wrapper->workFunc,也即HdfWorkInit接口传入的回调函数。

3.HdfAddWork接口

(1)接口定义如下

源文件:drivers/adapter/khdf/liteos/osal/src/osal_workqueue.c
代码如下:
bool HdfAddWork(HdfWorkQueue *queue, HdfWork *work)
{
    ......

    return queue_work(queue->realWorkQueue, &((struct WorkWrapper *)work->realWork)->work.work);
}

(2)queue_work接口是一个宏,定义如下

源文件:drivers/adapter/khdf/liteos/osal/src/osal_workqueue.c
代码如下:
#define queue_work(wq, work) \
    linux_queue_work(wq, work)

源文件:kernel/liteos_a/bsd/compat/linuxkpi/src/linux_workqueue.c

linux_queue_work最终调用的接口是InsertWork,中间的调用如下

linux_queue_work()
   |--> QueueWorkOn()
           |--> QueueWork()
                  |-->InsertWork()

接口实现:
STATIC VOID InsertWork(cpu_workqueue_struct *cwq, struct work_struct *work, 
struct list_head *head, UINT32 *intSave)
{
#ifdef WORKQUEUE_SUPPORT_PRIORITY
    WorkListAdd(&work->entry, head, work->work_pri);
#else
    WorkListAddTail(&work->entry, head);
#endif
    LOS_SpinUnlockRestore(&g_workqueueSpin, *intSave);
    (VOID)LOS_EventWrite(&(cwq->wq->wq_event), 0x01);
    LOS_SpinLockSave(&g_workqueueSpin, intSave);
}

从实现上看这里就是写了一个Event,还记得前面在init里面一个阻塞在读Event的吗?这里的LOS_EventWrite就代表了事件发生,然后就可以正常处理work了


总结

OpenHarmony的liteos-a内核中工作队列的实现就是参照linux内核的实现,只是底层使用的是嵌入式系统中事件处理机制。

OpenHarmony在内核上层做了一层封装(OSAL),在使用linux和liteos-a内核时工作队列的使用方式统一,驱动开发时无感知。

芯海科技作为OpenHarmony项目群B类捐赠人,已加入DriverFramework SIG和DevBoard SIG。在DriverFramework SIG中负责心率传感器PPG驱动模型和HDI的实现。CS1262是芯海科技最新推出的一款用于光电血管容积图(PPG)信号采集的高端模拟前端(AFE),通过SPI总线与主控通信。关键性能指标达到业界一流水平:

高精度测量:最高PPG SNR高达110dB

◆ 超强抗干扰:PSRR ≥ 90dB(0.5Hz~10MHz 范围内的Boost噪声)

◆ 低功耗:83uA@100Hz@4Ch@2阶环境光

◆ 全肤色支持:单路LED Driver最大电流可达125mA,两路合并后支持250mA

◆ 高可靠:支持过温保护/关键寄存器保护/LED过流保护/SPI通讯可靠性check

◆ 易用性:支持1/2阶环境光消减/硬件佩戴检测/自动调光/动态配置刷新


声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 芯片
    +关注

    关注

    455

    文章

    50714

    浏览量

    423139
  • OpenHarmony
    +关注

    关注

    25

    文章

    3713

    浏览量

    16254
收藏 人收藏

    评论

    相关推荐

    科技CS1262:高精度全场景PPG AFE

    科技CS1262作为一款高精度全场景PPG AFE芯片,以其卓越的性能指标、全面的功能特性和低功耗特性,为用户提供了高效、可靠的生命体征
    的头像 发表于 12-17 17:32 126次阅读
    <b class='flag-5'>芯</b><b class='flag-5'>海</b>科技<b class='flag-5'>CS1262</b>:高精度全场景<b class='flag-5'>PPG</b> AFE

    科技可穿戴设备PPG方案:结构、工艺与高精度设计探索

    CS1262芯片以其卓越的性能指标,如高配置、高精度测量、超强抗干扰、低功耗、全肤色支持、高可靠性和易用性等,赢得了业界的广泛认可。未来,随着科技在健康测量领域的持续投入和创新,
    的头像 发表于 12-17 17:27 104次阅读
    <b class='flag-5'>芯</b><b class='flag-5'>海</b>科技可穿戴设备<b class='flag-5'>PPG</b>方案:结构、工艺与高精度设计探索

    科技系列OpenHarmony3.1芯片支持智慧生态构建

    随着OpenHarmony3.1的正式发布,科技作为OpenHarmony生态的重要参与者,及时推出了两款与系统适配的新品:健康测量CS1262
    的头像 发表于 12-17 17:25 120次阅读
    <b class='flag-5'>芯</b><b class='flag-5'>海</b>科技系列<b class='flag-5'>OpenHarmony</b>3.1<b class='flag-5'>芯片</b>支持智慧生态构建

    科技深化穿戴健康测量产品布局,CS1262CS1253芯片引领创新

    随着全球健康意识的提升,健康测量设备需求激增,特别是智能手表和手环等腕带设备,年均复合增长率超过15%。科技自2007年起便布局健康测量领域,从基础的体重秤、体脂秤到复杂的PPG、BIA等智能
    的头像 发表于 12-17 17:23 80次阅读
    <b class='flag-5'>芯</b><b class='flag-5'>海</b>科技深化穿戴健康测量产品布局,<b class='flag-5'>CS1262</b>与<b class='flag-5'>CS</b>1253<b class='flag-5'>芯片</b>引领创新

    求一份科技CS8M188案例教程和demo

    科技CS8M188案例教程和demo,邮箱18819974553@163.com 不要汇编,只需要C语言,为什么官方资源那么少的
    发表于 12-17 10:58

    科技CS32A01X系列芯片闪耀第七届集创赛,助力半导体人才培育

    科技的SmartAnalog芯片CS32A01X系列产品,凭借其卓越的综合性能,被“曾益慧创杯”选定为数模混合信号赛题的测试芯片,为
    的头像 发表于 11-28 09:15 209次阅读
    <b class='flag-5'>芯</b><b class='flag-5'>海</b>科技<b class='flag-5'>CS32A</b>01X系列<b class='flag-5'>芯片</b>闪耀第七届集创赛,助力半导体人才培育

    科技车规级SAR ADC新品CS1795X荣获“中国

    11月7日,2024中国微电子产业促进大会暨第十九届“中国”优秀产品征集结果发布仪式在珠海横琴粤澳深度合作区隆重举行。科技旗下集成高精度基准的低功耗汽车级SARADC芯片
    的头像 发表于 11-09 01:06 339次阅读
    <b class='flag-5'>芯</b><b class='flag-5'>海</b>科技车规级SAR ADC新品<b class='flag-5'>CS</b>1795X荣获“中国<b class='flag-5'>芯</b>”

    CS1262资料

    求一波CS1262芯片的例程代码、寄存器配置、完整数据手册等,邮箱24181214463@stu.xidian.edu.cn。谢谢大家啦
    发表于 10-22 11:22

    ADC 芯片 CS1237 为例,简述国产 ADC 芯片的优势

    科技的 ADC 芯片 CS1237 便是国产 ADC 芯片中的佼佼者,通过对其的分析,我们可以一窥国产 ADC
    的头像 发表于 07-23 15:25 1225次阅读

    科技 PD 应用笔记: 基于 CS32G02X 的 PD3.1-EPR 系统设计

    本文档介绍和说明科技旗下 CS32G02X 芯片的 PD3.1-EPR 系统系统设计,包括 PD3.1-EPR 协议介绍、PD3.1-EPR 硬件设计要求、PD3.1-EPR 软件
    发表于 05-16 14:18

    应用笔记:通用 MCU 基于 IAR 芯片包 IAR9 开发指南

    科技与 IAR Systems 达成合作,IAR Embedded Workbench for Arm 已全面支持科技CS32F10
    发表于 05-16 11:52

    科技通用 MCU 应用笔记: CS32F103 FOC 电机评估板使用指南

    本文档描述了基于 CS32F103 系列芯片的矢量控制电机开发板 V1.00 的设计理念、结构和使用说明,以帮助用户快速使用此开发板,评估 CS
    发表于 05-16 11:35

    OpenHarmony内核编程实战

    编程入门[Hello,OpenHarmony]在正式开始之前,对于刚接触OpenHarmony的伙伴们,面对大篇幅的源码可能无从下手,不知道怎么去编码写程序,下面用一个简单的例子带伙伴们入门。▍任务
    的头像 发表于 03-27 08:31 807次阅读
    <b class='flag-5'>OpenHarmony</b><b class='flag-5'>内核</b>编程<b class='flag-5'>实战</b>

    Linux 6.9-rc1发布,加入定时器、工作队列及AMD P-State优化

    内核方面,6.9版本进行了定时器的大幅重构,增加了每个CPU核心的时间轮支持,以提升定时器运效率,尤其在网络应用中表现出色。此外,工作队列子系统新增BH工作队列支持,摒弃了老旧的tasklet机制。
    的头像 发表于 03-25 13:49 448次阅读

    【鸿蒙】小型系统LiteOS-A内核

    LiteOS-A 内核 移植概述 移植场景 LiteOS-A 当前支持 ARMv7-a 指令集架构,如果三方芯片为 ARMv7-
    的头像 发表于 02-29 16:16 1167次阅读
    【鸿蒙】小型系统<b class='flag-5'>LiteOS-A</b><b class='flag-5'>内核</b>