TI-毫米波雷达芯片解读->代码解读部分-4_DP专题(1)
开设DP专题的原因,是因为DP作为TI雷达数据处理的核心承载了数据流控制、信号处理、数据处理这些重要模块的具体实现。把DP分析清楚了就能够理解TI信号处理+数据处理的算法原理及实现原理,同时对雷达数据处理流程也能够有更深层次的认识。那么为了达到这个目的,需要从以下几个方面来分析DP:1). DP是什么?2). DP由什么组成的?3). DP能干什么?4). DP是如何工作(或者说应用层是如何使用D
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完成消息接收和处理。

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