Presentation is loading. Please wait.

Presentation is loading. Please wait.

第11章 应用程序开发.

Similar presentations


Presentation on theme: "第11章 应用程序开发."— Presentation transcript:

1 第11章 应用程序开发

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

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

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

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

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

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

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

11 11.1.1 串行口主要函数介绍 (2) 校验位和停止位的设置: ① 无效验8 位 Option.c_cflag &= ~PARENB;
串行口主要函数介绍 (2) 校验位和停止位的设置: ① 无效验8 位 Option.c_cflag &= ~PARENB; Option.c_cflag &= ~CSTOPB; Option.c_cflag &= ~CSIZE; Option.c_cflag |= ~CS8; ③ 偶效验(Even) 7 位 Option.c_cflag |= ~PARODD; 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 &= &~CSIZE; Option.c_cflag |= CS8;

12 11.1.1 串行口主要函数介绍 3. 读写串口 2 位:options.c_cflag |= CSTOPB;
串行口主要函数介绍 ⑤ 设置停止位 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 串行口主要函数介绍 4. 关闭串口 (2) 读取串口数据 int nByte;
串行口主要函数介绍 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 串口举例 假设接收程序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>
串口举例 发送程序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,在接收端接收并显示接收到的数据。 这里请同学们注意的是:
串口举例 本例程实现: 在发送端发送数字123,在接收端接收并显示接收到的数据。 这里请同学们注意的是: 发送方按字符发送数据,接收方将接收的字符相应的ascii值与字符0所对应的ascii值相减,最终得到实际的十进制数值。

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

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

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

23 图 数据的封装过程

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

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

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

27 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 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(“ ”); /*设置地址为 */ bzero(&(mysock.sin_zero),8); /*设置sin_zero为8位保留字节*/ 注意: 如果mysock.sin_addr.s_addr= INADDR_ANY,则不指定IP地址(用于Server程序)。

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

31 图 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 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 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 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 #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 */

43 break; case 'o': ComPort = atoi(optarg); 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");

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"); 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"); 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); printf("\ncurrent time %d s\n",start);

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);

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); free(buffer); exit(0);

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

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

51 11.2.2 web服务器应用 2. 基于uClinux的Web技术
Httpd是最简单的Web服务器,功能比较简单,不支持认证和CGI。thttpd和boa均支持认证和CGI,功能比较丰富,其中boa是一个单任务的http服务器,源代码开放,性能比较高,占用系统资源比较少。 boa服务器在接收到请求时,不启动多个服务进程处理多个请求,在一个进程内处理所有服务请求。 由于目前的uClinux还不支持ASP、PHP等动态网页技术,所有在uClinux下通过CGI技术连接web页和本地程序,提供动态和交互的特性。 boa的配置文件boa.conf:在boa.conf中,对boa的一些配置项进行了配置,这些配置项大多可以使用默认值。

52 11.2.2 web服务器应用 本程序将在EV44B0II uClinux系统中,启动一个web服务器,并添加简单的网页和CGI程序。
在对内核配置时,选择 boa程序 和 cgi 。 将cgi_generic文件夹下的cgi.c 和 makefile,覆盖到程序包中的/usr/cgi_generic/。 将 boa文件夹下的hash.c,覆盖到程序包中的/usr/boa/src。 将rc文件,覆盖到程序包中的/vendor/Miectek/44b0。 要运行boa,使用 “> boa –c /tmp/ & ”命令。

53 11.2.2 web服务器应用 3. 程序说明 程序说明如下: /* rc */
/* 本脚本将为boa建立运行的根目录,和其他运行环境 */ hostname Samsung /bin/expand /etc/ramfs.img /dev/ram0 mount -t proc proc /proc mount -t ext2 /dev/ram0 /var mkdir /var/config mkdir /var/tmp mkdir /var/log mkdir /var/run mkdir /var/lock cat /etc/motd ifconfig lo

