0x1005 SYNC CON ID 涉及CANopen主机发送的SYNC 同步帧ID与使能
0x1005 子指标0x00的值:
31:无意义
30:gen 0:不生成同步消息 1:生成同步消息
29:frame 0 11位CAN-ID有效 1 29位CAN-ID有效
28:0 ID 0:10 11位ID 0:28 29位ID
一般主机定义为0x40000080 发送同步消息,11位ID,ID=0x80。
即SYNC发送的周期单位ms 0则禁用SYNC 1000则1S发送一个同步帧
表示主机在发送SYNC之后多长时间内收到的同步消息是有效的,暂时没用
设置值的类型是32位的,前16位代表从机ID号,后16位为检查时间。0x107D0的意思是从机一检测时间为0x07d0也就是2S。
这个暂时没看到效果
PDO接收映射地址,例0x1600 点入之后可右键子指标添加子指标,子指标0x00指示PDOx映射多少个地址,除0x00之外的子指标,可选的值为0x2000-0x5FFF区域定义的标量,本区域配置完成后可以用较短的PDO指令更改映射地址的值。这个尽量在字典中就配置好,不用在运行中用PDO协议去改。
例:0x1403 0x01=DI+0x500;0x1603 0x00=1;0x01=0x2003;ID=0x03;
则发送 ID=0x503 data=06 05 03 04 最后结果是0x2003的值根据自身数据类型更新,若为u8类型,则变为0x06,u32类型变为0x04030506。
0x1403 0x01=DI+0x500;0x1603 0x00=2;0x01=0x2003;0x02=0x2004;ID=0x03;
则发送 ID=0x503 data=06 05 03 04 0x2003和0x2004为u16类型, 0x2003=0x0506,0x2004=0x0403。
注意ID格式 PDO的不同区域,data是没有要求的,但是ID不同将直接对应到不同的PDO区域。
这个以上关于数据的地方有错误,0x1600-0x17FF和0x1A00-0x1BFF是PDO的映射,他的四个数据中有两个数据是映射关系(对应对象字典的变量),一个是映射关系的数据类型,一个是映射关系的子索引。
例:值 20000108 h 为映射到索引 2000h的子索引 01h,对象是 8 位 从左往右 2000是映射关系01是子索引,08表示数据类型
上面这个例子,用SDO指令写为 23 01 00 16 08 01 00 20,详情请参考PDO使用及例程的配置对应变量
数据类型
0x08 | 8位 |
0x10 | 16位 |
0x20 | 32位 |
这个区域和0x1600-0x17FF用法类似,不同在于0x1600-0x17FF是接收外面来的数,0x1A00-0x1BFF发送自身的数,要发送的数据直接对应映射地址。
这个区域是重点理解。以0x1800为例,子指标一般是七个
0x00 :子指标个数
0x01 :ID 0x180+ 固定
0x02 :Transmission type 发送类型
00h:非循环同步
只能异步发送(即接收到发送请求或者主动发送),接收到SYNC帧不会发送。
01h-FBh:循环同步
根据TestSlave_obj1400_SYNC_start_value
里面设置的值接收到SYNC的个数发送一次。
FCh:远程同步
当收到PDO请求,若该PDO_status的状态位PDO_RTR_SYNC_READY置位(会在接收到SYNC信号调用的_sendPDOevent中BuildPDO并置该位),则直接发出PDO的最后一帧。
FDh:远程异步
当收到PDO请求(SendPDORequest)后立即BuildPDO发送被请求的PDO帧(cob_id标示)。
FEh:异步,制造商特定事件和FFh:异步,设备子协议特定事件
调用sendPDOevent()(未被阻止inhibited)后建立PDO并检查其PDO帧与上一次发送(PDO_status.last_message)的PDO帧内容是否发生变化,若发生变化则发送,否则不发送。接收到SYNC帧不会发送该帧。但是若接收到PDO请求则强制建立并发送。
0x03:Inhibit Time PDO发送的最小周期 若设为1S,则1S中最多发送一次,其余的不执行,可限制值改变时的发送频率。0x00不生效
0x04:Compatibility Entry 暂时无用
0x05:Event Time 定时发送时间,若0x03=0xFE或0xFF,在Canopen操作状态时,按此变量规定时间定时发送:1000=1S。0x00不定时发送。
0x06:同步传输的 PDO,收到诺干个同步包后,才进行发送 这个没试出效果。
用户可自行配置的区域,一般新建后是空白的,可添加地图变量:
指标:0x2000-0x5FFF任意,这个用于PDO地址映射或SDO直接控制
名字:程序里的变量名,单片机PDO、SDO发送的数和接收后被更改的数就是这个名字。
类型:VAR不可扩展的单个变量;ARRAY 数组;REC 多个变量;ARRAY 和REC 在添加地图变量时输入的数字就是数组的元素个数和变量个数,注意个地图变量和子指标里面的名字命名,出现相同命名程序会报错。
这个在建立对象字典时若Profile选用不是毫无,则会在这片区域自动定义一些变量,属于不可更改区,使用时和用户自定义区一样,属于通用协议。
先说SDO,这个比较简单,而且PDO配置会用到SDO方式
字节数 | ID | RTR | 功能码 | Index_L | Index_H | Sub index | data | data | data | data |
---|---|---|---|---|---|---|---|---|---|---|
一个字节 | 600 + ServNodeId | 0 | 2F | Index_L | Index_H | Sub index | D0 | - | - | - |
二个字节 | 600 + ServNodeId | 0 | 2B | Index_L | Index_H | Sub index | D0 | D1 | - | - |
三个字节 | 600 + ServNodeId | 0 | 27 | Index_L | Index_H | Sub index | D0 | D1 | D2 | - |
四个字节 | 600 + ServNodeId | 0 | 23 | Index_L | Index_H | Sub index | D0 | D1 | D2 | D3 |
成功时:
ID | RTR | 功能码 | Index_L | Index_H | Sub index | data | data | data | data |
---|---|---|---|---|---|---|---|---|---|
580 + Serv NodeId | 0 | 60 | Index_L | Index_H | Sub index | 00 | 00 | 00 | 00 |
失败时:
ID | RTR | 功能码 | Index_L | Index_H | Sub index | data[0…3] |
---|---|---|---|---|---|---|
580 + Serv NodeId | 0 | 80 | Index_L | Index_H | Sub index | SDO abort code error |
实例:DI:0x621 DLC 8 标准帧 数据帧 DATA:2F 01 14 02 FD 00 00 00
写入ID为0x21的0x1401,子指标0x02,数据为0xFD
成功:DI:0x5A1 DLC 8 标准帧 数据帧 DATA:60 01 14 02 00 00 00 00
DI:0x621 DLC 8 标准帧 数据帧 DATA:23 01 16 02 FD 65 43 21
写入ID为0x21的0x1601,子指标0x02,数据为0x314365FD
ID | RTR | 功能码 | Index_L | Index_H | Sub index | data | data | data | data |
---|---|---|---|---|---|---|---|---|---|
600 + Serv NodeId | 0 | 40 | Index_L | Index_H | Sub index | 00 | 00 | 00 | 00 |
成功:
字节数 | ID | RTR | 功能码 | Index_L | Index_H | Sub index | data | data | data | data |
---|---|---|---|---|---|---|---|---|---|---|
一个字节 | 580 + ServNodeId | 0 | 4F | Index_L | Index_H | Sub index | D0 | - | - | - |
二个字节 | 580 + ServNodeId | 0 | 4B | Index_L | Index_H | Sub index | D0 | D1 | - | - |
三个字节 | 580 + ServNodeId | 0 | 47 | Index_L | Index_H | Sub index | D0 | D1 | D2 | - |
四个字节 | 580 + ServNodeId | 0 | 43 | Index_L | Index_H | Sub index | D0 | D1 | D2 | D3 |
失败
ID | RTR | 功能码 | Index_L | Index_H | Sub index | data[0…3] |
---|---|---|---|---|---|---|
580+ Serv NodeId | 0 | 8 | Index_L | Index_H | Sub index | SDO abort code error |
实例:DI:0x621 DLC 8 标准帧 数据帧 DATA:40 01 14 02 00 00 00 00
读取ID为0x21的0x1401,子指标0x02
成功:DI:0x5A1 DLC 8 标准帧 数据帧 DATA:60 01 14 02 FD 00 00 00
读取到的数值为0xFD
DI:0x621 DLC 8 标准帧 数据帧 DATA:40 01 16 02 00 00 00 00
成功:DI:0x5A1 DLC 8 标准帧 数据帧 DATA:60 01 14 02 FD 65 43 21
读取到的数据为0x214365FD
PDO默认有4个发送区四个接收区,配置PDO就是配置这4R4T对应的程序变量,以及这4R4T的工作模式。
方法一就是在对象字典中直接配好
方法二在程序中配置,使用SDO方式更改Mapping(0x1A00-0x1BFF和0x1600-0x17FF)里面的值
例:
605 2F 00 18 02 FF 00 00 00 --设置索引Index 1800,事件传输
605 2F 00 18 05 CB 00 00 00 --设置索引Index 1800,时间间隔200ms
605 2F 00 1A 00 00 00 00 00 --设置子索引禁用
605 23 00 1A 01 10 00 30 40 --0x40300010,设置映射索引0x4030,子索引00,大小0x10(16位)
605 23 00 1A 02 20 00 10 20 --0x20100020,设置映射索引0x2010,子索引00,大小0x20(32位)
605 2F 00 1A 00 02 00 00 00 --设置映射数量,用多少设多少,这里用了01–02
注意:定义映射时,先设置子索引禁用;再设置相应映射;然后设置映射数量
实例:
ID=0x700+ID,rtr=1(远程帧) 请求节点状态
DI=0x00,rtr=0,DLC=2,Data[0]=0x01(Start),Data[1]=指定设备地址,全部则为0. 设置节点start
DI=0x00,rtr=0,DLC=2,Data[0]=0x02(Stop),Data[1]=指定设备地址,全部则为0. 设置节点Stop
DI=0x180+ID,RTR=0 PDOTX1遥控帧
DI=0x280+ID,RTR=0 PDOTX2遥控帧
0x08 | 8位 |
0x10 | 16位 |
0x20 | 32位 |
就是配置0x1400-0x19FF和0x1800-0x19FF,0x1400-0x19FF区管单片机接收,这个目前没有看出效果。用的话和发送类似,可参考0x1800-0x19FF区。
上一章已经介绍过0x1800-0x19FF各个参数,配置时主要配置0x01,0x05。0x03一般是固定的。
例:
605 2F 00 18 02 FF 00 00 00 --设置索引Index 1800,事件传输
605 2F 00 18 05 CB 00 00 00 --设置索引Index 1800,时间间隔200ms
605 2F 00 1A 00 00 00 00 00 --设置子索引禁用
605 23 00 1A 01 10 00 30 40 --0x40300010,设置映射索引0x4030,子索引00,大小0x10(16位)
605 23 00 1A 02 20 00 10 20 --0x20100020,设置映射索引0x2010,子索引00,大小0x20(32位)
605 2F 00 1A 00 02 00 00 00 --设置映射数量,用多少设多少,这里用了01–02
以上配置完成后可直接发送数据
0x180+id 数据(无功能码,全部有效) 12 34 56 78 9A 00 则0x4030=0x3412 0x2010=0x009A7856
1、在对象字典中勾选有回访
2、定义回调函数
typedef UNS32 (*ODCallback_t)(CO_Data* d, const indextable *, UNS8 bSubindex);
例:
/* index 0x2000 : Mapped variable value */
UNS32 index2000_callback(CO_Data* d, const indextable *table, UNS8 bSubindex)
{
printf("value=%x\r\n",*(uint8_t*)table->pSubindex[bSubindex].pObject);
return OD_SUCCESSFUL;
}
3、回调函数接入
第一种是直接在字典中赋值:
/* index 0x2000 : Mapped variable value */
subindex slave_objdict_Index2000[] =
{
{ RW, uint8, sizeof (UNS8), (void*)&value, index2000_callback }
};
第二种是调用UNS32 RegisterSetODentryCallBack(CO_Data* d, UNS16 wIndex, UNS8 bSubindex, ODCallback_t Callback)
这个函数:
RegisterSetODentryCallBack(&slave_objdict_Data,0x2000, 0, index2000_callback);
特殊的回调函数:主机检测从机丢失
void _heartbeatError(CO_Data* d, UNS8 heartbeatID){heartbeat_error(d,heartbeatID);}
//d为CO_Data ,heartbeatID为从机ID值。
//函数_heartbeatError位于lifegrd文件,原函数为void _heartbeatError(CO_Data* d, UNS8 heartbeatID){(void)d;(void)heartbeatID;}
//改为void _heartbeatError(CO_Data* d, UNS8 heartbeatID){heartbeat_error(d,heartbeatID);}
//讲自己写的函数所在头文件加入lifegrd,引入声明
//书写函数体
void heartbeat_error(CO_Data* d, UNS8 heartbeatID)
{
printf("heartbeatID %d\r\n",heartbeatID);
}
//声明
void heartbeat_error(CO_Data* d, UNS8 heartbeatID);
//关于index 0x1016
//前16位代表从机ID号,后16位为检查时间 20000+(0x02<<16) ID 0x02超时时间20000ms=20s
//起始时间为接收到ID 0x700+ID +data data=0x00,即Boot up状态起始
参考CANOpen-memento文档
CAN帧ID 11位
COB-ID十一位
Node-ID:节点, ID 号后七位
ID号的10-7高四位,后文统称功能码为ID号后七位为0,只有高四位
就是反馈CANopen系统状态,也就是心跳帧
功能码=0x700,ID=0x700+Node-ID 长度DLC=1
Data : 00 节点上线报文(boot-up)
04 h 为停止状态 stop
05 h 为操作状态 operational
7F h 为预操作状态 pre-operational
ID固定为0x80 DLC=0; 仅对PDO有效
远程帧,DLC恒定为0,ID结合功能码直接影响从机,当从机PDO Map有发送类型>=FC的,将会相应。
0x180+Node-ID PDO1发送
0x200+Node-ID PDO1接收
0x280+Node-ID PDO2发送
0x300+Node-ID PDO2接收
0x380+Node-ID PDO3发送
0x400+Node-ID PDO3接收
0x480+Node-ID PDO4发送
0x500+Node-ID PDO4接收
地址同PDO遥控帧,帧格式要求为数据帧,即DLC和Data有意义,这个在配置好PDO map之后,右单片机发出的这种帧格式和接收到的帧格式中,Data全部为有效数据。
0x180+Node-ID PDO1发送
0x200+Node-ID PDO1接收
0x280+Node-ID PDO2发送
0x300+Node-ID PDO2接收
0x380+Node-ID PDO3发送
0x400+Node-ID PDO3接收
0x480+Node-ID PDO4发送
0x500+Node-ID PDO4接收
NMT Module Control
ID 恒定 0x00
DLC=1时广播 DLC=2时可广播可指定
Data[0]:
0x01 operational 模式 操作模式(正常工作模式)
0x02 stop 模式 (静默)
0x80 pre-operational模式 (等待)
0x81 reset-application 模式 (复位)
0x82 reset-communication模式 (复位)
Data[1]: Node-ID 网络中其他设备的状态变为Data[0]指定的状态
例:DLC=2 Data: 0x02 0x55 节点0x55变为停止状态
无论单片机发送还是其他发送到单片机,即无论主机还是从机,无论发送还是读取。
发送发送指令或发送读取指令:ID=0x600+Node-ID
发送指令返回或读取指令返回:ID=0x580+Node-ID
参考 SDO使用及例程
1、配置过滤器必须使过滤器组工作在初始化模式,配置完成后进入正常模式
CAN1->FMR|=1<<0; //过滤器组0工作在初始化模式
//CAN1->FMR|=1<
/*...配置*/
CAN1->FMR&=0<<0; //过滤器组0进入正常模式
//CAN1->FMR&=0<
2、配置某个过滤器时也要先关闭该过滤器的激活
CAN1->FA1R&=~(1<<0); //过滤器0不激活
//CAN1->FA1R&=~(1<
CAN1->FA1R|=1<<0; //激活过滤器0
//CAN1->FA1R|=1<
3、设置过滤器位宽,有32位和16位两种模式,这个影响到后面过滤屏蔽的ID长度
CAN1->FS1R|=1<<0; //过滤器0位宽为32位
CAN1->FS1R&=~(1<<0); //过滤器0位宽为16位
CAN1->FS1R|=1<<n; //过滤器n位宽为32位
CAN1->FS1R&=~(1<<n); //过滤器n位宽为16位
STID标准ID
EXID扩展ID
IDE
RTR
32位模式
CAN_FxR1[31:21] | CAN_FxR1[20:3] | CAN_FxR1[2] | CAN_FxR1[1] | CAN_FxR1[0] |
---|---|---|---|---|
CAN_FxR2[31:21] | CAN_FxR2[20:3] | CAN_FxR2[2] | CAN_FxR2[1] | CAN_FxR2[0] |
STID[10:0] | EXID[17:0] | IDE | RTR | 0 |
16位模式
CAN_FxR1[15:5] | CAN_FxR1[4] | CAN_FxR1[3] | CAN_FxR1[2:0] |
---|---|---|---|
CAN_FxR1[31:21] | CAN_FxR1[20] | CAN_FxR1[19] | CAN_FxR1[18:16] |
CAN_FxR2[15:5] | CAN_FxR2[4] | CAN_FxR2[3] | CAN_FxR2[2:0] |
CAN_FxR2[31:21] | CAN_FxR2[20] | CAN_FxR2[19] | CAN_FxR2[18:16] |
STID[10:0] | IDE | RTR | EXID[17:15] |
4、设置过滤器工作模式,标识符屏蔽位模式和标识符列表模式
CAN1->FM1R&=~(1<<0); //过滤器0工作在标识符屏蔽位模式
CAN1->FM1R|=1<<0; //过滤器0工作在标识符列表模式
CAN1->FM1R&=~(1<<n); //过滤器n工作在标识符屏蔽位模式
CAN1->FM1R|=1<<n; //过滤器n工作在标识符列表模式
5、过滤器关联到FIFOn n=0,1
CAN1->FFA1R&=~(1<<0); //过滤器0关联到FIFO0CAN1->FFA1R|=1<<0; //过滤器0关联到FIFO1CAN1->FFA1R&=~(1<FFA1R|=1<
6、设置过滤规则
CAN1->sFilterRegister[0].FR1=0x21<<21;//32位ID CAN1->sFilterRegister[0].FR2=(0x7F<<21);//32位MASK
标识符模式
寄存器的每位对应于所期望的标识符的相应位的电平。
0: 期望相应位为显性位;
1: 期望相应位为隐性位。
屏蔽位模式
寄存器的每位指示是否对应的标识符寄存器位一定要与期望的标识符的相应位一致。
0: 不关心,该位不用于比较;
1: 必须匹配,到来的标识符位必须与滤波器对应的标识符寄存器位相一致
标识符模式就是FR2为1的位标识出需要验证的位,FR1说明被标识出的位必须是什么,也就是一个过滤器一个规则,此处的逻辑类似于&运算
CAN1->sFilterRegister[0].FR1=0x21<<21;//32位ID CAN1->sFilterRegister[0].FR2=(0x7F<<21);//32位MASK//表示检查SDID标准帧ID的低七位,低七位必须是0x21的才能通过
屏蔽位模式 则是规定特定的一个ID,ID的所有位都要符合设置值
CAN1->sFilterRegister[0].FR1=0x21<<21;//32位IDCAN1->sFilterRegister[0].FR2=0xFF<<21)//32位MASK//只有0x00和0xFF可以通过过滤
程序例程
void CAN_Set_Filter(u8 Addr){ Addr=Addr&0x7F; //过滤器初始化 CAN1->FMR|=1<<0; //过滤器组工作在初始化模式/*过滤规则1 CAN ID的低七位必须与Addr一致,其他位不关心 */ CAN1->FA1R&=~(1<<0); //过滤器0不激活 CAN1->FS1R|=1<<0; //过滤器位宽为32位. CAN1->FM1R|=0<<0; //过滤器0工作在标识符屏蔽位模式 //CAN1->FM1R|=1<<0; //过滤器0工作在标识符列表模式 CAN1->FFA1R|=0<<0; //过滤器0关联到FIFO0 CAN1->sFilterRegister[0].FR1=Addr<<21;//32位ID CAN1->sFilterRegister[0].FR2=(0x7F<<21);//32位MASK CAN1->FA1R|=1<<0; //激活过滤器0/*过滤规则2 CAN ID的第7位必须为1,即只有0x80可以通过*/ CAN1->FA1R&=~(1<<1); //过滤器1不激活 CAN1->FS1R|=1<<1; //过滤器1位宽为32位. CAN1->FM1R|=0<<1; //过滤器1工作在标识符屏蔽位模式 //CAN1->FM1R|=1<<1; //过滤器1工作在标识符列表模式 CAN1->FFA1R|=0<<1; //过滤器1关联到FIFO0 CAN1->sFilterRegister[1].FR1=0x80<<21;//32位ID CAN1->sFilterRegister[1].FR2=(0x80<<21);//32位MASK CAN1->FA1R|=1<<1; //激活过滤器1/*过滤规则3 CAN ID的低八位必须为0x00*/ CAN1->FA1R&=~(1<<2); //过滤器2不激活 CAN1->FS1R|=1<<2; //过滤器2位宽为32位. CAN1->FM1R|=0<<2; //过滤器2工作在标识符屏蔽位模式 //CAN1->FM1R|=1<<2; //过滤器2工作在标识符列表模式 CAN1->FFA1R|=0<<2; //过滤器2关联到FIFO0 CAN1->sFilterRegister[2].FR1=0x00<<21;//32位ID CAN1->sFilterRegister[2].FR2=(0xFF<<21);//32位MASK CAN1->FA1R|=1<<2; //激活过滤器2 CAN1->FMR&=0<<0; //过滤器组进入正常模式}