1 概述

        开设DP专题的原因,是因为DP作为TI雷达数据处理的核心承载了数据流控制、信号处理、数据处理这些重要模块的具体实现。把DP分析清楚了就能够理解TI信号处理+数据处理的算法原理及实现原理,同时对雷达数据处理流程也能够有更深层次的认识。

        那么为了达到这个目的,需要从以下几个方面来分析DP:

        1). DP是什么?

        2). DP由什么组成的?

        3). DP能干什么?

        4). DP是如何工作(或者说应用层是如何使用DP的?)

        5). 回答完上述问题,我们能从中得到什么?

        那么接下来就以回答上面问题的方式来对DPC进行详细分析;

2.DP是什么?

        大家在学习和使用TI毫米波雷达芯片时,会去开用户手册(sdk user guide),在用户手册里面会搜索到Data Path ,这个就是DPC的英文全称中文翻译为数据路径。

       TI毫米波芯片集成了接收机的射频、频综、中频、AD和处理器,芯片通过天线接收到回波信号经过放大、滤波后由AD将模拟信号转换成数字信号;然后对AD数据进行预处理、频域滤波、CFAR检测、AoA估计最终形成点云数据;同时为了提升处理效率通过dma和hwa进行传输和数据处理的加速,TI把上述除去模拟前端的工作,都纳入到了DP中来;总结DP就是进行数据传输、信号处理、数据处理、点云估计等所有的处理的有机集合,所以Path的含义就在于他是一个完整的处理路径而不是单纯的模块集合。

       DP它即包含了各个处理节点所需的处理算法程序(FFT、CFAR、AOA等等),还包含了各个处理节点间数据是如何交互传输的。同时这里为了提高整个处理效率数据传输采用dma方式,大运算量计算如FFT、CFAR等都通过HWA来实现。

        从狭隘的软件层面来定义DP就是:完成数据传输控制及数据处理的完整路径;从广义层面DP应该是包含软件、硬件的处理架构,通过硬件实现加速,通过软件完成控制和处理。这里也就回答了DP能干什么。

3.DP由什么组成?

3.1 DP基本组成框图

     

        上图给出了DP整个框架,核心部分包括DPM、DPC、DPU及DPEDMA、DPIF,数据路径管理器(DPM)为应用程序提供了一个简化的API结构,同时隐藏了任务间和处理器间通信的复杂性。从以下图表中可以看出,应用程序只需调用各种DPM API来控制处理链(在下面的梯形图中以“蓝色”显示为函数调用),并在报告回调中对这些API的结果做出反应。数据处理链(DPC)也通过DPM为应用程序提供了一个标准化的API结构,并通过数据处理单元(DPUs)封装数据流的实现,同时提供了一个简单的基于IOCTL的接口来配置和触发数据流。

        DPM 是架构中实现“可扩展性”方面的基础层。这一层吸收了所有消息传递的复杂性(跨核和核内),并为应用层以及任何“数据处理链”的集成提供标准化的API。应用层将能够从任何域(MSS或DSS)调用DPM的API,并控制“数据处理链”的配置和执行。DPM提供的API将在MSS和DSS上都可用。它可以支持的部署方式包括(但不限于):

  • 数据路径控制在R5F上,数据路径执行在R5F/HWA和DSP之间分配(分布式)

  • 数据路径控制在R5F上,数据路径执行在R5F上使用HWA(本地)

  • 数据路径控制在R5F上,数据路径执行在DSP上(有无HWA)(远程)

  • 数据路径控制在DSP上,数据路径执行在DSP+HWA上(本地)

  • 数据路径控制在DSP上,数据路径执行在DSP上(本地)

        API 的文档可通过 doxygen 获取,并放置于 mmwave_mcuplus_sdk_<ver>\ti\control\dpm\docs\doxygen\html\index.html。

        DPC 是数据路径中的一个独立层,封装了毫米波应用的所有数据处理需求,并为与应用的集成提供了明确定义的接口。在SDK中,有一个参考实现,对应于通用的“目标检测”链,这在过去版本的开箱即用演示中已经存在。此链将符合DPM规定的标准API定义。在内部,这一层将使用数据处理单元(DPUs)、数据路径接口和数据路径管理器(DPM)所暴露的功能来实现“目标检测”链所需的数据流。

        API 的文档可通过 doxygen 获取,位于 mmwave_mcuplus_sdk_<ver>\ti\datapath\dpc\objectedetection<deployment_type>\docs\doxygen\html\index.html,并且可以通过 mmwave_mcuplus_sdk_<ver>/docs/mmwave_sdk_module_documentation.html