54 route add -net 127.0.0.0 netmask 255.255.255.0 lo
dhcpcd -p -a eth0 & cd /tmp mkdir cgi_bin cd /etc cp index.html /tmp cp boa.conf /tmp cp mime.types /tmp cp cgi_bin/* /tmp/cgi_bin chmod 775 /tmp/cgi_bin/* #mount -t jffs /dev/mtdblock1 /var #ifconfig eth0 hw ether 00:11:22:33:44:55 #ifconfig eth #login

55 /* cgi.c */ /* 本程序接收boa传递的参数,控制数码管显示指定的数字,并返回网页 */ #include <stdio.h> #include <string.h> #include "cgivars.h" #include "htmllib.h" #include "template.h" int main( ) { char **postvars = NULL; /* POST request data repository */ char **getvars = NULL; /* GET request data repository */ int form_method; /* POST = 1, GET = 0 */ form_method = getRequestMethod( ); /* decide form request method ,POST or GET */ if(form_method == POST) { /* get vars from request string */ getvars = getGETvars( ); postvars = getPOSTvars( );

56 } else if(form_method == GET)
{ /* In this exp,we use GET in index.html */ getvars = getGETvars( ); if(getvars) { int i = 0; while(getvars[ i]) if(strcmp(getvars[i],"LampNum") == 0) { /* get which number is on */ /* control reg */ static volatile unsigned int*iopmod = (volatile unsigned int *) (0x3ff5000); static volatile unsigned int*iopdata = (volatile unsigned int *) (0x3ff5008); int lampnum =0; lampnum = strtoul(getvars[i+1],NULL,0);

57 /* light number */ iopmod[0]=0x000000ff; iopdata[0] = (~lampnum); break; } i += 2; /* write web page to std out */ htmlHeader("Demo Web Page"); htmlBody(); template_page(postvars, form_method); htmlFooter(); cleanUp(form_method, getvars, postvars); /* display */ fflush(stdout); exit(0);

58 11.2.2 web服务器应用 4. 运行过程 (1)对程序修改后,重新编译内核并焼写烧写到EV44B0II评估板。
(2) 启动EV44B0II评估板。确认EV44B0II的IP地址。 (3) 运行boa -c /tmp/ & ,启动web服务器。 (4) 打开PC上的浏览器(如IE6或Netscape)输入EV44B0II的IP作为网 址,例如 ,这时就可以看到要测试的网页内 容。网页内容是/tmp/index.html文件。 (5) 在lamp number 文本框中,写入“1”,然后点击 “display”。 (6) EV44B0上的数码管显示对应数字。 (7) 在浏览器上显示“Demo Web Page”。

59 音频设备应用 S3C44B0II包含一个IIS总线接口,它可以作为连接8位或16位立体声编解码集成电路的接口,IIS总线接口提供内置FIFO的DMA传送模式,可以同时接收和发送,或单独接收和发送。 IIS总线接口可以发送和接收采样量化过的声音数据,它为外部的CODEC芯片提供工作主时钟,数据收发时钟和左右声道选择信号。具体的采样量化工作留给CODEC芯片完成。在接收方向,CODEC将模拟声音采样量化;在发送方向,CODEC将声音数据还原为模拟声音。 UDA1341和IIS的驱动程序,位于/Linux/drivers/char目录下。它们是ev44b0_sound.c、ev44b0_sound.h、l3-ev44b0.c。 音频设备文件的设备名为/dev/audio。

60 11.3.1 常用音频文件格式 1. WAV文件 (1) 概述 (2) WAV文件的结构
常用音频文件格式 1. WAV文件 (1) 概述 WAV是Microsoft Windows本身提供的音频格式,由于Windows本身的影响力,这个格式已经成为了事实上的通用音频格式。 (2) WAV文件的结构 在Windows环境下,大部分的多媒体文件都遵循着一种结构来存放信息,这种结构称为“资源互换文件格式”(Resources lnterchange File Format),简称RIFF。 WAV为WAVEFORM(波形)的缩写。声音文件的结构如图11-6所示,“RIFF”的格式辨别码为“WAVE”。 整个文件由两个chunk所组成:辨别码"fmt"(注意,最后一个是空白字符!)及"data"。在"fmt"的chunk下包含了一个PCMWAVEFORMAT数据结构,其定义如下:

61 图 WAVE文件结构

62 常用音频文件格式 整个文件由两个chunk所组成:辨别码“fmt”(注意,最后一个是空白字符!)及“data”。在“fmt”的chunk下包含了一个PCMWAVEFORMAT数据结构,其定义如下: typedef struct pcmwaveformat - tag {   WAVEFORMAT wf ;   WORD wBitsPerSample; } PCMWAVEFORMAT; typedef struct waveformat - tag {    WORD wFormatTag ;    WORD nChannels;    DWORD nSamplesPerSec;    DWORD nAvgBytesperSec;    WORD nBlockAlign;    } WAVEFORMAT;

63 常用音频文件格式 其意义分别为: wFormatTag:记录着此声音的格式代号,例如WAVE_FORMAT_PCM,WAVE_F0RAM_ADPCM等等。 nChannels:记录声音的频道数。 nSamp1esPerSec:记录每秒取样数。 nAvgBytesPerSec:记录每秒的数据量。 nBlockA1ign:记录区块的对齐单位。 wBitsPerSample:记录每个取样所需的位元数。

64 11.3.1 常用音频文件格式 2. mp3格式 (2) MP3的文件结构
常用音频文件格式 2. mp3格式 (1) 概述 mp3是Fraunhofer-IIS研究所的研究成果。mp3是第一个实用的有损音频压缩编码。 衡量mp3文件的压缩比例通常使用比特率来表示。 由于mp3是世界上第一个有损压缩的编码方案,所以可以说所有的播放软件都支持它,否则就根本没有生命力。在制作方面,也曾经产生了许多第三方的编码工具。 (2) MP3的文件结构 MPEG音频文件没有文件头而是由很多独立的数据帧构成,每个帧都是独立的可以被单独播放,每个帧都有自己的帧头和音频信息。 帧头是有32bits(4bytes)构成,起始的11bit是帧同步信息。帧可以有CRC校验信息,也可以没有。一般来说都没有CRC校验。CRC校验信息为16bit长,它紧跟在帧头的后面,在校验信息后就是经过压缩的音乐文件数据了。 MP3文件的比特率表如表11-1所示。

65 表 11-1 MP3文件的比特率

66 常用音频文件格式 MP3文件的采样率表如表11-2所示。 表 MP3文件的采样率表

67 常用音频文件格式 MP3文件的播放模式表如表11-3所示。 表 MP3文件的采样率表

68 播放WAV文件举例 1. 程序 该程序由sound.c和wave.h组成,sound.c中包含了*.wave文件的处理,和sound驱动的使用;wave.h中包含了wave文件的宏定义。

69 附源程序: #include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <Linux/sound.h> #include <Linux/soundcard.h> #include "wave.h" #define WAVEFILE "/bin/test.wav" #define AUDIOBUFLEN 4096 /* 4K bytes buffer */ static unsigned int buffer[AUDIOBUFLEN/4]; int main(void) { int fd_dev,fd_file; int result,len,i;

70 struct wave_s *wave_head;
struct audio_mix mix; char *buf =(char *) buffer; char ctl_buf[100]; fprintf(stderr,"\n\t\tWelcome to MICETEK World\nNow doing sound test...\n\n"); fprintf(stderr,"Open audio device : /dev/audio\n"); if (-1 == (fd_dev = open( "/dev/audio", O_WRONLY))) { fprintf(stderr, "Failed to open audio device\n" ); return 0; } fprintf(stderr,"Open wave file : %s\n",WAVEFILE); if ( -1 == (fd_file = open(WAVEFILE, O_RDONLY)))

71 fprintf(stderr, "Failed to open /bin/test.wave file\n" );
return 0; } result = read(fd_file,buf,sizeof(struct wave_s)); if (result < 0) { fprintf(stderr, "Read wave file error.\n" ); wave_head = (struct wave_s *) buf; if((wave_head->format[0]!= 'R')||(wave_head->format[1]!= 'I')||(wave_head->format[2] != 'F')|| (wave_head->format[3] != 'F')) fprintf(stderr, "File format (1) error.\n" ); printf("File length= %d\n",wave_head->file_length);

72 wave_head->format2[7] = 0;
if (strcmp(wave_head->format2,"WAVEfmt") != 0) { fprintf(stderr, "File format (2) error.\n" ); return 0; } printf("Channels = %d\n",wave_head->nChannels); printf(" IO ctrl Channels\n"); sprintf(ctl_buf,"%d",wave_head->nChannels); ioctl(fd_dev,SNDCTL_DSP_CHANNELS,ctl_buf); printf("Samples Per Sec = %d\n",wave_head->nSamplesPerSec); printf(" IO ctrl SamplesPerSec\n"); sprintf(ctl_buf,"%d",wave_head->nSamplesPerSec); ioctl(fd_dev,SNDCTL_DSP_SPEED,ctl_buf); printf("Avg Bytes Per Sec = %d\n",wave_head->nAvgBytesPerSec); printf("Block Align = %d\n",wave_head->nBlockAlign); printf("Bits Per Sample = %d\n",wave_head->wBitsPerSample);

73 if((wave_head->format3[0]!='d')||(wave_head->format3[1]!=
'a')||( wave_head->format3[2] != 't')|| (wave_head->format3[3] != 'a')) { fprintf(stderr, "File format (3) is Unknown.\n" ); } else fprintf(stderr, "File format (3) is data.\n" ); printf("Total Data Bytes = %d\n",wave_head->nDataBytes); len = wave_head->nDataBytes; /* real audio length*/ while (len) unsigned int length,ret; if (len > AUDIOBUFLEN) length = AUDIOBUFLEN;

74 else length = len; result = read(fd_file,buf,length); if (result < 0) { printf("Read File error.\n"); return 0; } ret = wriate(fd_dev,buf,result); if (ret < 0) printf("Write Audio error.\n"); len -= result; close(fd_file); printf("Close Wave File: %s\n",WAVEFILE);

75 close(fd_dev); printf("Close device File : /dev/audio \n"); /*Record test*/ fprintf(stderr,"Open audio device for Record\n"); if (-1 == (fd_dev = open( "/dev/audio", O_RDONLY))) { fprintf(stderr, "Failed to open audio device\n" ); return 0; } buf = malloc(600000); if (buf == 0) fprintf(stderr, "Failed to malloc a buffer for Record.\n" ); fprintf(stderr,"Record will start after 3 seconds, and record time will be 3s.\n"); sleep(3);

76 fprintf(stderr,"Record start!\n" );
read(fd_dev,buf,600000); fprintf(stderr,"Record OK.\n" ); close(fd_dev); /*Playback*/ fprintf(stderr,"Play back now and test the Mix.\n"); if (-1 == (fd_dev = open( "/dev/audio", O_WRONLY))) { fprintf(stderr, "Failed to open audio device\n" ); free(buf); return 0; } memset(&mix,0,sizeof(struct audio_mix)); /* Set all Maximum on Bass and Treble*/ mix.mode = 3; /* test Bass*/ for (i =0 ; i < 15; i++)

77 { fprintf(stderr,"BassBoost ++\n"); mix.BassBoost ++; ioctl(fd_dev,0x4d00,&mix); write(fd_dev,buf,600000); sleep(4); } mix.BassBoost = 0; /*reset Bass*/ /* test mute, mute is 1 bits!*/ for (i =0 ; i < 2; i++) fprintf(stderr,"mute ++\n"); mix.mute ++; /*test Treble*/

78 for (i =0 ; i < 4; i++) { fprintf(stderr,"Treble ++\n");a mix.Treble ++; ioctl(fd_dev,0x4d00,&mix); write(fd_dev,buf,600000); sleep(4); } mix.Treble = 0; /*reset Treble*/ /*Test volume*/ for (i =0 ; i < 10; i++) fprintf(stderr,"volume ++\n"); mix.volume -=5;

79 mix.volume = 0; /*reset volume*/
fprintf(stderr, "Save record to file /var/tmp/rec.wav\n" ); if ( -1 == (fd_file = open("/var/tmp/rec.wav", O_CREAT))) { fprintf(stderr, "Failed to open file /var/tmp/rec.wav \n" ); free(buf); close(fd_dev); return 0; } wave_head->file_length = ; wave_head->nDataBytes = ; if (write(fd_file,(char *)wave_head,sizeof(struct wave_s)) != sizeof(struct wave_s) ) fprintf(stderr, "Failed to write file /var/tmp/rec.wav \n" );

80 close(fd_dev); close(fd_file); return 0; } if (write(fd_file,buf,600000) != ) { fprintf(stderr, "Failed to write file /var/tmp/rec.wav \n" ); free(buf); fprintf(stderr, "Save file OK\n" ); return 1;

81 11.3.2 播放WAV文件举例 2. 运行过程 (1) 首先配置Hitool for ARM uClinux,使其以MDB方式调试。
(2) 将本程序的可执行文件及Test.wav文件下载到评估板并运行。首先将同一目录下的Test.wav文件播放出来,然后进行3秒的录音。

82 11.4 键盘及LCD显示应用 LCD介绍 1. 概述 长期以来,常见的掌上电脑(PDA)等小型手持式设备上,由于硬件条件的限制,我们看到的显示器件通常是单色LCD,用户界面也非常简单,几乎看不到PC机上美观整齐的图形界面(GUI)支持因为早期嵌入式处理器的速度有限,在处理图形和多媒体数据方面显得力不从心。 随着高性能嵌入式处理器的普及和硬件成本的不断降低,尤其是AFM系列处理器的推出,嵌入式系统的功能也越来越强。 在多媒体应用的推动下,彩色LCD也越来越多地应用到了嵌入式系统中,如新一代掌上电脑PDA多采用TFl显示器件,支持彩色图形界面,图片显示和视频媒体播放。 LCD控制器的功能是产生显示驱动信号,驱动LCD显示器。 用户只需要通过读写一系列的寄存器,完成配制和显示控制。 在本例中,笔者采用的是夏普LQ035Q2DD54 TFT,显示模块,在240ⅹ320分辨率下可提供16位彩色显示。

83 LCD介绍 2. Linux的帧缓冲设备 帧缓冲(Framebuffer)是Linux伪显示设备提供的一个接口,把显存抽象后的一种设备,他允许上层应用程序在图形模式下直接对显示缓冲区进行读写操作。 帧缓冲驱动的应用广泛,在Linux的桌面系统中,X Windows服务器就是利用帧缓冲进行窗口的绘制。 帧缓冲设备对应的设备文件为/dev/fb*如果系统有多个显示卡,Linux下还可支持多个帧缓冲设备,最多可达3外,分别为/dev/fb0---/dev/fb31,而/dev/fb为当前缺省的帧缓冲设备。 帧缓冲设备为标准字符设备,主设备号为29,次设备号则从0到310分别对应/dev/fb0----dev/fb3l。

84 11.4.1 LCD介绍 通过/dev/fb,应用程序的操作主要有这几种: 读写(read/write)
/dev/fb相当于读泻屏幕缓冲区。例如用cp /dev/fb0 tmp命令可将当前屏幕的内容拷贝到一个文件中,而命令cp tmp > /dev/fb0则将图形文件tmp显示在屏幕上。 映射MAP操作 对于帧缓冲设备,则可通过映射操作,可将屏幕缓冲区的物理地址映射到用户空间的一段虚拟地址中,之后用户就可以通过读写这段虚拟地址访问屏幕缓冲区,在屏幕上绘图了。 I/O控制对于帧缓冲设备 对设备文件的ioctl操作可读取设置显示设备及屏幕的参数,如分辨率,显示颜色数,屏幕大小等等。ioctl的操作是由底层的驱动程序来完成的。

85 11.4.1 LCD介绍 在应用程序中,操作/dev/fb的一般步骤如下: 打开/dev/fb设备文件。
用ioctrl躁作取得当前显示屏幕的参数,如屏幕分辨率,每个像素点的比特数。根据屏幕参数可计算屏幕缓冲区的大小。 将屏幕缓冲区映射到用户空间。 映射后就可以直接读写屏幕缓冲区,进行绘图和图片显示了。

86 11.4.1 LCD介绍 典型程序段如下: #include<Linux/fb.h> int main() {
int fbfd=0 struct fbv_ar_screeninfo fovinfo struct fb_fix_screeninfo ofinfo long int screensize=0 /*打开设备文件*/ fbfd=open (“/dev/fb0” ; O_RONLY); /*取得屏幕相关参数*/

87 11.4.1 LCD介绍 ioctl (fbfd FBIOGET_FSCREENINFO &finfo);
ioctl (fbfd FBIOGET_VSCREENINFO &vinfo); /*计算屏幕缓冲区大小*/ screensize=vinfo->xres*vinfo-> yres*vinfo->bits_per_pixel/8 /*映射屏幕缓冲区到用户地址空间*/ fbp=(char*)mmap(0, screensize, PFOT_READ| PFOT_WRITE, MAP_SHARED fbfd, 0); /*下面可以通过fb指针读写缓冲区*/ /*部分省略*/ }

88 11.4.1 LCD介绍 输入码是为了使输入设备将汉字输入到计算机而专门编制的一种代码。常见的有国标码,区位码,拼音码和五笔字型等。
国标码和区位码是专业人员使用的一种汉字编码,它是以数字代码来区别每个汉字的。 拼音码是最容易学校的一种,但它的重码太多,检字太慢。 五笔字型则是以结构来区分每个汉字,它的重码烧,是目前推广的一种比较简单,易学,易记的的输入码。 区位码是将GB 方案种的字符按其位置划分为94个区,每个区94个字符。区的编号是从1~94,区内字符编号也是从1~94。 机内码是指机器内部处理和存储汉字的一种代码。常用的一种汉字机内码是用两个字节表示一个汉字。

89 LCD介绍 汉字的显示一般是16×16的点阵 因此一个汉字需要16×2=32个字节来存储字模,在嵌入式系统设计中实现汉字的显示有两种方案。 第一种是使用通用汉字字库,这中方案适合于在应用中要大量显示不同的汉字.因此可以把整个汉字字库放入内存中,这种方案是以浪费内存为代价的,因为16×16点阵的汉字字库一般在260KB左右。 第二种方案是设计一个具有少量常用汉字的简单汉字字库这个方案适合于应用种用到少量汉字的字库,但必须借助汉字点阵提取软件制作一个含有能用到的汉字的简单字库。

90 11.4.1 LCD介绍 ① 第一种解决方案 在第一种方案种汉字点阵信息提取的代码实现: int fp;
unsigned char Data[32]; unsigned char qh,wh; unsigned int offset; fp=open("/var/tmp/HZK16",0); //打开汉字字库。 qh=*(s)-0xa0; //计算要显示汉字的区号 wh=*(s+1)-0xa0; //计算要显示汉字的区内号 offset=(94*(qh-1)+(wh-1))*32; //计算要显示汉字在字库中的偏移量 lseek(fp,offset,SEEK_SET); //移动文件指针 read(fp,Data,32); //读取汉字点阵信息 close(fp); 执行完后Data[32]数组种存放汉字的点阵信息。

91 ② 第二种解决方案 第二种方案的汉字显示的代码实现: 构造简单汉字字库其结构如下: struct HZ_Table_Struct { int Code; unsigned char Data1[32]; }; const struct HZ_Table_Struct HZ_Table []= /*嵌*/ 0, {0x00,0x80,0x31,0x84,0x30,0x84,0x3F,0xFC,0x3F,0xFC,0x33,0x20,0x33,0x7E,0x7F,0xFE,0x33,0xC6,0x3F,0x10,0x3F,0x30,0x33,0x38,0x3F,0x28,0x3F,0x6C,0x33,0xC6,0x00,0x02}, 整型Code是汉字在这个简单字库中的序号,以便查看和显示。 数组Data[32]是按行存放汉字的点阵信息。

92 键盘实现 参见5.4.1节键盘编程

93 11.5 汉字音乐点播应用 包括LCD汉字显示、WAV文件播放、键盘识别部分,其中LCD汉字显示、WAV文件播放采用在设备驱动之上编程,而16键盘识别比较简单,采用直接操纵方式。 程序如下:

94 /*******************************LCD定义部分*******************************/
#include <stdio.h> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <Linux/fb.h> #include <dirent.h> #include <math.h> #define SCREEN_X 240 #define LCD_XDIM (SCREEN_X/4) #define LCD_YDIM 64 int fd; unsigned char buffer[4800];

95 /******************************键盘定义变量和端**************************/
#define rpg (*(volatile unsigned *)0x01d20044) #define rpf (*(volatile unsigned *)0x01d20038) #define rpgc (*(volatile unsigned *)0x01d20040) #define pupg (*(volatile unsigned *)0x01d20048) #define rPCONF (*(volatile unsigned *)0x1d20034) #define rPDATF (*(volatile unsigned *)0x1d20038) #define rPUPF(*(volatile unsigned *)0x1d2003c) #define rPCONG(*(volatile unsigned *)0x1d20040) #define rPDATG(*(volatile unsigned *)0x1d20044) #define rPUPG(*(volatile unsigned *)0x1d20048) #define rWTCON(*(volatile unsigned *)0x1d30000) #define rWTDAT(*(volatile unsigned *)0x1d30004) #define rWTCNT(*(volatile unsigned *)0x1d30008) #define MCLK ( ) static int delayLoopCount=400;

96 /****************************播放函数的定义部分****************************/
#include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <Linux/sound.h> #define SIOC_OUT 0x #define SIOC_IN 0x #define SIOC_INOUT (SIOC_IN|SIOC_OUT) #define SIOCPARM_MASK 0x1fff #define_SIOWR(x,y,t) ((int)(SIOC_INOUT|((sizeof(t)&SIOCPARM_MASK)<<16)|(x<<8)|y)) #define SNDCTL_DSP_CHANNELS _SIOWR('P', 6, int) #define SNDCTL_DSP_SPEED_SIOWR(‘P’, 2, int ) #define AUDIOBUFLEN 4096 主程序略……(见课本P357)

97 11.6 本章小结 串口应用程序的编写方法; TCP/IP协议以及Socket的编写方法; 基于uClinux音频接口的应用程序的开发;
本章小结 串口应用程序的编写方法; TCP/IP协议以及Socket的编写方法; 基于uClinux音频接口的应用程序的开发; 键盘和LCD的应用程序的开发。 给出了一个汉字音乐点播程序,说明了具 体应用程序的编写方法。

98 练习题: 1. 简述串口应用常用的接口函数及使用方法。 2. uClinux下的web服务器的工作原理。
3. 试编写一个播放mp3文件的应用程序。 4. LCD的字符显示原理。


Download ppt "第11章 应用程序开发."

Similar presentations


Ads by Google