基于mmWave SDK创建一个IWR6843工程模板(MSS侧)
这两天拿到了一套Ti毫米波雷达的评估套件(IWR6843ISK-ODS + MMWAVEICBOOST),于是便开始了漫长的学习,写下此篇以记录学习过程。
Ti官方实际上也有提供基于SYSBIOS从头开始创建工程的教程,但较为简略且没有具体演示。按照我开发单片机的惯例,既然这集成SoC内部也是MCU+DSP,那么肯定要先点个灯呀!
结果不看不要紧,一看发现SDK里的驱动设计很不友好。它没有工程,无法直接导入CCS调试,只能通过SDK用户手册的4.5节内容对例程进行编译并下载编译后文件。而能供CCS直接导入使用的工程仅在mmWave Industrial Toolbox中提供,但这些工程都十分复杂(打开个main文件一看都有数千行),非常不利于入门。
遂决定自己动手,对比了官方在mmWave Industrial Toolbox中提供的一系列高级例程,搭建一个可以用于驱动测试和功能开发的模板。
一、搭建前的准备
确保以下各项均已被安装:
- mmWave SDK
- CCS
- mmWave Radar Device support
上述这些软件/库如果没安装好的话,推荐先安装好,否则可能会因为某个库/支持包未安装导致工程无法导入、导入后无法编译等问题。
二、在CCS中新建工程(以CCSV10为例)
1. 创建一个CCS工程
打开CCS,在上方菜单栏中选择 Project->New CCS Project…,弹出以下界面
目标器件选择IWR6843,由于我的雷达模块是搭配MMWAVEICBOOST载板使用的,所以我的连接器选择了XDS110。工程名可自定,工程模板选择 SYS/BIOS->Ti Target Examples->Typical,之后Next
Next之后要求选择Target和Platform,在页面下方处如下填写:
Target: ti.targets.arm.elf.R4Ft
Platform: ti.platforms.cortexR:IWR68XX:false:200
Build-profile我选的debug,release应该也是可行的
这样工程就创建好了。在左侧的Project Explorer中,应该能看到名为“Template_IWR68xx”的工程。接下来,我们需要对工程的属性进行调整,并覆盖一些工程自带的文件来满足开发需求。
首先修改main.c,打开工程内的main.c并修改为如下内容:
#include <stdint.h>
#include <stdlib.h>
#include <stddef.h>
#include <string.h>
#include <stdio.h>
#include <math.h>
/* BIOS/XDC Include Files. */
/* SYS/BIOS所使用的头文件 */
#include <xdc/std.h>
#include <xdc/cfg/global.h>
#include <xdc/runtime/IHeap.h>
#include <xdc/runtime/System.h>
#include <xdc/runtime/Error.h>
#include <xdc/runtime/Memory.h>
#include <ti/sysbios/BIOS.h>
#include <ti/sysbios/knl/Task.h>
#include <ti/sysbios/knl/Event.h>
#include <ti/sysbios/knl/Semaphore.h>
#include <ti/sysbios/knl/Clock.h>
#include <ti/sysbios/heaps/HeapBuf.h>
#include <ti/sysbios/heaps/HeapMem.h>
#include <ti/sysbios/knl/Event.h>
#include <ti/sysbios/family/arm/v7a/Pmu.h>
#include <ti/sysbios/family/arm/v7r/vim/Hwi.h>
#include <ti/sysbios/utils/Load.h>
/* mmWave SDK Include Files: */
/* SDK头文件 */
#include <ti/common/sys_common.h>
#include <ti/common/mmwave_sdk_version.h>
#include <ti/drivers/soc/soc.h>
#include <ti/drivers/esm/esm.h>
#include <ti/drivers/crc/crc.h>
#include <ti/drivers/gpio/gpio.h>
#include <ti/drivers/mailbox/mailbox.h>
#include <ti/drivers/pinmux/pinmux.h>
#include <ti/control/mmwave/mmwave.h>
#include <ti/control/dpm/dpm.h>
#include <ti/drivers/osal/DebugP.h>
#include <ti/drivers/uart/UART.h>
#include <ti/utils/cli/cli.h>
#include <ti/utils/mathutils/mathutils.h>
#include <ti/utils/testlogger/logger.h>
MCPI_LOGBUF_INIT(9216);
void MM_initTask(UArg arg0, UArg arg1)
{
while(1)
{
System_printf("Hello world!\n"); //在控制台输出Hello world!
Task_sleep(1000); //任务调度,挂起1s
}
}
int main (void)
{
int32_t errCode; //存放SOC初始化错误代码
SOC_Handle socHandle; //SOC句柄
SOC_Cfg socCfg; //SOC配置结构体
Task_Params taskParams; //任务参数
ESM_init(0U); //与安全等有关,可略过
/* Initialize the SOC confiugration: */
/* 初始化SOC配置结构体 */
memset ((void *)&socCfg, 0, sizeof(SOC_Cfg));
/* 配置SOC配置结构体 */
/* Populate the SOC configuration: */
socCfg.clockCfg = SOC_SysClock_INIT;
socCfg.mpuCfg = SOC_MPUCfg_CONFIG;
/* require to unhalt the DSS if this core is available in selected device */
socCfg.dssCfg = SOC_DSSCfg_UNHALT;
/* Initialize the SOC Module: This is done as soon as the application is started
* to ensure that the MPU is correctly configured. */
/* SOC初始化,必须在系统运行后尽快调用该初始化函数 */
socHandle = SOC_init (&socCfg, &errCode);
/* Initialize the Task Parameters. */
/* 创建一个任务用于测试 */
Task_Params_init(&taskParams);
taskParams.stackSize = 4*1024; //任务堆栈大小4KB
Task_create(MM_initTask, &taskParams, NULL);
/* Start BIOS */
BIOS_start();
return 0;
}
这段代码的main函数部分源自官方参考回答,用于整块SoC的初始化。之后我添加了一个MM_initTask线程,该线程每隔1s会在控制台中打印一次输出以表示程序正常运行。
先别急!此时如果编译的话,会出现很多错误,例如找不到头文件和库文件等。所以,接下来还需要对工程进行一些修改。
2. 拷贝cmd文件
对于Ti的产品,大部分都是通过.cmd文件来确定内存划分情况的。虽然也可以在构建SYS/BIOS时动态生成,但总归不太好用。
因此,我们从 <SDK安装路径>/Packages/ti/platform/xwr68xx 下,拷贝r4f_linker.cmd到工程中,该cmd文件是Ti提供的用于不同平台的通用cmd文件,添加完成后工程如下
打开r4f_linker.cmd,会发现有几个未定义的宏。不过不用担心找不到,之后在工程属性配置里会对这些宏进行预定义以保证编译能通过。
3. 替换.cfg文件
.cfg文件是用于构建SYS/BIOS的配置文件。通常来说,SYS/BIOS的工程都是根据.cfg文件,在构建工程时动态地“组装”出整个系统。这里,我们也使用官方在sdk中提供的.cfg文件对工程自带的.cfg文件进行替换。
打开**<SDK安装路径>/Packages/ti/drivers**下的任意一个驱动文件夹,这里以gpio驱动为例,则打开<SDK安装路径>/Packages/ti/drivers/gpio/test/xwr68xx,从中拷贝mss.cfg到工程中,此时工程如下
此时点击编译,SYS/BIOS系统可以构建出来,只剩下头文件和库文件找不到的问题了。
4. 修改工程属性
在左侧工程视图中,右击工程名,选择Properties,进入工程配置页面。
(1)添加mmWave SDK路径相关的环境变量
Resource->Linked Resources,右侧点击New添加条目MMWAVE_SDK_DIR(名称可自定),路径定位到mmWave SDK安装路径,如下
设置这个变量的好处是,其他需要定位到SDK路径的地方,只需要使用该变量名进行定位即可。当SDK换了位置,也只需要更改这一个变量的值,十分方便。
(2)修改XDCtools编译指令
在XDC构建SYS/BIOS的过程中,需要让其将枚举的类型设置为int型,否则可能会导致诸如串口无法开启、控制台无法打印输出等问题,具体原理暂时不明。
Build->XDCtools->Advanced Options中,页面下方的Additional complier options,填入"–enum_type=int ",如下
(3)修改编译器的处理器选项
Build->Arm Complier->Processor Options,右侧的Designate code state,从32修改为16,否则会产生一些奇奇怪怪的问题。
(4)添加编译器包含路径
Build->Arm Complier->Include Options,右侧上方的搜索路径列表,点击带有绿色加号的图标添加,添加目录 ${MMWAVE_SDK_DIR}/Packages
(5)添加编译器预定义宏
mmWave SDK提供的库在设计的时候考虑了整个系列雷达芯片的兼容性,因此库内有多套代码,通过宏定义来区分需要使用哪一段代码进行编译。为此,我们需要向编译器的预定义符号里添加一些用来表示当前目标芯片的宏。
Build->Arm Complier->Prodefined Symbols,右侧上方添加以下符号(直接复制下面的内容,然后点击框内原先已有的宏,按Ctrl+V即可一次性直接粘贴进去)
SOC_XWR68XX
SUBSYS_MSS
DOWNLOAD_FROM_CCS
MMWAVE_L3RAM_NUM_BANK=6
MMWAVE_SHMEM_TCMA_NUM_BANK=0
MMWAVE_SHMEM_TCMB_NUM_BANK=0
MMWAVE_SHMEM_BANK_SIZE=0x20000
DebugP_ASSERT_ENABLED
_LITTLE_ENDIAN
OBJDET_NO_RANGE
ISK
添加成功后的效果如下
(6)更改编译器的高级选项
此处需要更改Runtime Model Options内的选项,同样也是将enum类型设置为int,与在XDCtools中的设置保持一致。
Build->Arm Complier->Advanced Options,右侧Designate enum type更改为int类型
(7)设定包含的库文件和库文件路径
大部分驱动的函数都封装成库提供给用户了,所以需要将库文件添加到连接器的搜索路径中,否则会出现未解析符号的问题。
Build->Arm Linker->File Search Path,上方添加如下内容(一样可以直接复制过去)
libosal_xwr68xx.aer4f
libesm_xwr68xx.aer4f
libtestlogger_xwr68xx.aer4f
libgpio_xwr68xx.aer4f
libsoc_xwr68xx.aer4f
libpinmux_xwr68xx.aer4f
libcrc_xwr68xx.aer4f
libuart_xwr68xx.aer4f
libmailbox_xwr68xx.aer4f
libmmwavelink_xwr68xx.aer4f
libmmwave_xwr68xx.aer4f
libadcbuf_xwr68xx.aer4f
libdma_xwr68xx.aer4f
libedma_xwr68xx.aer4f
libcli_xwr68xx.aer4f
libhwa_xwr68xx.aer4f
libdpm_xwr68xx.aer4f
libmathutils.aer4f
libcbuff_xwr68xx.aer4f
libhsiheader_xwr68xx.aer4f
librangeproc_hwa_xwr68xx.aer4f
libdpedma_hwa_xwr68xx.aer4f
libqspi_xwr68xx.aer4f
libqspiflash_xwr68xx.aer4f
libsleep_xwr68xx.aer4f
rtsv7R4_T_le_v3D16_eabi.lib
下方添加如下内容,其中MMWAVE_SDK_DIR和前面设定Linked Resources时添加的变量名保持一致即可。
${MMWAVE_SDK_DIR}/packages/ti/control/mmwave/lib
${MMWAVE_SDK_DIR}/packages/ti/control/mmwavelink/lib
${MMWAVE_SDK_DIR}/packages/ti/control/dpm/lib
${MMWAVE_SDK_DIR}/packages/ti/drivers/adcbuf/lib
${MMWAVE_SDK_DIR}/packages/ti/drivers/crc/lib
${MMWAVE_SDK_DIR}/packages/ti/drivers/dma/lib
${MMWAVE_SDK_DIR}/packages/ti/drivers/edma/lib
${MMWAVE_SDK_DIR}/packages/ti/drivers/esm/lib
${MMWAVE_SDK_DIR}/packages/ti/drivers/gpio/lib
${MMWAVE_SDK_DIR}/packages/ti/drivers/hwa/lib
${MMWAVE_SDK_DIR}/packages/ti/drivers/mailbox/lib
${MMWAVE_SDK_DIR}/packages/ti/drivers/osal/lib
${MMWAVE_SDK_DIR}/packages/ti/drivers/pinmux/lib
${MMWAVE_SDK_DIR}/packages/ti/drivers/soc/lib
${MMWAVE_SDK_DIR}/packages/ti/drivers/uart/lib
${MMWAVE_SDK_DIR}/packages/ti/utils/cli/lib
${MMWAVE_SDK_DIR}/packages/ti/utils/mathutils/lib
${MMWAVE_SDK_DIR}/packages/ti/datapath/dpu/rangeproc/lib
${MMWAVE_SDK_DIR}/packages/ti/datapath/dpedma/lib
${MMWAVE_SDK_DIR}/packages/ti/drivers/cbuff/lib
${MMWAVE_SDK_DIR}/packages/ti/utils/hsiheader/lib
${MMWAVE_SDK_DIR}/packages/ti/drivers/qspi/lib
${MMWAVE_SDK_DIR}/packages/ti/drivers/qspiflash/lib
${MMWAVE_SDK_DIR}/packages/ti/utils/libsleep/lib
${MMWAVE_SDK_DIR}/packages/ti/utils/testlogger/lib
添加完成后的效果大致如下
(8)设定链接器的预处理命令
同样地,在此处也需要填入一些预定义的符号,比如r4f_linker.cmd文件中用到的几个宏的大小。
Build->Arm Linker->Advanced Options->Command File PreProcess,上方添加
MMWAVE_L3RAM_NUM_BANK=6
MMWAVE_SHMEM_TCMA_NUM_BANK=0
MMWAVE_SHMEM_TCMB_NUM_BANK=0
MMWAVE_SHMEM_BANK_SIZE=0x20000
添加后效果如下
点击右下角Apply and Close,再对工程重新编译,可以看到所有错误都消失了。但仍然有一个警告,提示缺少systemHeap段定义。因此打开r4f_linker.cmd文件,手动在SECTIONS内添加:
systemHeap : {} > DATA_RAM
之后再编译就不会出现问题了。
三、上板测试
经历了千辛万苦,终于把工程配好了,开始做上板的测试。
点击上方工具栏的绿色甲虫,进入调试模式。点击全速运行,控制台每隔一秒正常输出"Hello World!",工程创建成功。
四、进行一个串口回环的测
最后测试一下串口回环,以下是写好的代码,调用Driver内的uart驱动
#include <stdint.h>
#include <stdlib.h>
#include <stddef.h>
#include <string.h>
#include <stdio.h>
#include <math.h>
/* BIOS/XDC Include Files. */
#include <xdc/std.h>
#include <xdc/cfg/global.h>
#include <xdc/runtime/IHeap.h>
#include <xdc/runtime/System.h>
#include <xdc/runtime/Error.h>
#include <xdc/runtime/Memory.h>
#include <ti/sysbios/BIOS.h>
#include <ti/sysbios/knl/Task.h>
#include <ti/sysbios/knl/Event.h>
#include <ti/sysbios/knl/Semaphore.h>
#include <ti/sysbios/knl/Clock.h>
#include <ti/sysbios/heaps/HeapBuf.h>
#include <ti/sysbios/heaps/HeapMem.h>
#include <ti/sysbios/knl/Event.h>
#include <ti/sysbios/family/arm/v7a/Pmu.h>
#include <ti/sysbios/family/arm/v7r/vim/Hwi.h>
#include <ti/sysbios/utils/Load.h>
/* mmWave SDK Include Files: */
#include <ti/common/sys_common.h>
#include <ti/common/mmwave_sdk_version.h>
#include <ti/drivers/soc/soc.h>
#include <ti/drivers/esm/esm.h>
#include <ti/drivers/crc/crc.h>
#include <ti/drivers/gpio/gpio.h>
#include <ti/drivers/mailbox/mailbox.h>
#include <ti/drivers/pinmux/pinmux.h>
#include <ti/control/mmwave/mmwave.h>
#include <ti/control/dpm/dpm.h>
#include <ti/drivers/osal/DebugP.h>
#include <ti/drivers/uart/UART.h>
#include <ti/utils/cli/cli.h>
#include <ti/utils/mathutils/mathutils.h>
#include <ti/utils/testlogger/logger.h>
MCPI_LOGBUF_INIT(9216);
void MM_idleTask(UArg arg0, UArg arg1)
{
while(1)
{
System_printf("System running...\n");
Task_sleep(5000);
}
}
void MM_initTask(UArg arg0, UArg arg1)
{
/* 串口回环测试 */
UART_Params params; //串口配置结构体
UART_Handle handle; //串口句柄
Task_Params taskParams;
uint8_t dat;
MCPI_Initialize();
GPIO_init(); //GPIO初始化
UART_init(); //串口初始化
/* 串口引脚初始化 */
/* 共有两个串口,分别为UART-1和UART-3,序号为0和2 */
/* Setup the PINMUX to bring out the MSS UART-1 */
Pinmux_Set_FuncSel(SOC_XWR68XX_PINN5_PADBE, SOC_XWR68XX_PINN5_PADBE_MSS_UARTA_TX);
Pinmux_Set_OverrideCtrl(SOC_XWR68XX_PINN5_PADBE, PINMUX_OUTEN_RETAIN_HW_CTRL, PINMUX_INPEN_RETAIN_HW_CTRL);
Pinmux_Set_FuncSel(SOC_XWR68XX_PINN4_PADBD, SOC_XWR68XX_PINN4_PADBD_MSS_UARTA_RX);
Pinmux_Set_OverrideCtrl(SOC_XWR68XX_PINN4_PADBD, PINMUX_OUTEN_RETAIN_HW_CTRL, PINMUX_INPEN_RETAIN_HW_CTRL);
/* Setup the PINMUX to bring out the MSS UART-3 */
Pinmux_Set_OverrideCtrl(SOC_XWR68XX_PINF14_PADAJ, PINMUX_OUTEN_RETAIN_HW_CTRL, PINMUX_INPEN_RETAIN_HW_CTRL);
Pinmux_Set_FuncSel(SOC_XWR68XX_PINF14_PADAJ, SOC_XWR68XX_PINF14_PADAJ_MSS_UARTB_TX);
//创建任务,每隔5s打印信息以表示系统正常运行
Task_Params_init(&taskParams);
taskParams.stackSize = 2*1024;
Task_create(MM_idleTask, &taskParams, NULL);
UART_Params_init(¶ms);
params.baudRate = 115200; //波特率115200
params.clockFrequency = 200 * 1000000; //系统时钟频率200MHz
params.dataLength = UART_LEN_8; //8bit字长
params.isPinMuxDone = 1; //已经完成引脚复用初始化
params.parityType = UART_PAR_NONE; //无校验位
params.readDataMode = UART_DATA_BINARY; //按位方式读取数据
params.readEcho = UART_ECHO_OFF; //关闭回显
params.readReturnMode = UART_RETURN_FULL; //完整读取
params.stopBits = UART_STOP_ONE; //1停止位
params.writeDataMode = UART_DATA_BINARY; //按位方式写入(发送)数据
handle = UART_open(0, ¶ms); //打开串口
if(handle == NULL)
{
System_printf("UART open fail\n!");
return;
}
else
{
System_printf("UART open success\n!");
while(1)
{
//不断读取收到的数据,并通过串口发送出去
//该函数会使进程进入挂起等待状态,不会使其他任务无法执行
//可以发现打印任务也可以执行
UART_read(handle, &dat, 1);
UART_write(handle, &dat, 1);
}
}
}
int main (void)
{
int32_t errCode; //存放SOC初始化错误代码
SOC_Handle socHandle; //SOC句柄
SOC_Cfg socCfg; //SOC配置结构体
Task_Params taskParams; //任务参数
ESM_init(0U); //与安全等有关,可略过
/* Initialize the SOC confiugration: */
/* 初始化SOC配置结构体 */
memset ((void *)&socCfg, 0, sizeof(SOC_Cfg));
/* 配置SOC配置结构体 */
/* Populate the SOC configuration: */
socCfg.clockCfg = SOC_SysClock_INIT;
socCfg.mpuCfg = SOC_MPUCfg_CONFIG;
/* require to unhalt the DSS if this core is available in selected device */
socCfg.dssCfg = SOC_DSSCfg_UNHALT;
/* Initialize the SOC Module: This is done as soon as the application is started
* to ensure that the MPU is correctly configured. */
/* SOC初始化,必须在系统运行后尽快调用该初始化函数 */
socHandle = SOC_init (&socCfg, &errCode);
/* Initialize the Task Parameters. */
/* 创建一个任务用于测试 */
Task_Params_init(&taskParams);
taskParams.stackSize = 4*1024; //任务堆栈大小4KB
Task_create(MM_initTask, &taskParams, NULL);
/* Start BIOS */
BIOS_start();
return 0;
}
运行结果如下,回环测试成功。
接下来,就可以愉快地使用模板进行开发了。
参考资料:
[FAQ] 如何从头开始创建新的毫米波传感器工程 [Project0]? https://e2echina.ti.com/support/tools/ccs/f/code-composer-studio-forum/210739/faq-project0