CAN-open程序解读

整理文档很辛苦,赏杯茶钱您下走!

免费阅读已结束,点击下载阅读编辑剩下 ...

阅读已结束,您可以下载文档离线阅读编辑

资源描述

刘怡师兄CANopen协议从节点实现程序简单说明作者邱家齐CAN从节点初始化:(从节点决定了其扮演的角色,也决定了其初始化,主从节点是不同的)voidCAN0_Init(void)功能:11位ID,设置速率500K,接收发送对应的ID/messageobject/数据长度发送与接收的配置只有3个寄存器不同CAN0IF1MC/CAN0IF1A2/CAN0IF1CR接收SYNC设置11-bitidentifier:0x0080(低11位00010000000)接收NMT0x0000接收RPDO10x0200+NodeId=0x0202(此从节点NodeId=0x02一个从节点可有多个RPDO)接收rSDO0x0600+NodeId=0x0602接收NODEGUARD0x0702发送EMCYmessage0x0082发送TPDO10x0182发送tSDO0x0582(从节点需要tSDO发送应答)发送NODEStatus0x0702CAN发送函数canSend(Message*m)m中包括了cob_id/datas[],根据cob_id找到对应的messageobject,配置好CAN0IF2CM/CAN0IF2CR就好了数字可编程电位器输出函数OutPut(U8VC)100tap数字可编程电位器DPP,UD/INC/CS控制信号根据VC与Pre_VC(前一次输出)的差值,控制UD,INC使输出VC设置从节点初始态setState(&ObjDict_Data,Initialisation)从节点状态改变主要是由主节点(上位机)发NMT报文,从节点收到NMT报文,则调用proceedNMTstateChange(d,m)进行状态转换从节点之后进入while()大循环,用到switchcase,根据从节点当前状态nodeState,做相应处理,不断查询nodeState其中_initialisation()和_preOperational()为空函数,因为我们在前面已经做好了初始化工作关键是主节点控制从节点使其进入Operational后,调用AD()采样函数,如果工作电压电流超过阈值(2000V/500mA),调用canSend(&m)发送报警给主节点(上位机),调用OutPut(0),然后关电源,如果输出正常,根据控制命令(自动or手动)与当前状态(自动or手动)进行相应处理,调用OutPut(Write_Analogue_Output_8_Bit[0])输出主节点(上位机)设定的值,很明显Write_Outputs_8_Bit[]与Write_Analogue_Output_8_Bit[]是主节点发给从节点的指令与数据(主节点可以通过tSDO与tPDO),从节点可以通过tPDO将AD采样数据Read_Analogue_Input_16_Bit[]实时传输给主节点(只能是tPDO)再来看看CAN中断服务函数INTERRUPT(CAN0_ISR,INTERRUPT_CAN0)接收到报文m(包括datas[]/cob_id/len/rtr),然后调用canDispatch(&ObjDict_Data,&m)处理接收报文,下面分析canDispatch()函数canDispatch()根据cob_id的功能码进行跳转,有4个分支proceedSYNC(d):SYNC同步报文是主节点发的,SYNC主要服务于tPDO(tPDO有6种传输模式,其中3种需要SYNC信号)如果从节点处于operational状态(此态允许tPDO),则在proceedSYNC(d)里调用A_sendPDOevent(d,1/*isSyncEvent*/)该函数一次处理所有tPDO(因为return是在while之后),首先从OD中取出tPDO传输类型n(0—255(241—251保留)),第一个if0n241且++d-PDO_status[pdoNum].transmit_type_parameter==n则ResetcountofSYNC,调用buildPDO(d,pdoNum,&pdo),然后把pdo存起来d-PDO_status[pdoNum].last_message=pdo,并调用canSend(&pdo)这里其实有对SYNC进行计数,收到n个SYNC报文,才发送tPDO第二个elseifn==TRANS_RTR_SYNC调用buildPDO(d,pdoNum,&d-PDO_status[pdoNum].last_message)如果建立成功,则将其d-PDO_status[pdoNum].transmit_type_parameter设置为PDO_RTR_SYNC_READY,这样从节点接收到主节点发的PDO请求后,调用proceedPDO(d,m)就会发送tPDO,如果建立没有成功,则状态设置~PDO_RTR_SYNC_READY第三个elseifn==0||((n==254||255)&&(n!=PDO_INHIBITED))调用buildPDO(d,pdoNum,&pdo),如果newandoldPDO相同,处理下一个tPDO,如果不相同,如果EventTimerDuration与InhibitTimerDuration不为零,则等待时间到,然后调用canSend(&pdo)第四个elsetPDO与SYNC无关,处理下一个tPDOproceedPDO(d,m)该函数处理接收rPDO首先判断m报文是否是请求报文(远程帧)如果不是请求报文(数据帧),在从节点的接收rPDO中查找,如果其pwCobId与m报文的cob_id相同,就找到了numPdo,也可以得到pMappingCount,(下图中为2)从子索引1开始,得到Size=8,调用CopyBits()将m-datas[]写入tmp[],然后调用setODentry()将tmp[]中数据写入对应对象的子索引中,然后处理子索引2……如果是请求报文(远程帧),在从节点的发送tPDO中查找,如果其pwCobId与m报文的cob_id相同,就找到了numPdo,然后得到其传输类型pTransmissionType第一个ifpTransmissionType==TRANS_RTR,则buildPDO(d,numPdo,&pdo),然后canSend(&pdo)第二个elseifpTransmissionType==TRANS_RTR_SYNC,则判断d-PDO_status[numPdo].transmit_type_parameter是否PDO_RTR_SYNC_READY,如果ready,调用canSend(&d-PDO_status[numPdo].last_message)如果没有ready,发送currentdata(即buildPDO(d,numPdo,&pdo),canSend(&pdo))第三个elseif传输类型TRANS_EVENT_PROFILE||TRANS_EVENT_SPECIFIC,则调用PDOEventTimerAlarm(d,numPdo)(还需要仔细阅读)第四个else返回0xFFproceedSDO(d,m)目前只考虑从节点的响应(从节点接收主节点读写对象字典请求,并给出应答)该函数首先判断本节点是SDO_SERVER还是SDO_CLIENT,本例中为从节点,故为SDO_SERVER,(程序中考虑了多个SDO的情况,但是通常只有1个rSDO和1个tSDO,主从节点都如此)查找方法与上面提到的方法类似,找到后判断m报文数据长度是否为8,不为8则认为出错,然后程序根据m-datas[0]的高3位(高3位决定SDO传送类型(见协议))进行跳转,采用的是switchcasecase1:启动域下载(站在主节点的角度,是主节点请求写从节点的对象字典)从m中提取index与subIndex,然后调用getSDOlineOnUse()查看是否SDOtransfert已经开始过了(即不是SDO_RESET),如果没有,那很好,调用getSDOfreeLine(d,whoami,&line)找1个空闲线程,然后initSDOline(d,line,nodeId,index,subIndex,SDO_DOWNLOAD_IN_PROGRESS),如果SDO加速传送(最多传送4字节)调用SDOtoLine()将m中数据写入d-transfers[line]中(写入线程中),然后调用SDOlineToObjdict(d,line)将数据写入对象字典中index和subIndex对应的对象中,之后释放该线程如果SDO分段传送如果指明了数据长度(长度对应上图中4字节计数器),调用setSDOlineRestBytes(d,nodeId,nbBytes)然后从节点调用sendSDO(d,whoami,sdo)给出SDO应答case0:启动域分段下载(分段下载的第一帧已经在case1中处理了)从节点是SDO_SERVER,调用err=getSDOlineOnUse(d,nodeId,whoami,&line),因为case0中SDO分段传送调用过initSDOline(d,line,nodeId,index,subIndex,SDO_DOWNLOAD_IN_PROGRESS),状态变为SDO_DOWNLOAD_IN_PROGRESS了,所以err=0,从transfers[line]中取出index和subindex,之后进行toggle测试,m中数据第1字节是命令字节,包括此帧数据长度,调用SDOtoLine(d,line,nbBytes,(*m).datas+1)将数据写入线程中,然后为SDO应答做好准备,调用sendSDO(d,whoami,sdo)给出应答,如果是最后的segment,调用SDOlineToObjdict(d,line),将数据写入对象字典中index和subIndex对应的对象中,释放该线程,如果不是最后的segment,返回case2:启动域上传(主节点读从节点对象字典请求)从节点从m中提取index和subindex,调用getSDOlineOnUse()查看是否SDOtransfert已经开始过了(即不是SDO_RESET),如果Nolineonuse那很好,找一个空闲的线程line,然后initSDOline(d,line,nodeId,index,subIndex,SDO_UPLOAD_IN_PROGRESS),objdictToSDOline(d,line),getSDOlineRestBytes(),如果要传送的nbBytes4,则进行分段传送,准备应答sdo,sdo数据的后4个字节代表nbBytes,然后调用sendSDO(d,whoami,sdo)给出应答,如果nbBytes=4,就不用分段传送了,是加速传送,对于加速传送,这里还要调用lineToSDO(d,line,nbBytes,sdo.body.datas+4),将line中数据存入sdo.body.datas[]中,sdo数据的后4个字节就是主节点需要的数据,调用sendSDO(d,whoami,sdo)应答,之后释放该SDO线程case3:域分段上传首先调用getSDOlineOnUse(d,nodeId,whoami,&line)判断SDO传送是否已经开始过(即是否收到启动域上传报文且传送模式为分段传送(nbBytes4)),如果是,则从d-transfers[line]提取index和subindex,(case2中初始化过的),进行Toggletest,调用getSDOlineRestBytes(d,line,&nbBytes)得到还需传送的nbBytes,如果nbBytes7,则不是最后的segment,先调用lineToSDO(d,line,7,sdo.body.datas+1),然后调用sendSDO(d,whoami,sdo)作为应答如果n

1 / 5
下载文档,编辑使用

©2015-2020 m.777doc.com 三七文档.

备案号:鲁ICP备2024069028号-1 客服联系 QQ:2149211541

×
保存成功