第11章 应用程序开发.

Slides:



Advertisements
Similar presentations
Linux 环境及 Shell 程序 操作系统实验 1. 二、 Shell 编程与进程通信 常用 shell 命令 文件及文件属性操作 ls 、 cp 、 mv 、 rm ln 、 ln –s 、 chmod 、 groupadd 、 useradd 输入输出操作 echo 、 cat >> 、
Advertisements

COM 串口程序设计 Tel:
现代电子技术实验 ——综合实验之单片机部分
6.1 区域委派与域名转发 6.2 虚拟主机技术 6.3 架设FTP服务器 6.4 动态主机分配协议 6.5 架设Mail服务器
动态网站开发 【HTTP与网络基础】 李博杰
项目四:Internet基础与接入方法 第八章 应用服务器安装配置
第11章 应用程序开发.
第七章 操作系统接口 7.1 联机用户接口 7.2 Shell命令语言 7.3 系统调用 7.4 UNIX系统调用 7.5 图形用户接口.
第一章 C语言概述 计算机公共教学部.
嵌入式图形显示.
马志强 软件学院501室 网络应用开发 马志强 软件学院501室
第9章 文件系统.
第9章 文件系统.
数据转发过程.
编译原理上机实习
基于ARM和linux的开发 华中科技大学 武汉创维特 2017/3/20.
網頁介面設計的基礎理論 講師:鄭靜怡 本教材內容出自於網頁界面設計藝術教程,人民郵電出版社.
NetGuru 創新 網路通訊實驗教學解決方案 PART I TCP/IP通訊協定深入剖析/以NetGuru實作
補充: Input from a text file
基于操作系统的编程复习 张玉宏
计算机系统安全 第10章 常用攻击手段.
Socket.
第 5 章 文件I/O操作.
计算机网络实验介绍 信息网络实验室 2017/9/13 04:55:22.
Lab312.
Advanced Sockets Programming
TCP、UDP 通信实践 广州创龙电子科技有限公司 01 广州创龙电子科技有限公司
複習 struct score_Type{ int chinese,english; }; struct my_Type{
第 13 章 DNS 著作權所有 © 旗標出版股份有限公司.
第3讲 网络安全协议基础 此为封面页,需列出课程编码、课程名称和课程开发室名称。
(C) Active Network CO., Ltd
chapter 1-Introduction
臺東縣中小學資訊教育校園網路管理暨資訊安全防護計畫研習
计算概论 第二十一讲 文件操作 北京大学信息学院.
Socket Programming in C
網路伺服器應用 Linux Server Andres, Wen-Yuan Liao
Socket Programming.
Socket 基本觀念.
Internet Protocol (IP)
C 程式設計— 語言簡介 台大資訊工程學系 資訊系統訓練班.
第7章 Linux环境编程.
TCP/IP Protocol Suite TCP/IP協定 第二組 投影片製作by簡嘉宏 綦凱宏 林睿敏 滕孟哲.
第十一章 文件 文件概述 文件操作 文件操作实例 本章小结 作业: 练习:
C++ 程式設計— 語言簡介 台大資訊工程學系 資訊系統訓練班.
在一定程度上 人类的思维产生于 简单个体之间的相互作用 ——Marvin Minsky.
嵌入式系统设计与实例开发 ——ARM与C/OS-Ⅱ 第六讲 基于ARM的嵌入式硬件结构设计.
中国科学技术大学计算机系 陈香兰(0512- ) Autumn 2010
第十二章 文件 12.1 C文件概述 文件:存储在外部介质上数据的集合,是操作系统数据管理的单位 文件分类 按文件的逻辑结构: 按存储介质:
第七讲 网际协议IP.
作業系統實習課(四) -檔案管理- 實驗室:720A 助教:鄧執中.
第2章 套接字网络编程基础 2.1 UNIX套接字网络编程接口的 产生与发展 2.2 套接字编程的基本概念 2.3 面向连接的套接字编程
THE C PROGRAMMING LANGUAGE
Ch9 Communicating with Hardware
Linux 文件操作——系统调用和标准 IO 库
第十三章 TCP/IP 與 Internet 網路連結技術
Web Server 王宏瑾.
第1章 概述 本章要点: C语言程序结构和特点 C语言程序的基本符号与关键字 C语言程序的编辑及运行 学习方法建议:
Westmont College 网络应用软件 第一讲 (客户-服务器 概念, 协议端口的使用, 套接字API)
Network Application Programming(3rd Edition)
Advister: Quincy Wu Speaker: Chenglin Tsai Date:3/26
SOCKET( ).
2019/5/3 JAVA Socket(UDP).
<编程达人入门课程> 本节内容 为什么要使用变量? 视频提供:昆山爱达人信息技术有限公司 官网地址: 联系QQ:
第六章 S3C2410的串口UART及编程 通用异步收发传输器(Universal Asynchronous Receiver/Transmitter)
助教:廖啟盛 JAVA Socket(UDP) 助教:廖啟盛
实验三 Linux文件目录操作 一、目的 二、要求 了解并掌握Linux文件目录结构。 了解Linux文件系统与目录操作:
《操作系统设计与实现》 第5章 文件系统.
《操作系统设计与实现》 Linux系统编程.
Presentation transcript:

第11章 应用程序开发

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

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

11.1 串口应用程序 S3C44B0X提供2个UART收发器,对它们可以操作在中断方式或DMA方式。 11.1 串口应用程序 S3C44B0X提供2个UART收发器,对它们可以操作在中断方式或DMA方式。 它们内置波特率发生器,波特率发生器的时钟源为S3C44B0X的系统使用,所以最高速率可达115.2K bps。

二个串口有单独的波特率发生器,接收,发送和控制单元,支持红外方式的传送和接收。 同时,在S3C44B0X串口的接收器和发送器中都有16字节的FIFO,FIFO可以有效的降低接收器和发送器对CPU的中断频率,提高发送和接收的效率。

串口设备的可配置参数包括波特率,起始位数量,数据位数量,停止位数量和流量控制协议。 一般来讲,起始位为1bit,数据位为8bit,停止位为1bit,流量控制协议为“无流量控制”,波特率为115200bit/s。

在Linux操作系统中,设备驱动是以主设备号为主,每个设备都有唯一的主设备号和从设备号。

作为字符型设备,串口设备的设备主标识为4,次设备号从64开始,并随着串口号的增加而增加,如/dev/ttyS10对应的次设备号为74;可以通过 ls –al 命令,列出/dev 目录下所有设备文件的设备类型、主设备号和次设备号。

在串口设备初始化函数中,设备驱动程序使用设备类型和主设备号将该驱动程序的操作接口注册到内核的设备表中,该接口表连接了内核和设备驱动程序。 该接口包含设备打开,设备释放,设备读和设备写等。

设备文件是用来表示Linux所支持的大多数设备的,每个设备文件除了设备名,还有3个属性:设备类型、主设备号、从设备号。 设备文件是用来表示Linux所支持的大多数设备的,每个设备文件除了设备名,还有3个属性:设备类型、主设备号、从设备号。 设备文件在Linux下可以通过mknod系统调用来创建;

在Hitool环境下,可以通过修改 /vendor/micetek/EV44B0II/makefile来增加新的设备文件,在编译时会在/romfs/dev下生成对应的设备文件,然后通过genromfs实用工具生成romfs.img,这个文件会被打包到linux.elf中,作为uClinux的根文件系统; 这样,在uClinux中可以看到新建的设备文件。

当应用程序打开或读取设备文件时,对应的系统调用将访问该设备文件在VFS文件系统中的inode数据结构, 然后找到该设备文件对应的操作接口,这时所使用的操作接口一般是同一类型设备的统一操作接口, 接着通过主设备号将找到设备的实际操作接口,最后操作将在该设备的驱动程序中执行。

11.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(" 提示错误!"); }

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 */ };

