CANOpen过程数据对象报文
数据长度被限制为1~8字节。这就要看一下PDO报文格式了,如下图PDO请求(左),PDO数据报文(右):
PDO数据内容只有它映射参数对象定义,生产者和消费者知道这个PDO的数据内容
首先在CANOpen字典中,需要定义两个PDO参数,通信参数和映射参数。
从站接收到PDO远程请求之后,通过对比PDO通信参数中所指定的COB-ID和接收消息中COB-ID来确定是否回应主站并确定这是第几个PDO通道,并通过映射参数得知该通道要回应什么样的值。
同样,主站也是根据通信参数和映射参数来确定是否接收并未将该值存放至何处。
通信参数:
映射参数:
在PDO通信参数中:
PDO可以指定禁止时间,避免由于高优先级信息的数据量太大,始终占用总线。而使其它优先级较低的数据无力竞争总线的问题。禁止时间单位100us。
PDO可以指定一个事件定时周期,当超过定时 时间后,一个PDO传输可以被触发(不需要触发位)。事件周期由16位无符号整数定义,单位1ms。
PDO传输模式
同步非周期模式:收到同步报文之后,启动pdo事件。
同步周期模式:收到指定个同步报文后,启动pdo事件。
同步远程模式:收到同步报文后准备好pdo报文,等待接收到远程请求帧时发送报文。
异步远程模式:收到远程请求帧时,构建pdo报文并发送。
异步特殊模式:pdo报文初始化或接收到远程请求帧时,启动pdo事件。特殊事件无视直接禁止时间,直接取消禁止然后构建pdo报文并发送。
/* PDO报文初始化 */
void PDOInit(CO_Data *d)
{
/* TPDO在字典中的索引 */
UNS16 pdoIndex = 0x1800;
/* 第一个TPDO通讯参数在字典中的下标 */
UNS16 offsetObjdict = d->firstIndex->PDO_TRS;
/* 最后一个TPDO通讯参数在字典中的下标 */
UNS16 lastIndex = d->lastIndex->PDO_TRS;
/* 如果字典中配置了TPDO通讯参数 */
if(offsetObjdict)
{
/* 遍历所有TPDO通讯参数 */
while(offsetObjdict <= lastIndex)
{
UNS32 errorCode;
ODCallback_t *CallbackList;
/* 按索引查字典,获取回调函数指针列表 */
scanIndexOD(d, pdoIndex, &errorCode, &CallbackList);
if(errorCode == OD_SUCCESSFUL && CallbackList)
{
/* 传输类型改变,回调函数 */
CallbackList[2] = &TPDO_Communication_Parameter_Callback;
/* pdo最小禁止时间改变,回调函数 */
CallbackList[3] = &TPDO_Communication_Parameter_Callback;
/* 两次pdo最大间隔时间改变,回调函数 */
CallbackList[5] = &TPDO_Communication_Parameter_Callback;
}
/* 索引值加一 */
pdoIndex++;
/* TPDO在字典中的下标加一 */
offsetObjdict++;
}
}
/* 启动异步特殊事件 */
_sendPDOevent(d, 0);
}
/* 启动所有需要出发的pdo事件 */
UNS8 _sendPDOevent(CO_Data *d, UNS8 isSyncEvent)
{
UNS8 pdoNum = 0x00;
UNS8 *pTransmissionType = NULL;
UNS8 status = state3;
/* 第一个TPDO通讯参数在字典中的下标 */
UNS16 offsetObjdict = d->firstIndex->PDO_TRS;
/* 第一个TPDO映射参数在字典中的下标 */
UNS16 offsetObjdictMap = d->firstIndex->PDO_TRS_MAP;
/* 最后一个TPDO通讯参数在字典中的下标 */
UNS16 lastIndex = d->lastIndex->PDO_TRS;
/* pdo报文使能 */
if(!d->CurrentCommunicationState.csPDO)
{
return 0;
}
/* 如果字典中配置了TPDO通讯参数 */
if(offsetObjdict)
{
Message pdo;
/* 清空pdo报文 */
memset(&pdo, 0, sizeof(pdo));
/* 遍历所有TPDO通讯参数 */
while(offsetObjdict <= lastIndex)
{
/* 检查pdo状态,进行相应操作 */
switch(status)
{
/* 状态3:构建pdo报文 */
case state3:
/* cob-id必须合法,pdo配置字典时为bit31不能为0 */
if(*(UNS32 *)d->objdict[offsetObjdict].pSubindex[1].pObject & 0x80000000)
{
MSG_WAR(0x3960, "Not a valid PDO ", 0x1800 + pdoNum);
status = state11;
break;
}
/* pdo发送方式 */
pTransmissionType = (UNS8 *)d->objdict[offsetObjdict].pSubindex[2].pObject;
MSG_WAR(0x3962, "Reading PDO at index : ", 0x1800 + pdoNum);
/* 同步周期模式:收到字典中指定次数(1-240)的同步报文后,启动pdo事件 */
if(isSyncEvent && (*pTransmissionType >= TRANS_SYNC_MIN) && (*pTransmissionType <= TRANS_SYNC_MAX) &&
(++d->PDO_status[pdoNum].transmit_type_parameter == *pTransmissionType))
{
/* 重置同步帧个数 */
d->PDO_status[pdoNum].transmit_type_parameter = 0;
MSG_WAR(0x3964, " PDO is on SYNCHRO. Trans type : ", *pTransmissionType);
/* 清空pdo报文 */
memset(&pdo, 0, sizeof(pdo));
/* 构建pdo报文 */
if(buildPDO(d, pdoNum, &pdo))
{
MSG_ERR(0x1906, " Couldn't build TPDO number : ", pdoNum);
status = state11;
break;
}
status = state5;
}
/* 远程同步模式:接收到同步报文后构建报文,收到远程请求报文将构建好的报文发出去,如果没有构建好要临时构建 */
else if(isSyncEvent && (*pTransmissionType == TRANS_RTR_SYNC))
{
/* 构建pdo报文,构建失败,将状态设置为未准备好 */
if(buildPDO(d, pdoNum, &d->PDO_status[pdoNum].last_message))
{
MSG_ERR(0x1966, " Couldn't build TPDO number : ", pdoNum);
d->PDO_status[pdoNum].transmit_type_parameter &= ~PDO_RTR_SYNC_READY;
}
/* 构建成功,将状态设置为准备好 */
else
{
/* 远程同步已经准备好 */
d->PDO_status[pdoNum].transmit_type_parameter |= PDO_RTR_SYNC_READY;
}
status = state11;
break;
}
else
{
/* 同步非周期模式:收到一个同步帧后,启动pdo事件 */
if((isSyncEvent && (*pTransmissionType == TRANS_SYNC_ACYCLIC)) ||
/* 非同步模式 */
(!isSyncEvent &&
(*pTransmissionType == TRANS_EVENT_PROFILE || *pTransmissionType == TRANS_EVENT_SPECIFIC) && //一些特定事件:报文特殊,直接解除禁止并启动pdo事件
!(d->PDO_status[pdoNum].transmit_type_parameter & PDO_INHIBITED))) //不在禁止时间
{
/* 启动指定通道的pdo事件 */
sendOnePDOevent(d, pdoNum);
status = state11;
}
else
{
MSG_WAR(0x306C, " PDO is not on EVENT or synchro or not at this SYNC. Trans type : ", *pTransmissionType);
status = state11;
}
}
break;
/* 状态5:发送pdo报文 */
case state5:
sendPdo(d, pdoNum, &pdo);
status = state11;
break;
/* 状态11:pdo操作结束继续下一个 */
case state11:
pdoNum++;
offsetObjdict++;
offsetObjdictMap++;
MSG_WAR(0x3970, "next pdo index : ", pdoNum);
status = state3;
break;
default:
MSG_ERR (0x1972, "Unknown state has been reached :", status);
return 0xFF;
}
}
}
return 0;
}
UNS8 proceedPDO(CO_Data *d, Message *m)
{
UNS8 numPdo;
UNS8 numMap;
UNS8 *pMappingCount = NULL;
UNS32 *pMappingParameter = NULL;
UNS8 *pTransmissionType = NULL;
UNS16 *pwCobId = NULL;
UNS8 Size;
UNS8 offset;
UNS8 status;
UNS32 objDict;
UNS16 offsetObjdict;
UNS16 lastIndex;
status = state2;
MSG_WAR(0x3935, "proceedPDO, cobID : ", (UNS16_LE(m->cob_id) & 0x7ff));
offset = 0x00;
numPdo = 0;
numMap = 0;
/* pdo响应 */
if((*m).rtr == NOT_A_REQUEST)
{
/* 第一个RPDO通讯参数在字典中的下标 */
offsetObjdict = d->firstIndex->PDO_RCV;
/* 最后一个RPDO通讯参数在字典中的下标 */
lastIndex = d->lastIndex->PDO_RCV;
/* 如果字典配置了接收pdo */
if(offsetObjdict)
{
/* 遍历所有接收pdo */
while(offsetObjdict <= lastIndex)
{
/* 判断状态 */
switch(status)
{
/* 状态2:检查cob_id */
case state2:
/* 从字典中取出cob_id */
pwCobId = d->objdict[offsetObjdict].pSubindex[1].pObject;
/* 如果接收pdo的cob_id吻合,则要拷贝数据 */
if(*pwCobId == UNS16_LE(m->cob_id))
{
status = state4;
MSG_WAR(0x3936, "cobId found at index ", 0x1400 + numPdo);
break;
}
/* 如果接收pdo的cob_id不吻合,则继续检查下一个接收pdo */
else
{
numPdo++;
offsetObjdict++;
status = state2;
break;
}
/* 状态4:将数据拷贝出来 */
case state4:
/* 第一个RPDO映射参数在字典中的下标 */
offsetObjdict = d->firstIndex->PDO_RCV_MAP;
/* 最后一个RPDO映射参数在字典中的下标 */
lastIndex = d->lastIndex->PDO_RCV_MAP;
/* RPDO映射参数的子索引数 */
pMappingCount = (UNS8 *)(d->objdict + offsetObjdict + numPdo)->pSubindex[0].pObject;
/* 遍历所有映射参数子索引 */
numMap = 0;
while(numMap < *pMappingCount)
{
UNS8 tmp[] = {0, 0, 0, 0, 0, 0, 0, 0};
UNS32 ByteSize;
/* pdo映射参数子索引值 */
pMappingParameter = (UNS32 *)(d->objdict + offsetObjdict + numPdo)->pSubindex[numMap + 1].pObject;
if(pMappingParameter == NULL)
{
MSG_ERR(0x1937, "Couldn't get mapping parameter : ", numMap + 1);
return 0xFF;
}
/* 从映射参数中取出位数 */
Size = (UNS8)(*pMappingParameter & (UNS32)0x000000FF);
if(Size && ((offset + Size) <= (m->len << 3)))
{
/* 将小端数据从源地址拷贝到目标地址 */
CopyBits(Size, (UNS8 *)&m->data[offset >> 3], offset % 8, 0, ((UNS8 *)tmp), 0, 0);
/* 将位数转换为字节数 */
ByteSize = (UNS32)(1 + ((Size - 1) >> 3));
/* 通过字典索引和子索引将数据拷贝进去 */
objDict = setODentry(d, (UNS16)((*pMappingParameter) >> 16), (UNS8)(((*pMappingParameter) >> 8) & 0xFF), tmp, &ByteSize, 0);
if(objDict != OD_SUCCESSFUL)
{
MSG_ERR(0x1938, "error accessing to the mapped var : ", numMap + 1);
MSG_WAR(0x2939, " Mapped at index : ", (*pMappingParameter) >> 16);
MSG_WAR(0x2940, " subindex : ", ((*pMappingParameter) >> 8) & 0xFF);
return 0xFF;
}
MSG_WAR(0x3942, "Variable updated by PDO cobid : ", UNS16_LE(m->cob_id));
MSG_WAR(0x3943, " Mapped at index : ", (*pMappingParameter) >> 16);
MSG_WAR(0x3944, " subindex : ", ((*pMappingParameter) >> 8) & 0xFF);
offset += Size;
}
/* 映射值进行偏移,开始拷贝第二个数据 */
numMap++;
}
/* RPDO事件定时事件 */
if(d->RxPDO_EventTimers)
{
TIMEVAL EventTimerDuration = *(UNS16 *)d->objdict[offsetObjdict].pSubindex[5].pObject;
/* RPDO事件定时事件 */
if(EventTimerDuration)
{
DelAlarm(d->RxPDO_EventTimers[numPdo]);
d->RxPDO_EventTimers[numPdo] = SetAlarm(d, numPdo, d->RxPDO_EventTimers_Handler, MS_TO_TIMEVAL(EventTimerDuration), 0);
}
}
return 0;
}
}
}
}
/* pdo请求 */
else if((*m).rtr == REQUEST)
{
MSG_WAR(0x3946, "Receive a PDO request cobId : ", UNS16_LE(m->cob_id));
status = state1;
/* 第一个TPDO通讯参数在字典中的下标 */
offsetObjdict = d->firstIndex->PDO_TRS;
/* 最后一个TPDO通讯参数在字典中的下标 */
lastIndex = d->lastIndex->PDO_TRS;
/* 如果配置了TPDO */
if(offsetObjdict)
{
/* 遍历所有的TPDO */
while(offsetObjdict <= lastIndex)
{
/* 判断状态 */
switch(status)
{
/* 如果Cob-Id和该TPDO能够匹配,则触发pdo事件 */
case state1:
pwCobId = (d->objdict + offsetObjdict)->pSubindex[1].pObject;
if(*pwCobId == UNS16_LE(m->cob_id))
{
status = state4;
break;
}
/* 如果和该TPDO不匹配,则继续检查下一个TPDO */
else
{
numPdo++;
offsetObjdict++;
}
status = state1;
break;
/* 判断传输类型并执行pdo事件 */
case state4:
/* 从TPDO中取出传输类型 */
pTransmissionType = (UNS8 *)d->objdict[offsetObjdict].pSubindex[2].pObject;
/* 如果传输模式是远程索取模式,则直接构建pdo并且发送 */
if(*pTransmissionType == TRANS_RTR)
{
status = state5;
break;
}
/* 如果传输模式是远程同步模式 */
else if(*pTransmissionType == TRANS_RTR_SYNC)
{
/* 如果远程同步报文已经准备好了,则发送出去 */
if(d->PDO_status[numPdo].transmit_type_parameter & PDO_RTR_SYNC_READY)
{
canSend(d->canHandle, &d->PDO_status[numPdo].last_message);
return 0;
}
/* 如果没有准备好,则临时创建报文并发送出去 */
else
{
MSG_ERR(0x1947, "Not ready RTR_SYNC TPDO send current data : ", UNS16_LE(m->cob_id));
status = state5;
}
break;
}
/* 如果传输类型为特殊事件,则解除pdo禁止,并且发送时间 */
else if((*pTransmissionType == TRANS_EVENT_PROFILE) || (*pTransmissionType == TRANS_EVENT_SPECIFIC))
{
d->PDO_status[numPdo].event_timer = DelAlarm(d->PDO_status[numPdo].event_timer);
d->PDO_status[numPdo].inhibit_timer = DelAlarm(d->PDO_status[numPdo].inhibit_timer);
d->PDO_status[numPdo].transmit_type_parameter &= ~PDO_INHIBITED;
/* pdo事件定时事件 */
PDOEventTimerAlarm(d, numPdo);
return 0;
}
else
{
MSG_WAR(0x2947, "PDO is not to send on request : ", UNS16_LE(m->cob_id));
return 0xFF;
}
/* 构建pdo报文,并且发送 */
case state5:
{
Message pdo;
/* 构建pdo报文 */
if(buildPDO(d, numPdo, &pdo))
{
MSG_ERR(0x1948, " Couldn't build TPDO number : ", numPdo);
return 0xFF;
}
canSend(d->canHandle, &pdo);
return 0;
}
}
}
}
}
return 0;
}