《通信嵌入式系统应用》实验与程序设计教程_第1页
《通信嵌入式系统应用》实验与程序设计教程_第2页
《通信嵌入式系统应用》实验与程序设计教程_第3页
《通信嵌入式系统应用》实验与程序设计教程_第4页
《通信嵌入式系统应用》实验与程序设计教程_第5页
已阅读5页,还剩48页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

《通信嵌入式系统应用》实验与程序设计教程戴虹编计算机与信息工程学院2023年6月目录上篇Linux操作系统程序设计实验一Linux下C语言编程入门实验二Linux编程初步实验三进程和进程间通信(1)实验四进程间通信(2)与多线程程序设计实验五网络程序设计下篇基于ICETEK-AM3359-A的通信嵌入式系统实验实验一LED控制实验实验二按键控制实验实验三驱动模块编写实验实验四GPIO控制实验实验五内存设备模块应用实验实验六LCD显示实验实验七触摸屏编程实验实验八Qt编程实验参考文献上篇Linux操作系统程序设计实验一Linux下C语言编程入门实验目的1.掌握在Linux下利用gcc编译器编译C语言程序。2.掌握利用Gdb调试器对C语言程序进行调试。2.掌握makefile文件编写方法,对C语言程序进行编译。二、实验环境1.PC机2.虚拟机软件:VMware3.Ubuntu10.04Linux操作系统(安装在虚拟机上)三、实验内容1.利用gcc编译器编译C语言程序(1)利用gcc编辑C语言程序启动虚拟机,输入密码:1,打开终端,输入:gedithello.c,从而打开了gedit编辑器:在gedit编辑器中输入hello.c的源代码(书p53):#include<stdio.h>main(){printf("helloworld!\n");}再回到终端界面。预处理(p53)输入:gcc–Ehello.c–ohello.i实验要求:在终端输入:ls,显示画面:在gedit中读取hello.i的内容,并显示画面:问:预处理的作用是什么?编译(p53)输入:gcc–Shello.i–ohello.s实验要求:(1)在终端输入:ls,显示画面:(2)在gedit中读取hello.s的内容,并显示画面:(3)问:编译的作用是什么?汇编(p54)输入:gcc–chello.s–ohello.o实验要求:(1)在终端输入:ls,显示画面:(2)问:汇编的作用是什么?链接和运行(p55)输入:gcchello.o–ohello实验要求:(1)在终端输入:ls,显示画面:(2)问:链接的作用是什么?(3)运行可执行文件:hello,(命令:./hello)显示结果画面:2.利用Gdb调试器对C语言程序进行调试(1)gdb基本操作在命令行上键入gdb并按回车键,显示出现的画面:下表列出了利用GDB调试时会用到的一些命令。命令 命令描述break 在代码里设置断点,这将使程序执行到这里时被挂起file 装入想要调试的司可执行文件kill 终止正在调试的程序list 列出产生执行文件的源代码的——部分make 使在退出gdb时就可以重新产生可执行文件next 执行—行源代码但小进入函数内部print 显示表达式的值quit 终止gdbrub 执行当前被调试的程序shell 使能不离开gdb就执行shell命令step 执行一行源代码而且进入函数内部watch 监视一个变量的值而不管它何时被改变(2)利用gdb调试C语言程序[1](p30-31)调试test.c在gedit中输入以下程序代码:#include<stdio.h>intmain(){ charstr1[16]="Hello,world"; charstr2[16]; inti=0; for(i=0;i<16;i++) str2[i]='*'; str2[15]=0; i=0; while(str1[i]!='\0') { str2[i]=str1[i]; i++; } printf("Thestring1is:%s.\n",str1); printf("Thestring2is:%s.\n",str2); return0; } 实验要求:(1)对test.c这个源程序进行编译,编译和执行命令如下:gcc–g–otesttest.c./test显示结果画面:(2)在GDB命令输入行输入以下命令来调试该程序:#gdbtest再输入:list,显示画面:(3)接下来要对程序中可能会出错的地方设置断点,设置断点的方法如下,断点设置在第16行,然后用run命令运行程序,显示结果画面:(gdb)break16(gdb)run(4)继续调试程序:1)现在我们来看看到底str2的内容与str1的有何不同,显示结果画面:(gdb)printstr1(gdb)pstr22)上面我们用print命令(也可以简写成p)来分别显示str1字符串和str2字符串的内容,可以发现:str1字符串在最后一个字母d后面是字符串结束符0(用\000表示的),而str2的d后面仍然是*,这是因为程序少复制了一个字符所致。下面用next单步执行命令查看程序输出结果,再用continue命令继续运行直到程序结束再退出GDB,显示结果画面:(gdb)next(gdb)n(gdb)continue(gdb)quit3)现在我们发现了程序的问题所在,将test.c的第11行和第15行修改一下:#include<stdio.h>intmain(){charstr1[]="Hello,world";charstr2[11]="";inti=0;do{str2[i]=str1[i];i++;}while(str1[i-1]!='\0');printf("Thestring1is:%s.\n",str1);printf("Thestring2is:%s.\n",str2);return0;}重新编译运行,显示结果画面。*[2]采用同样的方法调试p58)的test1.c程序,调试过程见p58-61),显示调试过程中的各个结果画面:

