2.30. CANFESTIVAL

Overview

CanFestival是基于CANOPEN的一个开源协议栈,而CANOPEN是一个基于CAN的高层应用协议,内部对CAN报文的11/29位ID、8字节数据的使用都进行了严格的定义。在运用CanFestival前需要有CAN以及CANOPEN的基本知识。目前SDK适配CanFestival-v3.10。

提供CanFestival应用示例、配置介绍和常见问题。

2.30.1. 应用实例

示例演示:

  • 演示协议栈主站、从站使用等。

example: 具体示例代码详见 apps/common/example/third_party/canfestival/canfestival_main.c

2.30.2. 配置说明

  • 1.在板级文件 apps/.../board.h 中打开宏 TCFG_CAN_ENABLE 并打开宏 TCFG_CANOPEN_ENABLE ,若运行CANOPEN务必将CAN的模式设定为增强模式。
    #define TCFG_CAN_ENABLE                             1
    #if TCFG_CAN_ENABLE
    #define TCFG_CANOPEN_ENABLE                         1
    #if TCFG_CANOPEN_ENABLE
    #undef TCFG_CAN_ENABLE
    #define TCFG_CAN_ENABLE                             1
    #endif
    #endif
    

  • 2.在CanFestival配置文件 apps/common/example/third_party/canfestival/include/jieli/canopen_def.h 中选择CanFestival的主从机模式以及定时中断使用的定时器。最好不要同时使能主站和从站,可以主站和从站动态切换

    #define TCFG_CANOPEN_MASTER_ENABLE              0       /*!< CANOPEN协议栈作主机模式*/
    #define TCFG_CANOPEN_SLAVE_ENABLE               1       /*!< CANOPEN协议栈作从机模式*/
    #define TCFG_CANOPEN_TIMER_10MS_ENABLE          1       /*!< 使能选择使用系统定时器最小支持10ms心跳时间,失能选择使用独立定时器最小支持1ms心跳时间*/
    

2.30.3. 流程说明

  • 1.canfestival根目录下的canfestival_main.c是CANOPEN协议栈的主函数入口;/src是CanFestival的.c源文件;/include是CanFestival的.h源文件;/drive是CanFestival的接收与发送等接口文件,包括字典.c/.h文件;/bsp是CanFestival的CAN和Timer硬件接口。

  • 2.CANOPEN协议栈首先在canfestival_main.c创建主线程,并对硬件和协议栈进行初始化。

    static void canopen_main_task(void *p)
    {
        canopen_can_init();//can硬件初始化
    #if TCFG_CANOPEN_TIMER_10MS_ENABLE
        canopen_sys_timer_enable();//系统定时器初始化
    #else
        canopen_solo_timer_enable();//独立定时器初始化
    #endif
        canopen_driver_init(); //can接收任务与定时中断调用任务创建
    
    #if TCFG_CANOPEN_MASTER_ENABLE
        unsigned char nodeID = 0x01;
        setNodeId(&TestMaster_Data, nodeID);//设置canfestival主机ID
        setState(&TestMaster_Data, Initialisation);//初始化canopen NMT网络主机设备
        setState(&TestMaster_Data, Operational);//进入操作状态
    #endif
    
    #if TCFG_CANOPEN_SLAVE_ENABLE
        unsigned char nodeID = 0x03;
        setNodeId(&TestSlave_Data, nodeID);//设置canfestival从机ID
        setState(&TestSlave_Data, Initialisation);//初始化canopen NMT网络从机设备
        setState(&TestSlave_Data, Operational);//进入操作状态
    #endif
    
        while (1) {
            /*应用层,可在此处添加canopen协议条件发送报文。SDO或PDO*/
            os_time_dly(50);
        }
    }
    

Note

说明:

  • SDK中CANOPEN协议栈主站中仅有2s一次的心跳报文;从站集合了心跳报文、SDO、PDO等报文,具体请查看 /drive/TestSlave.eds/drive/TestSlave.od。其中 /drive/TestSlave.c/drive/TestSlave.h 是对应从站字典文件生成的.c/.h文件。

  • CANOPEN协议栈最重要的是字典文件,如果想基于SDK示例中的字典文件进行二次开发,建议熟悉一下CANOPEN协议栈的字典文件内部定义以及CanFestival字典文件如何使用,其中CanFestival字典文件需要配置对应的python环境才能打开.od文件。.od字典文件准备好后建议导出的.c/.h文件名和SDK中使用的主站从站.c/.h文件名一致,否则会报错,需要根据错误修改代码。

2.30.4. 常见问题

  • 协议栈启动后容易出现死机、复位、CPU占用高等情况。

    答:排查是否配置了阻塞式接收死等方式,如果是则改用信号量的方式;检查是否CanFestival定时中断过于频繁,可以根据实际心跳报文的设置时间,调整定时回调的时间,注意不能动态调整回调时间。

  • 心跳报文设定在1s一次,用不上10ms或者1ms的回调,如何更改为100ms回调?

    答:在 canfstival/bsp/bsp_canopen.c 中找到系统定时器或独立定时器的配置函数进行修改。修改bsp_canopen.c后需要同步修改 canfstival/include/jieli/timerscfg.h 中的协议栈心跳时间

    ///canfstival/bsp/bsp_canopen.c
    ......
    //独立定时器修改方法
    void canopen_timer_run(void)
    {
        u8 precesion;
        //precesion = 10;//单位0.1ms
        //从1ms回调修改为100ms回调
        precesion = 1000;//单位0.1ms
        ......
    }
    //系统定时器修改方法
    void canopen_sys_timer_enable(void)
    {
        os_sem_create(&canopen_timer_sem, 0);
        /*sys_hi_timer_add(NULL, canopen_sys_timer_isr, 10);*/
        sys_hi_timer_add(NULL, canopen_sys_timer_isr, 100);//从10ms回调修改为100ms回调
    }
    
    ///canfstival/include/jieli/timerscfg.h
    ......
    #define MS_TO_TIMEVAL(ms) ((ms) / 100)//(ms)/回调时间ms = 1ms
    #define US_TO_TIMEVAL(us) ((us) / 100000)//(us)/回调时间us = 1us
    

  • 目前SDK中CANOPEN协议栈的CAN硬件功能使用较少,能否自行添加功能?

    答:可以,建议在 canfstival/bsp/bsp_canopen.c 中can初始化函数执行相关硬件初始化操作。

    void canopen_can_init(void)
    {
        canopen_hdl = dev_open("can", NULL);
        dev_ioctl(canopen_hdl, IOCTL_CAN_SET_DMA_FRAMES, 1);
        dev_ioctl(canopen_hdl, IOCTL_CAN_SET_RECV_WAIT_SEM, 0);
        //自行添加中断或其他功能
    }