通过对c_cflag的赋值,设置波特率,字符大小,使能本地连接,使能串行口驱动读取输入数据。 通过设置c_iflag ,控制端口对字符的输入处理过程,IGNPAR符号常量表示忽略奇偶性错误的字节,并不对输入数据进行任何校验,ICRNL 将回车符映射为换行符。

这里就只考虑常见的一些设置: (1) 波特率设置 下面是修改波特率的代码: struct termios Opt; tcgetattr(fd, &Opt); cfsetispeed(&Opt,B19200); /*设置为19200Bps*/ cfsetospeed(&Opt,B19200); tcsetattr(fd,TCANOW,&Opt);

(2) 校验位和停止位的设置: ① 无效验8 位 Option.c_cflag &= ~PARENB; Option.c_cflag &= ~CSTOPB; Option.c_cflag &= ~CSIZE; Option.c_cflag |= ~CS8;

② 奇效验(Odd) 7 位 Option.c_cflag |= ~PARENB; Option.c_cflag &= ~PARODD; Option.c_cflag &= ~CSTOPB; Option.c_cflag &= ~CSIZE; Option.c_cflag |= ~CS7;

③ 偶效验(Even) 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;

⑤ 设置停止位 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; 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);

11.1.2 串口举例 例:举一个有关接收和发送数据的程序,通过串口交叉线的连接运行在不同设备(也可以是同一台设备)上的例子。

假设接收程序readtest. c运行在装有标准Linux的PC机上,发送程序writetest 假设接收程序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 "math.h"

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); perror("open /dev/ttyS1")

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

if(retv==-1) perror("read"); 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;

发送程序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");

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

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

11.2 网络应用

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

该技术的标准由IEEE委员会的802委员会的802工作组制定和维护,该标准为IEEE802.3。 在EV44B0II上,我们提供了一个10M/100M自适应以太网口。

以太网只具有MAC(Medium Access Control)层,它使用CSMA/CD协议来维护物理网络资源的共享访问。 通过该协议,以太网提供了尽最大努力的服务,即不是完全可靠的服务,差错的纠正由协议栈的高层来完成。

一个以太网包的最大长度为1536字节,其中包含了6字节的目的地址,6字节的源地址,2字节的长度/类型,用户数据部分的长度为64字节到1500字节,最后的4字节是对前面内容的CRC32校验和。

MAC层完成了数据的以太网包封装,以太网包的发送和接收。 但是由于网络信号在电平和编码等方面与MAC层发出信号的不同,所以在MAC层和物理插座之间需要增加变压器(TRANSFORM)和物理接口电路(PHY)

物理接口电路对发送的数据进行编码,并在发送数据的前面插入7字节的前同步码和1字节的帧定界符; 使用前同步码同步接收数据的时钟,对接收的数据进行解码;并为MAC层的发送和接收提供同步信号,变压器对以太网络上的信号进行双极性的合成和电平变换,整个电路结构如图11-1所示。

图11-1以太网电路结构图

这里我们将MAC层和PHY画在了一起,这主要是因为EV44B0II使用的SMSC91C113是同时具备MAC层功能和PHY层功能的芯片。

1. 网络基础 (1) TCP/IP协议分层模型 要接触到网络编程,必须首先了解一下计算机网络的构成框架。 网络之所以能够在不同的机器和不同的操作系统之间进行自由的通信,是由一系列规范的协议来保障的。

这些协议分不同的层次进行开发和运作,每一层负责一定的通信功能,并且都与其他层次有相关接口,这样组合成一个完整的网络传输系统如图11-2所示。

图11-2 TCP/IP网络协议的四个层次

在实际操作中接触到的通常只是网络系统的最高层——应用层的用户界面。实际上要进行网际的数据传送,需要经过如下的步骤: ① 需要发送的数据如E-mail、web页等,通过用户界面由应用程序传送到应用程序的数据发送缓冲区,并设置好与下一层连接的参数等待发送。 这是在用户的本地机上完成的,通常我们称之为应用层的操作。

常用的应用层程序与协议有Telnet远程登录程序、HTTP超文本传输协议、FTP文件传输协议、SMTP简单邮件传输协议和SNMP简单网络管理协议等。

② 数据做好传输前的准备工作,进入传输层。传输层主要负责为两台主机上的应用程序提供端口到端口的通信。 一般由TCP(传输控制协议)和UDP(用户数据报协议)来完成这项任务。

它们的工作就是接收应用层发送数据缓冲区中的待发送数据,将这些数据分组、打包、标识以便传输到下一层,同时TCP还要负责数据传输成功的确认等可靠性工作。 而应用层则可以忽略这些细节,这就是分层传输的好处所在。

③ 然后进入网络层的范畴。这里主要处理数据分组在网络中的活动,例如分组的选路。 ③ 然后进入网络层的范畴。这里主要处理数据分组在网络中的活动,例如分组的选路。 一般来说,应用在这一层的协议有IP协议(网际协议)、ICMP协议(Intemet控制报文协议)、IGMP协议(Intemet组管理协议)等。 这里,由传输层分组的数据将决定它们的传输目的地,并分配传送路线。