/*test.c*/#include<stdio.h>intsum(intm);intmain(){inti,n=0;sum(50);for(i=1;i<=50;i++)n+=i;printf("Thesumof1-50is%d\n",n);}intsum(intm){inti,n=0;for(i=1;i<=m;i++)n+=i;printf("Thesumof1-mis%d\n",n);}*参考资料:在保存退出后首先使用Gcc对test.c进行编译,注意一定要加上选项“-g”,这样编译出的可执行代码中才包含调试信息,否则之后Gdb无法载入该可执行文件。#gcc-gtest.c-otest虽然这段程序没有错误,但调试完全正确的程序可以更加了解Gdb的使用流程。接下来就启动Gdb进行调试。注意,Gdb进行调试的是可执行文件,而不是如“.c”的源代码,因此,需要先通过Gcc编译生成可执行文件才能用Gdb进行调试。#gdbtestGNUgdb6.8-debianCopyright(C)2008FreeSoftwareFoundation,Inc.LicenseGPLv3+:GNUGPLversion3orlater</licenses/gpl.html>Thisisfreesoftware:youarefreetochangeandredistributeit.ThereisNOWARRANTY,totheextentpermittedbylaw.Type"showcopying"and"showwarranty"fordetails.ThisGDBwasconfiguredas"i486-linux-gnu".(gdb)可以看出,在Gdb的启动画面中指出了Gdb的版本号、使用的库文件等信息,接下来就进入了由“(gdb)”开头的命令行界面了。<1>查看文件在Gdb中键入“l”(list)就可以查看所载入的文件,如下所示:(Gdb)l1#include<stdio.h>2intsum(intm);3intmain()4{5 inti,n=0;6 sum(50);7 for(i=1;i<=50;i++)8 {9 n+=i;10 }(Gdb)l11 printf("Thesumof1~50is%d\n",n);1213}14intsum(intm)15{16 inti,n=0;17 for(i=1;i<=m;i++)18 n+=i;19 printf("Thesumof1~mis=%d\n",n);20}可以看出,Gdb列出的源代码中明确地给出了对应的行号,这样可以大大地方便代码的定位。<2>设置断点设置断点是调试程序中是一个非常重要的手段,它可以使程序到一定位置暂停它的运行。因此,程序员在该位置处可以方便地查看变量的值、堆栈情况等,从而找出代码的症结所在。在Gdb中设置断点非常简单,只需在“b”后加入对应的行号即可(这是最常用的方式,另外还有其他方式设置断点)。如下所示:(Gdb)b6Breakpoint1at0x804846d:filetest.c,line6.要注意的是,在Gdb中利用行号设置断点是指代码运行到对应行之前将其停止,如上例中,代码运行到第6行之前暂停(并没有运行第6行)。<3>查看断点情况在设置完断点之后,用户可以键入“infob”来查看设置断点情况,在Gdb中可以设置多个断点。(Gdb)infobNumTypeDispEnbAddressWhat1breakpointkeepy0x0804846dinmainattest.c:6<4>运行代码接下来就可运行代码了,Gdb默认从首行开始运行代码,可键入“r”(run)即可(若想从程序中指定行开始运行,可在r后面加上行号)。(Gdb)rStartingprogram:/root/workplace/Gdb/testReadingsymbolsfromsharedobjectreadfromtargetmemory...done.LoadedsystemsuppliedDSOat0x5fb000Breakpoint1,main()attest.c:66sum(50);可以看到,程序运行到断点处就停止了。<5>查看变量值在程序停止运行之后,程序员所要做的工作是查看断点处的相关变量值。在Gdb中只需键入“p”+变量值即可,如下所示:(Gdb)pn$1=0(Gdb)pi$2=134518440在此处,为什么变量“i”的值为如此奇怪的一个数字呢?原因就在于程序是在断点设置的对应行之前停止的,那么在此时,并没有把“i”的数值赋为零,而只是一个随机的数字。但变量“n”是在第四行赋值的,故在此时已经为零。<6>单步运行单步运行可以使用命令“n”(next)或“s”(step),它们之间的区别在于:若有函数调用的时候,“s”会进入该函数而“n”不会进入该函数。因此,“s”就类似于VC等工具中的stepin”,“n”类似与VC等工具中的“stepover”。它们的使用如下所示:(gdb)nThesumof1-mis12757 for(i=1;i<=50;i++)(gdb)s8 n+=i;可见,使用“n”后,程序显示函数sum的运行结果并向下执行,而使用“s”后则进入到sum函数之中单步运行。<7>恢复程序运行在查看完所需变量及堆栈情况后,就可以使用命令“c”(continue)恢复程序的正常运行了。这时,它会把剩余还未执行的程序执行完,并显示剩余程序中的执行结果。以下是之前使用“n”命令恢复后的执行结果:(Gdb)cContinuing.Thesumof1-50is:1275Programexitedwithcode031.可以看出,程序在运行完后退出,之后程序处于“停止状态”。Makefile文件编写入门[1]Makefile基本结构Makefile是Make读入的惟一配置文件,因此本节的内容实际就是讲述Makefile的编写规则。在一个Makefile中通常包含如下内容:需要由make工具创建的目标体(target),通常是目标文件或可执行文件要创建的目标体所依赖的文件(dependency_file)创建每个目标体时需要运行的命令(command)它的格式为:target:dependency_filescommand例如,有两个文件分别为hello.c和hello.h,创建的目标体为hello.o,执行的命令为gcc编译指令:gcc–chello.c,那么,对应的Makefile就可以写为:#Thesimplestexamplehello.o:hello.chello.hgcc–chello.c–ohello.o接着就可以使用make了。使用make的格式为:maketarget,这样make就会自动读入Makefile(也可以是首字母小写makefile)并执行对应target的command语句,并会找到相应的依赖文件。如下:[davinci@davinci-desktop]#makehello.ogcc–chello.c–ohello.o[davinci@davinci-desktop]#lshello.chello.hhello.oMakefile可以看到,Makefile执行了“hello.o”对应的命令语句,并生成了“hello.o”目标体。注意:在Makefile中的每一个command前必须有“Tab”符,否则在运行make命令时会出错。实验要求:根据上述方法操作,最终生成了Makefile文件,用gedit打开Makefile文件,显示makefile文件内容画面:*[2]makefile变量和规则(p65-69)实验二Linux编程初步实验目的掌握不带缓存的文件I/O编程方法。掌握标准I/O编程方法(带缓存)。掌握嵌入式Linux串口读写编程方法。实验环境1.PC机2.虚拟机软件VMware3.UbuntuLinux操作系统三、实验内容1.不带缓存的文件I/O编程(1)open和close函数(p77)/*open1.c*/#include<unistd.h>#include<sys/types.h>#include<sys/stat.h>#include<fcntl.h>#include<stdlib.h>#include<stdio.h>intmain(void){intfd;/*调用open函数,以可读写的方式打开,注意选项可以用“|”符号连接*/if((fd=open("/tmp/hello.c",O_CREAT|O_TRUNC|O_WRONLY,0600))<0){perror("open:");exit(1);}else{printf("Openfile:hello.c%d\n",fd);}if(close(fd)<0){perror("close:");exit(1);}elseprintf("Closehello.c\n");exit(0);}实验要求:运行此程序,显示运行结果界面,open和close函数的功能分别是什么?read,write和lseek函数(p79-80)/*write1.c*/#include<unistd.h>#include<sys/types.h>#include<sys/stat.h>#include<fcntl.h>#include<stdlib.h>#include<stdio.h>#include<string.h>#defineMAXSIZEintmain(void){inti,fd,size,len;char*buf="Hello!I'mwritingtothisfile!";charbuf_r[10];len=strlen(buf);/*首先调用open函数,并指定相应的权限*/if((fd=open("/tmp/hello.c",O_CREAT|O_TRUNC|O_RDWR,0666))<0){perror("open:");exit(1);}elseprintf("openfile:hello.c%d\n",fd);/*调用write函数,将buf中的内容写入到打开的文件中*/if((size=write(fd,buf,len))<0){perror("write:");exit(1);}elseprintf("Write:%s\n",buf);/*调用lseek函数将文件指针移到文件起始,并读出文件中的10个字节*/lseek(fd,0,SEEK_SET);if((size=read(fd,buf_r,10))<0){perror("read:");exit(1);}elseprintf("readfromfile:%s\n",buf_r);if(close(fd)<0){perror("close:");exit(1);}elseprintf("Closehello.c\n");exit(0);}实验要求:1)运行此程序,显示运行结果界面。2)read,write和lseek函数的功能分别是什么?(3)fcntl和lock函数(p82-85)/*fcntl_write.c测试文件写入锁主函数部分*/#include<unistd.h>#include<sys/file.h>#include<sys/types.h>#include<sys/stat.h>#include<stdio.h>#include<stdlib.h>/*lock_set函数*/voidlock_set(intfd,inttype);intmain(void){intfd;/*首先打开文件*/fd=open("hello",O_RDWR|O_CREAT,0666);if(fd<0){perror("open");exit(1);}/*给文件上写入锁*/lock_set(fd,F_WRLCK);getchar();/*给文件解锁*/lock_set(fd,F_UNLCK);getchar();close(fd);exit(0);}/*lock_set函数*/voidlock_set(intfd,inttype){structflocklock;lock.l_whence=SEEK_SET;//赋值lock结构体lock.l_start=0;lock.l_len=0;while(1){lock.l_type=type;/*根据不同的type值给文件上锁或解锁*/if((fcntl(fd,F_SETLK,&lock))==0){if(lock.l_type==F_RDLCK)printf("readlocksetby%d\n",getpid());elseif(lock.l_type==F_WRLCK)printf("writelocksetby%d\n",getpid());elseif(lock.l_type==F_UNLCK)printf("releaselockby%d\n",getpid());return;}/*判断文件是否可以上锁*/fcntl(fd,F_GETLK,&lock);/*判断文件不能上锁的原因*/if(lock.l_type!=F_UNLCK){/*该文件已有写入锁*/if(lock.l_type==F_RDLCK)printf("readlockalreadysetby%d\n",lock.l_pid);/*该文件已有读取锁*/elseif(lock.l_type==F_WRLCK)printf("writelockalreadysetby%d\n",lock.l_pid);getchar();}}}实验要求:1)在2个终端上运行此程序,显示运行结果界面。2)fcntl和lock函数的功能是什么?3)同样在两个终端上运行以下读取锁程序,显示结果画面:/*fcntl_read.c测试文件读取锁主函数部分*/#include<unistd.h>#include<sys/file.h>#include<sys/types.h>#include<sys/stat.h>#include<stdio.h>#include<stdlib.h>voidlock_set(intfd,inttype);intmain(void){intfd;fd=open("hello",O_RDWR|O_CREAT,0666);if(fd<0){perror("open");exit(1);}/*给文件上读取锁*/lock_set(fd,F_RDLCK);getchar();/*给文件接锁*/lock_set(fd,F_UNLCK);getchar();close(fd);exit(0);}/*lock_set函数*/voidlock_set(intfd,inttype){structflocklock;lock.l_whence=SEEK_SET;//赋值lock结构体lock.l_start=0;lock.l_len=0;while(1){lock.l_type=type;/*根据不同的type值给文件上锁或解锁*/if((fcntl(fd,F_SETLK,&lock))==0){if(lock.l_type==F_RDLCK)printf("readlocksetby%d\n",getpid());elseif(lock.l_type==F_WRLCK)printf("writelocksetby%d\n",getpid());elseif(lock.l_type==F_UNLCK)printf("releaselockby%d\n",getpid());return;}/*判断文件是否可以上锁*/fcntl(fd,F_GETLK,&lock);/*判断文件不能上锁的原因*/if(lock.l_type!=F_UNLCK){/*该文件已有写入锁*/if(lock.l_type==F_RDLCK)printf("readlockalreadysetby%d\n",lock.l_pid);/*该文件已有读取锁*/elseif(lock.l_type==F_WRLCK)printf("writelockalreadysetby%d\n",lock.l_pid);getchar();}}}(4)select函数(p87-89)/*select1.c*/#include<fcntl.h>#include<stdio.h>#include<unistd.h>#include<stdlib.h>#include<time.h>intmain(void){intfds[2];charbuf[7];inti,rc,maxfd;fd_setinset1,inset2;structtimevaltv;/*首先按一定的权限打开hello1文件*/if((fds[0]=open("hello1",O_RDWR|O_CREAT,0666))<0)perror("openhello1");/*再按一定的权限打开hello2文件*/if((fds[1]=open("hello2",O_RDWR|O_CREAT,0666))<0)perror("openhello2");if((rc=write(fds[0],"Hello!\n",7)))printf("rc=%d\n",rc);lseek(fds[0],0,SEEK_SET);/*取出两个文件描述符中的较大者*/maxfd=fds[0]>fds[1]?fds[0]:fds[1];/*初始化读集合inset1,并在读集合中加入相应的描述集*/FD_ZERO(&inset1);FD_SET(fds[0],&inset1);/*初始化写集合inset2,并在写集合中加入相应的描述集*/FD_ZERO(&inset2);FD_SET(fds[1],&inset2);tv.tv_sec=2;tv.tv_usec=0;/*循环测试该文件描述符是否准备就绪,并调用select函数对相关文件描述符做对应操作*/while(FD_ISSET(fds[0],&inset1)||FD_ISSET(fds[1],&inset2)){if(select(maxfd+1,&inset1,&inset2,NULL,&tv)<0)perror("select");else{if(FD_ISSET(fds[0],&inset1)){rc=read(fds[0],buf,7);if(rc>0){buf[rc]='\0';printf("read:%s\n",buf);}elseperror("read");}if(FD_ISSET(fds[1],&inset2)){rc=write(fds[1],buf,7);if(rc>0){buf[rc]='\0';printf("rc=%d,write:%s\n",rc,buf);}elseperror("write");sleep(10);}}}exit(0);}实验要求:1)运行此程序,显示运行结果界面,体会I/o复用原理。2)select函数的功能是什么?2.标准I/O编程(1)文件的打开,关闭和写入程序(p102):/*fwrite1.c*/#include<stdio.h>intmain(){FILE*stream;chars[3]={'a','b','c'};/*首先使用fopen打开文件,之后再调用fwrite写入文件*/stream=fopen("what","w");i=fwrite(s,sizeof(char),nmemb,stream);printf("i=%d",i);fclose(stream);}实验要求:1)运行此程序,显示运行结果界面。2)fopen,fwrite和fclose函数的功能是什么?(2)输入输出函数(p104)/*gets1.c*/#include<stdio.h>main(){chars[80];/*同上例,把fgets的结果作为fputs的输入*/fputs(fgets(s,80,stdin),stdout);}实验要求:1)运行此程序,显示运行结果界面。2)此处fputs和fgets函数的功能是什么?3.嵌入式Linux串口读写编程(p94-98)/*读写串口的主函数receive.c*/#include<stdio.h>#include<string.h>#include<sys/types.h>#include<errno.h>#include<sys/stat.h>#include<fcntl.h>#include<unistd.h>#include<termios.h>#include<stdlib.h>/*读串口程序*//*设置串口函数*/intset_opt(intfd,intnSpeed,intnBits,charnEvent,intnStop);/*打开串口函数*/intopen_port(intfd,intcomport);intmain(void){intfd;intnread,i;charbuff[]="Hello\n";if((fd=open_port(fd,1))<0) //打开串口{perror("open_porterror");return;}if((i=set_opt(fd,115200,8,'N',1))<0) //设置串口{perror("set_opterror");return;}printf("fd=%d\n",fd);fd=3;nread=read(fd,buff,8);//读串口printf("nread=%d,%s\n",nread,buff);close(fd);return;}/*设置串口函数*/intset_opt(intfd,intnSpeed,intnBits,charnEvent,intnStop){structtermiosnewtio,oldtio;/*保存测试现有串口参数设置,在这里如果串口号等出错,会有相关的出错信息*/if(tcgetattr(fd,&oldtio)!=0){perror("SetupSerial1");return-1;}bzero(&newtio,sizeof(newtio));/*步骤一,设置字符大小*/newtio.c_cflag|=CLOCAL|CREAD;newtio.c_cflag&=~CSIZE;/*设置停止位*/switch(nBits){case7:newtio.c_cflag|=CS7;break;case8:newtio.c_cflag|=CS8;break;}/*设置奇偶校验位*/switch(nEvent){case'O'://奇数newtio.c_cflag|=PARENB;newtio.c_cflag|=PARODD;newtio.c_iflag|=(INPCK|ISTRIP);break;case'E'://偶数newtio.c_iflag|=(INPCK|ISTRIP);newtio.c_cflag|=PARENB;newtio.c_cflag&=~PARODD;break;case'N'://无奇偶校验位newtio.c_cflag&=~PARENB;break;}/*设置波特率*/switch(nSpeed){case2400:cfsetispeed(&newtio,B2400);cfsetospeed(&newtio,B2400);break;case4800:cfsetispeed(&newtio,B4800);cfsetospeed(&newtio,B4800);break;case9600:cfsetispeed(&newtio,B9600);cfsetospeed(&newtio,B9600);break;case115200:cfsetispeed(&newtio,B115200);cfsetospeed(&newtio,B115200);break;case460800:cfsetispeed(&newtio,B460800);cfsetospeed(&newtio,B460800);break;default:cfsetispeed(&newtio,B9600);cfsetospeed(&newtio,B9600);break;}/*设置停止位*/if(nStop==1)newtio.c_cflag&=~CSTOPB;elseif(nStop==2)newtio.c_cflag|=CSTOPB;/*设置等待时间和最小接收字符*/newtio.c_cc[VTIME]=0;newtio.c_cc[VMIN]=0;/*处理未接收字符*/tcflush(fd,TCIFLUSH);/*激活新配置*/if((tcsetattr(fd,TCSANOW,&newtio))!=0){perror("comseterror");return-1;}printf("setdone!\n");return0;}/*打开串口函数*/intopen_port(intfd,intcomport){char*dev[]={"/dev/ttyS0","/dev/ttyS1","/dev/ttyS2"};longvdisable;if(comport==1)//串口1{fd=open("/dev/ttyS0",O_RDWR|O_NOCTTY|O_NDELAY);if(-1==fd){perror("Can'tOpenSerialPort");return(-1);}}elseif(comport==2)//串口2{fd=open("/dev/ttyS1",O_RDWR|O_NOCTTY|O_NDELAY);if(-1==fd){perror("Can'tOpenSerialPort");return(-1);}}elseif(comport==3)//串口3{fd=open("/dev/ttyS2",O_RDWR|O_NOCTTY|O_NDELAY);if(-1==fd){perror("Can'tOpenSerialPort");return(-1);}}/*恢复串口为阻塞状态*/if(fcntl(fd,F_SETFL,0)<0)printf("fcntlfailed!\n");elseprintf("fcntl=%d\n",fcntl(fd,F_SETFL,0));/*测试是否为终端设备*/if(isatty(STDIN_FILENO)==0)printf("standardinputisnotaterminaldevice\n");elseprintf("isattysuccess!\n");printf("fd-open=%d\n",fd);returnfd;}实验要求:1)运行此程序,显示运行结果界面。2)问此处的set_opt()和open_port()子函数的功能是什么?3)读