3.2 DP目录结构

  • sdk/ti/

    • control\dpm\src

      • dpm_core.c 该文件主要负责dpm外接口实现的实现,包括dpm_init,dpm_ctl,dpm_start,dpm_execute,dpm_sync,dpm_sendResult等,应用层通过调用这些接口完成具体处理

      • dpm_mailbox.c 主要实现远程消息处理,包括消息发送、结果发送、消息同步等。 实现方式通过函数列表方式注册到ptrDPM->ptrDomainFxnTable,由ptrDomainFxnTable执行具体函数调用

      • dpm_pipe.c 主要负责消息管理,通过链表形式的队列对消息进行管理,包括将消息放入等待队列,从等待队列中获取需要处理的消息,并把处理后的消息放入空消息队列等

      • dpm_msg.c 负责消息解析执行,在dpm_execute中通过调用 DPM_msgRecv接口获取到消息,并对消息进行解析,然后执行相关处理

    • datapath

      • dpc

        • \dpc\objectdetection\common

          • gen_frame_data.c 用于生成雷达测试输入数据,主要用于模拟雷达信号的采集过程。它通过生成目标对象的信号,并结合噪声,模拟雷达的回波数据

        • \dpc\objectdetection\objdethwa\src

          • objectdetection.c 目标检测 DPC 的主要功能是处理雷达信号,检测目标并计算其位置、速度等信息。它通过一系列的信号处理步骤(如范围处理、多普勒处理、CFAR 检测和角度估计)来实现这一目标。代码中包含了初始化、配置、执行和停止等操作,支持动态调整处理参数。 对外是通过 DPM_ProcChainCfg gDPC_ObjectDetectionCfg = { DPC_ObjectDetection_init, /* Initialization Function: */ DPC_ObjectDetection_start, /* Start Function: */ DPC_ObjectDetection_execute, /* Execute Function: */ DPC_ObjectDetection_ioctl, /* Configuration Function: */ DPC_ObjectDetection_stop, /* Stop Function: */ DPC_ObjectDetection_deinit, /* Deinitialization Function: */ NULL, /* Inject Data Function: */ NULL, /* Chirp Available Function: */ DPC_ObjectDetection_frameStart /* Frame Start Function: */ };

        • dpc\objectdetection\objdethwaDDMA\src

          • DDMA处理

      • dpedma\src

        • dpedma.c 代码提供了一系列用于配置和管理 EDMA 传输的函数,包括: 配置 EDMA 通道的参数集(PaRAM)。 设置同步传输(Sync A 和 Sync AB)。 配置 EDMA 通道的链接(包括主通道和阴影链接)。 触发 EDMA 传输。 分配和释放 EDMA 资源。

        • dpedmahwa.c 它实现了与 HWA(Hardware Accelerator,硬件加速器)相关的 EDMA(Enhanced Direct Memory Access,增强型直接存储器访问)配置功能。这些功能主要用于支持 HWA 的 DMA 触发模式,特别是通过 EDMA 将特定的 32 位签名(1-hot signature)传输到 HWA 的 DMA 完成寄存器中。

      • ti\datapath\dpu

        • 到达角估计

        • CFAR处理

        • 多普勒处理

        • 距离维处理

3.3总结

        通过上面的分析对DP的组成应该有较深的认识和理解了,总结下来DP主要包括以下几部分:用于对外交互dpm模块也是dp的控制模块,用于实现算法的dpu模块,用于实现处理链的objdethwa内部模块,用于进行edma和hwa控制的模块。

4.DP是如何工作(或者说应用层是如何使用DP的?)

        从以下几个方面来回答这个问题:

        a). 示例程序是如何调用这些接口的?

        b).DP内部的调用逻辑是什么样的?

        c).核心的数据是怎样传递的?

4.1 示例程序是如何调用这些接口的?

        这里主要是分析DSS工程中对应的调用关系,从dss_main.c函数开始

