4. Application SWC建模实战
应用层SWC(Application Software Component)是AUTOSAR虚拟功能总线(VFB)上最贴近控制策略的软件单元。它不直接操作寄存器,也不关心CAN帧或LIN调度,只负责把输入信号按算法转换为输出信号,并通过RTE(Runtime Environment)与其它SWC或BSW交互。本节以一个“车窗防夹控制”应用SWC为例,完整走通从创建SWC、定义Runnable Entity、绑定触发事件到RTE端口映射的建模过程。示例中的参数、周期和阈值均为教学假设,不代表任何具体车型的实测标定值。
一、先明确SWC的边界与接口
在DaVinci Developer中新建SWC时,第一步不是急着画内部逻辑,而是先确定它对外暴露什么。车窗防夹SWC需要三类接口:
- 输入:车窗位置(0~100%,来自位置传感器SWC)、防夹力(N,来自电流采样SWC)、车窗开关命令(上升/下降/停止,来自车身控制SWC)。
- 输出:电机驱动占空比(0~100%)、车窗状态(初始化/运行/防夹/故障)。
- 标定参数:防夹力阈值(示例取80 N)、防夹反转时间(示例取300 ms)、软停止区间(示例取95%~100%)。
这些接口在DaVinci Developer中分别对应Sender-Receiver端口和Parameter端口。输入用Require端口,输出用Provide端口,标定参数用Parameter端口。端口方向一旦确定,后续RTE生成的数据类型和访问函数就基本定型,因此建议在创建SWC时就把端口一次性建全,避免后期反复调整。
二、创建SWC并定义Runnable Entity
在DaVinci Developer的“Application Component”视图中新建一个SWC,命名为Swc_WindowAntiPinch。随后在“Runnable”页签中添加Runnable Entity。Runnable是SWC内部可被RTE调度的最小执行单元,它不直接对应操作系统任务,而是由RTE根据触发事件映射到具体Task上运行。
本示例定义两个Runnable:
RE_AntiPinch_10ms:周期10 ms执行,负责采样防夹力、判断是否超过阈值、输出电机占空比。RE_WindowState_Init:初始化时执行一次,负责把车窗状态置为“初始化”,并清零内部积分量。
在Runnable的属性中需要设置minimumStartInterval(最小启动间隔)。对RE_AntiPinch_10ms,示例设为0.01 s,与10 ms周期一致;对初始化Runnable,可设为0或留空,表示由初始化事件触发。Runnable的并发属性canBeInvokedConcurrently应保持false,因为防夹算法内部有状态变量,不允许重入。
三、触发事件:让Runnable真正跑起来
Runnable本身只是函数体,必须绑定触发事件才会被RTE调用。DaVinci Developer中常用的触发事件有三类:
- TimingEvent:周期触发,适合控制算法。本示例给
RE_AntiPinch_10ms绑定一个周期为0.01 s的TimingEvent。 - OperationInvokedEvent:由Client-Server接口调用触发,适合服务型Runnable。
- InitEvent:初始化触发,给
RE_WindowState_Init绑定。
绑定TimingEvent时要注意:周期值必须与后续RTE映射到的Task周期兼容。如果Task周期是10 ms,而TimingEvent写成5 ms,RTE生成阶段会报周期不匹配。因此建议在建模阶段就与基础软件工程师确认Task周期,再回填到TimingEvent中。
四、RTE端口映射:把SWC接到VFB上
端口映射的本质是告诉RTE:SWC的哪个端口对应哪个数据元素,以及Runnable通过什么API访问它。在DaVinci Developer中,端口映射分为两步:先在SWC内部把端口与数据元素关联,再在系统层面把SWC端口与其它SWC端口或BSW端口连接。
以输入端口为例,R-Port_WindowPos关联数据元素DE_WindowPosition,类型为uint8,范围0~100。RTE会据此生成Rte_Read_R-Port_WindowPos_DE_WindowPosition()这样的读取函数。在RE_AntiPinch_10ms中调用它,即可拿到当前车窗位置。输出端口P-Port_MotorDuty关联DE_MotorDuty,RTE生成Rte_Write_P-Port_MotorDuty_DE_MotorDuty()。
标定参数端口P-Port_PinchThreshold关联DE_PinchThreshold,类型为uint16,单位N。RTE生成Rte_Parameter_P-Port_PinchThreshold_DE_PinchThreshold(),在初始化阶段读取一次即可,不必每个周期都读。这样既减少运行开销,也避免标定值在运行中被意外改写。
五、一个可执行的Runnable伪代码示例
下面给出RE_AntiPinch_10ms的核心逻辑伪代码,用于说明RTE API在Runnable内部如何被调用。示例中的阈值和反转时间均为教学假设值。
void RE_AntiPinch_10ms(void)
{
uint8 windowPos;
uint16 pinchForce;
uint8 switchCmd;
uint8 motorDuty;
uint16 threshold;
/* 通过RTE读取输入 */
Rte_Read_RPort_WindowPos_DE_WindowPosition(&windowPos);
Rte_Read_RPort_PinchForce_DE_PinchForce(&pinchForce);
Rte_Read_RPort_SwitchCmd_DE_SwitchCmd(&switchCmd);
/* 读取标定参数 */
Rte_Parameter_PPort_PinchThreshold_DE_PinchThreshold(&threshold);
/* 防夹判断:力超过阈值且车窗处于上升区间 */
if ((pinchForce > threshold) && (switchCmd == CMD_UP)) {
motorDuty = 0; /* 立即停转 */
Rte_Write_PPort_WindowState_DE_WindowState(STATE_ANTIPINCH);
/* 启动300 ms反转计时,此处用静态变量示意 */
antiPinchTimer = 30; /* 10 ms × 30 = 300 ms */
} else if (antiPinchTimer > 0) {
motorDuty = 60; /* 反转占空比示例值 */
antiPinchTimer--;
Rte_Write_PPort_WindowState_DE_WindowState(STATE_REVERSING);
} else {
motorDuty = (switchCmd == CMD_UP) ? 80 : 0;
Rte_Write_PPort_WindowState_DE_WindowState(STATE_RUNNING);
}
Rte_Write_PPort_MotorDuty_DE_MotorDuty(motorDuty);
}
这段代码里,所有跨SWC的数据访问都通过RTE API完成,没有直接读写全局变量。这正是Application SWC建模的核心约束:SWC只依赖RTE,不依赖具体ECU的硬件和OS。
六、建模完成后的检查要点
在DaVinci Developer中完成上述建模后,建议按以下顺序做一次一致性检查:
- 每个Runnable是否都绑定了触发事件,且TimingEvent周期与目标Task周期一致。
- 每个端口是否都关联了数据元素,数据类型和范围是否与信号实际物理量匹配。
- 标定参数是否放在Parameter端口而非Sender-Receiver端口,避免运行期被误写。
- Runnable的
canBeInvokedConcurrently是否为false,防止状态变量重入。 - 端口映射后,RTE生成的API名称是否与Runnable中调用的名称一致。