4、第一个SWC设计:从零创建一个Application SWC、定义P端口与R端口、生成代码框架

好,咱们正式开始动手了。

前面几章我们把工具链、工程结构、方法论都捋了一遍。说实话,光看不练容易犯困。这一章咱们直接上手,从零创建一个Application SWC,把P端口和R端口定义好,最后把代码框架生成出来。

我当年第一次用DaVinci Configurator做SWC时,踩了不少坑。最典型的一个——端口类型选错了,结果生成代码后,RTE死活连不上。折腾了两天才发现是Interface类型不匹配。嗯,这种低级错误,我希望你别再犯。

4.1 创建Application SWC

打开你的DaVinci Configurator,找到Component层级。右键点击,选择「New Application SwComponentType」。

这里有个小细节:命名规范。我个人习惯用「Swc_」前缀,比如 Swc_AppCtrl。别用中文,也别用特殊符号。工具链对符号很敏感,你加个空格都可能报错。

我的习惯:SWC名称尽量简短,但要有语义。比如 Swc_ThrottleCtrlSwc_TC 好一万倍。半年后你自己回头看代码,也能一眼看懂。

创建完成后,你会看到一个空的SWC。它现在什么都没有,就像一个刚出生的婴儿。接下来,我们要给它「长出手脚」——也就是端口。

4.2 定义P端口(提供端口)

P端口,全称是Provided Port。说白了,就是你这个SWC能给别人提供什么服务或数据。

举个例子:你做了一个温度传感器SWC,它需要对外提供当前温度值。那这个温度值,就通过P端口发布出去。

操作步骤很简单:

  1. 选中你的SWC,右键 → New Port → Provided Port
  2. 给端口起个名字,比如 Port_TempOut
  3. 最关键的一步:选择Interface类型

Interface类型有三种常见选择:

Interface类型 适用场景 我踩过的坑
SenderReceiverInterface 数据发布/订阅,比如传感器值 记得定义Data Element,否则生成代码是空的
ClientServerInterface 函数调用,比如请求执行某个操作 Operation参数类型要匹配,否则RTE报错
ModeSwitchInterface 模式切换通知 这个用得少,新手先跳过
注意:Interface必须先定义好,再分配给端口。我曾经图省事,先建端口再补Interface,结果工具链提示「Interface not found」。顺序很重要:先定义Interface,再创建端口并引用它。

4.3 定义R端口(需求端口)

R端口,Required Port。它表示你这个SWC需要从别人那里获取什么。

你想想看,一个SWC不可能独立存在。它总要读点数据、调个函数。这些外部依赖,就通过R端口来表达。

操作和P端口类似:

  1. 右键 → New Port → Required Port
  2. 命名,比如 Port_SpeedIn
  3. 同样要绑定一个Interface

这里有个容易混淆的地方:P端口和R端口可以绑定同一个Interface吗?

答案是:可以,但要看场景。如果是SenderReceiverInterface,一个SWC的P端口发数据,另一个SWC的R端口收数据,它们用同一个Interface是合理的。但如果是ClientServerInterface,P端口提供服务,R端口调用服务,Interface定义的是同一个服务接口。

核心原则:P端口是「我能给什么」,R端口是「我需要什么」。两者通过Interface匹配,才能建立连接。如果Interface不匹配,RTE连接时会报错「Port Interface Mismatch」。

4.4 生成代码框架

端口定义好了,接下来就是见证奇迹的时刻——生成代码。

在DaVinci Configurator中,找到菜单栏的「Code Generation」或者直接按快捷键(我记不清具体是哪个了,反正菜单里找)。

生成前,建议检查以下几点:

  • 所有端口都绑定了Interface
  • Interface中定义了必要的数据元素或操作
  • SWC没有未解决的依赖(比如R端口引用了不存在的Interface)

点击生成后,工具链会输出一堆文件。你重点关注这几个:

Swc_AppCtrl.c      // SWC的源文件,包含RTE接口函数的桩代码
Swc_AppCtrl.h      // 头文件,包含端口定义、数据类型
Rte_Swc_AppCtrl.h  // RTE生成的头文件,包含运行时接口声明
Rte_Type.h         // 全局类型定义

打开 Swc_AppCtrl.c,你会看到类似这样的代码:

/* 这是RTE生成的端口读取函数 */
Std_ReturnType Rte_Read_Port_SpeedIn(uint16 *data)
{
    /* 这里暂时是空的,需要你自己实现 */
    return RTE_E_OK;
}

/* 这是RTE生成的端口写入函数 */
Std_ReturnType Rte_Write_Port_TempOut(uint16 data)
{
    /* 这里暂时是空的,需要你自己实现 */
    return RTE_E_OK;
}

看到没?工具链帮你生成了函数的骨架。你只需要在函数体里填上真正的业务逻辑就行。

避坑指南:我曾经犯过一个错——直接修改了RTE生成的文件。结果下次重新生成代码时,我的修改全被覆盖了。记住:永远不要手动修改RTE生成的文件。你的业务逻辑应该写在SWC自己的源文件里,通过RTE接口函数来读写数据。

4.5 一个小例子:温度采集SWC

为了让你更清楚,我举个完整的例子。

假设我们要做一个温度采集SWC:

  • 它有一个R端口 Port_SensorRaw,从底层驱动读取原始ADC值
  • 它有一个P端口 Port_TempCelsius,对外发布转换后的温度值
  • 内部逻辑:读取ADC值 → 查表或计算 → 输出摄氏度

定义好端口后,生成的代码框架大概长这样:

/* 在Swc_TempSensor.c中 */
void Swc_TempSensor_Run(void)
{
    uint16 adcValue;
    uint16 tempCelsius;

    /* 读取原始数据 */
    Rte_Read_Port_SensorRaw(&adcValue);

    /* 转换逻辑:ADC值转温度 */
    tempCelsius = (adcValue * 100) / 4095;  // 假设12位ADC,0-4095对应0-100度

    /* 发布温度值 */
    Rte_Write_Port_TempCelsius(tempCelsius);
}

你看,代码框架是工具链生成的,你只需要关注业务逻辑。这就是DaVinci Configurator的价值——把繁琐的接口代码交给他,你把精力放在算法和功能上。

4.6 本章小结

这一章我们做了三件事:

  1. 创建了一个Application SWC
  2. 定义了P端口和R端口,并绑定了Interface
  3. 生成了代码框架,看到了RTE接口函数的模样

说实话,第一次做的时候可能会觉得步骤有点多。但多做几次,你会发现这其实是个肌肉记忆的过程。就像骑自行车,学会了就不会忘。

下一章,我们会把多个SWC连接起来,看看RTE到底是怎么把端口「接上线」的。到时候你会更清楚,为什么端口类型和Interface匹配这么重要。

嗯,今天就到这里。动手试试吧,别光看。