初始化的调用关系如下,初始化时由initTask通过调用DPM_init和DPM_synch完成DPM的初始化

  • MmwDemo_initTask() dss_main.c

    • MmwDemo_dataPathOpen() data_path.c,主要是打开相应的dp需要的设备,edma、hwa、uart

    • 初始化接收消息实体 gMmwDssMCB.recvParams.msgObject

    • gMmwDssMCB.dataPathObj.objDetDpmHandle = DPM_init (&dpmInitCfg, &errCode); dpm_core.c,dpm初始化

    • syncStatus = DPM_synch (gMmwDssMCB.dataPathObj.objDetDpmHandle, &errCode); dpm_core.c,与mss端进行同步

执行时的调用关系如下,其中处理主要是在DPM_execute中进行

  • MmwDemo_nonOSLoop

    • MmwDemo_DPC_ObjectDetection_dpmTask

      • DPM_execute

      • MmwDemo_copyResultToHSRAM

      • DPM_sendResult

    • MmwDemo_msgRead

      • RPMessage_recv

      • RPMessage_send

从上面的分析可以发现应用层在使用DP是其调用接口非常简单,很多复杂的实现细节都隐藏在了DP内部,这也说明“DP内部的调用逻辑是什么样的?”,这里面要分析的内容会非常多。

4.2 DP内部的调用逻辑是什么样的?数据路径管理

        DP软件对外接口由数据路径管理(DPM)提供,TI采用了ioctl+消息驱动架构来设计整个数据路径管理功能,首先通过ioctl方式实现DPM层的控制,从而简化DPM层的控制接口;同时通过消息传递机制(消息队列)分离系统组件,消除直接依赖。

        用户通过调用iotcl进行DPM控制,DPM内部有ioctl进行一次命令解析,当解析不是控制执行而是消息时,则将消息放入pipe队列中,DPM在执行函数中负责将pipe队列里的消息取出,然后按照指令执行相应的命令。

        DPM一共定义了4个ioctl命令,每个命令对应的解释如下

/**
 * @brief
 * 这是应用程序可以用来通知处理链缓冲区可供处理的命令。
 *      arg    = &DPM_Buffer
 *      argLen = sizeof (DPM_Buffer)
 * 该命令仅通知本地处理链,不会将请求转发到远程或分布式域。
 *
 * *注意*: 请参考 DPC 文档,了解要传递的数据的确切格式。
 */
#define DPM_CMD_INJECT_DATA             1

/**
 * @brief
 * 这是应用程序可以用来通知模块 mmWave 链路层报告了 CPU/ESM 故障的命令。
 * 该命令传递给处理链以采取适当措施。
 *      arg    = NULL
 *      argLen = 0
 */
#define DPM_CMD_BSS_FAULT               2

/**
 * @brief
 * 这是处理链可以用来通知应用程序链遇到的故障/异常的命令。
 * 通过报告 API 通知应用程序有关断言的信息。
 *      arg    = &assert
 *      argLen = sizeof (DPM_DPCAssert)
 * @sa
 * DPM_Report_DPC_ASSERT
 */
#define DPM_CMD_DPC_ASSERT              3

/**
 * @brief
 * 这是处理链可以用来向应用程序发送任何自定义信息的通用命令。
 * 框架不会解释有效载荷,而是通过报告机制将其传递给应用程序。
 *      arg    = &info
 *      argLen <= sizeof(DPM_DPCInfo)
 * @sa
 *  DPM_Report_DPC_INFO
 */
#define DPM_CMD_DPC_INFO                4

/**
 * @brief
 * 处理链特定命令起始索引: 在此之前的命令索引保留供核心模块使用。
 * 所有处理链应使用此索引作为其自定义命令的起始索引。
 */
#define DPM_CMD_DPC_START_INDEX         100

        DPM的执行函数主要任务有两个:1)、处理进入pipe队列中的消息;2)、根据同步参数,进行距离维、速度维、aoa估计完成数据处理流程;这里有个问题:进行上述处理后如何把结果传到MSS端呢?这个问题作为一个悬念后面来解答。

        上面讲了DPM的设计架构,为了支撑这个架构,DPM还需要有以下功能:1)、pipe队列管理;2)、消息处理模块即每个消息的处理方式;3)、与MSS之间的通信实现。对于execute内执行的信号处理和cfar检测数据dpc部分内容这里不展开讲。

        先讲pipe管理,dpm的pipe队列管理是在dpm_pipe.c中进行的。对外接口是DPM_pipeSend和DPM_pipeRecv,DPM_pipeSend是将数据放入管道队列,DPM_pipeRecv是将数据从pipe队列中取出来。具体实现函数如下:

/**
 *  @b Description
 *  @n
 *      The function is used to receive a message from the pipe module
 *
 *  @param[in] ptrDPM
 *      Pointer to the DPM module
 *  @param[out] ptrMessage
 *      Pointer to the DPM message populated by the API
 *  @param[out] errCode
 *      Error code populated on error
 *
 *  \ingroup DPM_INTERNAL_FUNCTION
 *
 *  @retval
 *      0   -   No more message available
 *  @retval
 *      1   -   Message available
 *  @retval
 *      <0  -   Error
 */
int32_t DPM_pipeRecv (DPM_MCB* ptrDPM, DPM_Msg* ptrMessage, int32_t* errCode)
{
    DPM_PipeMsg*    ptrPipeMsg;
    int32_t         retVal = MINUS_ONE;

    /* Dequeue a message from the pending queue: */
    ptrPipeMsg = DPM_pipeDequeue (ptrDPM);从pipe队列里面去一个节点出来用于进行消息处理
    if (ptrPipeMsg == NULL)
    {
        /* There are no more messages. */
        retVal = 0;
        goto exit;
    }

    /* Copy over the header */
    memcpy ((void*)&ptrMessage->header, (void*)&ptrPipeMsg->msg.header, sizeof(DPM_HeaderMsg));

    /* Does the message have any payload? */
    if (ptrMessage->header.payloadLength != 0U)
    {
        /* YES: Copy the payload */
        memcpy ((void*)&ptrMessage->u.msgPayload,
                (void*)&ptrPipeMsg->msg.u.msgPayload,
                ptrMessage->header.payloadLength);
    } 将数据进行处理

    /* Free the message: */
    DPM_pipeQueueFree (ptrDPM, ptrPipeMsg);将刚刚取到的节点放入free队列中进行节点回收。

    /* There is one message received: Setup the return value */
    retVal = 1U;

exit:
    return retVal;
}

/**
 *  @b Description
 *  @n
 *      The function is used to send a message to the IPC Module
 *
 *  @param[in] ptrDPM
 *      Pointer to the DPM module
 *  @param[in] ptrMessage
 *      Pointer to the DPM message to be sent
 *  @param[out] errCode
 *      Error code populated on error
 *
 *  \ingroup DPM_INTERNAL_FUNCTION
 *
 *  @retval
 *      Success   - 0
 *  @retval
 *      Error     - <0
 */
int32_t DPM_pipeSend (DPM_MCB* ptrDPM, DPM_Msg* ptrMessage, int32_t* errCode)
{
    int32_t         retVal = MINUS_ONE;
    DPM_PipeMsg*    ptrPipeMsg;

    /* Allocate the pipe message: */
    ptrPipeMsg = DPM_pipeQueueAllocate (ptrDPM);//申请一个节点

    /* In order to ensure that the provisioning is correct;
     * we should always have a message */
    if(ptrPipeMsg == NULL)
    {
        retVal= -1;
        goto exit;
    }

    CacheP_wbInv((void*)ptrPipeMsg, sizeof(DPM_PipeMsg), CacheP_TYPE_ALLD);

    /* Copy over the header: */
    memcpy ((void*)&ptrPipeMsg->msg.header, (void*)&ptrMessage->header, sizeof(DPM_HeaderMsg));

    /* Does the message have any payload? */
    if (ptrMessage->header.payloadLength != 0U)
    {
        /* YES: Copy the payload */
        memcpy ((void*)&ptrPipeMsg->msg.u.msgPayload,
                (void*)&ptrMessage->u.msgPayload,
                ptrMessage->header.payloadLength);
    }//将数据放入这个节点

    /* Add the message to the pending queue: */
    DPM_pipeEnqueue (ptrDPM, ptrPipeMsg);//将该节点加入pipe队列

    /* Post the DPM Semaphore: */
    SemaphoreP_post (&ptrDPM->semaphoreHandle);

    /* This line reached -> Success*/
    retVal = 0;

exit:
    return retVal;
}

        围绕这个两个函数还有:DPM_pipeQueueAllocate申请节点、DPM_pipeDequeue删除一个节点、DPM_pipeQueueFree回收被删除的节点、DPM_pipeEnqueue将申请的节点加入队列。四个函数完成节点的管理,从而实现最终pipe管理。

        具体消息时如何处理的呢?为了保证整个软件架构的可扩展性以及为了便于后续好维护,消息的处理方式采用了回调函数列表的方式,即具体消息如何执行可以通过修改函数实体来执行而不需要改变dpm_core.c相应函数实体部分。具体是在DPM_msg.c来实现的,其中对外接口是DPM_msgRecv(),该函数会在DPM_execute中被调用,也就是说来进行消息接收、解析和执行。其内部如下所示:

        