④ 当然最终数据还是要靠物理层的电磁波或光导纤维来传输。 ⑤ 在接收的一方是相反的过程,数据从最底层一直到应用层还原为用户可以识别的信息,这一切都是由上面的协议来规范的。

(2) 数据的封装与分用 ① 数据的封装 用户数据从应用层逐级传送到链路层,每经过一层都要被该层的协议进行一定的封装、标识和改造,就是给这个数据增加一些头部信息(或尾部信息)。

我们把每层经过封装的数据单元加以识别,比如TCP传给IP的成为TCP段(TCP segment),IP层的称作IP数据报(IP datagram)等。 数据封装过程如图11-3所示。

图 11-3 数据的封装过程

UDP数据与TCP数据基本一致,只是它们的头部长度不同,名称也不同。 许多应用程序都可以使用TCP或UDP来传送数据。传输层协议在生成数据段首部时要加入一个该应用程序的标识符。 TCP和UDP都用一个16位的端口号来表示不同的应用程序,这些端口号都标识在数据报的头部。

② IP头部要说明数据传输的目的地以及上一层协议的类型标识等。 ③ 数据的分用(解包) 在接收端接收这些数据的时候,经过拆分的数据要重新组合,并且去掉各层加上的头部信息,把数据还原。

这是一个与上面相反的逆过程,具体地说,首先根据从Ethernet最底层来的数据进行分用,判断链路层协议是IP、ARP、RARP等协议; 进而通过TCP/UDP中应用程序端口号的头信息进行应用程序分用,直到最后数据达到最高层交付用户。

(3) 客户——服务器模型 目前大多数网络应用程序在编写时都采用客户——服务器模型,假设—端是客户,另一端是服务器,让服务器提供给客户一定的服务内容。 这里可以再分为两种具体类型:并发型交互与重复型交互。

①并发型交互 在并发型交互模式下,程度的主要运作步骤如下: ◆ 等待一个客户请求的到来; ◆ 生成一个新的进程或者任务来处理这个客户请求,同时这里还可以接收其他客户的请求。处理结束后,终止这个进程。 ◆ 反馈客户端; ◆ 等待新的客户请求的到来并进行下一次服务,如此循环运作。

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

这里重复型交互的缺点就是在服务器给二个客户处理请求服务的时候,不能接收和处理其他客户的请求; 而并发型交互就可以解决这个问题,但要多线程操作系统的支持。 —般来说,UDP服务器采用重复型交互,TCP服务器采用并发型服务。

2. TCP套接字 Linux系统的套接字是一个通用的网络编程接口。 TCP协议就是通过套接字来实现连接的建立的,这里将就这个过程具体化并对其内部的函数进行必要的说明。

一个TCP连接的建立开始于TCP客户机创建一个套接字,然后调用connect函数启动TCP协议的三方握手操作,并与远程服务器建立连接。

在服务器方面,当套接字建立以后,调用函数bind绑定自己的公认端口号,然后调用listen来准备接收客户端请求,最后调用函数accept完成接收,这样一次通讯才告结束。 直到最后需要关闭连接的时候,客户机与服务器调用函数close来关闭连接。

(1) 套接字地址结构 Linux系统的套接字可以支持多种协议,每一种协议都使用不同的套接字地址结构。在头文件<Linux/socket.h>中定义了以下结构来保持套接字函数调用参数的一致性。 struct sockaddr { unsigned short sa_family; /*地址类型,格式为AF_xxx*/ char sa_data[14];/*14字节的协议地址*/ };

其中的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]; /*空字节*/ };

在编程中大多数是使用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两个数据结构保持大小相同而保留的空字节。

设置地址信息实例(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程序)。

(2) TCP客户-服务器通信模型 TCP客户-服务器通信过程如图11-4所示。

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

② 主机字节序和网络字节序的转换函数: #include <netinet/in ② 主机字节序和网络字节序的转换函数: #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) 

③ 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可知道出错的详细情况。

