基于canfestival的canopen主节点收发

1、新建字典

文件->新建

基于canfestival的canopen主节点收发_第1张图片

填名字,类型主控,其他默认

2、增加同步

这一步是为了接收数据

编辑->DS-301 Profile

基于canfestival的canopen主节点收发_第2张图片

选中同步窗口时间和通讯循环周期,将之添加到右侧

基于canfestival的canopen主节点收发_第3张图片

可以看到已经加进去了

基于canfestival的canopen主节点收发_第4张图片

设定同步窗口时间为0x40000080

基于canfestival的canopen主节点收发_第5张图片

设定通讯循环周期为0x00007530

基于canfestival的canopen主节点收发_第6张图片

3、增加pdo接收

增加一个380的pdo接收

基于canfestival的canopen主节点收发_第7张图片

有了前文的同步,这里除了id改为380,其他全部默认,添加pdo接收的步骤会同步生成该pdo接收对应的mapping如下图

基于canfestival的canopen主节点收发_第8张图片

为这个rpdo1增加地址

基于canfestival的canopen主节点收发_第9张图片

为地址命名及设定空间大小

基于canfestival的canopen主节点收发_第10张图片

回到rpdo1的mapping,右侧表格项右键添加3个子指标,将前文的rpdo1地址与3个子指标分别绑定

基于canfestival的canopen主节点收发_第11张图片

4、增加pdo定时发送

增加一个400的pdo发送

基于canfestival的canopen主节点收发_第12张图片

类型0XFF表示事件方式,定时器0X0019表示每25毫秒触发一次事件从而执行一次发送

对于事件方式,有定时器存在则定时发送当前数据,故使用中只需修改数据即可,无定时器存在则需要调函数发送,且数据的值必须要有变化,数据无变化即使调用了函数进行发送也其实是不会发送的

如前文一般,同样会同时自动生成该tpdo1的mapping

基于canfestival的canopen主节点收发_第13张图片

为这个tpdo1增加地址

基于canfestival的canopen主节点收发_第14张图片

为地址命名及设定空间大小

基于canfestival的canopen主节点收发_第15张图片

回到tpdo1的mapping,右侧表格项右键添加3个子指标,将前文的rpdo1地址与3个子指标分别绑定

基于canfestival的canopen主节点收发_第16张图片

5、增加pdo变值发送

增加一个500的pdo发送

基于canfestival的canopen主节点收发_第17张图片

类型0XFF表示事件方式,定时器0表示不定时而以值发生变化的时候调用函数进行发送

自动生成的该tpdo2的mapping如下

基于canfestival的canopen主节点收发_第18张图片

为这个tpdo2增加地址

基于canfestival的canopen主节点收发_第19张图片

为地址命名及设定空间大小

基于canfestival的canopen主节点收发_第20张图片

回到tpdo2的mapping,右侧表格项右键添加8个子指标,将前文的rpdo1地址与8个子指标分别绑定

基于canfestival的canopen主节点收发_第21张图片

6、生成字典及其对应文件

文件->建立词典

基于canfestival的canopen主节点收发_第22张图片

可以看到,在选定的目录下已经有了字典对应的MasterNode.h和MasterNode.c文件

基于canfestival的canopen主节点收发_第23张图片

关闭Objdictedit软件

基于canfestival的canopen主节点收发_第24张图片

保存字典

基于canfestival的canopen主节点收发_第25张图片

可以看到,在选定的目录下已经有了MasterNode.od文件

基于canfestival的canopen主节点收发_第26张图片

7、增加pdo接收回调函数

修改MasterNode.c文件的后缀为.cpp,这里我是需要用c++,用c的可以不用改

前面步骤得到的MasterNode.cpp文件中并不会自动生成pdo接收的回调,需要在文件中该pdo接收所对应的地址位置手动增加

由前文可知,rpdo1所对应的地址在0x2000,故修改MasterNode.cpp文件,增加下图红框中内容

基于canfestival的canopen主节点收发_第27张图片

注意,上图下面蓝框中的内容是原有的,里面有4行,故红框中要加4个NULL,数目需保持一致

下面这个地方也需要增加一下,如图红框部分

基于canfestival的canopen主节点收发_第28张图片

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

基于canfestival的canopen主节点收发_第29张图片

9、设备上测试pdo接收

请确保设备已经开启了can口,已经跑了另一个向主节点发送id为0x0381的数据的端,如测试数据往设备上发没影响,也可以将后面的发送测试放到这里

编译,跑程序,看打印情况,正确则情形如下:

基于canfestival的canopen主节点收发_第30张图片

可以看到,回调的打印持续执行了,这里另一端发的数据本来就是0,所以打印的也是0

10、虚拟机模拟pdo发送

这是测试代码,不好往设备上乱发数据,故发送部分虚拟机虚拟can口来测试

编译脚本Makefile中编译环境改为g++

修改*/canfestival/drivers/can_socket/can_socket.cpp文件红框选中的can为vcan以支持虚拟can接口方便测试

基于canfestival的canopen主节点收发_第31张图片

确保开启了虚拟can接口(ifconfig命令可以找到)

基于canfestival的canopen主节点收发_第32张图片

编译程序运行

基于canfestival的canopen主节点收发_第33张图片

确保支持candump命令(不支持则安装),监听虚拟can口输入数据,先监听401,命令为candump vcan0 | grep 401

基于canfestival的canopen主节点收发_第34张图片

可以发现,有持续下发数据0,当我们改为1则下发1

继续监听501

基于canfestival的canopen主节点收发_第35张图片

可以发现,随着我们修改值则下发发生变化

你可能感兴趣的:(can,c++)