int32_t DPM_msgRecv (DPM_MCB* ptrDPM, int32_t* errCode)
{
    int32_t     retVal;
    DPM_Msg     rxMsg;
    DPM_Msg     respMsg;
    uint32_t    fxnTableIndex;
    bool        done = false;
    bool        processMessage;

    /* Cycle through until all the messages have been processed. */
    while (done == false)
    {
        /* Initialize the process message flag: */
        processMessage = false;

        /* Initialize the received message: */
        memset ((void *)&rxMsg, 0, sizeof(DPM_Msg));

        /* Read the message from the pipe:
         * - All DPM messages from the Local/Remote or Distributed domains are received
         *   and placed into the pipes. */
        retVal = DPM_pipeRecv (ptrDPM, &rxMsg, errCode);
        if (retVal < 0)
        {
            /* Error: Unable to receive the message: */
            done = true;
        }
        else if (retVal == 0)
        {
            /* There are no more messages to be processed: */
            done = true;
        }
        else
        {
            /* Process the received message: */
            processMessage = true;
        }

        /* Is there a message to be processed? */
        if (processMessage == true)
        {
            /* YES: Initialize the response message. */
            respMsg.u.responseMsg.requestMsgId = DPM_MessageId_INVALID;

            /* Initialize the error code: */
            *errCode = 0;

            /* Sanity Checking: Ensure that the message identifier is within range: */
            DebugP_assert (rxMsg.header.id > DPM_MessageId_INVALID);
            DebugP_assert (rxMsg.header.id < DPM_MessageId_MAX);

            /* Convert the message identifier into a function table index. */
            fxnTableIndex = (uint32_t)(rxMsg.header.id - DPM_MessageId_INVALID);
            DebugP_assert (gDPMMessageFxnTable[fxnTableIndex] != NULL);

            /* Process the message. Not all messages will result in a response message */
            retVal = gDPMMessageFxnTable[fxnTableIndex] (ptrDPM, &rxMsg, &respMsg, errCode);
            if (retVal < 0)
            {
                /* Error: Unable to process the message */
                done = true;
            }
            else
            {
                /* Message Processed: Domain specific Post Processing */
                retVal = DPM_msgPostProcess (ptrDPM, &rxMsg, &respMsg, errCode);
                if (retVal < 0)
                {
                    /* Error: Unable to send the message */
                    done = true;
                }
            }
        }
    }
    return retVal;
}

        通过上述代码可以看出来,DPM_msgRecv调用DPM_pipeRecv函数来获取消息数据,然后 retVal = gDPMMessageFxnTable[fxnTableIndex] (ptrDPM, &rxMsg, &respMsg, errCode);根据不同的消息ID执行相应的消息处理函数,同时通过 retVal = DPM_msgPostProcess (ptrDPM, &rxMsg, &respMsg, errCode);完成消息处理后处理。这里函数是由一个while循环体包裹的,说明只要调用一次DPM_msgRecv函数,就会把队列里所有的消息都处理完。

        接下来重点讲gDPMMessageFxnTable,这个实体在dpm_msg.c中的定义如下:

const DPM_MsgHandler gDPMMessageFxnTable[DPM_MessageId_MAX] =
{
    NULL,                           /* Invalid      : DPM_MessageId_INVALID         */
    DPM_msgStartHandler,            /* Start        : DPM_MessageId_START           */
    DPM_msgStopHandler,             /* Stop         : DPM_MessageId_STOP            */
    DPM_msgDPCCfgHandler,           /* DPC Cfg      : DPM_MessageId_DPC_CFG         */
    DPM_msgBSSFaultHandler,         /* BSS Fault    : DPM_MessageId_BSS_FAULT       */
    DPM_msgAssertHandler,           /* Assert       : DPM_MessageId_DPC_ASSERT      */
    DPM_msgResultHandler,           /* Result       : DPM_MessageId_RESULT          */
    DPM_msgDPCInfoHandler,          /* DPC Info     : DPM_MessageId_DPC_INFO        */
    DPM_msgResponseHandler          /* Response     : DPM_MessageId_RESPONSE        */
};

        通过上面的这个代码,大家可以发现每个处理程序都对应一个消息ID;这个消息ID的定义如下所示:

#define DPM_MessageId_INVALID     ((uint32_t) 1) /*! 无效消息标识符: */
#define DPM_MessageId_START       ((uint32_t) 2) /*! 发送此消息表示 DPC 已启动 */
#define DPM_MessageId_STOP        ((uint32_t) 3) /*! 发送此消息表示 DPC 已停止 */
#define DPM_MessageId_DPC_CFG     ((uint32_t) 4) /*! DPC 配置命令:此命令不会被 DPM 框架解释,而是直接传递给处理链。 */
#define DPM_MessageId_BSS_FAULT   ((uint32_t) 5) /*! 16 位输出格式 */
#define DPM_MessageId_DPC_ASSERT  ((uint32_t) 6) /*! 从 DPC 发送的消息,表示检测到断言。 */
#define DPM_MessageId_RESULT      ((uint32_t) 7) /*! 从 DPC 发送的消息,表示结果已可用 */
#define DPM_MessageId_DPC_INFO    ((uint32_t) 8) /*! 从 DPC 发送的消息,表示需要向应用程序报告自定义信息 */
#define DPM_MessageId_RESPONSE    ((uint32_t) 9) /*! 响应消息 */
#define DPM_MessageId_MAX         ((uint32_t) 10) /*! 最大消息:不超过此值。 */

        在DPM_msg.c中DPM_msgRecv函数会对接收的命令解析,其中解析部分的代码主要是根据上述MsgID执行向对应的处理程序,这部分具体代码如下:

/* Convert the message identifier into a function table index. */
fxnTableIndex = (uint32_t)(rxMsg.header.id - DPM_MessageId_INVALID);
DebugP_assert (gDPMMessageFxnTable[fxnTableIndex] != NULL);

/* Process the message. Not all messages will result in a response message */
retVal = gDPMMessageFxnTable[fxnTableIndex] (ptrDPM, &rxMsg, &respMsg, errCode);
 

        接下来主要分析一下上述提到的消息处理函数列表gDPMMessageFxnTable对应的函数。

        DPM_msgStartHandler函数主要功能是启动DPC,所以在函数内是调用回调ptrDPM->procChainCfg.startFxn (ptrDPM->dpcHandle);实现DPC启动。同时这个函数还要负责完成响应消息的生成。

        DPM_msgStartHandler函数主要功能是停止DPC,所以在函数内是调用回调ptrDPM->procChainCfg.stopFxn (ptrDPM->dpcHandle);实现DPC启动。同时这个函数还要负责完成响应消息的生成。

        DPM_msgDPCCfgHandler函数主要功能是功能是进行DPC配置,在进行DPC配置时由于要配置的参数及参数过多,所以也采用配置命令的方式进行配置。

        函数内部主要是调用    retVal = ptrDPM->procChainCfg.ioctlFxn (ptrDPM->dpcHandle,
                                            ptrMessage->u.dpcCfgMsg.cmd,
                                            &ptrMessage->u.dpcCfgMsg.data[0],
                                            ptrMessage->u.dpcCfgMsg.cmdLen);

        ptrDPM->procChainCfg.ioctlFxn这个回调函数的实体是objectdetection.c的DPC_ObjectDetection_ioctl函数,为什么实体是这个呢?这个就要再回到示例程序的初始化部分了,前面已经讲过示例程序中初始化函数为MmwDemo_initTask() dss_main.c(mss_main.c两个程序中都有这个),在这个函数内部通过调用DPM_init()完成DP的初始化,但是调用这个接口前需要对初始化所需的配置参数进行初始化:

        mss_main.c中参数赋值如下:

    /* Setup the configuration: */

    dpmInitCfg.ptrProcChainCfg  = NULL;

    dpmInitCfg.instanceId       = DPC_OBJDET_INSTANCEID;

    dpmInitCfg.domain           = DPM_Domain_REMOTE;

    dpmInitCfg.reportFxn        = MmwDemo_DPC_ObjectDetection_reportFxn;

    dpmInitCfg.arg              = &objDetInitParams;

    dpmInitCfg.argSize          = sizeof(DPC_ObjectDetection_InitParams);

        可以看到加红加粗的部分是NULL表示MSS这边是不进行DPC处理的。再看dss_main.c中相关部分是如何赋值的:

    /* Setup the configuration: */

    dpmInitCfg.ptrProcChainCfg = &gDPC_ObjectDetectionCfg;

    dpmInitCfg.instanceId      = 0xFEEDFEED;

    dpmInitCfg.domain          = DPM_Domain_REMOTE;

    dpmInitCfg.reportFxn       = MmwDemo_DPC_ObjectDetection_reportFxn;

    dpmInitCfg.arg             = &objDetInitParams;

    dpmInitCfg.argSize         = sizeof(DPC_ObjectDetection_InitParams);

        这里ptrProcChainCfg 是进行了赋值,首先我们看下这个ptrProcChainCf结构体的定义什么

