中国地质大学(北京) 实 验 报 告
课程名称:嵌入式系统
实验名称:嵌入式Linux系统串口和以太网编程
姓 名:
学 号: 班 级:
指导教师:曾 卫 华
评 分:
实验时间: 2013.5
实验题目:嵌入式Linux系统串口和以太网编程
一、实验目的
1、熟悉Linux环境
2、掌握嵌入式开发的基本流程,培养解决问题的能力
3、掌握Linux串口及以太网socket的应用程序开发
4、自学嵌入式Linux中多线程编程基础
二、实验内容
本次实验通过编写服务器(开发板)与客户端(虚拟机ubuntu)上的应用程序,来实现服务器与客户端之间信息的透明转发,构成类似于聊天的功能。该功能实现包括两个方面。
其一:服务器通过串口从终端(电脑键盘)上读取(read)数据,再通过网络(clifd)把读取到的数据发送到客户端,客户端在接收到数据后在显示器上打印出来; 其二:客户端把数据通过网络发送到主机上,主机接收到数据后通过串口写到电脑终端软件(SecureCRT)进行显示。实验流程图如下:
图一:总体框架图
三、基础知识
1、掌握linux串口和以太网socket应用程序开发方法:
串口配置:打开串口,获得串口的使用句柄fd 获取原先配置参数并进行保存设置波特率设置奇偶校验位设置设置停止位设置最少接受字符和等待时间设置数据位、无流控等相关参数激活最新配置结束时还原串口原先配置。 网络socket编程:
服务器端:创建一个socketbind(),给socket注册服务器端口地址listen(),
1 / 15
开始监听已经绑定的端口,创建监听队列accept(),返回一个新的socket,阻塞等待客户端client的连接 send(),recv()发送和接收数据close(),关闭服务器。客户端:创建一个socketconnect(),建立与服务器的连接send(),recv()发送和接收数据close(),关闭客户端。
2、掌握linux系统中线程的使用方法:
在linux中创建新的线程可以使用两种方式:pthread_create()函数和fork()函数。 pthread_create()使用:
int pthread_create(pthread_t *restrict thread, const pthread_attr_t *restrict attr, void *(*start_routine)(void*), void *restrict arg);
参数:thread输出线程id,attr 线程属性, 默认NULL,start_routine线程执行函数,arg线程执行参数 。函数成功时返回0, 否则返回错误码。 fork()函数的使用:
一个现有进程可以调用fork函数创建一个新进程。由fork创建的新进程被称为子进程(child proce)。fork函数被调用一次但返回两次。两次返回的唯一区别是子进程中返回0值而父进程中返回子进程ID。这样,对于程序,只要判断fork函数的返回值,就知道自己是处于父进程还是子进程中。
四、实现过程
1、4.4.3交叉编译工具的PATH已经生效,在任意目录下能够执行arm-linux-gcc –v
2、在开发板上进行内核以及根文件系统的移植,构建嵌入式linux系统
3、nfs网络文件系统的配置:
1.打开nfs1175.exe 2.配置板子ip,使其和电脑ip在同一个网段
3.挂载:在板子上运行 #mount -t nfs -o nolock 202.204.100.66:/f/aaa/LINUX/Share
/mnt/nfs
4.使用 cd /mnt/nfs/0522 进入nfs共享目录下,ls 显示共享目录下的文件;
4、仔细阅读提供的代码server.c和client.c Target为服务器,Ubuntu为客户端。修改和完善代码,在原代码的基础上实现新的功能
5、交叉编译server.c(# arm-linux-gcc -lpthread -o server server.c)
6、本地编译client.c(# gcc -o client client.c -lpthread)
7、在板子上运行server可执行文件: #chmod 777 server,运行server: #./server
8、在ubuntu上运行client程序:# ./client 202.204.100.67;
五、实验结果
客户端运行client与服务器端运行server建立通信
2 / 15
服务器端:
客户端:
按下$中断通信 服务器端:
客户端:
六、心得体会
在这一次的实验中,我们先建立硬件实验平台,又建立主机软件开发环境,接着为实验进行各项配置,最后完成了各个实验中的多种功能。很重要的一点,前期的硬件、软件准备
3 / 15
必须完整无误地实现,这是后续的实验能顺利进行的前提。
本实验中,已经给出了串口程序和socket编程的程序,需要我们自学多线程编程的知识并结合已经学过的串口和以太网编程来修改和完善代码,在原代码的基础上实现新的功能。即便如此,还是不太简单的。在这一次的实验中,我深深地感受到基础知识不扎实的严重后果:在实际的开发过程中会遇到各种各样的问题(如线程的使用,程序代码的容错处理,莫名问题的解决等)。究其原因还是对知识的掌握不够牢固,一知半解。所以,必须要详细了解其实现过程和原理才能够尽量避免开发过程中错误的发生,提高开发进度。 通过这一次的实验对于嵌入式linux的开发过程有了一个总体的把握和认识,我深知目前所掌握的嵌入式linux知识还只是皮毛,必须还要进一步的深入才能够有所提升。这也是我今后需要提高的方向。
最后感谢曾卫华老师的悉心教导!
附录(程序源码)
客户端代码: #include
#include
#include
#include
#include
#include
#include #include
#define SERVER_PORT 20000 // define the defualt connect port id
#define CLIENT_PORT ((20001+rand())%65536) // define the defualt client port as a random port
#define BUFFER_SIZE 256
#define LENGTH_OF_LISTEN_QUEUE 10 //length of listen queue in server
#define WELCOME_MESSAGE \"welcome to connect the server.\"
void usage(char* name)
{
}
struct sockaddr_in servaddr,cliaddr; int servfd,clifd,length = 0;
4 / 15
printf( \"usage: %s IpAddr\\n \" ,name); struct sockaddr_in servaddr,cliaddr;
socklen_t socklen = sizeof (servaddr);
char buf[BUFFER_SIZE],buf2[BUFFER_SIZE];
pthread_t tidp,tidp2; int pth;int runflag=0;
void *Thread1(void *arg) {
} void *Thread2(void *arg) {
} int main(int argc, char** argv)
{
if(argc
{
}
if((clifd = socket(AF_INET,SOCK_STREAM,0))
0 ) //tcp { printf( \" create socket error!\\n \" );
exit( 1 );
5 / 15
while(runflag){
memset(buf,0,BUFFER_SIZE); length = recv(clifd, buf,BUFFER_SIZE, 0);
if(strstr(buf,\"$\")>0){ runflag=0;printf(\"stop!\\n\");break;} if(length > 0) printf(\"from server: %s\",buf); } printf(\"Please input your words to Server: -- $ to stop\\n\"); while(runflag){
} memset(buf2,0,BUFFER_SIZE); scanf(\"%s\",buf2);send(clifd,buf2,strlen(buf2), 0); if(strstr(buf2,\"$\")>0){ runflag=0;printf(\"stop!\\n\");break;} usage(argv[0]);
exit( 1 );
}
srand(time(NULL)); // initialize random generator
bzero( & cliaddr, sizeof (cliaddr));
cliaddr.sin_family = AF_INET;
cliaddr.sin_port
= htons(CLIENT_PORT);
cliaddr.sin_addr.s_addr = htons(INADDR_ANY);
if(bind(clifd,(struct sockaddr* )&cliaddr,sizeof(cliaddr))
{ printf( \"bind to port %d failure!\\n \" ,CLIENT_PORT);
exit( 1 );
}//绑定的目的是让其端口是随机的,否则端口是自增1 //一般情况下client端不用绑定
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
inet_aton(argv[1], &servaddr.sin_addr);
servaddr.sin_port = htons(SERVER_PORT);
if(connect(clifd,( struct sockaddr *)&servaddr, socklen)
{
}
runflag=1;
6 / 15 printf( \"can\'t connect to %s!\\n\", argv[1]);
exit(1);
pth=pthread_create(&tidp,NULL,Thread1,NULL); if(pth!=0) {printf(\"error!\");return -1;}
pth=pthread_create(&tidp2,NULL,Thread2,NULL); if(pth!=0) {printf(\"error!\");return -1;} pthread_detach(tidp); pthread_detach(tidp2);
}
while(runflag){ ;}
close(clifd);
return 0;
服务器端代码: #include #include #include
#include
#include
#include #include
#include #include #include
#include #include #include #include #include #include #include #include #include
0 #define BLOCK_MODE 1 #define NONBLK_MODE 0 #define COM0
0 #define BLOCK_MODE 1 #define NONBLK_MODE 0 #define SERVER_PORT 20000
//define the defualt connect port id
#define LENGTH_OF_LISTEN_QUEUE 10 //length of listen queue in server
7 / 15
#define QUEUE 20 //length of listen queue in server
#define BUFFER_SIZE 256 #define WELCOME_MESSAGE \"welcome to connect the server.\"
static struct termios g_newtio,g_oldtio;
static int speed_arr[] = { B115200, B57600, B38400, B19200, B9600, B4800, B2400, B1200, B300, B115200, B57600, B38400, B19200, B9600, B4800, B2400, B1200, B300, };
static int name_arr[] = { 115200, 57600, 38400, 19200, 9600, 4800, 2400, 1200, 300,
115200, 57600, 38400, 19200, 9600, 4800, 2400, 1200, 300, };
/********************************************************************** * 函数名称: // 函数名称
* 功能描述: // 函数功能、性能等的描述
* 输入参数: // 输入参数说明,包括每个参数的作用、取值说明及参数间关系 * 输出参数: // 对输出参数的说明。 * 返 回 值:
-1:Failed
* 其它说明: // 其它说明
* 修改日期
版本号
修改人
修改内容 * ------------------------------ * 2007/10/10
V1.0
XXXX
XXXX ***********************************************************************/ int Init_COM(int Comm,int Baudrate,int Parity,int Stopbit,int Flagblock) {
int ret,i; char dev_buf[16];
if(Comm > 3)
8 / 15
{
}
memset(dev_buf,0x00,sizeof(dev_buf)); sprintf(dev_buf,\"/dev/ttyS%d\",Comm);
if(Flagblock) { } else { } if(ret
} printf(\"Open ttyS%d failed\\n\",Comm); return -1; ret = open(dev_buf, O_RDWR | O_NOCTTY | O_NONBLOCK); //以非阻塞方式打开; ret = open(dev_buf, O_RDWR | O_NOCTTY );
// 以默认阻塞方式打开; printf(\"Com%d not exist\\n\",Comm); return -1;
if( tcgetattr(ret, &g_oldtio)
}
for ( i= 0; i
if(Baudrate == name_arr[i]) {
cfsetispeed(&g_newtio,speed_arr[i]); cfsetospeed(&g_newtio,speed_arr[i]);
9 / 15 printf(\"Get Com Parameter Error.\\n\"); return -1;
} } break; if(i>=sizeof(speed_arr) / sizeof(int)) { printf(\"Unsupported Speed!\\n\");
} return -1;
switch (Parity) { // 设置奇偶校验位数
case \'n\': case \'N\':
g_newtio.c_cflag &= ~PARODD; //very important; zwh g_newtio.c_cflag &= ~PARENB; /* Clear parity enable */
break;
case \'o\':
case \'O\':
g_newtio.c_cflag |= PARENB;
/* Enable parity */
g_newtio.c_cflag |= PARODD;
/* 设置为奇效验*/
break;
case \'e\':
case \'E\':
g_newtio.c_cflag |= PARENB;
/* Enable parity */
g_newtio.c_cflag &= ~PARODD;
/* 转换为偶效验*/
break; default:
printf(\"Unsupported Parity\\n\");
} return -1;
/*stop bit */ switch(Stopbit)
10 / 15
{
} g_newtio.c_iflag = 0;
//输入无校验 g_newtio.c_oflag = 0;
//输出不处理 g_newtio.c_lflag = 0;
//RAW模式
g_newtio.c_cc[VTIME] = 1;
/* unit: 1/10 second.*/
g_newtio.c_cc[VMIN] = 1;
/* minimal characters for reading */ case 1: g_newtio.c_cflag &= ~CSTOPB; break; case 2: g_newtio.c_cflag |= CSTOPB; break; default: printf(\"Unsupported Stopbit!\\n\"); return -1;
g_newtio.c_iflag &= ~INPCK;
/* Disable parity checking */
}
11 / 15
g_newtio.c_cflag &= ~CRTSCTS;
//NO flow control g_newtio.c_cflag &= ~CSIZE; g_newtio.c_cflag |= CS8;
//databit=8 g_newtio.c_cflag |= CLOCAL;
//本地连接 g_newtio.c_cflag |= CREAD;
//允许接收数据
if( tcsetattr(ret, TCSANOW, &g_newtio) != 0 ) /*succe*/ {
}
tcflush (ret, TCIOFLUSH); //清空输入输出队列 printf(\"Set Com Parameter Error!\\n\"); return -1; return ret;
void RestoreComConfiguration(int fd,struct termios *ptios) {
} int fd;char buf[1024],buf2[1024]; int servfd,clifd;
struct sockaddr_in servaddr,cliaddr; int runflag=0; void *Thread1(void *arg) {
} void *Thread2(void *arg) {int length=0;
while(runflag) {char stdstr[1024]=\"from client:\";memset(buf2,0,BUFFER_SIZE);
length = recv(clifd, buf2,1024, 0);
if(length > 0)
{
}
12 / 15
if( tcsetattr(fd, TCSANOW, ptios) != 0 ) { }
/*succe*/ printf(\"Restore Com Parameter Error!\\n\"); printf(\"Please input your words to Client: -- $ to stop\\n\"); while(runflag) {
} memset(buf,0,BUFFER_SIZE); read(fd,buf,1024); send(clifd,buf,strlen(buf), 0); if(strstr(buf,\"$\")>0){ runflag=0;printf(\"stop!\\n\");break;} strcat(stdstr,buf2);strcat(stdstr,\"\\n\"); if(strstr(buf2,\"$\")>0){ runflag=0;printf(\"stop!\\n\");break;} write(fd,stdstr,strlen(stdstr)); } } void socket_init(void) { if((servfd = socket(AF_INET,SOCK_STREAM, 0 ))
{
} int main(int argc,char *argv[]) {
fd = Init_COM(COM0,115200,\'N\',1,BLOCK_MODE); socket_init(); pthread_t tidp,tidp2;
13 / 15
printf(\"create socket error!\\n\");
exit(1);
}
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port
= htons(SERVER_PORT);
servaddr.sin_addr.s_addr = htons(INADDR_ANY);
if(bind(servfd,(struct sockaddr *) &servaddr, sizeof(servaddr))
{
printf( \"bind to port %d failure!\\n\" ,SERVER_PORT);
exit(1);
}
if(listen(servfd,QUEUE)
} socklen_t length = sizeof(cliaddr); printf( \"call listen failure!\\n\" );
exit(1);
clifd = accept(servfd,( struct sockaddr *) &cliaddr, &length);
if(clifd = 0) RestoreComConfiguration(fd,&g_oldtio); runflag=1;
pth=pthread_create(&tidp,NULL,Thread1,NULL); if(pth!=0) {printf(\"error!\");return -1;} pth=pthread_create(&tidp2,NULL,Thread2,NULL); if(pth!=0) {printf(\"error!\");return -1;} pthread_detach(tidp); pthread_detach(tidp2); while(runflag){
;
}
}
close(clifd);close(fd);close(servfd); return 0;
14 / 15