CanFestival 源码剖析
(1)TIMER_HANDLE是回调任务的编号,当他是-1时代表没有回调任务
(2)TIMER_HANDLE SetAlarm(CO_Data d, UNS32 id, TimerCallback_t callback, TIMEVAL value, TIMEVAL period);*
这里的value时相对与这个函数的执行多长时间后执行回调,如果period是0,那么回调只执行一次
函数先从任务编号0开始到最后一个last_timer_raw,找到一个空的TIMER_FREE的编号,如果大于之前last_timer_raw(最大编号)就将last_timer_raw+1。然后获取从上一次进入定时器中断开始过去的时间elapsed_time,如果完整休眠时间大于elapsed_time并且elapsed_time加上设置的value还小于完整休眠时间,那么将完整休眠时间重新设置并设好定时器value时间后进入中断。
row->val进入中断的绝对时间,是从上一次进中断后多长时间后进入下一次定时器中断的间隔时间
(3)TIMER_HANDLE DelAlarm(TIMER_HANDLE handle)
这个函数是使handle编号的回调任务失能的
如果使最后一个那么last_timer_raw-1。失能方法就是直接将状态置为TIMER_FREE
(4)void TimeDispatch(void);
这是在定时器中断中执行的,注意在执行这个之前还有其他函数。此函数
是执行设定好的回调函数的
进入定时器中断后last_counter_val, elapsed_time(单位是us)会清零一次.TimeDispatch中先计算从进入中断到当前语句经过的时间overrun,再将overrun加上完全休眠时间得到正真的完全休眠时间。后面一个for循环中如果正真的完全休眠时间大于设定的row->val(从上一次进入中断开始多长时间后执行回调的时间)那么就使能回调的执行,非周期性的回调执行后就会失能(只执行一次),周期性的回调会计算overrun对回调周期的影响,重新给row->val赋值以确保实时性。如果row->val小于next_wakeup(下一次进入中断的时间,相当于最小的row->val)就next_wakeup = row->val;再将next_wakeup给total_sleep_time。设定next_wakeup进入中断。
如果for循环中有的回调任务时间没到,那么将对应的row->val减去真正的完全休眠时间
通过 d->scanIndexOD(该函数原型ObjDict_scanIndexOD)获得数据指针,如果获取成功并且要读的字节数小于获取到的。。。。就将数据拷贝到pDestData。如果要读的数据类型是字符串那么就加个’\0’,size+1
(2)UNS32 _setODentry( CO_Data d,
UNS16 wIndex,------------------>想要写的对象字典
UNS8 bSubindex,--------------->想要写的子索引
void * pSourceData,--------------->要写入的数据
UNS32 * pExpectedSize,------------->写入多少字节
UNS8 checkAccess,------------>是否检查可写
UNS8 endianize)----------------->大小端
先通过 d->scanIndexOD(该函数原型ObjDict_scanIndexOD)获得数据指针,。。。。过程类似与_getODentry,最后如果有注册写字典回调函数会执行回调
(3)UNS32 RegisterSetODentryCallBack(CO_Data d, UNS16 wIndex, UNS8 bSubindex, ODCallback_t Callback)
注册写字典回调函数。。。
(2)void canDispatch(CO_Data d, Message m)
这是can接受中断中的数据处理,Message *m是中断接收的数据包格式
(3)StartOrStop(CommType, FuncStart, FuncStop)
启动或停止某种通信模式 CommType是要控制的通信模式,FuncStart是启动函数,FuncStop是停止函数
(4)void switchCommunicationctate(CO_Data d, s_state_communication newCommunicationState)
切换通信模式,newCommunicationState是新的通信模式结构体,根据这个结构体使用StartOrStop来控制通信模式(被设置节点状态setState调用)
typedef struct
{
INTEGER8 csBoot_Up;------------------>启动
INTEGER8 csSDO;----------------------->SDO传输
INTEGER8 csEmergency;--------------->紧急报文
INTEGER8 csSYNC;---------------------->想同步报文
INTEGER8 csLifeGuard;------------------>节点守护报文
INTEGER8 csPDO;------------------>PDO传输
INTEGER8 csLSS;
} s_state_communication;
(5)UNS8 setState(CO_Data d, e_nodeState newState)
设置节点状态
初始化状态下:通信模式设为启动,节点状态会设为初始化
预操作状态下:通信模式中除了启动和LSS其他都打开,节点状态设为预操作,如果是主机会执行发送改变子节点转台的报文
操作状态下:如果节点正在初始化就返回,否则除了启动和lss其他通信模式都打开,节点状态设为操作状态
停止状态下:如果节点正在初始化就返回,否则除了LifeGuard和LSS打开其他都关闭,节点状态设为停止
(6)void setNodeId(CO_Data d, UNS8 nodeId)*
设置本设备节点ID,设置SDO和PDO报文的cob-ID
(2)UNS32 OnCOB_ID_SyncUpdate(CO_Data d, const indextable * unsused_indextable, UNS8 unsused_bSubindex)
开始同步报文的传输,当索引1005和1006内容更新后会被调用(1005是同步报文ID,1006同步帧的循环周期)
(3)void startSYNC(CO_Data d)
如果之前有同步报文回调那么就停止回调的执行,注销回调函数。然后注册新的回调,如果同步周期不为零就设置一个回调
(4)void stopSYNC(CO_Data d)
停止同步报文,注销回调。
(5)UNS8 sendSYNCMessage(CO_Data d)
只发送一个cobid,非远程帧
(6)UNS8 sendSYNC(CO_Data d)
此函数再定时同步回调函数中被调用,先发送一个同步报文,然后处理接受到的同步报文(proceedSYNC)
(7)UNS8 proceedSYNC(CO_Data d)
如果PDO模式没打开就返回,否则发送一个PDO事件(_sendPDOevent)
(2)UNS8 sendPDOrequest (CO_Data * d, UNS16 RPDOIndex)
RPDOIndex是需要请求的RPDO,如果当前状态不是PDO就退出。下面判断请求的PDO是否在合理范围,如果在就获取对应的cob-ID,然后发一个远程帧,没有数据的can报文
(3)UNS8 proceedPDO (CO_Data * d, Message * m)
该函数在can接受中断中被调用
首先判断是否是远程请求,如果不是就从第一个PDO到最后一个PDO一个个检查,如果传入的cob-ID与与其中一个PDO的cob-ID相等,那么就获取对应数目然后将所有数据拷贝到本地对象字典中。如果有设置时间事件回调那么就消除最近一次回调,然后重新设置一次回调(防止事件事件由更改),回调函数默认不做什么。
如果是远程请求,找到对应cob-ID相同的PDO,获取本地对象字典该PDO的传输类型,如果是远程异步那么就发送对应的PDO,如果是远程同步,且接受到同步事件会把最后一个PDO数据last_message发送出去否则正常发送PDO.如果传输类型是异步制造商特定事件或异步设备子协议特定事件,就先取消改PDO现有回调然后触发一个发送回调,发送一个事件
(4)static void sendPdo(CO_Data * d, UNS32 pdoNum, Message * pdo)
发送一个PDO,并且把这条PDO存在last_message里
(5)UNS8 _sendPDOevent (CO_Data * d, UNS8 isSyncEvent)
从第一个PDO到最后一个PDO,获取传输类型如果传输类型在合理的多少个同步帧后传输数据内并且是同步传输,那么就重置他的同步次数参数然后发送PDO报文并记录到last_message里。如果传输类型是远程同步,就将远程同步准备位置1.如果(是同步传输且传输类型不是周期循环的) ||(不是同步传输且传输类型是特定事件且不在禁止生产时间内)就发送一个PDO事件来设置事件时间回调和禁止时间回调并发送PDO
(6)UNS8 sendOnePDOevent (CO_Data * d, UNS8 pdoNum)
如果pdo传输未开启或者在禁止生产时间内就返回
先获取传输参数结构体然后根据pdo号生成can报文数据。跟last_message比较之后如果不一样就获取事件事件和生产禁止时间,然后设置事件时间和生产禁止回调,最后发送PDO。
(7)UNS32 TPDO_Communication_Parameter_Callback (CO_Data * d,const indextable * OD_entry, UNS8 bSubindex)
此回调函数在参数被写入的时候调用
如果传输类型,生产禁止时间,事件时间被改了那么就删除当前时间事件回调和生产禁止回调并再次触发事件时间回调
(8)void PDOInit (CO_Data * d)
设置好每个PDO的参数修改回调,并触发一个非同步事件
(2) UNS32 OnNumberOfErrorsUpdate(CO_Data* d, const indextable * unsused_indextable, UNS8 unsused_bSubindex)
错误历史更新时回调,在emcy初始化时被注册为字典写入回调
这是一个需要用户自己填写完整的函数
如果错误数量是0那么就将所有错误都清掉,否则自己写
(3)UNS8 sendEMCY(CO_Data* d, UNS16 errCode, UNS8 errRegister, const UNS8 errSpecific[5])
这是从机发送给主机的错误码,没什么好讲的
(4)UNS8 EMCY_setError(CO_Data* d, UNS16 errCode, UNS8 errRegMask, UNS16 addInfo)
先将参数传入的错误码与字典中的意义进行对比如果找到了对应的就查看是否有激活。。。
如果查完所有字典里的错误码都没有找到就找一个没有被激活的错误码给他覆盖掉(好粗暴),如果找不到就直接返回并输出log
接着将error_state置为错误发生,更新已激活的错误寄存器和错误历史寄存器
最后发送紧急报文(d->CurrentCommunicationState.csEmergency在起初将主机设为操作状态时已将其设为1)
(5)void EMCY_errorRecovered(CO_Data* d, UNS16 errCode)
这是错误码恢复函数,可被用户使用
首先将在字典中寻找指定错误码,如果找到该错误码并且时激活状态则将他的acticve置0
然后根据剩下的已激活错误码的掩码来更新错误寄存器的值,如果没有错误了发送一个紧急报文
(6)void proceedEMCY(CO_Data* d, Message* m)
这是主机处理紧急报文函数,在can接受中断时被调用
获取错误码和错误寄存器
该函数的最后一句是空的,用户可以自由发挥
(2)void ConsumerHeartbeatAlarm(CO_Data* d, UNS32 id)