Canfestival的移植思想
目录
1 序
2 移植
3 代码
4 字典
1 序
本文对Canfestival的移植核心点进行阐述;
阅读本文须先掌握CANopen基本理论,本文不再赘述。
I.MX RT1176实现:https://github.com/CenwJen/i.mxrt1176.git
2 移植
软件准备 |
python-2.7.15.amd64.msi |
wxPython2.8-win64-3.0.2.0-py27.exe |
Gnosis_Utils-1.2.2.zip |
CanFestival-3 |
对于CanFestival-3,包含1个官方源码与其3个分支,需要从中挑选支持自己平台的内核。常用的Cortex-M4/7,是Mongo-canfestival-3。
文件移植 |
Mongo-canfestival-3/src *.c |
Mongo-canfestival-3/include *.h |
Mongo-canfestival-3/include/(Platform Core)/ *.h |
Mongo-canfestival-3\examples\AVR\Slave\config.h |
对于移植的文件,需要移植到工程文件里并包含,注意:
- 检查canfestival.h是否有条件编译防止头文件重复包含。
- 删除dcf.c的内联关键字。
- 对于config.h,以cm4/7为参考,更改为以下代码,请仔细阅读对比,其中:
- AVR为不需要的头文件;
- SDO_BLOCK_SIZE为SDO块传输;
- CAN_BAUDRATE为CAN波特率;
- REPEAT_SDO_MAX_SIMULTANEOUS_TRANSFERTS_TIMES更改为REPEAT_SDO_MAX_SIMULTANEOUS_TRANSFERS_TIMES。
#ifndef _CONFIG_H_
#define _CONFIG_H_#ifdef __IAR_SYSTEMS_ICC__
#include <ioavr.h>
#include <intrinsics.h>
#include "iar.h"
#else // GCC
//#include <inttypes.h>
//#include <avr/io.h>
//#include <avr/interrupt.h>
//#include <avr/pgmspace.h>
//#include <avr/sleep.h>
//#include <avr/wdt.h>
#endif // GCC//#define WD_SLEEP
// Needed defines by Atmel lib
#define FOSC 8000 // 16 MHz External cristal
#ifndef F_CPU
#define F_CPU (1000UL*FOSC) // Need for AVR GCC
#endif
#define CAN_BAUDRATE 1000 //1000K, CAN BAUDRATE 1M// Needed defines by Canfestival lib
#define MAX_CAN_BUS_ID 1
#define SDO_MAX_LENGTH_TRANSFER 32
#define SDO_BLOCK_SIZE 16
#define SDO_MAX_SIMULTANEOUS_TRANSFERS 1
#define NMT_MAX_NODE_ID 128
#define SDO_TIMEOUT_MS 3000U
#define MAX_NB_TIMER 8// CANOPEN_BIG_ENDIAN is not defined
#define CANOPEN_LITTLE_ENDIAN 1#define US_TO_TIMEVAL_FACTOR 8#define REPEAT_SDO_MAX_SIMULTANEOUS_TRANSFERS_TIMES(repeat)\
repeat
#define REPEAT_NMT_MAX_NODE_ID_TIMES(repeat)\
repeat repeat repeat repeat repeat repeat repeat repeat repeat repeat repeat repeat repeat repeat repeat repeat repeat repeat repeat repeat repeat repeat repeat repeat repeat repeat repeat repeat repeat repeat repeat repeat repeat repeat repeat repeat repeat repeat repeat repeat repeat repeat#define EMCY_MAX_ERRORS 8
#define REPEAT_EMCY_MAX_ERRORS_TIMES(repeat)\
repeat repeat repeat repeat repeat repeat repeat repeat#endif /* _CONFIG_H_ */
applicfg.h提供调试输出宏在不需要使用时可以注释掉,因为串口速率远低于can速率,且其打印函数MSG(...)为阻塞函数,在can收发中等待打印会相当耗时,导致can速率大降。
另,留意其打印换行符为适应Linux。
3 代码
Canfestival的移植需要两个驱动:首先CANopen基于CAN,得实现平台的CAN驱动。其次,Canfestival协议是基于硬定时器的软定时,需要实现平台的硬定时中断驱动。
在Canfestival协议栈中,TimeDispatch()和canDispatch(&Master_Data, &rxMessage)是两个核心函数。
TimeDispatch()用于处理所有定时事件,调用频率至少应小于协议栈中最小时间事件间隔,在硬定时中断调用。其实现方式在timer.c,如下:
void TimeDispatch(void)
{TIMER_HANDLE i;TIMEVAL next_wakeup = TIMEVAL_MAX; /* used to compute when should normaly occur next wakeup *//* First run : change timer state depending on time *//* Get time since timer signal */UNS32 overrun = (UNS32)getElapsedTime();TIMEVAL real_total_sleep_time = total_sleep_time + overrun;s_timer_entry *row;for(i=0, row = timers; i <= last_timer_raw; i++, row++){if (row->state & TIMER_ARMED) /* if row is active */{if (row->val <= real_total_sleep_time) /* to be trigged */{if (!row->interval) /* if simply outdated */{row->state = TIMER_TRIG; /* ask for trig */}else /* or period have expired */{/* set val as interval, with 32 bit overrun correction, *//* modulo for 64 bit not available on all platforms */row->val = row->interval - (overrun % (UNS32)row->interval);row->state = TIMER_TRIG_PERIOD; /* ask for trig, periodic *//* Check if this new timer value is the soonest */if(row->val < next_wakeup)next_wakeup = row->val;}}else{/* Each armed timer value in decremented. */row->val -= real_total_sleep_time;/* Check if this new timer value is the soonest */if(row->val < next_wakeup)next_wakeup = row->val;}}}/* Remember how much time we should sleep. */total_sleep_time = next_wakeup;/* Set timer to soonest occurence */setTimer(next_wakeup);/* Then trig them or not. */for(i=0, row = timers; i<=last_timer_raw; i++, row++){if (row->state & TIMER_TRIG){row->state &= ~TIMER_TRIG; /* reset trig state (will be free if not periodic) */if(row->callback)(*row->callback)(row->d, row->id); /* trig ! */}}
}
其中,调用了getElapsedTime()和setTimer(next_wakeup)。这两个软定时函数本身在Canfestival并没有实现,需要配合硬定时器和源码注释提示来设计:
//Set the timer for the next alarm.
void setTimer(TIMEVAL value)
{
}//Return the elapsed time to tell the Stack how much time is spent since last call.
TIMEVAL getElapsedTime(void)
{
}
canDispatch(&Master_Data, &rxMessage)用于处理接收到的报文,调用时机为每次接收到报文后,在CAN接收中断的下半部调用。其实现方式在states.c,如下:
void canDispatch(CO_Data* d, Message *m)
{UNS16 cob_id = UNS16_LE(m->cob_id);switch(cob_id >> 7){case SYNC: /* can be a SYNC or a EMCY message */if(cob_id == 0x080) /* SYNC */{if(d->CurrentCommunicationState.csSYNC)proceedSYNC(d);} else /* EMCY */if(d->CurrentCommunicationState.csEmergency)proceedEMCY(d,m);break;case TIME_STAMP:case PDO1tx:case PDO1rx:case PDO2tx:case PDO2rx:case PDO3tx:case PDO3rx:case PDO4tx:case PDO4rx:if (d->CurrentCommunicationState.csPDO)proceedPDO(d,m);break;case SDOtx:case SDOrx:if (d->CurrentCommunicationState.csSDO)proceedSDO(d,m);break;case NODE_GUARD:if (d->CurrentCommunicationState.csLifeGuard)proceedNODE_GUARD(d,m);break;case NMT:if (*(d->iam_a_slave)){proceedNMTstateChange(d,m);}break;
#ifdef CO_ENABLE_LSScase LSS:if (!d->CurrentCommunicationState.csLSS)break;if ((*(d->iam_a_slave)) && cob_id==MLSS_ADRESS){proceedLSS_Slave(d,m);}else if(!(*(d->iam_a_slave)) && cob_id==SLSS_ADRESS){proceedLSS_Master(d,m);}break;
#endif}
}
相对于发送报文,则需要封装:
// The driver send a CAN message passed from the CANopen stack
unsigned char canSend(CAN_PORT port, Message *message)
{
}
其返回值应当判断报文发送是否成功:
unsigned char canSend(CAN_PORT port, Message *message)
{status_t status;...return (status == kStatus_Success) ? 0 : 1;
}
status_t定义在fsl_common.h,如下:
enum
{kStatus_Success = MAKE_STATUS(kStatusGroup_Generic, 0), /*!< Generic status for Success. */kStatus_Fail = MAKE_STATUS(kStatusGroup_Generic, 1), /*!< Generic status for Fail. */kStatus_ReadOnly = MAKE_STATUS(kStatusGroup_Generic, 2), /*!< Generic status for read only failure. */kStatus_OutOfRange = MAKE_STATUS(kStatusGroup_Generic, 3), /*!< Generic status for out of range access. */kStatus_InvalidArgument = MAKE_STATUS(kStatusGroup_Generic, 4), /*!< Generic status for invalid argument check. */kStatus_Timeout = MAKE_STATUS(kStatusGroup_Generic, 5), /*!< Generic status for timeout. */kStatus_NoTransferInProgress =MAKE_STATUS(kStatusGroup_Generic, 6), /*!< Generic status for no transfer in progress. */kStatus_Busy = MAKE_STATUS(kStatusGroup_Generic, 7), /*!< Generic status for module is busy. */kStatus_NoData =MAKE_STATUS(kStatusGroup_Generic, 8), /*!< Generic status for no data is found for the operation. */
};/*! @brief Type used for all status and error return values. */
typedef int32_t status_t;
另外,timerscfg.h的宏MS_TO_TIMEVAL(ms)和宏US_TO_TIMEVAL(us)需要根据当前硬定时中断的触发时间来更改,其参数应当被设置当前硬定时中断的触发时间通过计算而得。示例时间单位为1us,则如下:
// TIMEVAL is not at least on 32 bits
#define TIMEVAL UNS32// using 16 bits timer
#define TIMEVAL_MAX 0xFFFFFFFF// The timer is incrementing every 1 us.
#define MS_TO_TIMEVAL(ms) ((ms) * 1000)
#define US_TO_TIMEVAL(us) ((us) )
4 字典
首先安装前文的python2.7。
对于Gnosis_Utils-1.2.2,在目录下打开cmd,键入以下命令安装:
python setup.py install
然后将其目录下的gnosis文件复制到Mongo-canfestival-3\objdictgen目录下,然后通过python2.7打开方式打开Mongo-canfestival-3\objdictgen\objdictedit.py,即可配置字典。
Canfestival的字典索引配置皆由此配置:
通过[文件]选择[新建]可以创建一个新节点,创建后即可根据需求如PDO、SDO和心跳等配置。
通过[文件]选择[保存]可以生成.od文件保存当前词典配置。通过[文件]选择[建立词典]可以生成.c和.h的文件词典,该词典需要被canfestival调用。