logo资料库

嵌入式开发-基于嵌入式Linux的串口与Socket端口转发程序设计.pdf

第1页 / 共32页
第2页 / 共32页
第3页 / 共32页
第4页 / 共32页
第5页 / 共32页
第6页 / 共32页
第7页 / 共32页
第8页 / 共32页
资料共32页,剩余部分请下载后查看
目录 1 引言 ..............................................................................................................................................1 1.1 课题背景............................................................................................................................1 1.2 主要工作内容....................................................................................................................1 1.3 论文结构............................................................................................................................1 2 设计思路.......................................................................................................................................1 3 系统组成框图...............................................................................................................................2 4 服务端...........................................................................................................................................3 4.1 无线传感器网络................................................................................................................3 4.2 数据格式说明....................................................................................................................4 4.3 服务端实现........................................................................................................................6 5 客户端.........................................................................................................................................14 5.1 客户端思路......................................................................................................................14 5.2 客户端实现......................................................................................................................14 6 服务端移植.................................................................................................................................24 6.1 嵌入式 Linux ...................................................................................................................24 6.2 服务端移植至 liod270 开发平台....................................................................................25 7 测试结果.....................................................................................................................................25 7.1 测试环境..........................................................................................................................26 7.2 测试结果..........................................................................................................................26 8 总结 ............................................................................................................................................29 I
模式标识,据此开启线程。开启线程后,在串口回调函数的触发下,每次触发就发送一次串 口接收来的数据,为了使客户端与服务器端进行一些基本的错误处理能力,本文采用如下的 格式进行 Socket 数据传输: 表 2-1 本文 Socket 数据格式约定 本次发送的 Socket 数据长度 本次发送的 Socket 数据 其中本次发送的真实长度为数据长度加 1。 这样就可以根据实际发送长度和要发送长度进行判断,以处理 Socket 通信异常的一些 基本情况,例如客户端异常断开、服务端异常断开等。 根据无线传感器网络 EmBee 模块的相关材料中了解到,网络内和串口所传输的数据包 为如下格式: 表 2-2 基于 tinyos 操作系统的无线传感器网络数据包格式 Header Data (2 Bytes) 结束字段 (1 Bytes) 0x7E 校验 CCS MSG_HEADER (10 Bytes) MSG_DATA (长度不固定) 开始字段 (2 Bytes) 0x7E 0x42 其中开始字段和结束字段是固定不变的,故可将有开始字段和结束字段所包含的数据长 度是否大于 0 用来判断数据完整性。 客户端运行在 PC 上,将完成数据解析提取、显示、存储等工作,另外,通过一些简单 的协定,将系统扩展为可由客户端生成数据,由服务端从 Socket 接收后转发到串口,本文 的具体实现是客户端根据用户需求生成控制命令发给服务端,由服务端转发至串口,思路不 失一般性。 在互联网上,服务器处理的客户端可能往往不只一个,故将服务端设计成多客户端连接 模式。 3 系统组成框图 系统分为两部分,一是服务端程序,这是主要部分,运行在嵌入式 Linux 平台下,本文 测试平台为 liod 平台;二是客户端,这是与服务端配套的部分,运行在 PC 上,本文测试程 序为 MFC 对话框程序,运行在 Windows 下。 2
的 RAM、48KB Flash 和 128B 的 information flash。除此之外还提供了 8 个数模转换的控制 器端口、以及一系列的外设(例如 SPI、UART、数字 I/O 端口、看门狗等)。 EmBee使用 Chipcon CC2420 的 RF 进行无线通讯。CC2420 可由 TI MSP430 微控制器 通过 SPI 口、一系列的数字 I/O 线路和中断来控制。 EmBee 有 2 种可选择的天线,一种是内置于 PCB 板的内部天线,一种是通过 SMA 连 接器连接的外部天线。默认情况下 EmBee 使用内部天线,如果应用程序需要使用外部天线, 那么 SMA 连接器必须安装,外部天线就可以直接通过 SMA 连接上 EmBee 了。 EmBee 采用 DS1820 温度传感器,可提供 9 位温度读数。 4.2 数据格式说明 EmBee模块上位机和下位机通信时,所有的命令、应答均以如下数据包格式进行: 表 4-1 数据包格式 MSG_HEADER (10 Bytes) 开始字段 (2 Bytes) 0x7E 0x42 所 有 的 包 有 着 固 定 的 开 始 字 段 和 结 束 字 段 , 负 载 数 据 包 括 MSG_HEADER 和 MSG_DATA 两部分,其中 MSG_HEADER 是 10 个字节,主要包括以下 8 个字段: MSG_DATA (长度不固定) 校验 CCS 结束字段 (1 Bytes) 0x7E Header Data (2 Bytes) 字段名 length fcfhi fcflo dsn destpan addr type group 表 4-2 MSG_HEADER 各字段意义 字段类型 UINT8 UINT8 UINT8 UINT8 UINT16 UINT16 UINT8 UINT8 意义 MSG_DATA 的长度(字节数) FCF 的高字节,始终为 0 FCF 的低字节,始终为 0 Data Sequence Number,始终为 0 目标 PAN,始终为 0xff 目标地址,这里为广播地址,始终为 0xff 数据包类型 当前节点所在的组,始终为 0 其中,type 字段包含如下 4 种类型: 表 4-3 type 字段的可能值及意义 类型名 DataMSG OrderMSG DataACK OrderACK 值 0x11 0x12 0x13 0x14 意义 数据 命令 数据应答 命令应答 实际应用中,下位机将采集的数据,包括温度、湿度和灯的状态等信息封装在一个数据 包内,其数据部分格式为: Node ID Parent 各字段意义如下: 表 4-4 数据包 MSG_DATA 格式 MSG_DATA Epoch Voltage Temp Humi Light 表 4-5 数据包 MSG_DATA 各字段意义 字段名 Node ID 字段类型 UINT16 意义 节点号 4
SetDataRate 0x02 n SetRFPower 0x03 n=0~255 设置节点上传数据的速率,n 的单 位为毫秒 设置新的 Radio Frequency Power 4.3 服务端实现 4.3.1 服务端设计思路 程序的基本目的是为了实现串口/Socket 端口数据的互相转换,一方数据一到来,先存 在缓冲区,再立即发送到另一方,涉及到 Linux 下的串口编程、Socket 编程和 POSIX 多线 程编程。 Linux 系统中,设备作为一种特殊的文件管理,设备分为字符设备、块设备文件,串口 编程以往的经验是打开串口文件,如/dev/ttyS0,代表打开 COM1,进行相关数据结构各个 字段的设置,即可用基本的读写函数进行串口的读写。但这样有个缺点,往往人们在刚接触 Linux 串口编程时,经常会因为那些数据结构设置的问题而使效率变低。Windows 下的串口 编程模式,特别是 VC++,对串口编程方法提供了很多解决方法,值得借鉴的是封装。封装 简化了串口编程的工作过程,往往只需做些简单的调用,即可完成很多工作。鉴于此,在网 上找到一个 Linux 串口编程库 libcssl,该库提供了串口的基本接口,为使用者简化了设置的 问题,而且源码开放,利于移植,很适合本文应用。 Linux 下的 Socket 编程,有很多书籍都有介绍,流程也大同小异,但多是一个服务端对 一个客户端,而实际又是需要多客户端连接。本文提出一种多客户端的解决方案,利用 Linux 对多线程的支持,为每个客户端分配一个线程。将服务端程序阻塞在 accept 函数上,当函 数返回时,将连接的客户端标识符作为参数,新建一个处理客户端的线程,传入客户端标识。 使用 POSIX 线程库实现,具有良好的移植性。 程序多线程化了以后,就要充分考虑线程同步的问题,比如一个变量 data 是对所有线 程共享的,那么如果线程 1 有分别两次读取 data 的值,并进行比较,如果第一次读取后, 另外一个线程 2 将 data 的值改写,那么线程 1 再次读取 data 的值,比较的结果将是不等, 而线程 1 的本意是希望这两次读取到的值是相等的。这就需要慎重对共享数据的写入。本文 中串口回调函数写入缓冲区和 Socket 线程读取缓冲区所采用的是将缓冲区锁定后读写,既 在写的时候不能读,在读的时候不能写。为防止当客户端数目多的时候,单个缓冲区的读写 等待造成串口数据的丢失,本文采用多缓冲区的方式来避免这中情况发生。 由于在 Linux 下的 Socket 程序,服务端会因为客户端的异常退出而退出,这种情况若 出现在多客户端的情况下,一个客户端异常退出,导致服务端退出,进而使得其他客户端出 现异常。这是因为在客户端异常退出时,服务端会收到系统的 SIGPIPE 信号,而应用程序 默认对该信号的处理是程序退出。利用 Linux 下的信号屏蔽,在服务端屏蔽 SIGPIPE 信号, 即可避免上述情况发生。 4.3.2 服务端流程图 6
是接收模式,只需开启 sock_deal 线程;如果是控制模式,则多开启 sock_deal_ctrl 线程;如 果是其他模式,则客户端无效,不开启线程。处理完客户端连接后继续等待客户端连接。 其流程图如下: 图 4-2 wait_for_connect 线程流程图 wait_for_connect 实现多线程的关键代码如下: while(1) { memset(mod,0,60); if((clientfd = accept(servfd, (struct sockaddr *)&client_sockaddr, (socklen_t *)&sin_size)) == -1) //阻塞地等待客户端连接 continue; if(errno == EINTR) //多客户端后可能会出现这种错误,应屏蔽掉 perror("accept"); //其他错误则退出线程 pthread_exit(NULL); { } else { 个连接 recvlen = recv(clientfd, mod, 60, 0); //接收客户端模式的标识 if(-1 == recvlen)//如果接收出错,那么可能客户端退出,应继续等待下一 break; continue; { } else if(0 == recvlen)//如果收到的数据长度为 0,说明客户端此时异常 { } else { if(0 == strcmp(mod,"ctrlmod")) //控制模式, { pthread_create(&conn_th, NULL, sock_deal_ctrl, &clientfd); 8
serial 线程实现阻塞等待命令及串口事件的关键代码: while(1) { pthread_mutex_lock(&mm); pthread_cond_wait(&cc, &mm); //每次循环都阻塞在此,由 Socket 接收到命令 数据后解除阻塞 cssl_putdata(ser,recvdat+1,recvdat[0]); //Socket 收到命令数据,由串口转发 cssl_drain(ser); pthread_mutex_unlock(&mm); } 4.3.5 串口回调函数 串口回调函数负责将串口缓冲区的数据提取转存至数据缓冲区,供客户端线程读取。 客户端采取等待条件变量的方式阻塞地等待新数据,回调函数在更新完缓冲区后,解除 客户端线程对特定条件变量的阻塞,这样,Socket 每次发送的数据都是新的。 由于是 4 块缓冲区,回调函数每写一次,下次写的位置就后移一个,顺序循环地写入 4 块缓冲区,当当前写入的缓冲区正在读,说明其他缓冲区也已经写入过数据但还未被读取, 则稍做延时等待数据读取完。 其流程图如下: 串口回调函数关键代码如下: 图 4-4 串口回调函数流程图 int cur=b.cw; //取得上次改写的写入位置,为这次的写入位置 while(b.lock[cur]!=0) usleep(1000);//如果当前要写的位置有线程正在读,则等待 1 10 毫秒
return NULL; int sock; sock = *id; int cread=(b.cw-1+BUFBUN)%BUFNUM; //初始化读取位置为写入位置的前一个 printf("sock_deal: dealing with new clienct which was connected, id = %d\n",sock); if(pthread_detach(pthread_self())) //设置线程分离 for(;;) { pthread_mutex_lock(&m); while(b.cw==cread) usleep(1000); //如果缓冲区正在写,则等待 pthread_cond_wait(&c, &m); b.lock[cread]++; //读取标识加 1,非 0 说明有线程正在读取该缓冲区 if((b.data[cread][0]+1) != send(sock,b.data[cread],b.data[cread][0]+1,0)) { printf("send error\n"); //如果发送的实际长度和要求的不一样,说明客 户端异常退出,则客户端数目减 1,退出线程 sock_count--; break; } b.lock[cread]--; //读取结束,读取标识减 1 cread=(cread+1)%BUFNUM; //读取位置后移 pthread_mutex_unlock(&m); } b.lock[cread]--; 客户端异常退出时的处理, if(b.lock[cread]==0) pthread_mutex_unlock(&m); return NULL; cread=(cread+1)%BUFNUM; } 4.3.7 sock_deal_ctrl 线程 sock_deal_ctrl 线程也由 wait_for_connect 线程创建,在客户端模式选择控制模式时,服 务端为其开启 sock_deal_ctrl 线程和 sock_deal 线程, sock_deal_ctrl 线程将自己设置成分离后,阻塞在接收数据,接收到数据后,判断接收 是否成功。若不成功,则继续接收。若接收到的数据长度为 0,说明是客户端异常退出,则 退出线程。接收成功时判断接收的数据是否是客户端退出消息,是则退出线程,否则将接收 到的数据传到命令的缓冲区,然后解除 serial 的阻塞,继续接收数据。 其流程图如下: 12
据 } memcpy(recvdat,buf,60); pthread_cond_broadcast(&cc); pthread_mutex_unlock(&mm); } } return NULL; 5 客户端 5.1 客户端思路 客户端是同服务端配套的,服务端实现串口数据向 Socket 端口的转发,客户端要能接 收到转发的数据。服务端要实现 Socket 端口向串口转发数据控制节点行为,客户端要能发 送正确控制命令。 客户端分为两种模式:接收模式和控制模式。为方便以后的扩展,客户端还可以选择将 数据存储到数据库中。 利用 MFC 编程,很容易实现这些操作。设计对话框程序,连接时设置服务器地址,端 口号,选择客户端类型,是否写数据库,写入的数据库类型。连接成功后,Socket 接收到数 据后解析提取出节点号、父节点号和温度信息,用列表显示出来。 客户端通过可视化界面,实现对节点发送命令控制节点。程序根据用户的选择,自动生 成命令数据并计算校验,发送给服务端,由服务端转发。 客户端使用接收线程接收数据,接收到数据后发送更新消息,由更新消息处理函数负责 更新列表、存储数据等。 5.2 客户端实现 5.2.1 客户端主界面 客户端主界面包括一个菜单、一个节点设置按钮、一个列表。菜单包括退出、连接设置 和断开连接,节点设置按钮只在客户端为控制模式时才可用。界面如下所示: 14
分享到:
收藏