CANopen系列文章【1】--SYNC【4】-同步RPDO & 同步TPDO

在本论坛,有网友说:同步RPDO是由主站发送给从站的PDO,主站在发送SYNC之前,将所有从站的RPDO发送给从站,然后再发SYNC,此时所有从站同时处理此RPDO。

然后远方大侠同意这个观点。

我的问题是:假设主站发送SYNC
(1)那么假设这个同步RPDO的ID号是0x308,那么主站发送0x308的快慢可以任意由主站来决定么?
比如这个RPDO的传输type是2,那么 主站发送0x308的周期可以小于2个SYNC?可以大于2个SYNC,比如10个SYNC?比如可以由主站对该pdo进行周期触发和事件触发?
也就是说所谓同步,是不是都是针对于从站的同步,而对于主站发送和接收的PDO没有关系?


(2)第二个问题是关于同步TPDO

比如有20个从节点,从节点的TPDO的发送TYPE都设置成1,那么就是所有的从站接收到SYNC后马上采样然后发送TPDO。那么岂不是都堵在一起了,尽管可以仲裁,但堵在一起肯定不是什么好现象。那么请问CANopen有没有相关的协议解决这个问题,让同步TPDO尽量在同一个同步窗口内尽量的分散开,以降低瞬间负载?


=========================================================

[yuanfang]

1)同步RPDO有不同的做法,有些主站会遵循同步机制,按传输类型来发送RPDO,有些主站会采用异步方式来发送RPDO,但对于使用没有影响,因为重点是从站要同时执行他们收到的PDO。
但在运动控制主机中,RPDO是要按同步机制来严格发送的,不然从站伺服驱动器会报错

