CAPL 编程系列教程 - 第十八期:Message 类型与报文发送

CAPL 编程系列教程的第十八期内容,终于开始讲解 CAPL 中与 CAN 通信直接相关的核心内容:报文 (Message) 的处理,本期重点是定义报文变量发送报文

CAPL 编程系列教程 - 第十八期:Message 类型与报文发送

一、 课程回顾与本期目标

  • 回顾: 前面学习了 CAPL 的基础语法,包括数据类型、变量、运算符、控制流(分支、循环)、函数、定时器等。
  • 本期目标: 学习如何在 CAPL 中定义代表 CAN 报文的变量,给报文变量赋值(设置 DLC 和数据字节),并使用 output() 函数将报文发送到总线上。
  • 核心概念: 在 CAPL 中,CAN 报文被抽象为一种特殊的数据类型,关键字为 message

二、 定义/声明 message 类型的变量

  • 关键字: message
  • 两种主要声明方式:
    1. 基于报文 ID:
      • 语法: message <ID> VariableName;
      • <ID>: 指定报文的 CAN ID。推荐使用十六进制(以 0x 开头),但十进制也可以。
      • VariableName: 自定义的报文变量名。
      • 适用场景: 当 DBC 文件中没有定义该报文,或者不想依赖 DBC 文件时。
      • 示例: message 0x1A5 msg1; 声明一个 ID 为 0x1A5 的报文变量 msg1
    2. 基于 DBC 中的报文名称:
      • 语法: message <DBC_MessageName> VariableName;
      • <DBC_MessageName>: 在已加载的 DBC 文件中定义的报文的符号名称。
      • 优点: 代码可读性更好,不易出错。
      • 前提: CANoe 工程中必须已加载包含该报文名称的 DBC 文件。
      • 示例: message EngineState msg_engine; (假设 DBC 中有名为 EngineState 的报文)。(本期主要演示基于 ID 的方式,基于名称的方式将在后续讲解)
  • 声明位置: 与其他变量一样,message 变量的声明也必须放在其所在代码块(如 on key)的最前面。

三、 给 message 变量赋值

  • 目的: 在发送报文前,需要设置其属性,主要是数据长度码 (DLC) 和数据域 (Data Bytes)。

  • 访问属性 (使用选择器 Selectors): 通过点运算符 (.) 访问 message 变量的属性(选择器)。

    • 设置 DLC:
      • 选择器: .dlc
      • 语法: VariableName.dlc = value; (value 通常是 0-8)
      • 作用: 指定报文数据域的字节长度。
      • 默认值: 如果不设置,DLC 默认为 0。
      • 示例: msg1.dlc = 8;
    • 设置数据字节:
      • 按字节设置 (.byte()):
        • 选择器: .byte(index)
        • 语法: VariableName.byte(index) = byte_value;
        • index: 字节索引,从 0 开始 (0 到 7)。
        • byte_value: 要设置的字节值,推荐使用十六进制 (如 0xC7)。
        • 示例: msg1.byte(0) = 0xC7; msg1.byte(1) = 0x95;
      • 按字设置 (.word()):
        • 选择器: .word(index)
        • 语法: VariableName.word(index) = word_value;
        • index: 字的起始字节索引 (通常是偶数 0, 2, 4, 6)。
        • word_value: 要设置的字(2 字节)的值,推荐使用十六进制 (如 0x95C7)。
        • 字节序 (Byte Order): 注意! .word() 赋值时,word_value低字节会赋给 index 指定的字节,高字节会赋给 index + 1 的字节。
        • 示例: msg3.word(0) = 0x95C7; 效果等同于 msg3.byte(0) = 0xC7; msg3.byte(1) = 0x95; (低字节 C7 在前,高字节 95 在后)。
        • 示例: msg3.word(2) = 0x3F6B; 效果等同于 msg3.byte(2) = 0x6B; msg3.byte(3) = 0x3F; (低字节 6B 在前,高字节 3F 在后)。
      • 其他选择器: 教程提到还有 .dword() (4字节) 和 .qword() (8字节) 选择器,用法类似 .word(),也需注意字节序。
    • 未赋值字节: 如果设置的 DLC 大于实际赋值的字节数,未被赋值的数据字节默认为 0。
  • 声明时初始化:

    • 可以在声明 message 变量的同时,使用花括号 {} 初始化列表为其属性赋初值,类似结构体初始化。
    • 语法: message <ID or Name> VarName = { dlc = value, byte(0) = val0, byte(1) = val1, ... };
    • 示例:
      Code snippet
      message 0x2B6 msg2 = { 
          dlc = 4, 
          byte(0) = 0x2D, 
          byte(1) = 0x3F 
      }; // 初始化 DLC 为 4,前两个字节赋值,后两个字节默认为 0
      

四、 发送报文 (output() 函数)

  • 作用: 将一个配置好的 message 变量发送到其关联的总线上(由 CAPL 节点所在的仿真总线决定)。
  • 语法: output(MessageVariable);
  • MessageVariable: 要发送的报文变量名。
  • 示例: output(msg1); output(msg2);
  • 调用时机: 可以在任何需要发送报文的逻辑点调用,如 on key, on timer, 函数内部等。

五、 示例代码与验证

  • 教程演示了在 on key 'a' 事件中:
    1. 声明 message 0x1A5 msg1;
    2. 设置 msg1.dlc = 8;
    3. 设置 msg1.byte(0)msg1.byte(3) 的值。
    4. 声明并初始化 message 0x2B6 msg2 = {dlc = 4, byte(0) = 0x2D, byte(1) = 0x3F};
    5. 声明 message 0x3C7 msg3;
    6. 设置 msg3.dlc = 8;
    7. 使用 msg3.word(0) = 0x95C7;msg3.word(2) = 0x3F6B; 赋值。
    8. 调用 output(msg1); output(msg2); output(msg3); 发送报文。
  • 验证: 在 CANoe 的 Trace 窗口中观察,当按下 'A' 键后,是否出现了 ID 为 0x1A5, 0x2B6, 0x3C7 的报文,并且其 DLC 和数据字节是否与代码中设置的一致。

六、 总结与后续

  • 本期学习了 CAPL 中处理报文的基础:使用 message 关键字定义报文变量(通过 ID 或 DBC 名称),使用点运算符和选择器(.dlc, .byte(), .word() 等)设置报文属性,以及使用 output() 函数发送报文。
  • 理解了赋值和初始化的不同方式,特别是 .word() 的字节序问题。
  • 下一期 (第 19 期) 将讲解如何利用 DBC 文件,通过信号名称来更方便地操作报文数据,以及报文事件的处理。