CAPL Script

CAPL编程基础

先弄清单位和状态,再增加事件

CAPL 的表达式和控制流与 C 相似,但运行环境、变量生命周期需要单独理解。本篇用一个按键控制的状态模型,把变量、函数、报文和定时器连接起来。这是独立练习,不是第 06 篇的车门协议。

使用 CANoe Simulation Setup 节点、CAN1、经典 CAN 标准 ID 0x210,不依赖 DBC。关闭相同 ID 的其他发送源,在隔离配置中练习。以下代码尚未在本站编译或运行;先处理安装版本编译器给出的诊断,再启动测量。

选择类型时同时写清单位

类型或数值 本例含义 容易犯的错误
byte 无符号状态 0 或 1 在零值上减法后才检查边界
long 有符号 32 位运算 认为所有整数类型范围相同
dword 无符号 32 位数值 忽略长时间计数后的回绕
message CAN1.0x210 指定通道和 ID 的报文对象 误认为它自动保存最近收到的报文
msTimer 以毫秒指定延时 声明成 timer 后仍按毫秒理解
timer 以秒指定延时 此类型的 setTimer(t, 100) 是 100 秒
timeNow() 单位为 10 微秒的测量时间计数 5,000 个计数为 50 ms;50,000 个为 500 ms

单独的 on timer 事件需要引用的定时器,应声明在 variables 中。只在 on key 局部声明的定时器,对另一个事件过程不可见。

一个完整的状态与定时器练习

按 u 请求解锁状态 1,按 l 请求锁定状态 0。首次命令立即接受;之后直到 50 ms 定时器到期,忽略其他命令。被拒命令不会延长这一间隔。这是最小命令间隔限制,不是物理开关抖动模型。状态报文另按名义 3 秒周期请求发送。

variables
{
  msTimer reportTimer;
  msTimer debounceTimer;
  byte unlocked;
  byte debounceActive;
  message CAN1.0x210 status;
}

void reportState()
{
  status.RTR = 0;
  status.DLC = 1;
  status.byte(0) = unlocked;
  output(status);
  write("State output requested: unlocked=%d", unlocked);
}

int acceptCommand()
{
  if (debounceActive != 0) return 0;
  debounceActive = 1;
  setTimer(debounceTimer, 50);
  return 1;
}

on start
{
  cancelTimer(reportTimer);
  cancelTimer(debounceTimer);
  unlocked = 0;
  debounceActive = 0;
  reportState();
  setTimer(reportTimer, 3000);
}

on key 'u'
{
  if (acceptCommand() == 0)
  {
    write("Unlock ignored: 50 ms gate active");
    return;
  }
  unlocked = 1;
  reportState();
}

on key 'l'
{
  if (acceptCommand() == 0)
  {
    write("Lock ignored: 50 ms gate active");
    return;
  }
  unlocked = 0;
  reportState();
}

on timer debounceTimer
{
  debounceActive = 0;
}

on timer reportTimer
{
  reportState();
  setTimer(reportTimer, 3000);
}

acceptCommand() 返回是否接受,按键事件执行状态变化,定时器事件只解除间隔限制。共用一个标志意味着解锁后很快到来的锁定也会被拒。如果需要对两种命令分别限频,应先定义要求,再修改状态变量。

setTimer 安排一次到期。只有报告事件重新设置自己,因此报告重复,间隔限制只到期一次。相对重设不能保证无漂移的绝对时间表,测量方法见周期定时器。

output(status) 只是请求发送,不确认接收成功。应在 Trace 和独立接收观察点检查通道、RTR、DLC 与数据。Write 日志因此明确写成 output requested。

局部作用域不等于每次调用重新初始化

CAPL 局部变量的值会在调用之间保留,声明时初始化不是每次调用都执行的清零操作。需要每次重新累加时,在函数中显式赋值:

long sumBytes(byte values[])
{
  long total;
  long i;
  total = 0;
  for (i = 0; i < elcount(values); i++)
  {
    total = total + values[i];
  }
  return total;
}

这个独立函数片段假定输入数组较小,总和不超出 long。相同数组调用两次应返回相同结果。可把删除 total = 0 作为反例:累加器可能保留上次值。不同节点中同名的变量也不会因此成为共享状态。

原始信号值与物理值

有 DBC 时,msg.Rpm 访问原始信号值;msg.Rpm.phys 表达物理值。假设某信号的系数为 0.25、偏移为 0,原始值 8000 才表示 2000 rpm。直接赋原始值 2000 表示 500 rpm。必须检查实际信号的系数、偏移、范围、编码与取整规则;这里是手算示例,没有提供对应数据库。

// Fragment: requires EngineData.Rpm with the stated DBC scaling.
message EngineData msg;
msg.Rpm.phys = 2000.0;

设置信号不能证明报文已经发出,读取缓存信号也不能证明数据新鲜。两者需要独立检查。

验收记录表

操作 待验证预期 区分的问题
开始测量 状态 0,请求一次初始报告 是否显式初始化
首次输入 u 立即变成状态 1 没有初始 50 ms 禁入期
在接下来 50 ms 内明确注入 l 保持状态 1 共用间隔限制生效
到期后输入 l 状态 0 一次性定时器解除限制
不输入命令 名义每 3 秒报告 报告定时器重新设置
停止再启动 恢复状态 0 练习之间不携带状态

人工按键不适合精确的 49/51 ms 边界测试。应使用受控事件序列并保留时间戳;不要用恰好落在到期时刻的事件推断边界,因为事件处理顺序会影响结果。以上是待执行预期,不是通过记录。

单位与生命周期规则依据授权 Vector 参考中的 setTimer、timeNow、变量初始化和符号信号访问说明。间隔控制设计、缩放计算与验收表为本站分析。下一篇:与总线的核心交互。