2)TPDO的同步发送机制,原本就是利用了CAN的仲裁能力,可以使数据包在最短的时间内(同步窗口)发送完毕,因为此时数据量很大,总线不会有空闲,因此你不用再去费心仲裁的事情。
也因此,CAN的数据刷新是不确定的,有时候这台先发送数据,有时候那台先发送数据,但都在一个同步周期发送,对应用没有影响。后来的协议为了发送这种因仲裁带来的不确定性,使用了时间片轮转方法,先在配置 的时候就分配好每台从站数据更新的时机和占用总线时间,后面就按这个分配的时机来刷新数据,所以数据刷新的时机是一定的。像后来的FlexRay就是这样。


  • 0
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
以下是一个使用CANopen协议使用RPDO同步双驱动的示例代码,其中使用了SocketCAN进行CAN总线通信: ```c #include <stdio.h> #include <string.h> #include <unistd.h> #include <net/if.h> #include <sys/ioctl.h> #include <sys/socket.h> #include <linux/can.h> #include <linux/can/raw.h> #define CAN_INTERFACE "can0" // 定义CANopen协议中的相关信息 #define CANOPEN_COB_ID_SYNC 0x80 #define CANOPEN_COB_ID_RPDO1 0x200 #define CANOPEN_COB_ID_RPDO2 0x300 #define CANOPEN_COB_ID_TPDO1 0x180 #define CANOPEN_COB_ID_TPDO2 0x280 #define CANOPEN_OBJ_ID_CONTROL_WORD 0x6040 #define CANOPEN_OBJ_ID_STATUS_WORD 0x6041 #define CANOPEN_OBJ_ID_MODE_OF_OPERATION 0x6060 #define CANOPEN_OBJ_ID_TARGET_VELOCITY 0x60FF // 定义CANopen驱动的状态字 #define CANOPEN_STATUS_WORD_READY_TO_SWITCH_ON 0x0021 #define CANOPEN_STATUS_WORD_SWITCH_ON_DISABLED 0x0020 #define CANOPEN_STATUS_WORD_SWITCHED_ON 0x0023 #define CANOPEN_STATUS_WORD_OPERATION_ENABLED 0x0027 #define CANOPEN_STATUS_WORD_QUICK_STOP_ACTIVE 0x0007 #define CANOPEN_STATUS_WORD_FAULT 0x0008 // 定义CANopen驱动的控制字 #define CANOPEN_CONTROL_WORD_SHUTDOWN 0x0006 #define CANOPEN_CONTROL_WORD_SWITCH_ON 0x0007 #define CANOPEN_CONTROL_WORD_ENABLE_OPERATION 0x000F #define CANOPEN_CONTROL_WORD_QUICK_STOP 0x000B // 定义CANopen驱动的模式 #define CANOPEN_MODE_VELOCITY_CONTROL 0x03 // 定义CANopen RPDO消息结构体 struct canopen_rpdo { uint16_t control_word; uint16_t target_velocity; }; // 定义CANopen TPDO消息结构体 struct canopen_tpdo { uint16_t status_word; uint16_t actual_velocity; }; int main(void) { int sock; struct sockaddr_can addr; struct ifreq ifr; struct can_frame frame; struct can_filter filter[2]; // 创建SocketCAN套接字 sock = socket(PF_CAN, SOCK_RAW, CAN_RAW); if (sock < 0) { perror("socket"); return 1; } // 设置CAN接口 strcpy(ifr.ifr_name, CAN_INTERFACE); ioctl(sock, SIOCGIFINDEX, &ifr); addr.can_family = AF_CAN; addr.can_ifindex = ifr.ifr_ifindex; if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) { perror("bind"); return 1; } // 设置CAN过滤器 filter[0].can_id = CANOPEN_COB_ID_TPDO1; filter[0].can_mask = CAN_SFF_MASK; filter[1].can_id = CANOPEN_COB_ID_TPDO2; filter[1].can_mask = CAN_SFF_MASK; setsockopt(sock, SOL_CAN_RAW, CAN_RAW_FILTER, &filter, sizeof(filter)); // 初始化CANopen RPDO消息 struct canopen_rpdo rpdo = { .control_word = CANOPEN_CONTROL_WORD_SWITCH_ON, .target_velocity = 0 }; // 初始化CANopen TPDO消息 struct canopen_tpdo tpdo; // 发送CANopen RPDO1消息 memset(&frame, 0, sizeof(frame)); frame.can_id = CANOPEN_COB_ID_RPDO1; frame.can_dlc = sizeof(rpdo); memcpy(frame.data, &rpdo, sizeof(rpdo)); write(sock, &frame, sizeof(frame)); // 发送CANopen RPDO2消息 memset(&frame, 0, sizeof(frame)); frame.can_id = CANOPEN_COB_ID_RPDO2; frame.can_dlc = sizeof(rpdo); memcpy(frame.data, &rpdo, sizeof(rpdo)); write(sock, &frame, sizeof(frame)); while (1) { // 发送CANopen SYNC消息 memset(&frame, 0, sizeof(frame)); frame.can_id = CANOPEN_COB_ID_SYNC; frame.can_dlc = 0; write(sock, &frame, sizeof(frame)); // 读取CANopen TPDO1消息 if (read(sock, &frame, sizeof(frame)) > 0 && frame.can_id == CANOPEN_COB_ID_TPDO1) { memcpy(&tpdo, frame.data, sizeof(tpdo)); // 判断驱动是否处于故障状态 if ((tpdo.status_word & CANOPEN_STATUS_WORD_FAULT) == CANOPEN_STATUS_WORD_FAULT) { printf("Drive in fault state!\n"); break; } // 判断驱动是否准备好 if ((tpdo.status_word & CANOPEN_STATUS_WORD_READY_TO_SWITCH_ON) != CANOPEN_STATUS_WORD_READY_TO_SWITCH_ON) { rpdo.control_word = CANOPEN_CONTROL_WORD_SWITCH_ON; } else if ((tpdo.status_word & CANOPEN_STATUS_WORD_SWITCHED_ON) != CANOPEN_STATUS_WORD_SWITCHED_ON) { rpdo.control_word = CANOPEN_CONTROL_WORD_ENABLE_OPERATION; } else if ((tpdo.status_word & CANOPEN_STATUS_WORD_OPERATION_ENABLED) != CANOPEN_STATUS_WORD_OPERATION_ENABLED) { rpdo.control_word = CANOPEN_CONTROL_WORD_ENABLE_OPERATION; } else { rpdo.control_word = CANOPEN_CONTROL_WORD_ENABLE_OPERATION; rpdo.target_velocity = 1000; // 设定目标速度 } // 发送CANopen RPDO1消息 memset(&frame, 0, sizeof(frame)); frame.can_id = CANOPEN_COB_ID_RPDO1; frame.can_dlc = sizeof(rpdo); memcpy(frame.data, &rpdo, sizeof(rpdo)); write(sock, &frame, sizeof(frame)); } // 读取CANopen TPDO2消息 if (read(sock, &frame, sizeof(frame)) > 0 && frame.can_id == CANOPEN_COB_ID_TPDO2) { memcpy(&tpdo, frame.data, sizeof(tpdo)); printf("Actual velocity: %d\n", tpdo.actual_velocity); } usleep(1000); } // 发送CANopen RPDO1消息 memset(&rpdo, 0, sizeof(rpdo)); memset(&frame, 0, sizeof(frame)); frame.can_id = CANOPEN_COB_ID_RPDO1; frame.can_dlc = sizeof(rpdo); memcpy(frame.data, &rpdo, sizeof(rpdo)); write(sock, &frame, sizeof(frame)); // 发送CANopen RPDO2消息 memset(&rpdo, 0, sizeof(rpdo)); memset(&frame, 0, sizeof(frame)); frame.can_id = CANOPEN_COB_ID_RPDO2; frame.can_dlc = sizeof(rpdo); memcpy(frame.data, &rpdo, sizeof(rpdo)); write(sock, &frame, sizeof(frame)); close(sock); return 0; } ``` 以上代码仅供参考,具体实现需要根据实际情况进行调整。
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值