typedef struct DPM_ProcChainCfg_t
{
    /**
     * @brief   Initialization function:
     */
    DPM_ProcChainInitFxn                      initFxn;
    /**
     * @brief   Start function:
     */
    DPM_ProcChainStartFxn                     startFxn;
    /**
     * @brief   Execute function:
     */
    DPM_ProcChainExecuteFxn                   executeFxn;
    /**
     * @brief   IOCTL function:
     */
    DPM_ProcChainIoctlFxn                     ioctlFxn;
    /**
     * @brief   Stop function:
     */
    DPM_ProcChainStopFxn                      stopFxn;
    /**
     * @brief   Deinitialization function:
     */
    DPM_ProcChainDeinitFxn                    deinitFxn;
    /**
     * @brief   [Optional] Inject Data Function:
     */
    DPM_ProcChainInjectDataFxn                injectDataFxn;
    /**
     * @brief   [Optional] Chirp Available Callback function:
     */
    DPM_ProcChainChirpAvailableCallbackFxn    chirpAvailableFxn;
    /**
     * @brief   [Optional] Frame Start Callback function:
     */
    DPM_ProcChainFrameStartCallbackFxn        frameStartFxn;
}DPM_ProcChainCfg;

        这里定义都是回调函数指针其中就有 ioctlFxn,也就是说明ptrDPM->procChainCfg.ioctlFxn函数指针是在这里进行赋值的。那具体赋值是什么呢?gDPC_ObjectDetectionCfg变量的赋值如下:

DPM_ProcChainCfg gDPC_ObjectDetectionCfg =
{
    DPC_ObjectDetection_init,            /* Initialization Function:         */
    DPC_ObjectDetection_start,           /* Start Function:                  */
    DPC_ObjectDetection_execute,         /* Execute Function:                */
    DPC_ObjectDetection_ioctl,           /* Configuration Function:          */
    DPC_ObjectDetection_stop,            /* Stop Function:                   */
    DPC_ObjectDetection_deinit,          /* Deinitialization Function:       */
    NULL,                                /* Inject Data Function:            */
    NULL,                                /* Chirp Available Function:        */
    DPC_ObjectDetection_frameStart       /* Frame Start Function:            */
};

         这个定义是在C:\ti\mmwave_mcuplus_sdk_04_07_00_01\mmwave_mcuplus_sdk_04_07_00_01\ti\datapath\dpc\objectdetection\objdethwa\src里面的objectdetection.c中,而且每个函数实体也在这里。也就是说ptrDPM->procChainCfg.ioctlFxn实际执行的函数实体是DPC_ObjectDetection_ioctl。在这个函数里面,主要是根据配置命令执行相应配置操作,具体配置的命令如下:

#define DPC_OBJDET_IOCTL__STATIC_PRE_START_CFG                            (DPM_CMD_DPC_START_INDEX + 0U)

/**
 * @brief 与 @ref DPC_ObjectDetection_PreStartCommonCfg_t 相关的命令。必须在发出
 *        @ref DPC_OBJDET_IOCTL__STATIC_PRE_START_CFG 之前发出此命令
 */
#define DPC_OBJDET_IOCTL__STATIC_PRE_START_COMMON_CFG                     (DPM_CMD_DPC_START_INDEX + 1U)

