🍵移植篇:源码移植
拷贝源码文件
在目标工程下建立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)完美运行!!!
在此告诫自己遇到问题还是要不限范围搜索资料,同时做好工作笔记,以免以后再用时又得从头再来,浪费青春