CANopen从站协议在stm32分析和说明

TIM_TimeBaseStructure.TIM_Period = 9999;

TIM_TimeBaseStructure.TIM_Prescaler = 3599;//公式(1+3599)/72M*(1+9999)=0.5

TIM_TimeBaseStructure.TIM_ClockDivision = 0;

TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;

TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);

触发的定时器函数则进行数值累加,代码片段:

void TIM3_IRQHandler(void) {

if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET) {

/* Clear TIM3 update interrupt */ TIM_ClearITPendingBit(TIM3, TIM_IT_Update);

GPIOF->ODR ^= GPIO_Pin_7;//每次让连接PF7管脚的LED灯亮进行状态切换

a=a+501;//每次累加数值501 } }

另外涉及的heartbeatMGR心跳函数: 此函数功能是对定时器累加的数值做判断,当累加的数值达到对象字典内的数值时则发送心跳报文,代码片段:

if ( should_time )//这个变量为1017索引内的值,通过读取对象字典函数getODentry()获取

{ if( ( a >= should_time ) ) { msg.cob_id.w= bDeviceNodeId+ 0x700;

msg.len = (UNS8)0x01; msg.rtr = 0; msg.data[0] = nodeState;

can_send(&msg);//发送心跳报文 a=0;//发送完毕后将定时器累加的数值清0 } }

5 SDO服务数据对象介绍及设计

SDO服务数据对象可用来设置及读取远端节点的对象字典其中的资料。当主站要设置从站的对象字典的数据时,需要先发送一个SDO报文,当从站(stm32f103zet6)接收到报文后,会进行报文解析进而形成一个响应报文反馈给主站。参考以下流程图:

图3 从站处理主站SDO流程图

CANopen发送的SDO报文包括 11 位元的 ID、远端传输请求(RTR)位元及大小不超过8位元的资料。

表4报文介绍

图3 处理写入报文示例

图4处理读取报文示例

从图3看出,从站先接收到主站发送过来的报文06 05 2B 17 00 D0 07 00 00;其中06 05是主站的设置

05从站节点的ID(根据CANopen协议设定,主站发送的SDO报文格式为,COB-ID+NODE-ID+8位DATA,主站SDO报文的COB-ID为0x600,0605等于0x600+0x05,则NODE-ID等于0x05,即设定要设置的从站节点为5号节点),2B是代表设置的数据长度为2个字节(2F代表设置的数据长度为1个字节,27代表设置的数据长度 为2个字节,23代表设置的数据长度为4个字节),17 10则代表要设置10 17索引,后面的00代表设置0号索引,D0 07 00 00代表设置的2个数据为D0 07。(因为只设置2个字节数据长度,所以后面的00 00 可以忽略。)如图3所示,当经过SDO报文处理后,程序会反馈一个SDO响应报文 05 85 60 17 00 00 00 00 00 00;其中05 85是代表此数据为5号从站返回的数据(从站发送SDO响应报文格式为COB-ID+NODE-ID+8位数据,从站SDO响应报文的COB-ID为0x580,0585等于0x580+0x05,则NODE-ID等于0x05,即此数据为5号从站返回的数据),60 代表响应写入成功命令字(若是80的话则是写入错误),17 10代表是对10 17索引的反馈,00 00 00 00代表反馈响应数据成功(若返回失败则返回错误码如:00 00 02 06,则代表是对象字典不存在,此错误代码可以通过代码去设置)。图3是处理设置报文(download)示例。图4是处理读取报文(upload)示例,如图主站发送的报文的第三个字节是40,代表请求读取数据命令字。17 10代表是读取1017索引的数据,从站的响应报文4B代表索引内的数据为2个字节(4F代表索引内的数据为1个字节,47代表索引内的数据为3个字节,43代表索引内的数据为4个字节),数据为D0 07。

SDO功能主要涉及的函数代码为:proceedSDO(message *m)函数:

此函数主要功能是处理SDO报文并将其封装成响应SDO报文,代码片段:

if((nodeState == Operational) || (nodeState == Pre_operational)) {

sdo.nodeId = (UNS8) (GET_NODE_ID((*m)));

MSG_WAR1(0x3A19, \

sdo.len = (*m).len; if (sdo.len > 0)

sdo.body.SCS = m->data[0]; //封装SDO报文

for (i = 1 ; i < sdo.len ; i++) sdo.body.data[i - 1] = m->data[i];

报文通过sendSDO(s_SDO sdo)函数发送:

此函数主要功能是将封装后的SDO报文发送,代码片段:

m.cob_id.w = *pwCobId; m.rtr = DONNEES; m.len = 8;

if (sdo.len > 0)

m.data[0] = sdo.body.SCS; for (i = 1 ; i < sdo.len ; i++) { m.data[i] = sdo.body.data[i - 1]; }

for (i = sdo.len ; i < 8; i++)m.data[i]=0;//将封装的报文转换成CAN协议帧发送 return can_send(&m);

6 PDO过程数据对象及设计

过程数据对象 (PDO) 协定可用来在节点之间交换即时的资料。PDO分为两种:传送用的TPDO及接收用的RPDO。一个节点的TPDO是将资料由此节点传输到其他节点,而RPDO则是接收由其他节点传输的资料。从站(stm32f103zet6)目前只设置了TPDO用于传输从站的数据及传输错误信息,TPDO和RPDO从站可通过一个TPDO传送最多 8 字节资料给一设备。一个PDO可以由对象字典中几个不同索引的资料组成,规划方式则是透过对象字典中对应PDO映射及PDO参数的索引。

PDO可以用同步或异步的方式传送:同

步的PDO是从站接收到主站发送的SYNC讯息后触发,而异步的PDO是由从站内部达到一定条件或其他外部条件触发。同时从站报错事件也是由PDO发送。

图5 从站同步传输数据

图5是从站同步传输数据的报文,当从

站收到主站发送的00 80报文时,则进入同步传输数据函数,从站会将之前主站设置好的索引内的数据发送给主站,01 81是由主站设置的同步传输标识ID,02 67 6F则是3个索引里的数值,传换成十进制数为157551。

同步传输涉及的函数代码为:

Proceedsync(Message *m),此函数功能是封装同步传输PDO报文,代码片段为:

if(index==0x2000&&subIndex==1)//将2000索引的1号子索引的数值赋给pdo的报文 { *p=x; }

if(index==0x2000&&subIndex==3)// //将2000索引的3号子索引的数值赋给pdo的报文 { *p=y; }

if(index==0x2000&&subIndex==4)// //将2000索引的4号子索引的数值赋给pdo的报文 {

*p=z; } if( objDict == OD_SUCCESSFUL ){

MSG_WAR1(0x3011, \

联系客服:779662525#qq.com(#替换为@) 苏ICP备20003344号-4