/写串口分别用两个函数?实验三进程和进程间通信(1)实验目的掌握进程基础概念。掌握进程控制编程方法。掌握进程间通信方式之一:管道的编程方法。实验环境1.PC机2.虚拟机软件VMware3.UbuntuLinux操作系统三、实验内容1.进程基础编程实现:获得当前进程的进程号(PID)和父进程号(PPID),并显示。(p105)/*process.c*/#include<stdio.h>#include<unistd.h>#include<stdlib.h>intmain(){/*获得当前进程的进程PID和其父进程PPID*/printf("ThePIDofthisprocessis%d\n",getpid());printf("ThePPIDofthisprocessis%d\n",getppid());}实验要求:运行此程序,显示运行结果界面(不同计算机显示的进程号是不同的)。getpid和getppid函数的功能分别是什么?进程控制编程进程创建[1]fork函数--从已存在的进程中创建一个新进程(子进程),而原进程是父进程。这两个分别带回它们各自的返回值,其中父进程的返回值是子进程的进程号,而子进程则返回0。下面是fork函数的实例(p108)/*fork1.c*/#include<sys/types.h>#include<unistd.h>#include<stdio.h>#include<stdlib.h>intmain(void){pid_tresult;/*调用fork函数,其返回值为result*/result=fork();/*通过result的值来判断fork函数的返回情况,首先进行出错处理*/if(result==-1){perror("fork");exit;}/*返回值为0代表子进程*/elseif(result==0){printf("Thereturnvalueis%d\nInchildprocess!\nMyPIDis%d\n",result,getpid());}/*返回值大于0代表父进程*/else{printf("Thereturnvalueis%d\nInfatherprocess!\nMyPIDis%d\n",result,getpid());}}实验要求:运行此程序,显示运行结果界面。问:你的计算机父进程返回值是?子进程返回值是?[2]exec函数族:在一个进程中启动另一个程序。可以根据指定的文件名或目录名找到可执行文件,并用它来取代原调用进程的数据段、代码段和堆栈段,在执行完之后,原调用进程的内容除了进程号外,其他全部被新的进程替换了。例如:以下程序是使用文件名的方式来查找可执行文件,同时使用参数列表的方式。这里用的函数是execlp。(p111)/*execlp1.c*/#include<unistd.h>#include<stdio.h>#include<stdlib.h>intmain(){if(fork()==0){/*调用execlp函数,这里相当于调用了“ps-ef”命令*/if(execlp("ps","ps","-ef",NULL)<0)perror("execlperror!");}}实验要求:运行此程序,显示运行结果界面(部分)。问:本程序的功能是什么?直接在终端上输入命令:ps-ef,显示结果界面,问:3)与1)的结果是否一致?exit和_exit函数--终止进程。(p115)例1/*exit.c*/#include<stdio.h>#include<stdlib.h>intmain(){printf("Usingexit...\n");printf("Thisisthecontentinbuffer");exit(0);}例2/*_exit.c*/#include<stdio.h>#include<unistd.h>intmain(){printf("Using_exit...\n");printf("Thisisthecontentinbuffer");_exit(0);}实验要求:分别运行例1和例2程序,显示各自的结果界面。问:这两个函数的功能有何区别?[4]waitpid函数--使得父进程阻塞,直到有子进程退出。例:(p117)首先使用fork新建一子进程,然后让其子进程暂停5s(使用了sleep函数)。接下来对原有的父进程使用waitpid函数,并使用参数WNOHANG使该父进程不会阻塞。若有子进程退出,则waitpid返回子进程号;若没有子进程退出,则waitpid返回0,并且父进程每隔一秒循环判断一次。/*waitpid.c*/#include<sys/types.h>#include<sys/wait.h>#include<unistd.h>#include<stdio.h>#include<stdlib.h>intmain(){pid_tpc,pr;pc=fork();if(pc<0) printf("Errorfork.\n");elseif(pc==0) /*子进程*/{/*子进程暂停5s*/sleep(5);/*子进程正常退出*/exit(0);}else /*父进程*/{/*循环测试子进程是否退出*/do{/*调用waitpid,且父进程不阻塞*/pr=waitpid(pc,NULL,WNOHANG);/*若子进程还未退出,则父进程暂停1s*/if(pr==0){printf("Thechildprocesshasnotexited\n");sleep(1);}}while(pr==0);/*若发现子进程退出,打印出相应情况*/if(pr==pc) printf("Getchild%d\n",pr);else printf("someerroroccured.\n");}}实验要求:运行该程序,显示结果画面。问:经过几次循环后,捕捉到了子进程的退出信号?子进程号等于多少?守护进程守护进程,也就是通常所说的Daemon进程,是Linux中的后台服务进程。它是一个生存期较长的进程,通常独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件。守护进程常常在系统引导装入时启动,在系统关闭时终止。如果想让某个进程不因为用户或终端或其他的变化而受到影响,那么就必须把这个进程变成一个守护进程。可见,守护进程是非常重要的。例:首先建立一个守护进程,然后让该守护进程每隔10s在/tmp/dameon.log中写入一句话。/*dameon.c创建守护进程实例*/#include<stdio.h>#include<stdlib.h>#include<string.h>#include<fcntl.h>#include<sys/types.h>#include<unistd.h>#include<sys/wait.h>#defineMAXFILE65535intmain(){pid_tpc;inti,fd,len;char*buf="ThisisaDameon\n";len=strlen(buf);//第一步pc=fork();if(pc<0){printf("errorfork\n");exit(1);}elseif(pc>0)exit(0);/*第二步*/setsid();/*第三步*/chdir("/");/*第四步*/umask(0);/*第五步*/for(i=0;i<MAXFILE;i++) close(i);/*这时创建完守护进程,以下开始正式进入守护进程工作*/while(1){if((fd=open("/tmp/dameon.log",O_CREAT|O_WRONLY|O_APPEND,0600))<0){perror("open");exit(1);}write(fd,buf,len+1);close(fd);sleep(10);}}实验要求:(p122)运行此程序,并在终端输入:tail-f/tmp/dameon.log,显示运行结果;在终端输入:ps-ef|grepdameon,显示运行结果:

3.进程间通信编程(1)管道管道是Linux中进程间通信的一种方式。这里所说的管道主要指无名管道,它具有如下特点:它只能用于具有亲缘关系的进程之间的通信(也就是父子进程或者兄弟进程之间)。它是一个半双工的通信模式,具有固定的读端和写端。管道也可以看成是一种特殊的文件,对于它的读写也可以使用普通的read、write等函数,但是它不是普通的文件,并不属于其他任何文件系统,并且只存在于内存中。pipe--管道的创建(p127)/*pipe.c*/#include<unistd.h>#include<errno.h>#include<stdio.h>#include<stdlib.h>intmain(){intpipe_fd[2];/*创建一无名管道*/if(pipe(pipe_fd)<0){printf("pipecreateerror\n");return-1;}elseprintf("pipecreatesuccess\n");/*关闭管道描述符*/close(pipe_fd[0]);close(pipe_fd[1]);}实验要求:运行此程序,显示结果。2)该程序的功能是?管道的读写(p128-129)例:首先创建管道,之后父进程使用fork函数创建子进程,之后通过关闭父进程的读描述符和子进程的写描述符,建立起它们之间的管道通信。/*pipe_rw.c*/#include<unistd.h>#include<sys/types.h>#include<errno.h>#include<stdio.h>#include<stdlib.h>intmain(){intpipe_fd[2];pid_tpid;charbuf_r[100];char*p_wbuf;intr_num;memset(buf_r,0,sizeof(buf_r));/*创建管道*/if(pipe(pipe_fd)<0){printf("pipecreateerror\n");return-1;}/*创建一子进程*/if((pid=fork())==0){printf("\n");/*关闭子进程写描述符,并通过使父进程暂停2秒确保父进程已关闭相应的读描述符*/close(pipe_fd[1]);sleep(2);/*子进程读取管道内容*/if((r_num=read(pipe_fd[0],buf_r,100))>0)printf("%dnumbersreadfromthepipeis%s\n",r_num,buf_r);/*关闭子进程读描述符*/close(pipe_fd[0]);exit(0);}elseif(pid>0){/*关闭父进程读描述符,并分两次向管道中写入HelloPipe*/close(pipe_fd[0]);if(write(pipe_fd[1],"Hello",5)!=-1)printf("parentwrite1success!\n");if(write(pipe_fd[1],"Pipe",5)!=-1)printf("parentwrite2success!\n");/*关闭父进程写描述符*/close(pipe_fd[1]);sleep(3);/*收集子进程退出信息*/waitpid(pid,NULL,0);exit(0);}}实验要求:运行此程序,显示结果。问:管道的读和写分别用哪个函数?标准流管道与Linux中文件操作有基于文件流的标准I/O操作一样,管道的操作也支持基于文件流的模式(popen和pclose函数)。例:/*popen.c*/#include<stdio.h>#include<unistd.h>#include<stdlib.h>#include<fcntl.h>#defineBUFSIZE1000intmain(){FILE*fp;char*cmd="ps-ef";charbuf[BUFSIZE];/*调用popen函数执行相应的命令*/if((fp=popen(cmd,"r"))==NULL) perror("popen");else{while((fgets(buf,BUFSIZE,fp))!=NULL)printf("%s",buf);pclose(fp);}exit(0);}实验要求:运行此程序,显示结果画面。问:popen和pclose函数的功能是什么?FIFO(是有名管道)前面介绍的管道是无名管道,它只能用于具有亲缘关系的进程之间,这就大大地限制了管道的使用。有名管道的出现突破了这种限制,它可以使互不相关的两个进程实现彼此通信。该管道可以通过路径名来指出,并且在文件系统中是可见的。在建立了管道之后,两个进程就可以把它当作普通文件一样进行读写操作,使用非常方便。不过值得注意的是,FIFO是严格地遵循先进先出规则的,对管道及FIFO的读总是从开始处返回数据,对它们的写则把数据添加到末尾,它们不支持如lseek()等文件定位操作。有名管道的创建可以使用函数mkfifo(),该函数类似文件中的open()操作,可以指定管道的路径和打开的模式。在创建管道成功之后,就可以使用open、read、write这些函数了。例:该示例包含了两个程序,一个用于读管道,另一个用于写管道。其中在写管道的程序里创建管道,并且作为main函数里的参数由用户输入要写入的内容。读管道读出了用户写入管道的内容,这两个函数用的是非阻塞读写管道。/*fifo_write.c*/#include<sys/types.h>#include<sys/stat.h>#include<errno.h>#include<fcntl.h>#include<stdio.h>#include<stdlib.h>#include<string.h>#defineFIFO"/tmp/myfifo"main(intargc,char**argv)/*参数为即将写入的字节数*/{intfd;charw_buf[100];intnwrite;if(fd==-1)if(errno==ENXIO)printf("openerror;noreadingprocess\n");/*打开FIFO管道,并设置非阻塞标志*/fd=open(FIFO_SERVER,O_WRONLY|O_NONBLOCK,0);if(argc==1) printf("Pleasesendsomething\n");strcpy(w_buf,argv[1]);/*向管道中写入字符串*/if((nwrite=write(fd,w_buf,100))==1){if(errno==EAGAIN) printf("TheFIFOhasnotbeenreadyet.Pleasetrylater\n");}else printf("write%stotheFIFO\n",w_buf);}/*fifl_read.c*/#include<sys/types.h>#include<sys/stat.h>#include<errno.h>#include<fcntl.h>#include<stdio.h>#include<stdlib.h>#include<string.h>#defineFIFO"/tmp/myfifo"main(intargc,char**argv){charbuf_r[100];intfd;intnread;/*创建有名管道,并设置相应的权限*/if((mkfifo(FIFO,O_CREAT|O_EXCL)<0)&&(errno!=EEXIST))printf("cannotcreatefifoserver\n");printf("Preparingforreadingbytes...\n");memset(buf_r,0,sizeof(buf_r));/*打开有名管道,并设置非阻塞标志*/fd=open(FIFO,O_RDONLY|O_NONBLOCK,0);if(fd==-1){perror("open");exit(1);}while(1){memset(buf_r,0,sizeof(buf_r));if((nread=read(fd,buf_r,100))==-1){if(errno==EAGAIN)printf("nodatayet\n");}printf("read%sfromFIFO\n",buf_r);sleep(1);}pause();unlink(FIFO);}实验要求:为了能够较好地观察运行结果,需要把这两个程序分别在两个终端里运行,在这里首先启动读管道程序。由于这是非阻塞管道,因此在建立管道之后程序就开始循环从管道里读出内容。在启动了写管道程序后,读进程能够从管道里读出用户的输入内容。终端一命令为:#./read显示程序运行的结果画面:终端二命令为:#./writehello#./readFIFO显示程序运行的结果画面:实验四进程间通信(2)与多线程程序设计实验目的掌握进程间通信方式:信号、共享内存和消息队列编程方法。掌握Linux线程的基本概念。掌握多线程程序设计方法。实验环境1.PC机2.虚拟机软件VMware3.UbuntuLinux操作系统三、实验内容1.进程间通信方式(2)(1)信号通信1)信号的发送与捕捉(p137kill.c,p139alarm.c)2)信号处理(p140mysignal.cp143sigaction.c)(2)共享队列(p146shmadd.c)(3)消息队列(p149msg.c)3.Linux线程及实现(1)线程的基本操作(p154thread.c,p157pthread.c)(2)线程的访问控制(p160mutex.c,p164sem_mutex.c,sem_syn.c)实验要求:运行上述程序,并显示程序结果画面。实验五多线程程序设计与网络程序设计实验目的掌握网络基础编程方法。掌握网络高级编程方法。*3.掌握ping源码编程方法。实验环境1.PC机2.虚拟机软件VMware3.UbuntuLinux操作系统三、实验内容1.网络基础编程1)名字地址转化(p175getaddrinfo.c)2)socket基础编程(p180-182server.c与client.c)2.网络高级编程1)fcntl(p183-184fcntl.c)2)select(184-186select_socket.c)*3.掌握ping源码编程方法(p186-200)实验要求:运行上述程序,并显示程序结果画面。下篇基于ICETEK-AM3359-A的通信嵌入式系统实验实验设备的安装AM3359是一款基于ARMCortexTM-A8内核的低功耗应用处理器。AM3359所具有的强健的操作系统支持,丰富的用户界面,高处理性能和寿命,通过完全集成的混合处理器解决方案的灵活性,使得OEMs和ODMs能将产品迅速推向市场。同时,AM3359集成了高性能的ARMCortex-A8内核,使其在工业控制、便携式通信设备、USB、网络传输、高速编码和电力设备等行业得到更广泛的应用。ICETEK-AM3359-A开发套件采用A型底板与核心板相结合的方式,核心板尺寸较小,具有独立供电模块,板载HYNIXDDR3与NandFlash,能够独立运行。核心板所具有的100PIN插座可与A型底板连接,实现外扩接口的功能。A型底板则具有SD、网络、LCD、UART、USB等多种接口,并外扩DSP引脚,既便于开发人员进行设计与调试,也便于学生进行学习和实验,故采用基于ICETEK-AM3359-A的教学实验箱进行通信嵌入式系统实验。实验开发环境的设置参照《ICETEK-AM3359-ALinux使用手册》(见配套资源中的“补充参考手册”)安装构建评估板的Linux开发环境,因为本实验中用到了TI的ti-sdk-am335x-evm-05.05.00.006的环境,来编译可以在开发板上运行的QT程序,所以要参照其中的编译dvsdk章节,安装,配置并编译通过Ti的ti-sdk-am335x-evm-05.05.00.00。ICETEK-AM3359-A教学实验箱的连接1.连接电源:打开实验箱,取出三相电源连接线,将电源线的一端插入实验箱外部左侧箱壁上的电源插孔中。确认实验箱面板上电源总开关(位于实验箱底板左上角)处于“关”的位置(圆圈的一端按下为“关”的状态),连接电源线的另一端至220V交流供电插座上,保证稳固连接。2.使用电源连接线连接各模块电源:确认实验箱总电源断开。连接ICETEK-CTR板上边插座到实验箱底板上标有+12V电源插座;ICETEK-CTR板下边插座到实验箱底板上标有+5V电源插座;(注意:此处一定要防止将+12V和+5V的插座插反)如使用PP(并口)型仿真器,则连接仿真器上插座到实验箱底板上+5V电源插座;连接DSP评估板模块电源插座到实验箱底板上+5V电源插座。注意各插头要插到底,防止虚接或接触不良。3.连接DSP评估板信号线:当需要连接信号源输出到A/D输入插座时,使用信号连接线图分别连接相应插座。4.接通电源:检查实验箱上220V电源插座(箱体左侧)中保险管是否完好,在连接电源线以后,检查各模块供电连线是否正确连接,打开实验箱上的电源总开关(位于实验箱底板左上角),使开关位于“开”的位置(竖线的一端按下为“开”的状态),电源开关右侧的指示灯亮。实验一LED控制实验一、实验目的掌握Linux下系统led的控制方法。了解Linux3.2中设备的属性的查询与控制。掌握Linux下的程序通过NFS测试的方法。二、实验原理ICETEK-AM3359-A通过GPIO控制led的显示,并且在Linux的内核里集成了这些led的系统驱动,这样在系统的/sys/class/led/目录下就可以看到各个led的配置文件,用户只要通过简单的命令行,对每个led的brightness进行操作,就可以控制系统led的亮灭。原理图如下:图5.1ICETEK-AM3359-Aled接口三、实验设备ICETEK-AM3359-A,安装了Ubuntu10.04虚拟机的PC机。以及相应设备的配套电源。四、实验步骤(1)启动虚拟机。(2)用如下命令进行测试程序工程目录:realtimedsp@realtimedsp-desktop:~/$cd/home/realtimedsp/icetek-AM335x-kbma/projects/examples/app/leds(3)输入如下命令查看程序源码。…$gedit*.c在main.c中,利用文件操作函数,打开各led的brightness文件,然后向里面写1或者0来控制led的亮灭。(4)退出以上编辑窗口。(5)编译:…$make(6)复制编译后的可执行程序到nfs目录:…$sudocpled.out/opt/nfs/home/root[sudo]passwordforrealtimedsp:『输入密码,回车』(7)启动nfs进行测试在断电情况下连接ICETEK-AM3359-A板:ICETEK-AM3359-A板的串口J16的DB-9插座到开发主机COM1;连接ICETEK-AM3359-A板的网口J4相连的ICETEK-GNET到开发主机网卡,拨码开关SW3的状态拨为000110;在开发主机的Windows系统中启动超级终端【Sitara】接通ICETEK-AM3359-A板电源按回车键暂停linux的起动输入setenvbootcmd‘nandecchw2;nandread0x820000000x2800000x400000;bootm82000000’(不要输入)输入setenvbootargs'console=ttyO0,115200n8root=/dev/nfsnfsroot=03:/opt/nfs,nolockrwrootwaiticetek_board=a-rgmii1ip=:03::::eth0:offeth=d4:94:a1:39:88:da',如果使用者pc机的ip不在的子网内,请参见使用手册的第一章第三节的注意事项。(不要输入)输入boot观察【Sitara】中的输出的起动信息输入root登录输入cddemos进入测试目录输入./led.out运行测试程序,可以看到led,D1会不断闪烁。关闭ICETEK-AM3359-A板电源,结束实验。五、实验结果Led的D1会不断闪烁。六、问题与思考由于在Linux3.2中已经采用了新的内核驱动的编写架构,不是必需在dev中建立挂载点,而是有sys中输出驱动的属性文件,这样通过命令行的方式,用户就可以控制一些驱动变化,所以用户也可以通过在串口控制对口输入如下的命令来控制led,echo1>/sys/class/leds/am335x::led1/brightness和echo0>/sys/class/leds/am335x::led1/brightness试运行一下这两句命令,观察实验结果。

