Skip to main content

移植篇:源码移植

修改日期:2021年12月2日

拷贝源码文件#

在目标工程下建立CANOpen文件夹,新建CANOpen/src,CANOpen/inc,CANOpen/driver文件夹。新建driver/can_timer.c、driver/can_driver.c。

  • 将源码CanFestival-3/src中dcf.c、emcy.c、lifegrd.c、lss.c、nmtMaster.c、nmtSlave.c、objacces.c、pdo.c、sdo.c、states.c、sync.c、timer.c共12个文件文件拷贝到CANOpen/src。
    0.1

  • 将源码CanFestival-3/include中.h文件拷贝到CANOpen/inc。将CanFestival-3-10\include\AVR目录下的applicfg.h、canfestival.h、config.h、timerscfg.h共4个头文件拷贝到CANOpen/inc目录下;
    由于FREERTOS源码中包含了与canfestival同名的timers.c和timers.h,可以将canfestival中的改为timer.c/.h以免冲突。
    0.2

  • 将字典文件拷贝到driver目录下

  • 新建CO_slave.c,设置回调函数

    * CO_Data *OD_Data = &SillySlave_Data;//SillySlave_Data在字典.c最后一行
    void CO_slave_initialisation(CO_Data *d){
    }
    void CO_slave_preOperational(CO_Data *d){    printf("CO_slave_preOperational\n");}
    void CO_slave_operational(CO_Data *d){    printf("CO_slave_operational\n");}
    void CO_slave_stopped(CO_Data *d){    printf("CO_slave_stopped\n");}
    void CO_slave_post_sync(CO_Data *d){    printf("CO_slave_post_sync: \n");}
    void CO_slave_post_TPDO(CO_Data *d){    printf("CO_slave_post_TPDO: \n");    printf("LifeSignal = %u\n", LifeSignal);}
    void CO_slave_post_emcy(CO_Data *d, UNS8 nodeID, UNS16 errCode, UNS8 errReg, const UNS8 errSpec[5]){    //    printf("Slave received EMCY message. Node: %2.2xh  ErrorCode: %4.4x  ErrorRegister: %2.2xh\n", nodeID, errCode, errReg);}
    void CO_slave_heartbeatError(CO_Data *d, UNS8 heartbeatID){    //    printf("CO_slave_heartbeatError %d\n", heartbeatID);}
    UNS32 CO_slave_storeODSubIndex(CO_Data *d, UNS16 wIndex, UNS8 bSubindex){    /*TODO :      * - call getODEntry for index and subindex,      * - save content to file, database, flash, nvram, ...     *      * To ease flash organisation, index of variable to store     * can be established by scanning d->objdict[d->ObjdictSize]     * for variables to store.     *      * */    //    printf("CO_slave_storeODSubIndex : %4.4x %2.2xh\n", wIndex,  bSubindex);    return 0;}
    
    int canopen_init(void){
        OD_Data->heartbeatError = CO_slave_heartbeatError;    OD_Data->initialisation = CO_slave_initialisation;    OD_Data->preOperational = CO_slave_preOperational;    OD_Data->operational = CO_slave_operational;    OD_Data->stopped = CO_slave_stopped;    OD_Data->post_sync = CO_slave_post_sync;    OD_Data->post_TPDO = CO_slave_post_TPDO;    OD_Data->storeODSubIndex = (void *)CO_slave_storeODSubIndex;    OD_Data->post_emcy = (void *)CO_slave_post_emcy;        HAL_TIM_Base_Start_IT(&htim13);//开启定时器        setNodeId(OD_Data, NODE_SLAVE);    setState(OD_Data, Initialisation);        return 0;
    }
  • 在工程中添加.c文件,包含.h的路径。

编译工程#

源码需要我们配置几个函数来运行协议栈。

  • 在can_timer.c中添加几个空函数。

    #include "canfestival.h"void setTimer(TIMEVAL value){}TIMEVAL getElapsedTime(void){      return 1;}can_driver.c中添加unsigned char canSend(CAN_PORT notused, Message *m){      return 1;}
  • 注释或删除掉config.h文件中的如下几行:

    #include <inttypes.h>#include <avr\io.h>#include <avr\interrupt.h>#include <avr/pgmspace.h>#include <avr\sleep.h>#include <avr\wdt.h>
  • 若编译时dcf.c中报错inline内联函数未定义,只需声明一下原函数即可。
    0.3

  • 编译解决其他报错问题,直至编译通过

接口配置#

上方定义了几个空函数,函数void setTimer(TIMEVAL value)主要被源码用来定时的,时间到了就需要调用一下函数TimeDispatch(),函数TIMEVAL getElapsedTime(void)主要被源码用来查询距离下一个定时触发还有多少时间,unsigned char canSend(CAN_PORT notused, Message *m)函数主要被源码用来发一个CAN包的,需要调用驱动来将一个CAN包发出去。
在can_timer.c中:

unsigned int TimeCNT=0;//时间计数unsigned int NextTime=0;//下一次触发时间计数unsigned int TIMER_MAX_COUNT=70000;//最大时间计数static TIMEVAL last_time_set = TIMEVAL_MAX;//上一次的时间计数setTimer和getElapsedTime函数实现如下://Set the next alarm //void setTimer(TIMEVAL value){        NextTime=(TimeCNT+value)%TIMER_MAX_COUNT;}// Get the elapsed time since the last occured alarm //TIMEVAL getElapsedTime(void){        int ret=0;        ret = TimeCNT> last_time_set ? TimeCNT - last_time_set : TimeCNT + TIMER_MAX_COUNT - last_time_set;        last_time_set = TimeCNT;        return ret;}

在1ms定时器中断中调用以下函数

void timerForCan(void){        TimeCNT++;        if (TimeCNT>=TIMER_MAX_COUNT)        {                TimeCNT=0;        }        if (TimeCNT==NextTime)        {                TimeDispatch();        }}
  • cansend就不多说了,根据芯片、例程配置就行了。
  • 接下来来到了巨坑时刻!!!!
    按道理说上述接口配置完成后协议栈就应该运行起来了,但是我在写这篇时定时时间总是不准确,折腾的我精神衰弱,无数次怀疑接口是不是写错了,定时器是不是不准确,甚至自己根据多个工程案例写接口,直到有一天看到评论区一位救命恩人说timerscfg.h下的MS_TO_TIMEVALUS_TO_TIMEVAL需要根据定时情况更改。然后我立刻查看了工程,果然canfestival源码中定义的是

    // The timer is incrementing every 8 us.#define MS_TO_TIMEVAL(ms) ((ms) * 125)#define US_TO_TIMEVAL(us) ((us)>>3)

    意思是时钟每两次中断之间时间间隔为8us,然而我们的定时器是1ms,将其改为

    // The timer is incrementing every 1000 us.#define MS_TO_TIMEVAL(ms) ((ms) * 1)#define US_TO_TIMEVAL(us) ((us)/1000)

    完美运行!!!
    在此告诫自己遇到问题还是要不限范围搜索资料,同时做好工作笔记,以免以后再用时又得从头再来,浪费青春