CAPL 编程系列教程 - 第二期:运行机制与代码结构

 CAPL 编程系列教程的第二期内容,主要讲解在 CANoe 中 CAPL 的运行机制以及基本代码结构,并演示如何创建和运行一个简单的 CAPL 程序。

CAPL 编程系列教程 - 第二期:运行机制与代码结构

一、 学习 CAPL 的前置知识要求

  • 重要性: 先了解运行环境和基本结构,才能更好地学习和实践 CAPL 编程
  • 必备知识:
    • 了解 CAN 总线和 CAN 协议基础概念(ECU、报文、信号等)
    • 熟悉 CANoe 基本操作(创建工程、配置节点、观察报文等)
    • 熟悉 DBC 文件及其作用(包含报文和信号定义)
  • 可选知识 (用于高阶学习):
    • 了解 UDS 诊断协议(用于学习诊断自动化测试脚本编写)
  • 编程基础: 没有编程基础也没关系,课程会从零开始

二、 在 CANoe 中准备 CAPL 编程环境

  1. 创建 CANoe 工程:
    • 启动 CANoe
    • File -> New -> 选择 CAN 网络模板 (如 CAN 500 kBit/s Single Channel) 创建新工程
    • 保存工程 (Ctrl+S),建议创建专用项目文件夹 (如 Learning_CAPL01),配置文件名与文件夹名一致 (Learning_CAPL01.cfg)
  2. 整理 CANoe 界面:
    • 保留 Configuration 桌面的 Simulation Setup 面板(用于添加节点)
    • 保留 Trace 桌面的 Trace 窗口(用于观察报文)
    • 保留 Analysis 桌面的 DataGraphics 窗口(用于观察信号,可选)
  3. 加载 DBC 文件:
    • 在项目工程目录下创建 can_db 文件夹
    • 将所需的 DBC 文件(如 virtual_network.dbc)放入该文件夹
    • 在 CANoe Simulation Setup 面板的 Databases 节点下,右键 -> Add,添加该 DBC 文件
  4. 添加网络节点 (Network Node):
    • CAPL 代码通常需要依附于某个节点运行(模拟 ECU 或测试模块)
    • Simulation Setup 的 CAN 总线上右键 -> Insert Network Node(用于仿真 ECU)
    • 替代方案 (后续学习): Insert Test Module (用于编写测试用例)
    • 右键新添加的节点 -> Configuration,可以修改节点名称 (如 EMS)

三、 创建并关联 CAPL 文件 (.can)

  1. 创建 .can 文件:
    • 在节点的 Configuration 窗口(或右键节点 -> Edit)中,点击 CAPL 文件编辑框旁的按钮
    • 建议在项目工程目录下创建 Nodes 文件夹
    • 在此文件夹下,输入 CAPL 文件名(通常与节点名一致,如 EMS.can),点击“打开”或“保存”来创建文件
  2. CAPL Browser (浏览器/编辑器):
    • 创建 .can 文件后,会自动使用 CAPL Browser 工具打开该文件进行编辑 。这是 Vector 提供的 CAPL 代码编辑器,随 CANoe 安装
    • CANoe 和 CAPL Browser 是两个独立的窗口
  3. 关联: 创建并保存后,.can 文件就与 CANoe 中的仿真节点关联起来了 。后续可以通过双击 CANoe 中的节点图标或点击 Edit 按钮重新打开 CAPL Browser 编辑代码

四、 CAPL Browser 界面简介

  • 左侧大纲区: 显示代码的重要结构,如 includes (导入其他文件)、variables (全局变量区)、事件处理函数、自定义函数等
  • 右侧主要编辑区: 编写 CAPL 代码
  • 右侧辅助面板:
    • CAPL Functions: 列出并可搜索 CAPL 内置函数,方便查找和使用
    • Symbols: 显示当前工程加载的 DBC 文件中的报文 (Frames)、信号 (Signals) 等符号信息,方便在编码时查阅
  • 底部 Output 窗口: 显示编译信息,包括错误和警告

五、 CAPL 核心概念:事件驱动模型

  • CAPL (Communication Access Programming Language): Vector 公司设计的、类 C 的、用于 CANoe/CANalyzer 等工具的编程语言
  • 事件驱动: CAPL 不是像传统程序那样从头执行到尾,而是基于事件来触发代码执行
  • 运行机制: CANoe 工程运行后,总线上会发生各种事件(如测量开始/结束、按键按下、收到特定报文、信号值变化等)
  • 代码执行: 当某个事件发生时,如果 CAPL 文件中编写了处理该事件的代码块(通常以 on SomeEvent {...} 形式定义),那么该代码块内的语句就会被执行

六、 CAPL 代码结构与基础示例

  1. 文件编码: 文件开头的 encoding 936 注释允许在代码中使用中文注释
  2. includes: 用于导入其他 CAPL 文件(模块化编程时使用)
  3. variables: 全局变量定义区。在此定义的变量可以在文件内所有事件处理代码块中使用
  4. 事件处理代码块:
    • on start: 当 CANoe 测量开始时(即点击启动按钮后)执行一次
      • 示例:在此事件中发送单帧报文。
        • message 0x196 msg196;: 定义一个 ID 为 0x196 的报文变量 msg196 。可以基于 ID 或 DBC 中的报文名称定义
        • msg196.EMS_EngineSpeed.phys = 2800;: 设置报文中 EMS_EngineSpeed 信号的物理值为 2800 .phys 表示物理值
        • output(msg196);: 使用 output() 内置函数将配置好的报文发送到总线上
    • on timer <timer_variable>: 当指定的定时器超时时执行
      • 示例:实现周期性发送报文。
        • msTimer timerSendMessage196;: 在 variables 全局区定义一个毫秒级定时器变量 timerSendMessage196 。需要全局定义,因为要在多个事件中使用
        • on start { setTimerCyclic(timerSendMessage196, 100); }: 在 on start 事件中,使用 setTimerCyclic() 函数启动定时器,周期为 100 毫秒
        • on timer timerSendMessage196 { ... }: 定义定时器超时事件的处理代码。将之前发送单帧报文的代码(定义报文变量、赋值、output)放入此代码块内,即可实现每 100ms 发送一次更新后的报文
    • on key '<key_char>': 当按下键盘上指定的按键时执行
      • 示例:按 'x' 键停止周期发送。
        • on key 'x' { cancelTimer(timerSendMessage196); }: 定义按键 'x' 的事件处理。使用 cancelTimer() 函数停止之前启动的周期定时器
  5. 编译与运行:
    • 编写代码后,按 Compile (编译) 按钮或 Ctrl+S 保存(通常会自动编译),检查 Output 窗口是否有错误
    • 回到 CANoe 窗口,点击启动按钮运行工程。
    • 观察 Trace、Data、Graphics 窗口验证 CAPL 代码是否按预期执行(如报文是否发送、信号值是否正确、周期发送是否能被按键停止等)
  6. 获取帮助: 在 CAPL Browser 中,将光标放在函数或关键字上,按 F1 可以打开 CANoe 帮助文档,查看详细说明和用法

七、 总结与后续

  • 本期重点理解了 CAPL 的事件驱动模型,并通过实例演示了如何在 CANoe 中创建、编写和运行基础的 CAPL 代码,涉及了 on start, on timer, on key 三种常用事件。
  • 后续将深入学习 CAPL 的语法细节