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_ThrottleCtrl 比 Swc_TC 好一万倍。半年后你自己回头看代码,也能一眼看懂。
创建完成后,你会看到一个空的SWC。它现在什么都没有,就像一个刚出生的婴儿。接下来,我们要给它「长出手脚」——也就是端口。
4.2 定义P端口(提供端口)
P端口,全称是Provided Port。说白了,就是你这个SWC能给别人提供什么服务或数据。
举个例子:你做了一个温度传感器SWC,它需要对外提供当前温度值。那这个温度值,就通过P端口发布出去。
操作步骤很简单:
- 选中你的SWC,右键 → New Port → Provided Port
- 给端口起个名字,比如
Port_TempOut - 最关键的一步:选择Interface类型
Interface类型有三种常见选择:
| Interface类型 | 适用场景 | 我踩过的坑 |
|---|---|---|
| SenderReceiverInterface | 数据发布/订阅,比如传感器值 | 记得定义Data Element,否则生成代码是空的 |
| ClientServerInterface | 函数调用,比如请求执行某个操作 | Operation参数类型要匹配,否则RTE报错 |
| ModeSwitchInterface | 模式切换通知 | 这个用得少,新手先跳过 |
4.3 定义R端口(需求端口)
R端口,Required Port。它表示你这个SWC需要从别人那里获取什么。
你想想看,一个SWC不可能独立存在。它总要读点数据、调个函数。这些外部依赖,就通过R端口来表达。
操作和P端口类似:
- 右键 → New Port → Required Port
- 命名,比如
Port_SpeedIn - 同样要绑定一个Interface
这里有个容易混淆的地方:P端口和R端口可以绑定同一个Interface吗?
答案是:可以,但要看场景。如果是SenderReceiverInterface,一个SWC的P端口发数据,另一个SWC的R端口收数据,它们用同一个Interface是合理的。但如果是ClientServerInterface,P端口提供服务,R端口调用服务,Interface定义的是同一个服务接口。
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;
}
看到没?工具链帮你生成了函数的骨架。你只需要在函数体里填上真正的业务逻辑就行。
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 本章小结
这一章我们做了三件事:
- 创建了一个Application SWC
- 定义了P端口和R端口,并绑定了Interface
- 生成了代码框架,看到了RTE接口函数的模样
说实话,第一次做的时候可能会觉得步骤有点多。但多做几次,你会发现这其实是个肌肉记忆的过程。就像骑自行车,学会了就不会忘。
下一章,我们会把多个SWC连接起来,看看RTE到底是怎么把端口「接上线」的。到时候你会更清楚,为什么端口类型和Interface匹配这么重要。
嗯,今天就到这里。动手试试吧,别光看。