1、新建字典
文件->新建
填名字,类型主控,其他默认
2、增加同步
这一步是为了接收数据
编辑->DS-301 Profile
选中同步窗口时间和通讯循环周期,将之添加到右侧
可以看到已经加进去了
设定同步窗口时间为0x40000080
设定通讯循环周期为0x00007530
3、增加pdo接收
增加一个380的pdo接收
有了前文的同步,这里除了id改为380,其他全部默认,添加pdo接收的步骤会同步生成该pdo接收对应的mapping如下图
为这个rpdo1增加地址
为地址命名及设定空间大小
回到rpdo1的mapping,右侧表格项右键添加3个子指标,将前文的rpdo1地址与3个子指标分别绑定
4、增加pdo定时发送
增加一个400的pdo发送
类型0XFF表示事件方式,定时器0X0019表示每25毫秒触发一次事件从而执行一次发送
对于事件方式,有定时器存在则定时发送当前数据,故使用中只需修改数据即可,无定时器存在则需要调函数发送,且数据的值必须要有变化,数据无变化即使调用了函数进行发送也其实是不会发送的
如前文一般,同样会同时自动生成该tpdo1的mapping
为这个tpdo1增加地址
为地址命名及设定空间大小
回到tpdo1的mapping,右侧表格项右键添加3个子指标,将前文的rpdo1地址与3个子指标分别绑定
5、增加pdo变值发送
增加一个500的pdo发送
类型0XFF表示事件方式,定时器0表示不定时而以值发生变化的时候调用函数进行发送
自动生成的该tpdo2的mapping如下
为这个tpdo2增加地址
为地址命名及设定空间大小
回到tpdo2的mapping,右侧表格项右键添加8个子指标,将前文的rpdo1地址与8个子指标分别绑定
6、生成字典及其对应文件
文件->建立词典
可以看到,在选定的目录下已经有了字典对应的MasterNode.h和MasterNode.c文件
关闭Objdictedit软件
保存字典
可以看到,在选定的目录下已经有了MasterNode.od文件
7、增加pdo接收回调函数
修改MasterNode.c文件的后缀为.cpp,这里我是需要用c++,用c的可以不用改
前面步骤得到的MasterNode.cpp文件中并不会自动生成pdo接收的回调,需要在文件中该pdo接收所对应的地址位置手动增加
由前文可知,rpdo1所对应的地址在0x2000,故修改MasterNode.cpp文件,增加下图红框中内容
注意,上图下面蓝框中的内容是原有的,里面有4行,故红框中要加4个NULL,数目需保持一致
下面这个地方也需要增加一下,如图红框部分
8、写测试逻辑
拷贝抽出来的canfestival源码目录
编写main.cpp文件
#include
#include "canfestival.h"
#include "MasterNode.h"
/** rpdo1对应的地址0x2000处数据的回调 */
static UNS32 Index2000_callbacks(CO_Data *d, const indextable *unsused_indextable, UNS8 unsused_bSubindex)
{
printf("[%s:%d:%s]RPDO1_data01=%d, RPDO1_data23=%d\n", __FILE__, __LINE__, __FUNCTION__, RPDO1_data01, RPDO1_data23);
return 0;
}
static void MasterNode_heartbeatError(CO_Data *d, UNS8 heartbeatID)
{
printf("[%s:%d:%s]heartbeatID=%u\n", __FILE__, __LINE__, __FUNCTION__, heartbeatID);
}
/** 从节点id */
#define SLAVE_1_NODE_ID 0x01
static void MasterNode_initialisation(CO_Data *d)
{
printf("[%s:%d:%s]\n", __FILE__, __LINE__, __FUNCTION__);
UNS32 size = sizeof(UNS32);
UNS32 RPDO1_COBID = 0x0380 + SLAVE_1_NODE_ID;//pdo接收端1
writeLocalDict(&MasterNode_Data, 0x1400, 0x01, &RPDO1_COBID, &size, RW);//指定0x1400所对应的rpdo1接收id为0x0381的端的数据
UNS32 TPDO1_COBID = 0x0400 + SLAVE_1_NODE_ID;//pdo发送端1
writeLocalDict(&MasterNode_Data, 0x1800, 0x01, &TPDO1_COBID, &size, RW);//指定0x1800对应的tpdo1向id为0x0401的端发送数据
UNS32 TPDO2_COBID = 0x0500 + SLAVE_1_NODE_ID;//pdo发送端2
writeLocalDict(&MasterNode_Data, 0x1801, 0x01, &TPDO2_COBID, &size, RW);//指定0x1801对应的tpdo2向id为0x0501的端发送数据
}
static void MasterNode_preOperational(CO_Data *d)
{
printf("[%s:%d:%s]\n", __FILE__, __LINE__, __FUNCTION__);
setState(d, Operational);
}
static void MasterNode_operational(CO_Data *d)
{
printf("[%s:%d:%s]\n", __FILE__, __LINE__, __FUNCTION__);
}
static void MasterNode_stopped(CO_Data *d)
{
printf("[%s:%d:%s]\n", __FILE__, __LINE__, __FUNCTION__);
}
static void MasterNode_post_sync(CO_Data *d)
{
//printf("[%s:%d:%s]\n", __FILE__, __LINE__, __FUNCTION__);
}
static void MasterNode_post_TPDO(CO_Data *d)
{
//printf("[%s:%d:%s]\n", __FILE__, __LINE__, __FUNCTION__);
}
static void MasterNode_post_emcy(CO_Data* d, UNS8 nodeID, UNS16 errCode, UNS8 errReg)
{
printf("[%s:%d:%s]nodeID=%u,errCode=%u,errReg=%u\n", __FILE__, __LINE__, __FUNCTION__, nodeID, errCode, errReg);
}
static void InitNodes(CO_Data *d, UNS32 id)
{
RegisterSetODentryCallBack(&MasterNode_Data, 0x2000, 1, &Index2000_callbacks);//注册rpdo1对应的地址0x2000处数据的回调
setNodeId(&MasterNode_Data, 0x7f);
setState(&MasterNode_Data, Initialisation);
}
static void Exit(CO_Data *d, UNS32 id)
{
masterSendNMTstateChange(&MasterNode_Data, 0x02, NMT_Reset_Node);
setState(&MasterNode_Data, Stopped);
}
static int StartCan()
{
TimerInit();
s_BOARD MasterBoard = {(char *)"0", (char *)"500K"};
MasterNode_Data.heartbeatError = MasterNode_heartbeatError;
MasterNode_Data.initialisation = MasterNode_initialisation;
MasterNode_Data.preOperational = MasterNode_preOperational;
MasterNode_Data.operational = MasterNode_operational;
MasterNode_Data.stopped = MasterNode_stopped;
MasterNode_Data.post_sync = MasterNode_post_sync;
MasterNode_Data.post_TPDO = MasterNode_post_TPDO;
MasterNode_Data.post_emcy = MasterNode_post_emcy;
if(canOpen(&MasterBoard, &MasterNode_Data) == NULL)
{
printf("[%s:%d:%s]canOpen fail\n", __FILE__, __LINE__, __FUNCTION__);
return -1;
}
StartTimerLoop(&InitNodes);
return 0;
}
static void StopCan()
{
StopTimerLoop(&Exit);
canClose(&MasterNode_Data);
TimerCleanup();
}
int main()
{
if(StartCan() != 0)
{
printf("[%s:%d:%s]StartCan fail\n", __FILE__, __LINE__, __FUNCTION__);
return -1;
}
int it = -1;
while (1)
{
scanf("%d", &it);
printf("[%s:%d:%s]it=%d\n", __FILE__, __LINE__, __FUNCTION__, it);
if(it == 0)
break;
else if(it == 401)
{
TPDO1_data01 = 1;//事件方式带定时器的情况下,直接赋值即可
TPDO1_data23 = 0;
}
else if(it == 501)
{
TPDO2_data0 = (TPDO2_data0 == 0) ? 1 : 0;//要有变化
TPDO2_data1 = 0;
TPDO2_data2 = 0;
TPDO2_data3 = 0;
TPDO2_data4 = 0;
TPDO2_data5 = 0;
TPDO2_data6 = 0;
TPDO2_data7 = 0;
sendPDOevent(&MasterNode_Data);//事件方式没有定时器的情况下,一定要有变化函数才会执行发送
}
}
StopCan();
return 0;
}
源码主要地方作了注释
编写编译脚本Makefile
9、设备上测试pdo接收
请确保设备已经开启了can口,已经跑了另一个向主节点发送id为0x0381的数据的端,如测试数据往设备上发没影响,也可以将后面的发送测试放到这里
编译,跑程序,看打印情况,正确则情形如下:
可以看到,回调的打印持续执行了,这里另一端发的数据本来就是0,所以打印的也是0
10、虚拟机模拟pdo发送
这是测试代码,不好往设备上乱发数据,故发送部分虚拟机虚拟can口来测试
编译脚本Makefile中编译环境改为g++
修改*/canfestival/drivers/can_socket/can_socket.cpp文件红框选中的can为vcan以支持虚拟can接口方便测试
确保开启了虚拟can接口(ifconfig命令可以找到)
编译程序运行
确保支持candump命令(不支持则安装),监听虚拟can口输入数据,先监听401,命令为candump vcan0 | grep 401
可以发现,有持续下发数据0,当我们改为1则下发1
继续监听501
可以发现,随着我们修改值则下发发生变化