4、TPDO映射实战:TPDO通信参数配置,TPDO映射参数设置,动态PDO映射,代码实现与测试

好,咱们进入TPDO的实战环节。前面讲了PDO的底层原理,现在要真刀真枪地干了。

TPDO,说白了就是从站主动往外扔数据的通道。你想想看,一个传感器节点,采集到温度了,总得主动告诉主站吧?这就是TPDO干的事。

我个人习惯把TPDO配置分成三块:通信参数、映射参数,还有动态映射。咱们一块一块啃。

4.1 TPDO通信参数配置

通信参数决定了TPDO什么时候发、怎么发。核心就两个东西:COB-ID和传输类型。

参数 索引 子索引 说明
COB-ID 0x1800 01 TPDO1的标识符,决定总线上的优先级
传输类型 0x1800 02 0=同步非周期,1-240=同步周期,254=异步制造商,255=异步设备
禁止时间 0x1800 03 最小发送间隔,单位100μs
事件定时器 0x1800 05 定时发送周期,单位ms

这里有个坑,我当年第一次做项目时就踩过。COB-ID的计算公式是:0x180 + 节点ID。比如节点ID是5,那TPDO1的COB-ID就是0x185。但注意,如果你把COB-ID的最高位置1,就等于禁用了这个TPDO。

我曾经在调试时发现TPDO死活不发数据,查了半天,原来是COB-ID的最高位被意外置1了。记住:COB-ID有效时,bit31必须为0。

传输类型这块,我建议你记住三个最常用的:

  • 255(异步设备):数据变化就发,最常用
  • 1(同步周期):每个SYNC都发,适合实时控制
  • 0(同步非周期):收到SYNC后只发一次,适合事件触发

4.2 TPDO映射参数设置

映射参数决定了TPDO里装的是什么数据。说白了,就是把对象字典里的条目,塞进PDO的8字节数据里。

映射参数在0x1A00(TPDO1映射)里配置。每个子索引对应一个映射条目,格式是:

0xPPPP00SS + 08
|    |   |  |
|    |   |  +-- 数据长度(bit)
|    |   +----- 子索引
|    +--------- 索引高位
+-------------- 索引低位

举个例子,我想把0x6000 sub1(16位数据)映射到TPDO1:

映射值 = 0x60000110
// 0x6000 = 索引
// 01     = 子索引
// 10     = 16位数据(0x10 = 16)
我个人的习惯是先把映射表写在纸上,再填到代码里。因为映射值一旦写错,数据就全乱了。你想想看,主站收到一堆乱码,排查起来多痛苦。

4.3 动态PDO映射

动态映射,就是运行时改变PDO的内容。这功能在CANopen里是可选的,但实际项目中非常有用。

比如一个多功能IO模块,今天要采集模拟量,明天要采集数字量。总不能每次都改固件吧?动态映射就解决了这个问题。

实现动态映射的步骤:

  1. 使能动态映射:在对象字典0x1A00的sub0里写入映射条目数
  2. 写入映射条目:依次写入0x1A00的sub1、sub2...
  3. 重新映射:写入完成后,TPDO会自动按新映射发送数据
注意:动态映射时,一定要先禁用TPDO(把COB-ID的bit31置1),改完再启用。否则可能造成总线数据错乱。

4.4 代码实现

好,咱们看看CANopenNode里怎么实现。以下是一个TPDO1的配置示例:

// TPDO1通信参数
uint8_t tpdo1_comm[] = {
    0x05, 0x01, 0x80, 0x00,  // COB-ID = 0x180 + 5 = 0x185
    0xFF,                     // 传输类型 = 255(异步)
    0x00, 0x00,               // 禁止时间 = 0
    0x00, 0x00, 0x00, 0x00,  // 保留
    0x64, 0x00, 0x00, 0x00   // 事件定时器 = 100ms
};

// TPDO1映射参数
uint8_t tpdo1_map[] = {
    0x02,                     // 映射2个对象
    0x10, 0x00, 0x01, 0x10,  // 0x6000 sub1, 16位
    0x10, 0x00, 0x02, 0x10   // 0x6000 sub2, 16位
};

// 写入对象字典
OD_write(0x1800, 0x01, tpdo1_comm, 8);
OD_write(0x1A00, 0x00, tpdo1_map, 8);

这段代码配置了一个TPDO1,每100ms发送一次,数据包含0x6000的sub1和sub2两个16位值。

4.5 测试验证

代码写完了,怎么验证?我一般用CAN分析仪抓包。正常的TPDO报文长这样:

ID: 0x185  [8]  Data: 0x01 0x02 0x03 0x04 0x00 0x00 0x00 0x00
|     |         |              |
|     |         +--------------+-- 映射的数据内容
|     +-------------------------- 数据长度8字节
+-------------------------------- TPDO1的COB-ID

验证步骤:

  1. 启动从站,观察TPDO是否按预期发送
  2. 改变映射数据源,看数据是否更新
  3. 测试动态映射,运行时修改映射表
  4. 检查禁止时间是否生效
我曾经遇到一个诡异问题:TPDO偶尔丢包。后来发现是事件定时器和禁止时间设置冲突了。记住:事件定时器必须大于禁止时间,否则定时器触发时,禁止时间还没过,数据就发不出去。

嗯,TPDO这块内容不少,但核心就三点:通信参数管怎么发,映射参数管发什么,动态映射让配置更灵活。你把这些搞透了,CANopen的实时通信就算入门了。

TPDO配置流程 通信参数配置 COB-ID / 传输类型 映射参数配置 0x1A00 映射表 动态映射 运行时修改映射 代码实现 OD_write() 写入 测试验证 CAN分析仪抓包 关键要点 1. COB-ID的bit31必须为0,否则TPDO被禁用 2. 事件定时器必须大于禁止时间 3. 动态映射前先禁用TPDO,改完再启用 4. 映射值格式:0xPPPP00SS + 08