1 / 98

第 11 章 应用程序开发

第 11 章 应用程序开发. 本章主要内容. 串口应用程序的编写方法。 TCP/IP 协议以及 Socket 的编写方法。 基于 uClinux 音频接口的应用程序的编写 方法。 键盘和 LCD 的应用程序的编写方法。 汉字音乐点播程序的编写实例。. 第十一章 目录. 3 音频设备应用 常用音频文件格式 播放 WAV 文件举例 4 键盘及 LCD 显示应用 LCD 介绍 键盘实现 5 汉字音乐点播应用. 1. 串口应用程序 串口主要函数介绍 串口举例

gavril
Download Presentation

第 11 章 应用程序开发

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. 第11章 应用程序开发

  2. 本章主要内容 • 串口应用程序的编写方法。 • TCP/IP协议以及Socket的编写方法。 • 基于uClinux音频接口的应用程序的编写 方法。 • 键盘和LCD的应用程序的编写方法。 • 汉字音乐点播程序的编写实例。

  3. 第十一章 目录 • 3 音频设备应用 • 常用音频文件格式 • 播放WAV文件举例 • 4 键盘及LCD显示应用 • LCD介绍 • 键盘实现 • 5 汉字音乐点播应用 • 1.串口应用程序 • 串口主要函数介绍 • 串口举例 • 2.网络应用 • TCP/IP网络应用 • Web服务器应用

  4. 第十一章 目录 • 3 音频设备应用 • 常用音频文件格式 • 播放WAV文件举例 • 4 键盘及LCD显示应用 • LCD介绍 • 键盘实现 • 3 音频设备应用 • 1.串口应用程序 • 串口主要函数介绍 • 串口举例 • 2.网络应用 • TCP/IP网络应用 • Web服务器应用

  5. 本章从一个针对运行在S3C44B0X上的uClinux操作系统进行应用程序的开发入手,给出Windows操作系统平台上使用Hitool for uClinux等工具开发的多种应用程序。

  6. 本章主要介绍了: • 串口应用程序的编写方法。 • TCP/IP协议以及Socket的编写方法。 • 基于uClinux音频接口的应用程序的编写 方法。 • 键盘和LCD的应用程序的编写方法。 • 汉字音乐点播程序的编写实例。

  7. 11.1 串口应用程序 • S3C44B0X提供2个UART收发器,对它们可以操作在中断方式或DMA方式。它们内置波特率发生器,波特率发生器的时钟源为S3C44B0X的系统使用,所以最高速率可达115.2K bps。二个串口有单独的波特率发生器,接收、发送和控制单元,支持红外方式的传送和接收。 • 同时,在S3C44B0X串口的接收器和发送器中都有16字节的FIFO,FIFO可以有效的降低接收器和发送器对CPU的中断频率,提高发送和接收的效率。 • 串口设备的可配置参数包括波特率,起始位数量,数据位数量,停止位数量和流量控制协议。 • 在Linux操作系统中,设备驱动是以主设备号为主,每个设备都有唯一的主设备号和从设备号。

  8. 11.1.1 串行口主要函数介绍 1. 打开串口 在Linux下串口文件是位于/dev下,串口0为/dev/ttyS0,串口1为/dev/ttyS1,O_RDWR是以读写方式打开串口,O_NOCTTY表示该程序不会成为控制终端,避免了当在键盘输入类似ctrl+c的命令后,终止程序的运行。 打开串口是通过使用标准的文件打开函数操作: int fd; fd = open( "/dev/ttyS0", O_RDWR); if (-1 == fd) { perror(" 提示错误!"); }

  9. 11.1.1 串行口主要函数介绍 2. 设置串口 最基本的设置串口包括波特率设置,效验位和停止位设置。串口的 设置主要是设置如下struct termios结构体的各成员值: struct termios { unsigned short c_iflag; // 输入模式标志unsigned short c_oflag; // 输出模式标志 unsigned short c_cflag; // 控制模式标志unsigned short c_lflag; // local mode flags unsigned char c_line; // line discipline unsigned char c_cc[NCC]; // control characters };

  10. 11.1.1 串行口主要函数介绍 • 通过对c_cflag的赋值,设置波特率,字符大小,使能本地连接,使能串行口驱动读取输入数据。 • 通过设置c_iflag ,控制端口对字符的输入处理过程,IGNPAR符号常量表示忽略奇偶性错误的字节,不对输入数据进行任何校验,ICRNL将回车符映射为换行符。 这里就只考虑常见的一些设置: (1) 波特率设置: 下面是修改波特率的代码: struct termios Opt; tcgetattr(fd, &Opt); cfsetispeed(&Opt,B19200); // 设置为19200Bps cfsetospeed(&Opt,B19200); tcsetattr(fd,TCANOW,&Opt);

  11. (2) 校验位和停止位的设置: ① 无效验8 位 Option.c_cflag &= ~PARENB; Option.c_cflag &= ~CSTOPB; Option.c_cflag &= ~CSIZE; Option.c_cflag |= ~CS8; ③ 偶效验(Even) 7 位 Option.c_cflag &= ~PARENB; Option.c_cflag |= ~PARODD; Option.c_cflag &= ~CSTOPB; Option.c_cflag &= ~CSIZE; Option.c_cflag |= ~CS7; ② 奇效验(Odd) 7 位 Option.c_cflag |= ~PARENB; Option.c_cflag &= ~PARODD; Option.c_cflag &= ~CSTOPB; Option.c_cflag &= ~CSIZE; Option.c_cflag |= ~CS7; ④ Space效验7 位 Option.c_cflag &= ~PARENB; Option.c_cflag &= ~CSTOPB; Option.c_cflag &= &~CSIZE; Option.c_cflag |= CS8; 11.1.1 串行口主要函数介绍

  12. 11.1.1 串行口主要函数介绍 ⑤ 设置停止位 1 位:options.c_cflag &= ~CSTOPB; 2 位:options.c_cflag |= CSTOPB; 需要注意的是: 如果不是开发终端之类的,只是串口传输数据,而不需要串口来处理,那么使用原始模式(Raw Mode)方式来通讯。 设置方式如下: options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);//Input options.c_oflag &= ~OPOST;// Output 3. 读写串口 设置好串口之后,读写串口就很容易,把串口当作文件读写就可以了。 (1) 发送数据 char buffer[1024]; int Length=1024;

  13. 11.1.1 串行口主要函数介绍 int nByte; nByte = write(fd, buffer ,Length) (2) 读取串口数据 使用文件操作read函数读取,如果设置为原始模式(Raw Mode)传输数据,那么read函数返回的字符数是实际串口收到的字符数。可以使用操作文件的函数来实现异步读取,如fcntl,或者select等来操作。 char buff[1024]; int Len=1024; int readByte = read(fd, buff, Len); 4. 关闭串口 关闭串口就是关闭文件。 close(fd);

  14. 11.1.2 串口举例 假设接收程序readtest.c运行在装有标准Linux的PC机上,发送程序writetest.c运行在目标板S3C44B0X上,两台设备的串口通过交叉线连接在一起。 接收程序readtest.c的源码如下: #include <stdio.h> #include <string.h> #include <malloc.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <termios.h> #include <errno.h> #include < math.h>

  15. int spfd; int main() { char fname[16],hd[16],*rbuf; int retv,i,ncount=0; struct termios oldtio; int realdata=0; spfd=open("/dev/ttyS1",O_RDWR|O_NOCTTY); if(spfd<0) return -1; } tcgetattr(spfd,&oldtio);//保存串口的当前设置 cfmakeraw(&oldtio); cfsetispeed(&oldtio,B19200); cfsetospeed(&oldtio,B19200); tcsetattr(spfd,TCSANOW,&oldtio);//选择新设置,TCSANOW表示设 置立即生效rbuf=hd; printf("ready for receiving data...\n"); retv=read(spfd,rbuf,1);

  16. if(retv==-1) { perror("read"); reture -1; { while(*rbuf!='\0') { ncount+=1; rbuf++; retv=read(spfd,rbuf,1); printf("the number received is %d\n",retv); if(retv==-1) perror("read"); } for(i=0;i<ncount;i++) { realdata+=(hd[ i ]-48)*pow(10,ncount-i-1); } printf("complete receiving the data %d\n",realdata); close(spfd); return 0; }

  17. 11.1.2 串口举例 发送程序writetest.c的源码如下: #include <stdio.h> #include <string.h> #include <malloc.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <termios.h> int spfd; int main(int argc, char *argv[]) { char fname[16],*sbuf; int sfd,retv,i; struct termios oldtio; spfd=open("/dev/ttyS0",O_RDWR|O_NOCTTY); perror("open /dev/ttyS0");

  18. if(spfd<0) return -1; printf("ready for sending data...\n"); tcgetattr(spfd,&oldtio); cfmakeraw(&oldtio); cfsetispeed(&oldtio,B19200); cfsetospeed(&oldtio,B19200); tcsetattr(spfd,TCSANOW,&oldtio); fname[0]='1'; fname[1]='2'; fname[2]='3'; fname[3]='\0'; sbuf=(char *)malloc(4); strncpy(sbuf,fname,4); retv=write(spfd,sbuf,4); if(retv==-1) perror("write"); printf("the number of char sent is %d\n",retv); close(spfd); return 0; }

  19. 11.1.2 串口举例 本例程实现: 在发送端发送数字123,在接收端接收并显示接收到的数据。 这里请同学们注意的是: 发送方按字符发送数据,接收方将接收的字符相应的ascii值与字符0所对应的ascii值相减,最终得到实际的十进制数值。

  20. 11.2 网络应用11.2.1 TCP/IP网络应用 以太网技术作为当前局域网的主流技术,可以提供从10Mbit/s,100Mbit/s到1000Mbit/s的物理带宽,以及比较好的抗干扰性、比较大的网络半径和比较低系统维护费用;同时在不同速率以太网之间保持比较好的前向兼容性,所以在系统升级时很方便。 图11-1 以太网电路结构图

  21. 11.2.1 TCP/IP网络应用 1. 网络基础 (1) TCP/IP协议分层模型 在实际操作中接触到的通常只是网络系统的最高层——应用层的用户界面。实际上要进行网际的数据传送,需要经过如下的步骤: ① 需要发送的数据如E-mail、web页等,通过用户界面由应用程序传送到应用程序的数据发送缓冲区,并设置好与下一层连接的参数等待发送。 ② 数据做好传输前的准备工作,进入传输层。传输层主要负责为两台主机上的应用程序提供端口到端口的通信。 ③ 然后进入网络层的范畴。这里主要处理数据分组在网络中的活动,例如分组的选路。 ④ 当然最终数据还是要靠物理层的电磁波或光导纤维来传输。 ⑤ 在接收的一方是相反的过程,数据从最底层一直到应用层还原为 用户可以识别的信息,这一切都是由上面的协议来规范的。

  22. 11.2.1 TCP/IP网络应用 (2) 数据的封装与分用 ① 数据的封装 用户数据从应用层逐级传送到链路层,每经过一层都要被该层的协议进行一定的封装、标识和改造,就是给这个数据增加一些头部信息(或尾部信息)。 数据封装过程如图11-3所示。

  23. 图 11-3 数据的封装过程

  24. 11.2.1 TCP/IP网络应用 ② 数据的分用(解包) 在接收端接收这些数据的时候,经过拆分的数据要重新组合,并且去掉各层加上的头部信息,把数据还原。 (3) 客户—服务器模型 目前大多数网络应用程序在编写时都采用客户——服务器模型,假设—端是客户,另一端是服务器,让服务器提供给客户一定的服务内容。 ①并发型交互 在并发型交互模式下,程度的主要运作步骤如下: ◆ 等待一个客户请求的到来; ◆ 生成一个新的进程或者任务来处理这个客户请求,同时这里还可以 接收其他客户的请求。处理结束后,终止这个进程。 ◆ 反馈客户端; ◆ 等待新的客户请求的到来并进行下一次服务,如此循环运作。

  25. 11.2.1 TCP/IP网络应用 ②重复型交互 重复型交互摸式下,程序的的主要运作步骤如下: ◆ 等待一个客户请求的到来; ◆ 处理客户的请求,对客户进行服务; ◆ 给客户反馈信息,服务结束; ◆ 等待下一个请求到来,如此循环。

  26. 11.2.1 TCP/IP网络应用 2. TCP套接字 Linux系统的套接字是一个通用的网络编程接口。TCP协议就是通过套接字来实现连接的建立的,这里将就这个过程具体化并对其内部的函数进行必要的说明。 (1) 套接字地址结构 在头文件<Linux/socket.h>中定义了以下结构来保持套接字函数调用参数的一致性。 struct sockaddr { unsigned short sa_family;// 地址类型,格式为AF_xxx char sa_data[14];// 14字节的协议地址};

  27. 11.2.1 TCP/IP网络应用 其中的sa_family为套接字的协议簇地址类型,TCP/IP的协议对于IPv4地址类型为AF_INET。sa_data中存储具体的协议地址,不同的协议簇有不同的地址格式。但一般编程中并不直接针对此数据结构操作,而是使用另一个与sockaddr等价的数据结构sockaddr_in(在netinet/in.h中定义): struct sockaddr_in { unsigned short int sin_len;/*IPv4地址长度*/ short int sin_family;/*地址类型*/ unsigned short int sin_port;/*存储端口号*/ struct in_addr sin_addr;/*存储IP地址*/ unsigned char sin_zero[8]; /*空字节*/ };

  28. 11.2.1 TCP/IP网络应用 在编程中大多数是使用sockaddr_in这个结构来设置获取地址信息。 sin_family指协议族,在TCP套接字编程中只能是AF_INET;sin_port存储端口号(使用网络字节顺序),数据类型是一个16位的无符号整数类型; sin_addr存储IP地址,IP地址使用in_addr这个数据结构: struct in_addr { unsigned long s_addr; }; s_addr按照网络字节顺序存储IP地址;sin_zero是为了让sockaddr与sockaddr_in两个数据结构保持大小相同而保留的空字节。

  29. 11.2.1 TCP/IP网络应用 设置地址信息实例(IPv4) struct sockaddr_in mysock; /*设置sockaddr_in的结构体变量 mysock*/ mysock.sin_family=AF_INET; /*TCP地址结构*/ mysock.sin_port=htons(3490); /*short,NBO*/ mysock.sin_addr.s_addr=inet_addr(“166.111.160.10”); /*设置地址为166.111.160.10*/ bzero(&(mysock.sin_zero),8); /*设置sin_zero为8位保留字节*/ 注意:如果mysock.sin_addr.s_addr= INADDR_ANY,则不指定IP地址(用于Server程序)。

  30. 11.2.1 TCP/IP网络应用 (2) TCP客户-服务器通信模型 TCP客户-服务器通信过程如图11-4所示。

  31. 图 11-4 TCP客户-服务器通信过程

  32. 11.2.1 TCP/IP网络应用 (3) socket主要函数 ① 强制类型转换函数的调用: 将指向于特定协议的套接口地址结构的指针类型-> 指向通用套接口地址结构的指针。 int connect( int, struct sockaddr *, socklen_t) struct sockaddr-in servaddr; connect(sockfd,(sturct sockaddr *) &servaddr, sizeof(servaddr));

  33. 11.2.1 TCP/IP网络应用 ② 主机字节序和网络字节序的转换函数: #include <netinet/in.h> unit16_t htons(uint16_t host16bitvalue); unit32_t htons(uint32_t host32bitvalue); unit16_t ntohs(uint16_t net16bitvalue); unit32_t ntohs(uint32_t net32bitvalue);  h : host  n : network s : short (16 bits) l : long (32 bits) 

  34. 11.2.1 TCP/IP网络应用 ③int socket(int domain, int type, int protocol)  domain:说明我们网络程序所在的主机采用的通讯协族(AF_UNIX和AF_INET等)。 type: 我们网络程序所采用的通讯协议(SOCK_STREAM,SOCK_DGRAM等) SOCK_STREAM表明我们用的是TCP协议,这样会提供按顺序的,可靠,双向,面向连接的比特流。SOCK_DGRAM 表明我们用的是UDP协议,这样只会提供定长的,不可靠,无连接的通信。 protocol:由于我们指定了type, 所以这个地方我们一般只要用0来代替就可以了。 socket为网络通讯做基本的准备。成功时返回文件描述符,失败时返回-1,看errno可知道出错的详细情况。

  35. 11.2.1 TCP/IP网络应用 ④int bind(int sockfd, struct sockaddr *my_addr, int addrlen) sockfd:是由socket调用返回的文件描述符。  addrlen:是sockaddr结构的长度。  my_addr:是一个指向sockaddr的指针。 ⑤int listen(int sockfd, int backlog)  sockfd:是bind后的文件描述符。 backlog:设置请求排队的最大长度。当有多个客户端程序和服务端相连时, 使用这个表示可以介入的排队长度。 listen函数将bind的文件描述符变为监听套接字,返回的情况和bind一样。 ⑥int accept(int sockfd, struct sockaddr *addr, int *addrlen) sockfd:是listen后的文件描述符。  addr,addrlen是用来给客户端的程序填写的, 服务器端只要传递指针就可以了。bind, listen和accept是服务器端用的函数,accept调用时, 服务器端的程序会一直阻塞到有一个客户程序发出了连接。 accept成功时返回最后的服务器端的文件描述符, 这个时候服务器端可以向该描述符写信息了,失败时返回-1。 

  36. 11.2.1 TCP/IP网络应用 ⑦int connect(int sockfd, struct sockaddr * serv_addr,int addrlen)  sockfd是socket返回的文件描述符。  serv_addr:储存了服务器端的连接信息,其中sin_add是服务端的地址。 addrlen: serv_addr的长度 。 connect函数是客户端用来同服务端连接的。成功时返回0,sockfd是同服务端通讯的文件描述符,失败时返回-1。 ⑧ ssize_t write(int fd, const void *buf, size_t nbytes) write函数将buf中的nbytes字节内容写入文件描述符fd。 成功时返回写的字节数, 失败时返回-1。并设置errno变量,在网络程序中,当我们向套接字文件描述符写时有两种可能。  write的返回值大于0,表示写了部分或者是全部的数据。 返回的值小于0,此时出现了错误.我们要根据错误类型来处理。

  37. 11.2.1 TCP/IP网络应用 ⑨ ssize_t read(int fd, void *buf, size_t nbyte)  read函数是从fd中读取内容。当读成功时,read返回实际所读的字节数,如果返回的值是0 表示已经读到文件的结束了,小于0表示出现了错误。 ⑩recv和send函数提供了和read和write差不多的功能,不过提供了第四个参数来控制读写操作。  int recv(int sockfd, void *buf,int len, int flags) int send(int sockfd, void *buf, int len,int flags) 前面的三个参数和read, write一样,第四个参数可以是0或者是以下的组合: ◆MSG_DONTROUTE:不查找路由表 ◆ MSG_OOB:接受或者发送带外数据 ◆MSG_PEEK:查看数据,并不从系统缓冲区移走数据 ◆MSG_WAITALL :等待所有数据

  38. 11.2.1 TCP/IP网络应用 3.举例 我们将使用TCP协议提供的服务,组成一个简单的重复型的网络时间服务器。在一台EV44B0II系统中启动服务程序并指定服务端口。在另外一台EV44B0II系统中启动客户端程序并指定服务器IP地址和服务端口。服务器将接收该服务,并返回服务器本地的系统时间。 本程序使用TCP协议,可以工作在服务器或客户端状态。使用的默认端口号为9988。 程序流程图如图11-5所示:

  39. 图11-5 流程图

  40. 11.2.1 TCP/IP网络应用 附程序清单: /*TCP/IP nettime service*/ #include <sys/param.h> #include <sys/stat.h> #include <sys/ioctl.h> #include <sys/socket.h> #include <sys/time.h> #include <sys/file.h> #include <netinet/in.h> #include <netinet/ip.h> #include <netinet/tcp.h> #include <arpa/inet.h> #include <stdio.h>

  41. #include <signal.h> #include <string.h> #include <stdlib.h> #include <unistd.h> #include <errno.h> #include <netdb.h> #include <pwd.h> #include <stdarg.h> extern char *optarg; /* getopt */ #define COM_SERVER 1 #define COM_CLIENT 2 int ComStatus; #define PORT_NUMBER 0x1000 short ComPort ; int main(int argc,char * argv[ ]) { int fd_listen,fd_client,fd_service; char server_ip[64];

  42. int port; struct sockaddr_in sn = { AF_INET }; int sa_len; char * buffer; int start,packet_len,c,counter; int debug; ComStatus = COM_SERVER; ComPort = PORT_NUMBER; while((c = getopt(argc,argv,"sc:o:")) != -1) { switch(c) { case 'c':/* get server ip address */ memcpy(server_ip,optarg,(strlen(optarg)+1)); ComStatus = COM_CLIENT; break; case 's': /* open debug flag */ ComStatus = COM_SERVER;

  43. break; case 'o': ComPort = atoi(optarg); break; default: /* print usage */ fprintf(stderr, "Usage: %s [ [-c <server ip> ] | -s ] [-p <port>]\n", argv[0]); exit(1); } } /* setup address and port */ sn.sin_port = __constant_htons(ComPort); sn.sin_addr.s_addr = 0; if(argc < 2) { fprintf(stderr,"\n argv too less \n"); exit(1); }

  44. /* alloc mem for data buffer */ packet_len = 256; buffer = malloc(packet_len); if(buffer < 0) { fprintf(stderr,"\n malloc buffer error \n"); exit(1); } if(ComStatus == COM_SERVER) {/* server process */ if((fd_listen = socket(AF_INET,SOCK_STREAM,0)) < 0) { fprintf(stderr,"\ncan not open server socket ,exit\n"); exit(1); } if(bind(fd_listen,(struct sockaddr *)&sn, sizeof(sn)) < 0)

  45. { fprintf(stderr,"\ncan not bin server,socket,exit\n"); close(fd_listen); exit(1); } if (listen(fd_listen, 1) < 0) { fprintf(stderr,"listen failed,exit"); close(fd_listen); exit(1); } sa_len = sizeof(sn); { printf("\nget service request from %s\n",inet_ntoa(sn.sin_addr)); } start = time(0); *(int *)buffer = start; while(1)

  46. { /* loop service*/ fd_service = accept(fd_listen, (struct sockaddr *)&sn, &sa_len); if (fd_service < 0) { perror("accept failed"); exit(1); } else if(write(fd_service,buffer,packet_len) < 0) { perror("server write"); close(fd_listen); close(fd_service); exit(1); } printf("\ncurrent time %d s\n",start); close(fd_service);

  47. } close(fd_listen); } else { /* client process */ if((fd_client = socket(AF_INET,SOCK_STREAM,0)) < 0) { perror("client socket"); exit(1); } sn.sin_addr.s_addr = inet_addr(server_ip); sa_len = sizeof(sn); if(connect(fd_client,(struct sockaddr *)&sn,sa_len) < 0) { perror("client connect"); close(fd_client); exit(1); }

  48. if((counter = read(fd_client,buffer,packet_len)) <= 0) { perror("receive failed"); close(fd_client); exit(1); } start = *(int *)(buffer); printf("\nnet server time %d s\n",start); close(fd_client); } free(buffer); exit(0); }

  49. 11.2.1 TCP/IP网络应用 • 首先配置开发板IP地址(ifconfig eth0 10.10.16.220 netmask255.255.255.0),配置Hitools的调试协议为MDB,启动主机的Target Server程序运行,选择Hitools的load image then fork task窗口的Command Line中填写参数(–s –o 8888),然后带参下载运行。 • 其次启动客户端程序,在超级终端中,进入目录/var/tmp ,入 ./nettime–c 10.10.16.220 –o 8888回车。 • 超级终端显示客户端程序输出了服务器端的以秒为单位的时间:“net server time 333s”。

  50. 11.2.2 web服务器应用 1.Web服务器的原理 • 从原理上来说,Web服务器与其他在socket端口进行监听的应用程序差别不大,都是监听并接收用户请求,遵循HTTP协议,根据请求内容和类型,使用串行和并行方式提供相应的服务。 • 使用Web服务的好处是:在提供服务时,客户端的软件是标准的,不需要考虑客户端软件的开发问题,使用者容易上手;服务器端的开发,也是有很多现成的资源可以利用。

More Related