④ int bind(int sockfd, struct sockaddr ④ 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 ⑥ int accept(int sockfd, struct sockaddr *addr, int *addrlen)  sockfd: 是listen后的文件描述符。  addr,addrlen是用来给客户端的程序填写的, 服务器端只要传递指针就可以了。 bind, listen和accept是服务器端用的函数,accept调用时, 服务器端的程序会一直阻塞到有一个客户程序发出了连接。 accept成功时返回最后的服务器端的文件描述符, 这个时候服务器端可以向该描述符写信息了,失败时返回-1。 

⑦ 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,此时出现了错误.我们要根据错误类型来处理。 

⑨ 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  :等待所有数据

3.举例 我们将使用TCP协议提供的服务,组成一个简单的重复型的网络时间服务器。 在一台EV44B0II系统中启动服务程序并指定服务端口。在另外一台EV44B0II系统中启动客户端程序并指定服务器IP地址和服务端口。服务器将接收该服务,并返回服务器本地的系统时间。

本程序使用TCP协议,可以工作在服务器或客户端状态。 使用的默认端口号为9988。 程序流程图如下:

附程序清单: /*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>

#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_SERVER1 #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]; 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; 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"); exit(1); } /* 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) { 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);

while(1) {/* loop service*/ fd_service = accept(fd_listen, (struct sockaddr *)&sn, &sa_len); if (fd_service < 0) { perror("accept failed"); exit(1); }

else { printf("\nget service request from %s \n",inet_ntoa(sn.sin_addr)); } start = time(0); *(int *)buffer = start;

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

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

首先配置开发板IP地址(ifconfig eth0 10. 10. 16. 220 netmask 255. 255. 255 首先配置开发板IP地址(ifconfig eth0 10.10.16.220 netmask 255.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 其次启动客户端程序,在超级终端中,进入目录/var/tmp ,键入 ./nettime –c 10.10.16.220 –o 8888 回车。 超级终端显示客户端程序输出了服务器端的以秒为单位的时间:“net server time 333s”。

11.2.2 web服务器应用 1. Web服务器的原理 从原理上来说,Web服务器与其他在socket端口进行监听的应用程序差别不大,都是监听并接收用户请求,遵循HTTP协议,根据请求内容和类型,使用串行和并行方式提供相应的服务。

Web服务的客户端一般是Web浏览器,如IE 、Mozila等。

由于HTTP协议是基于请求/响应范式的(相当于客户机/服务器)。 一个客户机与服务器建立连接后,发送一个请求给服务器,请求方式的格式为:统一资源标识符(URL)、协议版本号,后边是MIME信息包括请求修饰符、客户机信息和可能的内容。

服务器接到请求后,给予相应的响应信息,其格式为一个状态行,包括信息的协议版本号、一个成功或错误的代码,后边是MIME信息包括服务器信息、实体信息和可能的内容。 许多HTTP通讯是由一个用户代理初始化的并且包括一个申请在源服务器上资源的请求。

最简单的情况可能是在用户代理和服务器之间通过一个单独的连接来完成。在Internet上,HTTP通讯通常发生在TCP/IP连接之上。 缺省端口是TCP 80,但其它的端口也是可用的。

2. 基于uClinux的Web技术 uClinux下,主要有三个Web服务器:httpd,thttpd和boa。 Httpd是最简单的Web服务器,功能比较简单,不支持认证和CGI。thttpd和boa均支持认证和CGI,功能比较丰富,其中boa是一个单任务的http服务器,源代码开放,性能比较高,占用系统资源比较少。

boa服务器在接收到请求时,不启动多个服务进程处理多个请求,在一个进程内处理所有服务请求。 对于CFG程序,将生成新的进程来处理。所以在嵌入式应用中使用boa比较多。

由于目前的uClinux还不支持ASP、PHP等动态网页技术,所有在uClinux下通过CGI技术连接web页和本地程序,提供动态和交互的特性。 CGI程序是本地的程序,需要编译成可执行文件,以便在被CGI调用时运行。

Web服务器将用户数据传递给CGI程序,并重定向CGI程序的输出到Web页,CGI程序在处理时要将数据封装成HTML形式发送到输出,这样在客户端将看到对应的Web页。

boa的配置文件boa.conf:在boa.conf中,对boa的一些配置项进行了配置,这些配置项大多可以使用默认值。 如PORT等。其中Document Root指定了web服务器的根,所有的web文件的查找都从这个根开始,所以如果要根据应用中的需要修改。

本程序将他们修改为 /tmp 。 在mime.type中,定义了目前boa支持的文件后缀,以及对应的类型。 如image/gif ――gif。通过这种对应关系,boa确定了如何处理这种后缀的文件,是显示,还是下载,还是执行。

本程序将在EV44B0II uClinux系统中,启动一个web服务器,并添加简单的网页和CGI程序。 这里要对EV44B0II Linux程序包中的一些文件进行修改:

在对内核配置时,选择 boa程序 和 cgi 。 将cgi_generic文件夹下的cgi.c 和 makefile,覆盖到程序包中的/usr/cgi_generic/。 将 boa文件夹下的hash.c,覆盖到程序包中的/usr/boa/src。 将rc文件,覆盖到程序包中的/vendor/Miectek/44b0。

EV44B0II uClinux系统中文件系统使用的是ROMFS和运行于RAM的EXT2(RAMDISK),其中ROMFS是只读的,RAMDISK是可以读写的。 为了可以通过FTP等工具更新网页, rc文件在/tmp下创建文件夹cgi_bin,然后将index.html,boa.conf和mime.type拷贝到/tmp下,将CGI程序拷贝到/tmp/cgi_bin下。到此建立了boa运行的基本环境。 要运行boa,使用 “> boa –c /tmp/ & ”命令。

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 127.0.0.1

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 eth0 172.20.3.44 #login /* 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(); } 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); /* 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); }

4. 运行过程 (1)对程序修改后,重新编译内核并焼写烧写到EV44B0II评估板。 (2) 启动EV44B0II评估板。确认EV44B0II的IP地址。 (3) 运行boa -c /tmp/ & ,启动web服务器。

(4) 打开PC上的浏览器(如IE6或Netscape)输入EV44B0II的IP作为网址,例如http://192. 168. 1 (4) 打开PC上的浏览器(如IE6或Netscape)输入EV44B0II的IP作为网址,例如http://192.168.1.125/ ,这时就可以看到要测试的网页内容。网页内容是/tmp/index.html文件。

(5) 在lamp number 文本框中,写入“1”,然后点击 “display”。 (6) EV44B0上的数码管显示对应数字。 (7) 在浏览器上显示“Demo Web Page”。

11.3 音频设备应用 S3C44B0II包含一个IIS总线接口,它可以作为连接8位或16位立体声编解码集成电路的接口,IIS总线接口提供内置FIFO的DMA传送模式,可以同时接收和发送,或单独接收和发送。

IIS总线接口可以发送和接收采样量化过的声音数据,它为外部的CODEC芯片提供工作主时钟,数据收发时钟和左右声道选择信号。 具体的采样量化工作留给CODEC芯片完成。在接收方向,CODEC将模拟声音采样量化;在发送方向,CODEC将声音数据还原为模拟声音。 EV44B0II使用的CODEC芯片是UDA1341TS。

UDA1341和IIS的驱动程序,位于/Linux/drivers/char目录下。 它们是ev44b0_sound.c、ev44b0_sound.h、l3-ev44b0.c。 音频设备文件的设备名为/dev/audio。

11.3.1 常用音频文件格式 1. WAV文件 (1) 概述 WAV是Microsoft Windows本身提供的音频格式,由于Windows本身的影响力,这个格式已经成为了事实上的通用音频格式。

不客气地说,它实际上是Apple电脑的AIFF格式的克隆。 通常我们使用WAV格式都是用来保存一些没有压缩的音频,但实际上WAV格式的设计是非常灵活(非常复杂)的,该格式本身与任何媒体数据都不冲突,换句话说,只要有软件支持,你甚至可以在WAV格式里面存放图像。

之所以能这样,是因为WAV文件里面存放的每一块数据都有自己独立的标识,通过这些标识可以告诉用户究竟这是什么数据。

在WINDOWS平台上通过ACM(Audio Compression Manager)结构及相应的驱动程序(在这里通常称为CODEC,编码/解码器),可以在WAV文件中存放超过20种的压缩格式,比如ADPCM、GSM、CCITT G.711、G.723等等,当然也包括MP3格式。

虽然WAV文件可以存放压缩音频甚至mp3,但由于它本身的结构注定了它的用途是存放音频数据并用作进一步的处理,而不是像mp3那样用于聆听。

目前所有的音频播放软件和编辑软件都支持这一格式,并将该格式作为默认文件保存格式之一。 这些软件包括:Sound Forge, Cool Edit Pro, WaveLab等等。由于WAV的支持实在是太广泛了,可以说,即使Windows退出历史舞台,WAV格式也不会消亡。

(2) WAV文件的结构 多媒体技术近年来发展很快,较好品质的声卡可以提供16位的立体声及44KHZ的播放录制能力. 它不仅可以提供原音逼真的取样,其合成的音质也十分理想,有的声卡还加入了数字信号处理器

可编程控制的DSP具有强大的运算能力,它可以用来作声音信息的压缩和一些特殊效果的处理。 具有此功能的声卡提供的WAV文件可以满足语音特征识别的要求。

在Windows环境下,大部分的多媒体文件都遵循着一种结构来存放信息,这种结构称为"资源互换文件格式"(Resources lnterchange File Format),简称RIFF。

例如声音的WAV文件、视频的AVI文件等等均是由此结构衍生出来的。 RIFF可以看做是一种树状结构,其基本构成单位为chunk,犹如树状结构中的节点,每个chunk由"辨别码"、"数据大小"及"数据"所组成。

辨别码由4个ASCII码所构成,数据大小则标示出紧跟其后数据的长度(单位为Byte),而数据大小本身也用掉4个Byte,所以事实上一个chunk的长度为数据大小加8。

一般而言,chunk本身并不允许内部再包含chunk,但有两种例外,分别为以“RIFF”及“L1ST”为辨别码的chunk。 而针对此两种chunk,RIFF又从原先的"数据"中切出4个Byte。 此4个Byte称为"格式辨别码",然而RIFF又规定文件中仅能有一个以"RIFF"为辨别码的chunk。

只要依循此一结构的文件,我们均称之为RIFF文档。此种结构提供了一种系统化的分类。 如果和MS DOS文件系统作比较,"RIFF"chunk就好比是一台硬盘的根目录,其格式辨别码便是此硬盘的逻辑代码(C:或D:),而"L1ST"chunk即为其下的子目录,其他的chunk则为一般的文件。

至于在RIFF文件的处理方面,微软提供了相关的函数。 视窗下的各种多媒体文件格式就如同在磁盘机下规定仅能放怎样的目录,而在该目录下仅能放何种数据。

WAV为WAVEFORM(波形)的缩写。声音文件的结构如图11-6所示,“RIFF”的格式辨别码为“WAVE”。

整个文件由两个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;

其意义分别为: wFormatTag:记录着此声音的格式代号,例如WAVE_FORMAT_PCM,WAVE_F0RAM_ADPCM等等。 nChannels:记录声音的频道数。 nSamp1esPerSec:记录每秒取样数。

nAvgBytesPerSec:记录每秒的数据量。 nBlockA1ign:记录区块的对齐单位。 wBitsPerSample:记录每个取样所需的位元数。

“data”Chunk包含真正的声音数据。Window目前仅提供WAVE_FORMAT_PCM一种数据格式,所代表的意义是脉派编码调制(Pu1se Code Modulation)。 针对此格式,Windows定义了在"data"的chunk中数据的存放情形,列出了四种不同频道数及取样所需的位元数以及位元位置的安排。

  "RIFF" 频道0 频道0 频道0 频道0   xxxx nChannels=1,wBitsPerSample=8   "WAVE" 频0(左) 频道1(右) 频道0(左) 频道1 (右)   "fmt "   nChannels=2,wBitsPerSample=8   sizeof(PCMWAVEFORMAT)

struct of PCMWAVEFORMAT 频道0(低位) 频道0(高位) 频道0(低位)频道0(高位)   "data" nChannels=1,wBitsPerSample=16   xxxx 频道0(低位) 频道0(高位) 频道0(低位)频道0(高位)   (低位) (高位) (低位) (高位)   wave form data nChannels=2,wBitsPerSample=16

2. mp3格式 (1) 概述 mp3是Fraunhofer-IIS研究所的研究成果。mp3是第一个实用的有损音频压缩编码。 在mp3出现之前,一般的音频编码即使以有损方式进行压缩能达到4:1的压缩比例已经非常不错了。

但是,mp3可以实现12:1的压缩比例,这使得mp3迅速地流行起来。

衡量mp3文件的压缩比例通常使用比特率来表示。 这个术语的英文是bps,表示每1秒钟的音频可以用多少个二进制比特来表示。通常比特率越高,压缩文件就越大,但音乐中获得保留的成分就越多,音质就越好。

由于比特率与文件大小音质的关系,所以后来又出现了vbr(Variant Bitrate 可变比特率)方式编码的mp3. 这种编码方式的特点是可以根据编码的内容动态地选择合适的比特率,因此编码的结果是在保证了音质的同时又照顾了文件的大小,结果大受欢迎。

其实mp3的编码标准本来就支持这种压缩方式, 但是第一个将此功能实现的反而是一个第三方工具:曾经非常有名的Xing Technology公司。

由于mp3是世界上第一个有损压缩的编码方案,所以可以说所有的播放软件都支持它,否则就根本没有生命力。在制作方面,也曾经产生了许多第三方的编码工具。 不过随着后来Fraunhofer-IIS宣布对编码器征收版税之后很多都消失了。

目前属于开放源代码并且免费的编码器是LAME (Lame Ain‘t Mp3 Encoder,)。

应该说,到了现在,MP3确实显现出疲态了。

不过由于mp3的影响力实在是太大了,支持mp3的软件多如牛毛,更别提众多支持mp3的硬件播放器,如MPMAN,DiscMan,CD/VCD/DVD机等等。 一句话,它依然是世界上最流行的音频压缩技术,所以要它真正退出舞台相信还有好一段时间。

(2) MP3的文件结构 MPEG音频文件没有文件头而是由很多独立的数据帧构成,每个帧都是独立的可以被单独播放,每个帧都有自己的帧头和音频信息。 这些也就是MPEG文件可以随意切割成为可能。

帧头是有32bits(4bytes)构成,起始的11bit是帧同步信息。帧可以有CRC校验信息,也可以没有。 一般来说都没有CRC校验。CRC校验信息为16bit长,它紧跟在帧头的后面,在校验信息后就是经过压缩的音乐文件数据了。

下面给出一个MPEG文件的形象描述: AAAA AAABBCCD EEEEFFGH I I J JKLMM A字段为11bit 帧同步标志。 B字段为2bit MPEG的版本信息 00 MPEG Version2.5 01保留 10 MPEG Version2 11 MPEG Version1

C字段为2bit 层描述 00 保留 01 Layer 3 10 Layer 2 11 Layer 1

D字段为1bit 校验信息标志位 0 有CRC校验 帧头后紧跟16bit校验信息 1 无CRC校验 帧头后紧跟数据 E字段位4位 比特率 MP3文件的比特率表如表11-1所示。

其中比特率的单位kbps,V1 是指MPEG Version1,V2是指MPEG Version2和MPEG Version2 其中比特率的单位kbps,V1 是指MPEG Version1,V2是指MPEG Version2和MPEG Version2.5,L1是指 Layer 1,L2是 Layer 2,L3是指Layer 3,Free是指动态比特率即VBR格式,Bad是指一个不合法的值,F字段位2bit 采样频率。 MP3文件的采样率表如表11-2所示。

表11-2 MP3文件的采样率表

G字段1bit Padding位 H字段1bit 私用位。作用不定 I字段 2bit 声道模式 00 立体声 01 Jonit立体声 10 双声道 11 单声道 J字段 2bit 模式扩展(只有在Jonit立体声模式下才有意义) MP3文件的播放模式表如表11-3所示。

表11-3 MP3文件的播放模式表

K字段1bit 版权信息 0 无版权 1 有版权 L字段 1bit 拷贝信息 0 拷贝版 1 原始音乐 M字段 2 bit 加重 00 无 01 50/15ms 10 保留 11 CCI T J.17 帧大小的计算方法: FrameSize = 144×比特率÷采样率+Padding

例如: 比特率=8000, 采样率=1000, Padding=0 可得FrameSize=417 bytes。 在MPEG音乐文件的最后128个字节描述了此音乐的作者,唱片集,标题,出版日期,和作者的类别等信息 AAABBBBB BBBBBBBB BBBBBBBB BBBBBBBB BCCCCCCC CCCCCCCC CCCCCCCC CCCCCCCD DDDDDDDD DDDDDDDD DDDDDDDD DDDDDEEE EFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFG

A字段 3 字节 必须位“TAG” B字段 30 字节 标题 C字段 30 字节 作者名 D字段 30 字节 唱片集名 E字段 4 字节 出版日期 F字段 30 字节 评论信息 G字段 1 字节 类别

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

附源程序: #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; 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))) { 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" ); return 0; } 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" ); return 0; }

printf("File length = %d\n",wave_head->file_length); 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);

if((wave_head->format3[0]. ='d')||(wave_head->format3[1] 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; else length = len; result = read(fd_file,buf,length);

if (result < 0) { printf("Read File error.\n"); return 0; } ret = write(fd_dev,buf,result);

if (ret < 0) { printf("Write Audio error.\n"); return 0; } len -= result;

close(fd_file); printf("Close Wave File : %s\n",WAVEFILE); 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" ); return 0; } fprintf(stderr,"Record will start after 3 seconds, and record time will be 3s.\n"); sleep(3);

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++) { 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 ++; ioctl(fd_dev,0x4d00,&mix); write(fd_dev,buf,600000); sleep(4); } /*test Treble*/

for (i =0 ; i < 4; i++) { fprintf(stderr,"Treble ++\n"); 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; ioctl(fd_dev,0x4d00,&mix); write(fd_dev,buf,600000); sleep(4); } 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 = 600000 + 44; wave_head->nDataBytes = 600000;

if (write(fd_file,(char. )wave_head,sizeof(struct wave_s)) 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" ); free(buf); close(fd_dev); close(fd_file); return 0; }

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

close(fd_file); fprintf(stderr, "Save file OK\n" ); free(buf); close(fd_dev); return 0; }

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

11.4 键盘及LCD显示应用

11.4.1 LCD介绍 1. 概述 长期以来,常见的掌上电脑(PDA)等小型手持式设备上,由于硬件条件的限制,我们看到的显示器件通常是单色LCD,用户界面也非常简单,几乎看不到PC机上美观整齐的图形界面(GUI)支持因为早期嵌入式处理器的速度有限,在处理图形和多媒体数据方面显得力不从心。

随着高性能嵌入式处理器的普及和硬件成本的不断降低,尤其是AFM系列处理器的推出,嵌入式系统的功能也越来越强。 在多媒体应用的推动下,彩色LCD也越来越多地应用到了嵌入式系统中,如新一代掌上电脑PDA多采用TFl显示器件,支持彩色图形界面,图片显示和视频媒体播放。

掌上电脑(PDA)的操作系统有微软WindowCF, Pa InflS等。 而Linux做为开放源代码的操作系统也在市场中占据了一席之地。 由于Linux成本低廉,任何人都可以得到其源代码并在其基础上进行开发,成为各家厂商极力发展的操作系统,加上其核心小,潜力可观。

在应用需求的推动下,Linux下也出现了许多图形界面软件包,如MiniGUI, Trolletech公司的Embedded QT等,其图形界面及开发工具与Windows CE不相上下。 在图形软件包的开发和移植工作中都牵涉到底层LCD的驱动问题。

LCD控制器的功能是产生显示驱动信号,驱动LCD显示器。 用户只需要通过读写一系列的寄存器,完成配制和显示控制。 Nx1中的LCIV制器可支持单色/彩色, LCD显示器。支持彩色,Fl时,可提供4/8/12/1啦颜色模式,其中16位颜色模式下可以显示64研中颜色。

配置l〔1才空制器重要的一步是指定显示缓冲区,显示的内容就是从缓冲区中读出的,其大小由屏幕分辨率和显示颜色数决定. 在本例中,笔者采用的是夏普LQ035Q2DD54 TFT,显示模块,在240ⅹ320分辨率下可提供16位彩色显示。

2. Linux的帧缓冲设备 帧缓冲(Framebuffer)是Linux伪显示设备提供的一个接口,把显存抽象后的一种设备,他允许上层应用程序在图形模式下直接对显示缓冲区进行读写操作。 这种操作是抽象的,统一的。

用户不必关心物理显存的位置、换页机制等等具体细节。这些都是由Framebuffer股备驱动来完成的。 帧缓冲驱动的应用广泛,在Linux的桌面系统中,X Windows服务器就是利用帧缓冲进行窗口的绘制。

尤其是通过帧缓冲可显示汉字点阵,成为Linux汉化的唯一可行方案。 帧缓冲设备对应的设备文件为/dev/fb*如果系统有多个显示卡,Linux下还可支持多个帧缓冲设备,最多可达3外,分别为/dev/fb0---/dev/fb31,而/dev/fb为当前缺省的帧缓冲设备

通常指向/dev/fb0当然在嵌入式系统中支持一个显示设备就够了。 帧缓冲设备为标准字符设备,主设备号为29,次设备号则从0到310分别对应/dev/fb0----dev/fb3l。

通过/dev/fb,应用程序的操作主要有这几种: 读写(read/write) /dev/fb相当于读泻屏幕缓冲区。例如用cp /dev/fb0 tmp命令可将当前屏幕的内容拷贝到一个文件中,而命令cp tmp > /dev/fb0则将图形文件tmp显示在屏幕上。

映射MAP操作 由于Linux工作在保护模式,每个应用程序都有自己的虚拟地址空间,在应用程序中是不能直接访问物理缓冲区地址的。 为此,Linux在文件操作file operation蛤构中提供了MMAP涵数,可将文件的内容映射到用户空间。

对于帧缓冲设备,则可通过映射操作,可将屏幕缓冲区的物理地址映射到用户空间的一段虚拟地址中,之后用户就可以通过读写这段虚拟地址访问屏幕缓冲区,在屏幕上绘图了。

而且若干个进程可以映射到同一个显示缓冲区。 实际上,使用帧缓冲设备的应用程序都是通过映射操作来显示图形的。 由于映射操作都是由内核来完成,下面我们将看到,帧缓冲驱动留给开发人员的工作并不多。

I/O控制对于帧缓冲设备 对设备文件的ioctl操作可读取设置显示设备及屏幕的参数,如分辨率,显示颜色数,屏幕大小等等。ioctl的操作是由底层的驱动程序来完成的。

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

典型程序段如下: #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); /*取得屏幕相关参数*/ 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指针读写缓冲区*/ //部分省略 }

3. 汉字显示 (1) 汉字存储和表示方法的介绍 输入码是为了使输入设备将汉字输入到计算机而专门编制的一种代码。 目前已经出现了上百中汉字输入方案,常见的有国标码,区位码,拼音码和五笔字型等。

国标码和区位码是专业人员使用的一种汉字编码,它是以数字代码来区别每个汉字的。 拼音码是最容易学校的一种,但它的重码太多,检字太慢。 五笔字型则是以结构来区分每个汉字,它的重码烧,是目前推广的一种比较简单,易学,易记的的输入码。

我国在1981年颁布了<<通用汉字字符集(基本集)及其交换标准>>GB2313-80方案,简称国标码。 它把6763个汉字归结在一起称为汉字基本字符集,在根据使用频度分为两级,第一级为3755个汉字,按拼音排序;第二级为3008个汉字;按部首排序。

此外,还有各种符号,数字,字母等682个。总计7445个汉字,字母等。 由于一个字节最多只能表示256种不同的字符,因而必须至少用两个字节来表示汉字,国标码就是用两个字节表示的,第一级3755个汉字安排在编号3021H~577AH之间。

区位码是将GB2312-80方案种的字符按其位置划分为94个区,每个区94个字符。区的编号是从1~94,区内字符编号也是从1~94。 其中1~9区为图形字符区,包括符号,序号,数字,拉丁字母,日文假名,希腊字母,俄文字母,汉语拼音符号等,共682个。

10~15区为空白区,16~55为第一级汉字区(含3755个汉字),56~87区为第二级汉字区(含3008个汉字),88~94区为空白区。 10~15区为空白区,16~55为第一级汉字区(含3755个汉字),56~87区为第二级汉字区(含3008个汉字),88~94区为空白区。 GB2312-80方案种的每个汉字可用区位码表示,前两位是区号,后两位是区内字符编号。区位码是国标码的变形,他们之间的关系可用下面的公式表示: 国标码(十六进制) = 区位码(十六进制) + 2020H [12]

他们是用数字进行编码的,即用4为数字串代表一个汉字输入。 机内码是指机器内部处理和存储汉字的一种代码。常用的一种汉字机内码是用两个字节表示一个汉字。 它是在国标码的基础上,在每个字节的最高位置”1”作为汉字标记产生的。机内码与国标码之前的转换关系为: 机内码(十六进制)=国标码(十六进制) + 8080H。

例如,“京”字的国标码为3E29H,其机内码为BEA9H。 汉字的显示一般是16×16的点阵 因此一个汉字需要16×2=32个字节来存储字模,在嵌入式系统设计中实现汉字的显示有两种方案,第一种是使用通用汉字字库,这中方案适合于在应用中要大量显示不同的汉字

因此可以把整个汉字字库放入内存中,这种方案是以浪费内存为代价的,因为16×16点阵的汉字字库一般在260KB左右。

① 第一种解决方案 在第一种方案种汉字点阵信息提取的代码实现: 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]数组种存放汉字的点阵信息。

② 第二种解决方案 第二种方案的汉字显示的代码实现: 构造简单汉字字库其结构如下: 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]是按行存放汉字的点阵信息。

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

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

/*************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];

/**************键盘定义变量和端口***************/ #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 (40000000) static int delayLoopCount=400;

/**********播放函数的定义部分****************/ #include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <sys/stat.h>

#include <fcntl.h> #include <Linux/sound.h> #define SIOC_OUT 0x20000000 #define SIOC_IN 0x40000000

#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 static unsigned int sbuffer[AUDIOBUFLEN/4]; int biao=1;

struct wave_s { unsigned char format[4]; /* Should be "RIFF"*/ unsigned int file_length; /* The file length - 8*/ unsigned char format2[8]; /* Should be "WAVEfmt "*/ unsigned int unknow0;

unsigned short wFormatTag; unsigned short nChannels; unsigned int nSamplesPerSec; unsigned int nAvgBytesPerSec; unsigned short nBlockAlign;

unsigned short wBitsPerSample; unsigned char format3[4]; /* "data" or other string*/ unsigned int nDataBytes; /* The real wave bytes*/ };

int play(char *music) { char *WAVEFILE; int fd_dev,fd_file; int result,len,i; struct wave_s *wave_head; char *buf =(char *) sbuffer; char ctl_buf[100]; WAVEFILE=music;

if (-1 == (fd_dev = open( "/dev/audio", O_WRONLY))) { fprintf(stderr, "Failed to open audio device\n" ); return 0; }

if ( -1 == (fd_file = open(WAVEFILE, O_RDONLY))) { 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" ); return 0; } wave_head = (struct wave_s *) buf;

if ((wave_head->format[0]. = 'R')||(wave_head->format[1] 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" ); return 0; } wave_head->format2[7] = 0;

if (strcmp(wave_head->format2,"WAVEfmt") != 0) { fprintf(stderr, "File format (2) error.\n" ); return 0; } sprintf(ctl_buf,"%d",wave_head->nChannels); ioctl(fd_dev,SNDCTL_DSP_CHANNELS,ctl_buf);

sprintf(ctl_buf,"%d",wave_head->nSamplesPerSec); ioctl(fd_dev,SNDCTL_DSP_SPEED,ctl_buf); 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" ); } len = wave_head->nDataBytes;

while (len) { unsigned int length,ret; if (len > AUDIOBUFLEN) length = AUDIOBUFLEN; else length = len; result = read(fd_file,buf,length);

if (result < 0) { printf("Read File error.\n"); return 0; } ret = write(fd_dev,buf,result);

if (ret < 0) { printf("Write Audio error.\n"); return 0; }

len -= result; } close(fd_file); close(fd_dev); return 0;

/***********播放函数部分结束***********/ char name[5][10]; int num1; int gg1; char *pp1; char pname[10];

/******************键盘部分函数定义***************/ void Delay(int time) { int i,adjust=0; if(time==0) time=200; adjust=1; delayLoopCount=400;

rWTCON=((MCLK/1000000-1)<<8)|(2<<3); // 1M/64,Watch-dog,nRESET,interrupt disable rWTDAT=0xffff; rWTCNT=0xffff; rWTCON=((MCLK/1000000-1)<<8)|(2<<3)|(1<<5); // 1M/64,Watch-dog enable,nRESET,interrupt disable }

for(;time>0;time--) for(i=0;i<delayLoopCount;i++); if(adjust==1) { rWTCON=((MCLK/1000000-1)<<8)|(2<<3); i=0xffff-rWTCNT; // 1count/16us????????? delayLoopCount=8000000/(i*64); //400*100/(i*64/200) }

int readkey() { unsigned int temp,m; int i,lie; int flag; start:

do { rpg&=0x0f; //高位送0 temp=((~(rpf&0x1E0))>>5); temp&=0xf; }while(temp==0); Delay(100);

temp=((~(rpf&0x1E0))>>5); temp&=0xf; if(temp==0) goto start; flag=0; m=0x8;

for(i=0;i<4;i++) { m<<=1; rpg=~m; temp=((~(rpf&0x1E0))>>5); temp&=0xf;

switch(temp) { case 0x08:lie=4;flag=1;break; case 0x04:lie=3;flag=1;break; case 0x02:lie=2;flag=1;break; case 0x01:lie=1;flag=1;break; default: flag=0;break; }

if(flag==1) { do { //printf(" "); rpg&=0x0f; //高位送0 temp=((~(rpf&0x1E0))>>5); temp&=0xf; }while(temp!=0);

return (4*i+lie); } return -1;

/**************键盘部分结束**************/ int select1(const struct dirent *dir) { if(strstr(dir->d_name,"wav")>1) return 1; else return 0; }

int main() { int i; int u,m,c; struct fb_fix_screeninfo fb_fix; struct fb_var_screeninfo fb_var; fd = open("/dev/fb0",O_RDWR);

if(ioctl(fd, FBIOGET_FSCREENINFO, &fb_fix) == -1 || ioctl(fd, FBIOGET_VSCREENINFO, &fb_var) == -1) { printf("Error reading screen info:\n"); goto out; }

rPCONF=0x2A; rPUPF=0x0; rPDATG=0x0; rPCONG=0x55FF; rPUPG=0x0;

if (fd < 0) goto out; blackall(); xianshi(3,1,"嵌"); xianshi(4,1,"入"); xianshi(5,1,"式"); xianshi(2,2,"歌");

xianshi(3,2,"曲"); xianshi(4,2,"点"); xianshi(5,2,"播"); xianshi(6,2,"系"); xianshi(7,2,"统"); xianshi(0,5,"播");

xianshi(1,5,"放"); xianshi(2,5,"中"); xianshi(0,7,"列"); xianshi(1,7,"表"); dir(); display(); close(fd);

while(1) { strcpy(pname,""); c=readkey(); if(c>0){ printf("%d\n",c); fd = open("/dev/fb0",O_RDWR);

if(ioctl(fd, FBIOGET_FSCREENINFO, &fb_fix) == -1 || ioctl(fd, FBIOGET_VSCREENINFO, &fb_var) == -1) { printf("Error reading screen info:\n"); goto out; }

if(c>num1) { xianshi(4,5,"无"); clear(5,5);clear(6,5);clear(7,5);clear(8,5); clear(9,5); display(); }

else { pp1=name[c-1]; printf("name[%d]:%s\n",c-1,name[c-1]); strcpy(pname,name[c-1]); strcat(pname,".wav"); printf("%s\n",pname);

for(gg1=0;gg1<strlen(name[c-1])/2;gg1++) { xianshi(4+gg1,5,pp1); pp1=pp1+2; }

display(); play(pname); close(fd); }

out: printf("\nclosing device\n"); close(fd); return 0; }

void cls() { int i; for(i=0;i<4800;i++) buffer[i]=0x00; write(fd,buffer,sizeof(buffer)); }

void blackall() { int i; for(i=0;i<4800;i++) buffer[i]=0xFF; }

void display() { write(fd,buffer,sizeof(buffer)); }

void clear(int x,int y) { int i,offset; offset=320*y+2*x; for(i=0;i<16;i++) buffer[offset+20*i]=0xFF; buffer[offset+20*i+1]=0xFF; }

void xianshi(int x,int y,char *s) { int i,offset,m=0; int fp; unsigned char Data1[32]; unsigned char qh,wh; unsigned int offset1; fp=open("/var/tmp/HZK16",0);

qh=*(s)-0xa0; wh=*(s+1)-0xa0; offset1=(94*(qh-1)+(wh-1))*32; lseek(fp,offset1,SEEK_SET); read(fp,Data1,32); close(fp); offset=320*y+2*x;

for(i=0;i<16;i++) { buffer[offset+20*i]=~Data1[m++]; buffer[offset+20*i+1]=~Data1[m++]; }

dir() { struct dirent **namelist; int i,total,k,m,gg; char *pp; char s[20],s2[20]; total=scandir("/tmp/",&namelist,select1,0); num1=total;

if(total<0) perror("scandir"); else { for(i=0;i<total;i++) strcpy(s,namelist[i]->d_name);

k=strstr(s,".wav"); m=k-(int)s; strncpy(s2,s,m); s2[m]='\0'; strcpy(name[i],s2); pp=s2;

if(*pp>128) { switch(i) case 0: xianshi(1,8+i,"1"); break; case 1: xianshi(1,8+i,"2"); break;

case 2: xianshi(1,8+i,"3"); break; default:break; }

for(gg=0;gg<strlen(s2)/2;gg++) { xianshi(2+gg,8+i,pp); pp=pp+2; }

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

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