移植篇:源码移植
修改日期: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。
将源码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以免冲突。
将字典文件拷贝到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内联函数未定义,只需声明一下原函数即可。
编译解决其他报错问题,直至编译通过
#
接口配置上方定义了几个空函数,函数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_TIMEVAL
和US_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)
完美运行!!!
在此告诫自己遇到问题还是要不限范围搜索资料,同时做好工作笔记,以免以后再用时又得从头再来,浪费青春