第三章:软件架构:AUTOSAR架构下的SWC划分、Runnable与Task设计、模式管理(Init/Normal/Fail)

好,咱们进入正题。前大灯水平调节系统,说白了就是个电机控制加位置反馈的闭环。但放到AUTOSAR架构里,怎么把这些功能拆成一个个SWC(软件组件),怎么安排Runnable和Task,怎么管理Init、Normal、Fail这些模式——这里头门道不少。我踩过的坑,今天一次性说清楚。

3.1 SWC划分:别把鸡蛋放一个篮子里

SWC划分,我个人的习惯是「高内聚、低耦合」。你想想看,一个前大灯调节系统,核心功能无非三块:

  • 控制逻辑:根据车身高度、车速、方向盘角度,算出目标调节位置。
  • 执行器驱动:控制步进电机或直流电机,让大灯动起来。
  • 传感器采集:读取位置传感器(比如霍尔传感器或电位计)的数值。

所以,我一般会拆成三个SWC:

SWC名称 功能描述 典型端口
Swc_LevelControl 水平调节控制算法,模式管理 RPort_HeightSensor, RPort_VehicleSpeed, PPort_MotorTarget
Swc_MotorDriver 电机PWM控制,电流检测,堵转保护 RPort_MotorTarget, PPort_MotorPWM, RPort_MotorCurrent
Swc_PositionSensor 位置传感器信号采集与滤波 RPort_SensorRaw, PPort_PositionFiltered

嗯,这里要注意:千万别把传感器滤波逻辑放到控制SWC里。我在项目中遇到过,有人图省事把滤波和PID控制写在一个Runnable里,结果后来换了个传感器型号,整个控制模块都得重新测试——那叫一个酸爽。

3.2 Runnable设计:每个函数都有它的脾气

Runnable是SWC里的最小执行单元。说白了,就是一个个C函数,被调度器按周期或事件触发。我建议这样划分:

  • InitRunnable:上电时跑一次,初始化参数、校准传感器零点。
  • ControlRunnable:10ms周期运行,执行PID控制算法。
  • MonitorRunnable:50ms周期运行,检查故障状态(比如电机堵转、传感器超范围)。
  • FailSafeRunnable:事件触发,一旦检测到故障,立即将大灯复位到安全位置。

关键点:Runnable的周期不是越短越好。10ms控制周期足够应对大灯调节的机械响应了。我曾经试过1ms周期,结果CPU负载飙到80%,电机还因为PWM更新太快产生啸叫——得不偿失。

代码示例(伪代码风格):

// Swc_LevelControl.c
FUNC(void, SWC_LEVELCONTROL_CODE) ControlRunnable_10ms(void)
{
    /* 读取传感器滤波值 */
    uint16 position = Rte_Read_PositionFiltered();
    
    /* 读取车速和车身高度 */
    uint16 vehicleSpeed = Rte_Read_VehicleSpeed();
    uint16 height = Rte_Read_HeightSensor();
    
    /* 查表或计算目标位置 */
    uint16 target = LookupTargetPosition(vehicleSpeed, height);
    
    /* PID控制 */
    int16 output = PID_Controller(target, position);
    
    /* 输出到电机驱动 */
    Rte_Write_MotorTarget(output);
}

3.3 Task设计:谁先跑,谁后跑,谁不能抢

AUTOSAR里,Runnable要挂到Task上才能被调度。Task分两种:

  • 基础任务(Basic Task):跑完就睡,不等人。
  • 扩展任务(Extended Task):可以等事件,适合故障处理这种不确定时机的活儿。

我一般这样分配:

Task名称 周期/触发 挂载的Runnable 优先级
Task_10ms 10ms定时 ControlRunnable_10ms
Task_50ms 50ms定时 MonitorRunnable_50ms
Task_FailSafe 事件触发 FailSafeRunnable 最高

警告:千万别把控制Runnable和监控Runnable放在同一个Task里。我曾经见过一个项目,监控逻辑里有个printf调试语句,结果把控制周期拖到了20ms以上——大灯调节变得一卡一卡的,像PPT动画。

3.4 模式管理:Init → Normal → Fail,一条不归路

模式管理,说白了就是状态机。前大灯水平调节系统,我习惯用三个模式:

  • Init模式:上电后进入。做自检、传感器校准、电机归零。完成后自动切到Normal。
  • Normal模式:正常工作。控制算法跑起来,实时调节大灯高度。
  • Fail模式:检测到故障后进入。比如电机堵转、传感器失效、通信超时。此时大灯回到安全位置(一般是水平位置),并上报故障码。

模式切换的逻辑,我建议用一张表来定义:

当前模式 触发条件 下一模式 动作
Init 自检完成 && 校准成功 Normal 使能控制Runnable
Init 自检失败 || 校准超时 Fail 设置故障码,大灯归零
Normal 任何故障被检测到 Fail 停止控制,执行安全策略
Fail 故障清除 && 重新上电 Init 重新初始化

小技巧:Fail模式不要自动恢复到Normal。为什么?因为故障可能是间歇性的,比如线束接触不良。如果自动恢复,大灯会忽高忽低,驾驶员会以为见鬼了。我一般要求:只有重新上电(钥匙OFF→ON)才能退出Fail模式。

代码示例(状态机实现):

// 模式管理Runnable
FUNC(void, SWC_LEVELCONTROL_CODE) ModeManagerRunnable_50ms(void)
{
    switch(CurrentMode)
    {
        case MODE_INIT:
            if(AllSelfCheckPassed())
            {
                CurrentMode = MODE_NORMAL;
                EnableControlRunnable();
            }
            else if(CheckTimeout())
            {
                CurrentMode = MODE_FAIL;
                SetFaultCode(FAULT_INIT_FAIL);
                MoveToSafePosition();
            }
            break;
            
        case MODE_NORMAL:
            if(DetectAnyFault())
            {
                CurrentMode = MODE_FAIL;
                DisableControlRunnable();
                MoveToSafePosition();
            }
            break;
            
        case MODE_FAIL:
            /* 啥也不干,等重新上电 */
            break;
            
        default:
            /* 不应该到这里 */
            break;
    }
}

嗯,最后说一句:模式管理里,Init模式的时间一定要可控。我见过一个项目,Init里做了个复杂的传感器自学习,结果上电后大灯要抖个3秒钟才能稳定——客户直接投诉说「这车是不是坏了」。后来我改成:Init阶段先快速归零到机械限位,再反向找零点,整个过程控制在500ms以内。

好了,这一章就到这里。下一章咱们聊聊「诊断与故障处理:UDS协议栈集成与DTC设计」,到时候我会讲讲怎么用UDS把故障码读出来,以及怎么设计一个让售后工程师爱你的诊断方案。