实验二按键控制实验实验目的了解GPIO按键的用处;了解TMS320AM335X连接的控制GPIO按键方法;掌握Linux下系统对GPIO按键的控制方法。二、实验原理ICETEK-AM3359-A通过GPIO来控制按键的,通过对对应的GPIO引脚的读,可以知道按键的状态,再通过对GPIO控制led的显示,把按键的状态显示出来。原理图如下:图5.2按键控制原理图在Linux的内核中,缺省将这些GPIO配置为按键,也就是做为一个输入设备,当按下或者抬起按键时,内核会收到事件,程序就会响应这个事件,去读写键盘输入,就可以监控并读出按键当前的状态,并根据状态和控制led的亮灭。三、实验设备ICETEK-AM3359-A,安装了WindowsXP和Linux虚拟机的PC机。以及相应设备的配套电源。四、实验步骤启动虚拟机(2)用如下命令进行测试程序工程目录realtimedsp@realtimedsp-desktop:~/$cd/home/realtimedsp/icetek-AM335x-kbma/projects/examples/app/keypad-led(3)输入如下命令查看程序源码…$gedit*.c在keypad-led.c中,利用文件操作函数,打开键盘输入设备,然后读键盘设备,当有按键产生时,读函数返回,然后再判断读的是哪个按键,并根据该按键的状态,利用system执行控制台命令,来控制相应led灯的亮灭;(4)编译…$make(5)复制编译后的可执行程序到nfs目录…$sudocpkeypad-led.out/opt/nfs/home/root[sudo]passwordforrealtimedsp:『输入密码,回车』(6)启动nfs进行测试在断电情况下连接ICETEK-AM3359-A板:ICETEK-AM3359-A板的串口J16的DB-9插座到开发主机COM1;连接ICETEK-AM3359-A板的网口J4相连的ICETEK-GNET到开发主机网卡,拨码开关SW3的状态拨为000110。在开发主机的Windows系统中启动超级终端【Sitara】接通ICETEK-AM3359-A板电源按回车键暂停linux的起动输入setenvbootcmd'nandecchw2;nandread0x820000000x2800000x400000;bootm82000000'输入setenvbootargs'console=ttyO0,115200n8root=/dev/nfsnfsroot=03:/opt/nfs,nolockrwrootwaiticetek_board=a-rgmii1ip=:03::::eth0:offeth=d4:94:a1:39:88:da',如果使用者pc机的ip不在的子网内,请参见使用手册的第一章第三节的注意事项。输入boot观察【Sitara】中的输出的起动信息。输入root登录输入cddemos进入测试目录输入./keypad-led.out/dev/input/event1运行测试程序,按动按键SW1,可以看到led,D1会根据按键的当前状态进行闪烁。关闭ICETEK-AM3359-A板电源,结束实验五、实验结果按动按键SW1,对应的led1灯会跟着亮灭。实验三驱动模块编写实验一.实验目的掌握Linux驱动的编写方法;掌握Linux驱动的测试方法;掌握Linux的驱动架构和编译环境的配置。二.实验原理Linux的驱动和大多数的系统的驱动驱动一样,需要实现一些固定的函数接口,这样系统才会知道,什么时候加载它,如何加载它,并通过系统API调用到它。一般Linux驱动,首先要声明并实现一个初始化函数,并且在初始化函数里,初始化配置相应的硬件设备,并设定这个设备的各种接口函数。三.实验设备ICETEK-AM3359-A,安装

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论