《四》canfestival系列教程之pdo发送流程代码分析
在canfestival中发送pdo需要:
用配套上位机软件生成字典.
配置好定时器.
切换canopen总线状态.(canfestival会根据配置,在中断里自动发送,不需要人干预)
下文中的图片看不清,可全屏看图,或保存图片下来.

canfestival的所有参数都记录在了字典里面,包括pdo的映射关系.
例如:
pdo1发送的数据是速度.
pdo2发送的数据是位置.
......
这些关系都记录在了字典里面.

canfestival中发送pdo程序会自动帮你创建一个软件定时器.
用于该事件的管理.
如果发一次性的pdo,那么软件定时器只执行一次,然后被销毁.
如果发送循环的pdo,软件定时器会被设置成周期触发的.
除了pdo,sdo,同步帧等事件也都有各自的软件定时器.

配置好字典.配置好时钟.
在主函数中通过canfestival的接口函数,切换can总线的状态为操作状态,
程序就会根据配制好的硬件定时器,启动软件定时器,然后软件定时器溢出后.
调用回调函数,发送pdo.
只要配置好参数,启动总线.剩余的事情是不用管的.主函数只留空循环就行.
这样看起来canfestival还是比较好用的.

了解了上面框架,
下面我们按照原码去分析一下pdo是怎么发送出去的.
1.先从字典入手
canfestival的字典是通过上位机进行生成的.
赋值代码在字典.c文件的最后一行.

每个人生成的文件名不一样.
我这的文件名叫做Master.所以传入的参数就是Master.这个CANOPEN_NODE_DATA_INITIALIZER并不是函数,而是一个宏定义.通过这个宏定义,会把字典.c里面的内容传给一个CO_Data类型的结构体变量.我这里面传入的是Master_Data这个变量.之后在主函数中调用Master_Data,就可以获得字典中的所有参数.
赋值的流程
CANOPEN_NODE_DATA_INITIALIZER是宏定义,里面用到了##连接符.连接符的作用就是吧##前后的两个东西合到一起,我们举个例子.
因为我生成的字典名字叫做Master.c,所以在字典文件中会有一个变量,Master_objdict.这里生成字典名字不同,变量名也会随之改变.

Master_objdict[]是字典里的变量,最终会通过
CO_Data Master_Data = CANOPEN_NODE_DATA_INITIALIZER(Master);
赋值给Master_Data_objdict.
因为:



2.分析字典参数
例子:
因为索引是0x1800,所以代表这个是pdo1 (ds301中有规定)
因为01h子索引是0x201,说明发送pdo的id号是201.
因为02h子索引是0xff ,说明发送pdo的方式是定时器触发.
因为05h子索引是0x05 ,说明发送pdo的周期是5ms(注意这里是16进制的).

上面通过配置字典中的通讯参数,配置了pdo的发送方式.
下面通过配置字典中的映射参数,配置pdo的传输数据类型和意义.
映射参数索引为0x1A00 (因为是用的pdo1,所以这里是规定好的,只能用0x1A00,ds301协议)
这样,因为配置为0x60ff0020,所以传输的数据是"速度值",类型是32位无符号.
(ds402中规定 : 索引0x60ff 子索引 00中的参数代表"速度值", 20代表32位无符号类型)


3.定时器配置
通过上面配置,pdo的参数已经完成了.
下面配置时钟.
需要配置一个定时器,然后定时器需要设置成中断模式.
在中断函数中调用canfestival的接口函数TimeDispatch();
需要配置timerscfg.h文件中三个变量.

还需要编写相应的两个函数,需要根据开发环境进行编写
TIMEVAL getElapsedTime(void)和void setTimer(TIMEVAL value)


4.主函数分析
进行了上面的操作.有了pdo的配置,有了pdo的时钟.万事俱备,只差开启了. 开启发送只需要在主函数中调用模式切换就行:

之后进入主while(1)空循环,
程序就会根据字典中配置好的5ms周期性发送pdo1了.

5.查找发送pdo代码
调用setState(&Master_Data, Operational);之后,就会发送pdo.
所以,跳入setState(&Master_Data, Operational);函数.
接着跳入switchCommunicationState(d, &newCommunicationState);函数

接着跳入PDOInit();函数

接着跳入_sendPDOevent(d,0);


接着跳入sendOnePDOevent(d, pdoNum);

在这里面SetAlarm()函数是创建软件定时器,
有了软件定时器,才能是pdo周期性的发送下去.
(软件定时器到期后,会触发,PDOEventTimerAlarm();进行下次pdo发送)
这里还有一个重要的函数buildPDO(),

接着跳入sendPdo();

canSend函数就是最后一层了,需要根据硬件开发环境编写响应的can底层发送.
以stm32单片机为例


发送完成之后,
还有没有记得中创建的软件定时器,它溢出后,会执行回调函数,然后准备下次发送.

回调函数是PDOEventTimerAlarm();