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, \