
基于CAN总线的modbus通信设计【研究分析】.doc
6页基于CAN总线的modbus通信设计 摘要:CAN总线是由德国BOSCH公司开发了的,其主要用于汽车计算机控制系统,而在工业控制领域较少适用,在工业领域用的最多的是RS485总线CAN总线较RS485总线具有网络各节点之间的数据通信实时性强,开发周期短,已形成国际标准的现场总线等优点因此CAN总线是比较有前途的现场总线之一,在工业控制领域代替RS485是有可能的 为了能简单地应用CAN总线代替RS485总线,如果采用比如CANopen等协议使得设备变得复杂化,那么有没有简单点的协议?很多做过工业设备的工程师都熟悉MODBUS协议,在RS485总线上跑的最多的协议就是MODBUS,如果我们在CAN总线上运行MODBUS协议,那么做过RS485的工程师就能很快地转移过来,减少了开发难度所以本文就以CAN总线上跑MODBUS协议为目的对数据网络层协议进行设计 概念:MODBUS 有主从概念,CAN总线支持多主,在这里我们把主机叫做客户端,从机叫做服务器在CAN总线上采用客户端服务器的概念 在进行协议介绍前先对CAN MODBUS进行简单的介绍 MODBUS:阅读过MODBUS协议的人都知道modbus有ASCII和RTU模式,其中RTU模式用的较多,一个数据包在网络上传输我们必须知道包的开始和结束,在RTU模式中数据包是以至少3.5 个字符的静默时间开始和结束的,如果信息结束前存在超过1.5 个字符以上的间隔时间则出错。
为了检测这些时间间隔,在程序上将变得很被动ASCII虽然有专用的开始结束标志符,但是ASCII需要把一个字节数据传换为两个字符发送所以其效率低所以还是得采用RTU模式,但得根据CAN总线的特点解决数据包开始结束标志的定义 CAN总线:CAN总线的数据传输是以帧为单位的,一个帧包含标识符、数据段CRC等,标识符表示该帧的发送优先级,数据段包含实际的数据,数据长度从1到8字节,CRC对该帧进行校验,因为帧中包含了数据校验功能,所以在CAN上跑modbus就不需要再对数据进行CRC校验在这里我们看到CAN总线是有优先级这个概念,但是没有出现像modbus这样有地址的概念,CAN总线上标识符段表示的是该帧的优先级,它面向的是数据,而modbus面向的是设备,每个数据包中都有地址,如果我们把地址放在CAN帧的数据段中那么所有设备都必须接收每一帧,然后对数据进行解析,如果网络负载比较大的是时候,这对设备不利CAN控制器一般都能对特定的标识符段进行过滤,那么我们为什么不能把标识符段作为设备的地址,然后设备对特定的标识符进行过滤,这样就减少了设备的负担但是如果标识符段作为目的地址,那么CAN总线上就不能有两个主机,应为CAN总线规定不能有两个相同标识符的帧同时发送。
既然标识符符不能作为目的地址,那么就实现不了设备对数据的过滤我的解决办法是把帧的标识符段分为两部分,一部分为发送方地址,另一部分为接收方地址,其中接收方地址在标识符的高端这样我们就能通过表标识符段知道这帧数据是谁发给谁的,对于接收方它通过设置过滤器只接收表识符高端数据为自己地址的帧,这样就解决了CAN总线中地址的概念这个地址还表示的设备的优先级,地址越小优先级就越高,接收方地址和发送方地址就决定这帧的发送优先级标识符段分为标准帧和扩展帧,标准帧的标识符为11bit,扩展帧的标识符为29bit,按照接收方地址加发送方地址的分配方法标准帧的地址范围为0~31,共计32个地址,如果用扩展帧的来分配地址范围为0~16383,共计16384个地址标识符接收方地址发送方地址 因为一帧最多能发送8字节数据,因此要发送超过8字节的数据就必须使用多帧,为了能适应多任务环境,我们在把帧的数据段的前两自己数据用作特殊用途:第一字节表示帧数据的ID,其bit7为1:表示服务器返回的数据帧,为0:表示客户端请求的数据帧;第二字节表示着一帧在这个数据包中的相对为位置,其bit7为1:表示这是最后一帧,因此我们可以根据这一帧就能计算出此次要传输的数据字节数,有这两字节的数据我们就能把一个数据包分成多个帧发送出去,然后在接收方根据这两字节的信息把数据包从新组装。
由第二字节信息我们可以计算出一次能传输的数据包的大小为128*6=768字节你也许会问,那个ID数据有什么用,为了支持多任务处理,一个客服端可能有多个任务同时把数据发送到同一个服务器,这个ID 就是区分不同任务的,客户端发送到的时候分配一个空闲的ID然后以此ID把数据发送出去服务器接受到同一个客服端发来的不同ID的数据包,那么服务器就根据ID的不同分别处理;回复客户端时把ID的最高位置1,然后发送回去,客服端收到发回复后根据ID把数据分发给对应的任务一个设备可以是客户端也可以是服务器,还可以包含两者,那么CAN接收服务程序就必须明确接收到的数据是自己收到的回复还是别人的请求,因此ID的最高位就是区分数据方向的在这里规定ID为0表示无效ID,故ID的取值范围为1到127因此能同时区分同一个设备的127个不同数据包在回头想想采用了这个结构后也就解决了MODBUS数据包的开始和结束的问题那么MODBUS就可也在CAN总线上运行了在CAN总线上已经包含了地址和CRC数据,因此MODBUS上就不在需要地址段和CRC段另外一个帧中包含6个数据,因此大多数MODBUS数据包都能够通过一帧发送出去基于LPC2292的CAN通信程序设计CAN驱动层:CAN驱动层主要做的工作是初始化设备,收发数据,下面是集CAN功能函数的结构体typedef struct{ uint8_t (*Init)(uint8_t port, uint16_t addr); void (*Open)( uint8_t port); void (*Close)( uint8_t port); uint8_t (*Wirte)(uint8_t port, Message *m); void (*SetBPS)(uint8_t port, uint32_t baud);}can_device;uint8_t (*Init)(uint8_t port, uint16_t addr);初始化设备,port:端口号,指示初始化哪个CAN,addr:设备在通信中的地址,地址的长度要根据标准帧或者扩展帧定义。
返回1:成功 0:失败void (*Open)( uint8_t port);打开设备void (*Close)( uint8_t port);关闭设备uint8_t (*Wirte)(uint8_t port, Message *m);向设备写数据void (*SetBPS)(uint8_t port, uint32_t baud);设置通信速率消息结构体如下typedef struct { uint32_t cob_id; /**< message's ID */ uint8_t rtr; /**< remote transmission request. (0 if not rtr message, 1 if rtr message) */ uint8_t len; /**< message's length (0 to 8) */ uint8_t data[8]; /**< message's datas */} Message;另外CAN接收中断服务程序序调用void CAN_msg_dispatch(can_uint8 port, Message *m),该函数对每一帧数据进行解析其工作流程如下: 接收到can数据解析数据的ID,ADDR在接收链表中是否存在相同ADDR和ID的数据包把数据存入对应的数据包是否接收完成?把接收完成的数据包发送给接收服务任务是是获取空闲包,并把数据存进数据包否等待新数据说明:在试验中用的操作系统为ucosII 2.86 , 把接收完成的数据包发送给接收服务任务是通过消息邮箱把数据包发送给接收服务任务的。
下面是数据包的结构体:typedef struct{ uint8_t port; uint16_t addr; uint8_t id; uint16_t size; uint16_t total_size; uint8_t message[CAN_MAX_LEN]; uint8_t ttl; void *next;}can_pkg;Port:设备端口Addr:数据来源地址Id:包IDSize:包的当前大小total_size:包的总大小message:消息缓冲区ttl:该包的生命周期next:指向下一个包total_size:是通过接收到最后一帧数据计算出来的,当size= total_size时表示接收完成message是一个数组,这里为了设计简单采用了数组,这个用户可以灵活设计成动态分配ttl:当接收一个包时,如果传输过程中丢失一个帧那么这个包就永远不能接收完成,因此设置了TTL没个周期扫描一次,TTL减一,当TTL等于0时,不管接收是否完成,都将把包移除接收链表Next:指向下一个包的指针,用作单向链表包在工作过程中分为三种状态 使用状态:包正在接收数据游离装态:数据接收完成,等待处理空闲状态:包处于空闲状态发送过程:发送过程建议采用对列中断发送,发送中断产生后通过查看队列中是否还有要发送的数据来循环发送,上层应用只需向队列写数据。
数据包ID采用递增循环方式发送,没发送一个新的数据包时,将在上次分配的ID上加1然后产看该ID是否实用,如果没有使用,将采用该ID,否者ID继续加1,直到找到空闲的ID,如果所有ID都在使用那么ID将返回0表示没有可用ID分配static void * id_queue[CAN_MAX_ID];ID分配队列是一个void*的数据,它指向的数据类型是用户定义的,它的作用是当接收服务任务接收到回应的数据时,通过该数据把接收的信息发送给使用该ID的任务 来自红叶 RedLeaf1技术l类别。