/**
 * @brief 与范围维度的 @ref DPC_ObjectDetection_CfarCfg_t 相关的命令。
 */
#define DPC_OBJDET_IOCTL__DYNAMIC_CFAR_RANGE_CFG                          (DPM_CMD_DPC_START_INDEX + 2U)

/**
 * @brief 与多普勒维度的 @ref DPC_ObjectDetection_CfarCfg_t 相关的命令。
 */
#define DPC_OBJDET_IOCTL__DYNAMIC_CFAR_DOPPLER_CFG                        (DPM_CMD_DPC_START_INDEX + 3U)

/**
 * @brief 与 @ref DPC_ObjectDetection_MultiObjBeamFormingCfg_t 相关的命令。
 */
#define DPC_OBJDET_IOCTL__DYNAMIC_MULTI_OBJ_BEAM_FORM_CFG                 (DPM_CMD_DPC_START_INDEX + 4U)

/**
 * @brief 与 @ref DPC_ObjectDetection_CalibDcRangeSigCfg_t 相关的命令。
 */
#define DPC_OBJDET_IOCTL__DYNAMIC_CALIB_DC_RANGE_SIG_CFG                  (DPM_CMD_DPC_START_INDEX + 5U)

/**
 * @brief 与 @ref DPC_ObjectDetection_StaticClutterRemovalCfg_t 相关的命令。
 */
#define DPC_OBJDET_IOCTL__DYNAMIC_STATICCLUTTER_REMOVAL_CFG                     (DPM_CMD_DPC_START_INDEX + 6U)

/**
 * @brief 与 @ref DPC_ObjectDetection_MeasureRxChannelBiasCfg_t 相关的命令。
 */
#define DPC_OBJDET_IOCTL__DYNAMIC_MEASURE_RANGE_BIAS_AND_RX_CHAN_PHASE   (DPM_CMD_DPC_START_INDEX + 7U)

/**
 * @brief 与 @ref DPU_AoAProc_compRxChannelBiasCfg_t 相关的命令。
 */
#define DPC_OBJDET_IOCTL__DYNAMIC_COMP_RANGE_BIAS_AND_RX_CHAN_PHASE       (DPM_CMD_DPC_START_INDEX + 8U)

/**
 * @brief 与 @ref DPC_ObjectDetection_fovRangeCfg_t 相关的命令。
 */
#define DPC_OBJDET_IOCTL__DYNAMIC_FOV_RANGE                               (DPM_CMD_DPC_START_INDEX + 9U)

/**
 * @brief 与 @ref DPC_ObjectDetection_fovDopplerCfg_t 相关的命令。
 */
#define DPC_OBJDET_IOCTL__DYNAMIC_FOV_DOPPLER                             (DPM_CMD_DPC_START_INDEX + 10U)

/**
 * @brief 与 @ref DPC_ObjectDetection_fovAoaCfg_t 相关的命令。
 */
#define DPC_OBJDET_IOCTL__DYNAMIC_FOV_AOA                                 (DPM_CMD_DPC_START_INDEX + 11U)

/**
 * @brief 与 @ref DPC_ObjectDetection_RangeAzimuthHeatMapCfg_t 相关的命令。
 */
#define DPC_OBJDET_IOCTL__DYNAMIC_RANGE_AZIMUTH_HEAT_MAP                  (DPM_CMD_DPC_START_INDEX + 12U)

/**
 * @brief 与 @ref DPC_ObjectDetection_extMaxVelCfg_t 相关的命令。
 */
#define DPC_OBJDET_IOCTL__DYNAMIC_EXT_MAX_VELOCITY                        (DPM_CMD_DPC_START_INDEX + 13U)

        这些配置命令主要负责完成距离维、速度维FFT配置、cfar配置、aoa配置等。 

        分析到这里我们再回顾一下DPM相关内容:

        1).示例程序中顶层应用程序在初始化阶段调用DPM_init完成初始化();mss_main中通过cli命令调用DPM_start、DPM_stop、DPM_ioctl进行相应配置,dss_main中通过DPM_execute来执行信号处理和数据处理操作然后再将结果发送到mss端

        2).参数配置方式通过消息命令方式来实现,DPM_start、DPM_stop、DPM_ioctl进行消息发送,DPM_execute完成消息接收和处理。

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