做了一个IO设备,需要实现CANopen从站,移植的是canfestival。在移植的过程中,CANOpen系列教程对我帮助很大,感谢博主。基于参考以上博客移植好的代码,对源码部分做一些分析。
用对象编辑器生成的对象字典,是一对.c/.h的文件。里面把对象字典编辑器里选择的对象和设置的参数都生成好了。自己定义的一些对象,旁边都自动生成了注释,索引和子所引。如下代码所示:
UNS8 Write_Outputs_8_Bit[] = /* Mapped at index 0x6200, subindex 0x01 - 0x01 */
{
0x0 /* 0 */
};
对象RPDO1 parameter内容如下:
/* index 0x1400 : Receive PDO 1 Parameter. */
UNS8 A46Slave_highestSubIndex_obj1400 = 6; /* number of subindex - 1*/
UNS32 A46Slave_obj1400_COB_ID_used_by_PDO = 0x200; /* 512 */
UNS8 A46Slave_obj1400_Transmission_Type = 0xFF; /* 255 */
UNS16 A46Slave_obj1400_Inhibit_Time = 0x0; /* 0 */
UNS8 A46Slave_obj1400_Compatibility_Entry = 0x0; /* 0 */
UNS16 A46Slave_obj1400_Event_Timer = 0x0; /* 0 */
UNS8 A46Slave_obj1400_SYNC_start_value = 0x0; /* 0 */
subindex A46Slave_Index1400[] =
{
{ RO, uint8, sizeof (UNS8), (void*)&A46Slave_highestSubIndex_obj1400, NULL },
{ RW, uint32, sizeof (UNS32), (void*)&A46Slave_obj1400_COB_ID_used_by_PDO, NULL },
{ RW, uint8, sizeof (UNS8), (void*)&A46Slave_obj1400_Transmission_Type, NULL },
{ RW, uint16, sizeof (UNS16), (void*)&A46Slave_obj1400_Inhibit_Time, NULL },
{ RW, uint8, sizeof (UNS8), (void*)&A46Slave_obj1400_Compatibility_Entry, NULL },
{ RW, uint16, sizeof (UNS16), (void*)&A46Slave_obj1400_Event_Timer, NULL },
{ RW, uint8, sizeof (UNS8), (void*)&A46Slave_obj1400_SYNC_start_value, NULL }
};
RPDO1 mapping内容如下:
/* index 0x1600 : Receive PDO 1 Mapping. */
UNS8 A46Slave_highestSubIndex_obj1600 = 1; /* number of subindex - 1*/
UNS32 A46Slave_obj1600[] =
{
0x62000108 /* 1644167432 */
};
subindex A46Slave_Index1600[] =
{
{ RW, uint8, sizeof (UNS8), (void*)&A46Slave_highestSubIndex_obj1600, NULL },
{ RW, uint32, sizeof (UNS32), (void*)&A46Slave_obj1600[0], NULL }
};
实际对象内容如下:
/* index 0x6200 : Mapped variable Write Outputs 8 Bit */
UNS8 A46Slave_highestSubIndex_obj6200 = 1; /* number of subindex - 1*/
subindex A46Slave_Index6200[] =
{
{ RO, uint8, sizeof (UNS8), (void*)&A46Slave_highestSubIndex_obj6200, NULL },
{ RW, uint8, sizeof (UNS8), (void*)&Write_Outputs_8_Bit[0], NULL }
};
以上三部分联系在一起的,通过RPDO1 parameter对象,找到RPDO1 mapping对象,再根据RPDO1 mapping对象的内容,找到实际的对象地址(真正的数据存放处,姑且这么叫)。那么,源码中是怎么一步一步来找的呢?
首先,有一个indextable类型的字典数组,里面列出了所有的对象。
/**************************************************************************/
/* Declaration of pointed variables */
/**************************************************************************/
const indextable A46Slave_objdict[] =
{
{ (subindex*)A46Slave_Index1000,sizeof(A46Slave_Index1000)/sizeof(A46Slave_Index1000[0]), 0x1000},
{ (subindex*)A46Slave_Index1001,sizeof(A46Slave_Index1001)/sizeof(A46Slave_Index1001[0]), 0x1001},
{ (subindex*)A46Slave_Index1002,sizeof(A46Slave_Index1002)/sizeof(A46Slave_Index1002[0]), 0x1002},
{ (subindex*)A46Slave_Index1003,sizeof(A46Slave_Index1003)/sizeof(A46Slave_Index1003[0]), 0x1003},
{ (subindex*)A46Slave_Index1005,sizeof(A46Slave_Index1005)/sizeof(A46Slave_Index1005[0]), 0x1005},
{ (subindex*)A46Slave_Index1006,sizeof(A46Slave_Index1006)/sizeof(A46Slave_Index1006[0]), 0x1006},
{ (subindex*)A46Slave_Index1007,sizeof(A46Slave_Index1007)/sizeof(A46Slave_Index1007[0]), 0x1007},
{ (subindex*)A46Slave_Index1008,sizeof(A46Slave_Index1008)/sizeof(A46Slave_Index1008[0]), 0x1008},
{ (subindex*)A46Slave_Index1009,sizeof(A46Slave_Index1009)/sizeof(A46Slave_Index1009[0]), 0x1009},
{ (subindex*)A46Slave_Index100A,sizeof(A46Slave_Index100A)/sizeof(A46Slave_Index100A[0]), 0x100A},
{ (subindex*)A46Slave_Index100C,sizeof(A46Slave_Index100C)/sizeof(A46Slave_Index100C[0]), 0x100C},
{ (subindex*)A46Slave_Index100D,sizeof(A46Slave_Index100D)/sizeof(A46Slave_Index100D[0]), 0x100D},
{ (subindex*)A46Slave_Index1014,sizeof(A46Slave_Index1014)/sizeof(A46Slave_Index1014[0]), 0x1014},
{ (subindex*)A46Slave_Index1017,sizeof(A46Slave_Index1017)/sizeof(A46Slave_Index1017[0]), 0x1017},
{ (subindex*)A46Slave_Index1018,sizeof(A46Slave_Index1018)/sizeof(A46Slave_Index1018[0]), 0x1018},
{ (subindex*)A46Slave_Index1200,sizeof(A46Slave_Index1200)/sizeof(A46Slave_Index1200[0]), 0x1200},
{ (subindex*)A46Slave_Index1400,sizeof(A46Slave_Index1400)/sizeof(A46Slave_Index1400[0]), 0x1400},
{ (subindex*)A46Slave_Index1401,sizeof(A46Slave_Index1401)/sizeof(A46Slave_Index1401[0]), 0x1401},
{ (subindex*)A46Slave_Index1402,sizeof(A46Slave_Index1402)/sizeof(A46Slave_Index1402[0]), 0x1402},
{ (subindex*)A46Slave_Index1403,sizeof(A46Slave_Index1403)/sizeof(A46Slave_Index1403[0]), 0x1403},
{ (subindex*)A46Slave_Index1404,sizeof(A46Slave_Index1404)/sizeof(A46Slave_Index1404[0]), 0x1404},
{ (subindex*)A46Slave_Index1405,sizeof(A46Slave_Index1405)/sizeof(A46Slave_Index1405[0]), 0x1405},
{ (subindex*)A46Slave_Index1406,sizeof(A46Slave_Index1406)/sizeof(A46Slave_Index1406[0]), 0x1406},
{ (subindex*)A46Slave_Index1407,sizeof(A46Slave_Index1407)/sizeof(A46Slave_Index1407[0]), 0x1407},
{ (subindex*)A46Slave_Index1408,sizeof(A46Slave_Index1408)/sizeof(A46Slave_Index1408[0]), 0x1408},
{ (subindex*)A46Slave_Index1409,sizeof(A46Slave_Index1409)/sizeof(A46Slave_Index1409[0]), 0x1409},
{ (subindex*)A46Slave_Index1600,sizeof(A46Slave_Index1600)/sizeof(A46Slave_Index1600[0]), 0x1600},
{ (subindex*)A46Slave_Index1601,sizeof(A46Slave_Index1601)/sizeof(A46Slave_Index1601[0]), 0x1601},
{ (subindex*)A46Slave_Index1602,sizeof(A46Slave_Index1602)/sizeof(A46Slave_Index1602[0]), 0x1602},
{ (subindex*)A46Slave_Index1603,sizeof(A46Slave_Index1603)/sizeof(A46Slave_Index1603[0]), 0x1603},
{ (subindex*)A46Slave_Index1604,sizeof(A46Slave_Index1604)/sizeof(A46Slave_Index1604[0]), 0x1604},
{ (subindex*)A46Slave_Index1605,sizeof(A46Slave_Index1605)/sizeof(A46Slave_Index1605[0]), 0x1605},
{ (subindex*)A46Slave_Index1606,sizeof(A46Slave_Index1606)/sizeof(A46Slave_Index1606[0]), 0x1606},
{ (subindex*)A46Slave_Index1607,sizeof(A46Slave_Index1607)/sizeof(A46Slave_Index1607[0]), 0x1607},
{ (subindex*)A46Slave_Index1608,sizeof(A46Slave_Index1608)/sizeof(A46Slave_Index1608[0]), 0x1608},
{ (subindex*)A46Slave_Index1609,sizeof(A46Slave_Index1609)/sizeof(A46Slave_Index1609[0]), 0x1609},
{ (subindex*)A46Slave_Index1800,sizeof(A46Slave_Index1800)/sizeof(A46Slave_Index1800[0]), 0x1800},
{ (subindex*)A46Slave_Index1801,sizeof(A46Slave_Index1801)/sizeof(A46Slave_Index1801[0]), 0x1801},
{ (subindex*)A46Slave_Index1802,sizeof(A46Slave_Index1802)/sizeof(A46Slave_Index1802[0]), 0x1802},
{ (subindex*)A46Slave_Index1803,sizeof(A46Slave_Index1803)/sizeof(A46Slave_Index1803[0]), 0x1803},
{ (subindex*)A46Slave_Index1804,sizeof(A46Slave_Index1804)/sizeof(A46Slave_Index1804[0]), 0x1804},
{ (subindex*)A46Slave_Index1805,sizeof(A46Slave_Index1805)/sizeof(A46Slave_Index1805[0]), 0x1805},
{ (subindex*)A46Slave_Index1A00,sizeof(A46Slave_Index1A00)/sizeof(A46Slave_Index1A00[0]), 0x1A00},
{ (subindex*)A46Slave_Index1A01,sizeof(A46Slave_Index1A01)/sizeof(A46Slave_Index1A01[0]), 0x1A01},
{ (subindex*)A46Slave_Index1A02,sizeof(A46Slave_Index1A02)/sizeof(A46Slave_Index1A02[0]), 0x1A02},
{ (subindex*)A46Slave_Index1A03,sizeof(A46Slave_Index1A03)/sizeof(A46Slave_Index1A03[0]), 0x1A03},
{ (subindex*)A46Slave_Index1A04,sizeof(A46Slave_Index1A04)/sizeof(A46Slave_Index1A04[0]), 0x1A04},
{ (subindex*)A46Slave_Index1A05,sizeof(A46Slave_Index1A05)/sizeof(A46Slave_Index1A05[0]), 0x1A05},
{ (subindex*)A46Slave_Index2000,sizeof(A46Slave_Index2000)/sizeof(A46Slave_Index2000[0]), 0x2000},
{ (subindex*)A46Slave_Index2001,sizeof(A46Slave_Index2001)/sizeof(A46Slave_Index2001[0]), 0x2001},
{ (subindex*)A46Slave_Index2002,sizeof(A46Slave_Index2002)/sizeof(A46Slave_Index2002[0]), 0x2002},
{ (subindex*)A46Slave_Index2003,sizeof(A46Slave_Index2003)/sizeof(A46Slave_Index2003[0]), 0x2003},
{ (subindex*)A46Slave_Index2004,sizeof(A46Slave_Index2004)/sizeof(A46Slave_Index2004[0]), 0x2004},
{ (subindex*)A46Slave_Index2005,sizeof(A46Slave_Index2005)/sizeof(A46Slave_Index2005[0]), 0x2005},
{ (subindex*)A46Slave_Index2006,sizeof(A46Slave_Index2006)/sizeof(A46Slave_Index2006[0]), 0x2006},
{ (subindex*)A46Slave_Index2401,sizeof(A46Slave_Index2401)/sizeof(A46Slave_Index2401[0]), 0x2401},
{ (subindex*)A46Slave_Index6000,sizeof(A46Slave_Index6000)/sizeof(A46Slave_Index6000[0]), 0x6000},
{ (subindex*)A46Slave_Index6005,sizeof(A46Slave_Index6005)/sizeof(A46Slave_Index6005[0]), 0x6005},
{ (subindex*)A46Slave_Index6006,sizeof(A46Slave_Index6006)/sizeof(A46Slave_Index6006[0]), 0x6006},
{ (subindex*)A46Slave_Index6007,sizeof(A46Slave_Index6007)/sizeof(A46Slave_Index6007[0]), 0x6007},
{ (subindex*)A46Slave_Index6008,sizeof(A46Slave_Index6008)/sizeof(A46Slave_Index6008[0]), 0x6008},
{ (subindex*)A46Slave_Index6200,sizeof(A46Slave_Index6200)/sizeof(A46Slave_Index6200[0]), 0x6200},
{ (subindex*)A46Slave_Index6401,sizeof(A46Slave_Index6401)/sizeof(A46Slave_Index6401[0]), 0x6401},
};
又定义了两个quick_index类型的索引
const quick_index A46Slave_firstIndex = {
15, /* SDO_SVR */
0, /* SDO_CLT */
16, /* PDO_RCV */
26, /* PDO_RCV_MAP */
36, /* PDO_TRS */
42 /* PDO_TRS_MAP */
};
const quick_index A46Slave_lastIndex = {
15, /* SDO_SVR */
0, /* SDO_CLT */
25, /* PDO_RCV */
35, /* PDO_RCV_MAP */
41, /* PDO_TRS */
47 /* PDO_TRS_MAP */
};
RPDO属于上面代码注释中/*PDO_RCV*/这一项,一个记录了RPDO在A46Slave_objdict[]中的开始位置,一个记录了RPDO在A46Slave_objdict[]中的结束位置。我们以RPDO1为例,RPDO1 parameter对象正好是A46Slave_objdict[]的第16项。然后是/*PDO_RCV_MAP*/就对应了mapping参数,RPDO1 mapping正好是A46Slave_objdict[]中第26项。通过这两个索引就可以把parameter对象和mapping对象关联起来。那么,要怎么找到实际的对象地址呢?
当设备收到ID在PDO的ID范围内,且是非远程帧(远程帧是TPDO)时,就遍历A46Slave_objdict[]中下标为16-25之间的元素,如果找到某一个元素的参数中的ID与收到的ID相同,那么就记住这个元素相对16的偏移量,有了这个偏移量,就可以从mapping对应的元素找到mapping对象。然后得到mapping对象中内容,比如RPDO1中的内容为0x62000108,然后会把这个参数分解为索引0x6200和子索引0x01。根据索引值,调用扫描索引函数A46Slave_scanIndexOD,就可以得到实际对象在A46Slave_objdict[]中的位置。得到了这个实际对象,就可以找到里面子索引为1变量地址,RPDO1找的是(void*)&Write_Outputs_8_Bit[0],这是就找到了文章最开始的那个变量,就可以对它进行操作了。
const indextable * A46Slave_scanIndexOD (CO_Data *d, UNS16 wIndex, UNS32 * errorCode)
{
(void)d;
int i;
switch(wIndex){
case 0x1000: i = 0;break;
case 0x1001: i = 1;break;
case 0x1002: i = 2;break;
case 0x1003: i = 3;break;
case 0x1005: i = 4;break;
case 0x1006: i = 5;break;
case 0x1007: i = 6;break;
case 0x1008: i = 7;break;
case 0x1009: i = 8;break;
case 0x100A: i = 9;break;
case 0x100C: i = 10;break;
case 0x100D: i = 11;break;
case 0x1014: i = 12;break;
case 0x1017: i = 13;break;
case 0x1018: i = 14;break;
case 0x1200: i = 15;break;
case 0x1400: i = 16;break;
case 0x1401: i = 17;break;
case 0x1402: i = 18;break;
case 0x1403: i = 19;break;
case 0x1404: i = 20;break;
case 0x1405: i = 21;break;
case 0x1406: i = 22;break;
case 0x1407: i = 23;break;
case 0x1408: i = 24;break;
case 0x1409: i = 25;break;
case 0x1600: i = 26;break;
case 0x1601: i = 27;break;
case 0x1602: i = 28;break;
case 0x1603: i = 29;break;
case 0x1604: i = 30;break;
case 0x1605: i = 31;break;
case 0x1606: i = 32;break;
case 0x1607: i = 33;break;
case 0x1608: i = 34;break;
case 0x1609: i = 35;break;
case 0x1800: i = 36;break;
case 0x1801: i = 37;break;
case 0x1802: i = 38;break;
case 0x1803: i = 39;break;
case 0x1804: i = 40;break;
case 0x1805: i = 41;break;
case 0x1A00: i = 42;break;
case 0x1A01: i = 43;break;
case 0x1A02: i = 44;break;
case 0x1A03: i = 45;break;
case 0x1A04: i = 46;break;
case 0x1A05: i = 47;break;
case 0x2000: i = 48;break;
case 0x2001: i = 49;break;
case 0x2002: i = 50;break;
case 0x2003: i = 51;break;
case 0x2004: i = 52;break;
case 0x2005: i = 53;break;
case 0x2006: i = 54;break;
case 0x2401: i = 55;break;
case 0x6000: i = 56;break;
case 0x6005: i = 57;break;
case 0x6006: i = 58;break;
case 0x6007: i = 59;break;
case 0x6008: i = 60;break;
case 0x6200: i = 61;break;
case 0x6401: i = 62;break;
default:
*errorCode = OD_NO_SUCH_OBJECT;
return NULL;
}
*errorCode = OD_SUCCESSFUL;
return &A46Slave_objdict[i];
}
至此,根据收到的ID找到了MCU里的变量,整个过程如下,还以RPDO1的索引为例0x1400-->0x1600-->0x6200-->Write_Outputs_8_Bit。
以上过程其实是pdo.c里proceedPDO这个函数的逻辑,后续也会对这个函数进行分析。
这个由对象编辑器生成的字典生成以后我们就不要去改动它了。可以看到,这些对象里的回调函数都是空的,即便在对象编辑器里相应的对象的“有回访”打上勾,也不会自动生成回调函数。需要用到回调函数的地方,我们可以在其他的地方写好,然后注册到相应的对象就可以。