国产精品久久久aaaa,日日干夜夜操天天插,亚洲乱熟女香蕉一区二区三区少妇,99精品国产高清一区二区三区,国产成人精品一区二区色戒,久久久国产精品成人免费,亚洲精品毛片久久久久,99久久婷婷国产综合精品电影,国产一区二区三区任你鲁

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階環境光消減/硬件佩戴檢測/自動調光/動態配置刷新


聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • 芯片
    +關注

    關注

    463

    文章

    54007

    瀏覽量

    465902
  • OpenHarmony
    +關注

    關注

    33

    文章

    3952

    瀏覽量

    21093
收藏 人收藏
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

    評論

    相關推薦
    熱點推薦

    ALED1262ZT汽車級12通道LED驅動芯片詳解

    ALED1262ZT汽車級12通道LED驅動芯片詳解 在汽車照明領域,LED 驅動芯片的性能和穩定性至關重要。ALED1262ZT 作為一款專為汽車應用設計的 12 通道 LED 驅動
    的頭像 發表于 01-26 17:25 ?536次閱讀

    CS5801搭配AS721芯片實現HDMI轉DP雙向互轉方案

    CS5801與AS721芯片組合實現HDMI與DP雙向互轉。CS5801支持HDMI2.0b轉DP1.4a,提供4K@60Hz傳輸;AS72
    的頭像 發表于 01-21 10:20 ?208次閱讀
    <b class='flag-5'>CS</b>5801搭配AS721<b class='flag-5'>芯片</b><b class='flag-5'>實現</b>HDMI轉DP雙向互轉方案

    【「Linux 設備驅動開發(第 2 版)」閱讀體驗】Linux內核開發基礎

    ,除非隱式地創建內核線程或使用線程中斷,否則工作隊列是唯一的選擇。 struct work_struct { atomic_long_t data; struct list_head entry
    發表于 01-12 22:45

    品速遞 | 科技CS32C010:以32位性能革新8位MCU場景應用

    在當前激烈的消費電子市場競爭中,如何在不顯著增加成本的前提下,為產品設計賦予更強大的處理性能,并滿足如多燈指示、TFT顯示等更豐富和復雜的控制需求?科技(股票代碼:688595)推出
    的頭像 發表于 11-27 09:34 ?862次閱讀
    <b class='flag-5'>芯</b>品速遞 | <b class='flag-5'>芯</b><b class='flag-5'>海</b>科技<b class='flag-5'>CS</b>32C010:以32位性能革新8位MCU場景應用

    在qemu上體驗來RISC-V處理器運行鴻蒙LiteOS-M內核

    在qemu上體驗來RISC-V處理器運行鴻蒙LiteOS-M內核 1.本文概述 2.下載qemu 3.下載鴻蒙LiteOS-M 4.運行與測試 5.gdb調試 1.本文概述
    發表于 10-31 09:04

    單片機 燒錄器提示“燒錄文件錯誤”怎么解決

    單片機燒錄器提示“燒錄文件錯誤”怎么解決? 簡易燒錄器 下載到燒錄器正常,讀取燒錄器正常,讀取芯片正常, 但是一燒錄就提示燒錄文件錯
    發表于 09-25 09:23

    請教一些關于CS1262芯片的問題

    ① 我們理解這款芯片是光電信號讀取(采集傳感器),對嗎?亦或者是發射和接收都有的集成芯片? ② 這款芯片輸出的信號是模擬信號還是數字信號?或是都可以? ③ 如果是數字信號的話,芯片輸出
    發表于 09-17 15:30

    [開發工具] CS32L015的相關資料,基于科技MCU的小尺寸彩屏顯示解決方案

    你好!看到CSCS32L015方案提供小屏幕解決方案,想要評估該方案,可以將 CS32L015 的相關資料 ( 用戶手冊、Pack包、屏幕開發上位機和keil對應工程demo、IAP升級Boot
    發表于 09-16 14:32

    Kubernetes集群運維經驗總結

    本文總結了我和團隊在K8s生產環境中遇到的10個最常見且最致命的坑,每個坑都配有真實案例、詳細分析和可執行的解決方案。
    的頭像 發表于 08-18 11:23 ?632次閱讀

    昂科燒錄器支持ChipSea科技的32位微控制器CS32F030C8T

    芯片燒錄領導者昂科技術其燒錄軟件迎來了一次重大的版本升級,在發布新版本燒錄軟件的同時,同步宣布新增了多款兼容的芯片型號,其中有科技的32位微控制器
    的頭像 發表于 06-20 19:40 ?757次閱讀
    昂科燒錄器支持ChipSea<b class='flag-5'>芯</b><b class='flag-5'>海</b>科技的32位微控制器<b class='flag-5'>CS</b>32F030C8T

    品速遞 | 科技CS32G501:高性能、高集成、安全型32位MCU新旗艦

    、極致功耗控制、高度集成與靈活性設計以及多重安全防護等。針對以上應用痛點,科技(股票代碼:688595)全新推出CS32G501系列MCU。該芯片基于32位高
    的頭像 發表于 06-13 18:52 ?1546次閱讀
    <b class='flag-5'>芯</b>品速遞 | <b class='flag-5'>芯</b><b class='flag-5'>海</b>科技<b class='flag-5'>CS</b>32G501:高性能、高集成、安全型32位MCU新旗艦

    射頻電路與芯片設計要點

    的研究生教材,也可供從事射頻電路和系統設計工作的工程技術人員參考。 此書是作者20年射頻設計成與敗的設計經驗總結,理論知識和實踐經驗都很豐富。講解獨特,強調匹配和射頻接地。 獲取完整文檔資料可下載附件哦!!!!如果內容有幫助
    發表于 06-13 17:07

    RDMA簡介5之RoCE V2隊列分析

    在RoCE v2協議中,RoCE v2隊列是數據傳輸的最底層控制機制,其由工作隊列(WQ)和完成隊列(CQ)共同組成。其中工作隊列采用雙向通道設計,包含用于存儲即將發送數據的發送
    發表于 06-05 17:28

    12V1A隔離電源芯片LP3669CS

    深圳市三佛科技有限公司供應12V1A隔離電源芯片LP3669CS,原裝現貨 型號:LP3669CS 品牌:茂微 封裝:SOP7L VCC
    發表于 04-02 15:34

    GaN E-HEMTs的PCB布局經驗總結

    GaN E-HEMTs的PCB布局經驗總結
    的頭像 發表于 03-13 15:52 ?1339次閱讀
    GaN E-HEMTs的PCB布局<b class='flag-5'>經驗總結</b>