1.1.1 娱乐助手简介及框架
(1) 娱乐助手系统介绍:本系统主要设计目标就是在实验箱上进行娱乐相关操作,并将相关操作过程在液晶显示屏上显示。所以本课题所设计的系统主要完成三项任务:一、显示界面的设计与实现,系统使用液晶显示屏完成这个任务;二、娱乐助手程序设计与编写;三、用户信息输入的采集。
(2) 娱乐助手的总体框架:根据本系统的主要功能,把娱乐助手划分为三大部分,具体参照图1.1:
图1.1 系统总体框架图
1.1.2 系统功能模块划分及描述
参照上面的总体框架,本文所研究的娱乐助手系统可以把组成系统的三大部分划分为以下四个功能模块:
(1) 系统初始化模块:本模块是系统的基础模块,负责对系统进行界面的初始化和模块展示。
(2) 娱乐选择模块:本模块是整个系统的最关键模块,它负责处理用户发出的控制请求,用户调用某个功能,则主程序跳转到相关程序代码段。该功能模块被调用后调用系统显示模块,如果相关功能模块需要用到键盘输入,则会调用键盘输入。
(3) 系统输入控制模块:本模块把矩阵键盘键盘作为输入端,用户通过键盘进行功能选取以及信息输入。键盘扫描程序通过扫描按键采集输入信息。输入控制程序对信息进行筛选、封装。
(4) 系统显示控制模块:本模块通过LCD进行功能选择,输入和游戏等信息显示操作。
1.1.3 系统使用说明
本文所研究的系统是一个娱乐助手的简单系统,由于硬件条件的限制,该系统缺少一些市售娱乐系统的可操作性和娱乐性。为了方便用户使用本系统,本文制定了一些针对本系统的使用规定,下面进行详细的说明。
(1) 功能选择界面。
该界面显示四大选项,包括猜数字游戏、MP3播放、蜂鸣器播放音乐、键盘弹钢琴这四种娱乐功能,进入系统后通过矩阵键盘输入1、2、3、4可进行4种功能的选择。
(2) 猜数字界面
该界面程序会随机10个数字,每次随机数字会以LED灯的形式显示出来,用户需要用键盘输入自己读得的数字,随后LCD提示高了、低了和正确。
(3) MP3播放界面
该界面会调用Madplay,在超级终端上显示播放的音乐信息。
(4) 蜂鸣器音乐界面
该界面可以通过蜂鸣器放出内置的蜂鸣器节奏,达到蜂鸣器放音乐效果。
(5) 键盘钢琴界面
在该界面下,通过按键可以发出1-7音调,当按下“*”时退回到选择界面。
1.2娱乐选择模块的设计与实现
1.2.1 系统设计要求
本文采用了传统的软件工程方法对娱乐助手进行了需求分析。通过分析需求和实验箱现有硬件,我们制定了详细的设计要求:
(1) 功能选择模块操作时按下按键可以立即跳转到相关模块操作,如果按下其余按键则系统无反应。
(2) 在猜数字模块,LED灯显示要准确,在按下按键后要及时提示正确错误或者按键太慢。在猜测10次后要返回功能选择模块。
(3) 在MP3播放和蜂鸣器播放结束后,程序自动回到功能选择界面。
(4) 在键盘弹钢琴界面,按下矩阵键盘后蜂鸣器立即发出相应音调,同时按下两个按键时,程序会按照按键的先后顺序调用蜂鸣器发出声音。
1.2.2 娱乐助手流程图
图1.2是娱乐助手运行过程的流程图。
图1.2 娱乐助手流程图
1.2.3 猜数字模块的具体实现
程序首先随机一个随机数,调用LED显示函数ioctl,同时检测按键是否按下。当按下时程序检测随机数字与按下数字是否相同。
LED电路原理图:
在实验平台的核心板上有四个LED灯。核心板上的四个LED都是直接与处理器相连,连接方式简单。其电路图如图3.3所示:
图1.3 LED电路图
上面左侧图为处理器引脚,右侧图为LED部分的电路,线路上的标签相同,表示这两条线的电气属性直接相连,例如左侧图中的nLED_1这条线与右侧图中nLED_1这条线直接相连。四个LED(LED1、LED2、LED3、LED4)采用共阳极连接方式,以LED1为例,当LED1左侧的电位为低电平时(即处理器的相应引脚输出低电平),在LED1上存在从右至左的电流流过,LED1处于导通状态,因而被点亮。当LED1左侧的电位为高电平时(即处理器的相应引脚输出高电平),在LED1上没有足够大的电流流过,LED1处于截止状态,因而未被点亮。因此,通过控制处理器相关引脚输出电平的高低即可达到控制对应LED亮灭的目的。
1.2.4 MP3播放器的具体实现
(1) 音频接口介绍
本试验箱使用的是I2S接口,I2S 接口(Inter-IC Sound)在20世纪80年代首先被飞利浦用于消费音频,并在一个称为LRCLK(Left/Right CLOCK)的信号机制中经过多路转换,将两路音频信号变成单一的数据队列。当LRCLK为高时,左声道数据被传输;LRCLK为低时,右声道数据被传输。与PCM相比,IIS更适合于立体声系统。当然,I2S的辩题也支持多通道的时分复用,因此可以支持多声道。
S3C2440内置I2S总线接口,可直接外接8/16比特的立体声CODEC,本开发板采用基于I2S总线的UDA1341芯片实现音频解码系统,该芯片内部寄存器的初始化和设置则是采用L3-bus总线连接控制实现的,在这里我们沿用了三星公板的设计,分别使用CPU的GPB2、GPB3、GPB4端口模拟实现L3-Bus规范的L3MODE、L3DATA、L3CLOCK,它们在初始化完UDA1341以后就不再有用了,因此这三条控制线也可以使用普通的单片机模拟实现。
图1.4 典型的数字音频系统电路
在Linux系统中,先后出现了音频设备的3种框架:OSS、ALSA和ASoC,这里Linux内核在配置的时候配置的是ALSA驱动框架,这里主要介绍ALSA驱动。在嵌入式为控制器中集成了PCM、I2S或AC’97音频接口,通过这些接口连接外部的音频解码器即可实现声音的AD和DA转换,如图3.4所示。
(2) Linux ALSA音频设备驱动
虽然OSS已经非常成熟,但它毕竟是一个没有完全开放源代码的商业产品,而ALSA (Advanced Linux Sound Architecture)恰好弥补了这一空白,它符合GPL,是在Linux下进行音频编程时另一种可供选择的声卡驱动体系结构,其官方网站为 http://www.alsa-project.org/。ALSA除了像OSS那样提供了一组内核驱动程序模块之外,还专门为简化应用程序的编写提供了相应的函数库,与OSS提供的基于ioctl的原始编程接口相比,ALSA函数库使用起来要更加方便一些。ALSA的主要特点有:
支持多种声卡设备
模块化的内核驱动程序
支持SMP和多线程
提供应用开发函数库(alsa-lib)以简化应用程序开发
支持OSS API,兼容OSS应用程序
ALSA 具有更加友好的编程接口,并且完全兼容于OSS,对应用程序员来讲无疑是一个更佳的选择。ALSA系统包括驱动包alsa-driver、开发包 alsa-libs、开发包插件alsa-libplugins、设置管理工具包alsa-utils、其他声音相关处理小程序包alsa-tools、特殊音频固件支持包alsa- firmware、OSS接口兼容模拟层工具alsa-oss共7个子项目,其中只有驱动包是必需的。
alsa-driver指内核驱动程序,包括硬件相关的代码和一些公共代码,非常庞大,代码总量达数十万行;alsa-libs指用户空间的函数库,提供给应用程序使用,应用程序应包含头文件asoundlib.h,并使用共享库libasound.so;alsa-utils包含一些基于ALSA的用于控制声卡的应用程序,如alsaconf(侦测系统中声卡并写一个适合的ALSA配置文件)、alsactl(控制ALSA声卡驱动的高级设置)、 alsamixer(基于ncurses的混音器程序)、amidi(用于读写ALSA RawMIDI)、amixer(ALSA声卡混音器的命令行控制)、aplay(基于命令行的声音文件播放)、arecord(基于命令行的声音文件录制)等。
目前ALSA内核提供给用户空间的接口有:
信息接口(Information Interface,/proc/asound)
控制接口(Control Interface,/dev/snd/controlCX)
混音器接口(Mixer Interface,/dev/snd/mixerCXDX)
PCM接口(PCM Interface,/dev/snd/pcmCXDX)
Raw迷笛接口(Raw MIDI Interface,/dev/snd/midiCXDX)
音序器接口(Sequencer Interface,/dev/snd/seq)
定时器接口(Timer Interface,/dev/snd/timer)
和OSS类似,上述接口也以文件的方式被提供,不同的是这些接口被提供给alsa-lib使用,而不是直接给应用程序使用的。应用程序最好使用alsa-lib,或者更高级的接口,比如jack提供的接口。
(3) madplay
mad是一种高质量的MPEG音频解码器。它目前支持MPEG-1和MPEG-2扩展到较低的采样频率,以及事实上的MPEG 2.5格式。三种音频层次全被实现。mad不支持MPEG-2多声道音频(虽然应该向后兼容)也不支持AAC。
mad具有以下特点:
24位PCM输出
100%定点(整数)的计算
全新的实现基于ISO / IEC标准
GNU通用公共许可证的条款下(GPL)
mad提供完整的24位PCM输出,应用程序使用了能够产生非常高质量的音频。甚至当输出设备只支持16位的PCM,应用程序可以使用额外的分辨率提高声音的动态范围通过使用抖动和噪声整形。使用整数运算而不是浮点数,它非常适合于架构没有浮点单元。所有的计算都是一个32位定点整数表示的。mad是一种新的ISO/IEC标准执行。mad不是ISO参考源或任何其他代码的推导。GPL的条款下OSI认证的开源软件,因为mad是可用的,它可以自由使用的其他软件,也可以立即获得商业许可的评价。
1.2.5 蜂鸣器播放音乐的具体实现
蜂鸣器简介及电路原理图
微处理器驱动蜂鸣器的方式有两种:一种是PWM 输出口直接驱动,另一种是利用I/O 定时翻转电平产生驱动波形对蜂鸣器进行驱动,在本系统中,我们使用PWM输出口直接驱动方式。PWM 输出口直接驱动是利用PWM 输出口本身可以输出一定的方波来直接驱动蜂鸣器。在单片机的软件设置中有几个系统寄存器是用来设置PWM 口的输出的,可以设置占空比、周期等等,通过设置这些寄存器产生符合蜂鸣器要求的频率的波形之后,只要打开PWM 输出,PWM 输出口就能输出该频率的方波,这个时候利用这个波形就可以驱动蜂鸣器了。比如频率为2000Hz 的蜂鸣器的驱动,可以知道周期为500μs,这样只需要把PWM 的周期设置为500μs,占空比电平设置为250μs,就能产生一个频率为2000Hz 的方波,通过这个方波再利用三极管就可以去驱动这个蜂鸣器了。
在实验平台的液晶显示器左侧配有一个蜂鸣器可用作此次实验,其电路原理图如图3.5所示。
图1.5 蜂鸣器电路图
PWM是指脉冲宽度调制,它是利用微处理器的数字输出来对模拟电路进行控制的一种非常有效的技术,广泛应用在从测量、通信到功率控制与变换的许多领域中。
脉冲宽度调制是一种模拟控制方脉冲宽度调制是利用微处理器的数字输出来对模拟电路进行控制的一种非常有效的技术,广泛应用在从测量、通信到功率控制与变换的许多领域中脉冲宽度调制是利用微处理器的数字输出来对模拟电路进行控制的一种非常有效的技术,广泛应用在从测量、通信到功率控制与变换的许多领域中。
随着电子技术的发展,出现了多种PWM技术,其中包括:相电压控制PWM、脉宽PWM法、随机PWM、SPWM法、线电压控制PWM等,而在镍氢电池智能充电器中采用的脉宽PWM法,它是把每一脉冲宽度均相等的脉冲列作为PWM波形,通过改变脉冲列的周期可以调频,改变脉冲的宽度或占空比可以调压,采用适当控制方法即可使电压与频率协调变化。可以通过调整PWM的周期、PWM的占空比而达到控制充电电流的目的。
操控PWM主要分为以下四步:
(1) PWM是通过引脚TOUT0输出的,而这个引脚是与GPB0复用的,因此要实现PWM功能首先要把相应的引脚配置成TOUT输出。
(2) 再设置定时器的输出时钟频率,它是以PCLK为基准,再除以用寄存器TCFG0配置的prescaler参数,和用寄存器TCFG1配置的divider参数。
(3) 然后设置脉冲的具体宽度,它的基本原理是通过寄存器TCNTBn来对寄存器TCNTn(内部寄存器)进行配置计数,TCNTn是递减的,如果减到零,则它又会重新装载TCNTBn里的数,重新开始计数,而寄存器TCMPBn作为比较寄存器与计数值进行比较,当TCNTn等于TCMPBn时,TOUTn输出的电平会翻转,而当TCNTn减为零时,电平会又翻转过来,就这样周而复始。因此这一步的关键是设置寄存器TCNTBn和TCMPBn,前者可以确定一个计数周期的时间长度,而后者可以确定方波的占空比。由于s3c2410的定时器具有双缓存,因此可以在定时器运行的状态下,改变这两个寄存器的值,它会在下个周期开始有效。
(4) 最后就是对PWM的控制,它是通过寄存器TCON来实现的,一般来说每个定时器主要有4个位要配置(定时器0多一个死区位):启动/终止位,用于启动和终止定时器;手动更新位,用于手动更新TCNTBn和TCMPBn,这里要注意的是在开始定时时,一定要把这位清零,否则是不能开启定时器的;输出反转位,用于改变输出的电平方向,使原先是高电平输出的变为低电平,而低电平的变为高电平;自动重载位,用于TCNTn减为零后重载TCNTBn里的值,当不想计数了,可以使自动重载无效,这样在TCNTn减为零后,不会有新的数加载给它,那么TOUTn输出会始终保持一个电平(输出反转位为0时,是高电平输出;输出反转位为1时,是低电平输出),这样就没有PWM功能了,因此这一位可以用于停止PWM。
播放音乐实现
通过定义频率数组和节拍数组,调用set_buzzer_freq函数设置蜂鸣器响频率,自定义Delay函数,延时值为节拍数组的值。这样定义好的频率和节拍就可以让蜂鸣器发出制定的音乐。
1.2.5 键盘弹钢琴的具体实现
定义1-7按键分别为音调1-7,每当按下按键时就会发出制定的音调,按照特定铺子弹奏就可以发出歌曲的声音。
1.3 系统输入模块的设计与实现
1.3.1 系统输入模块功能分析
本功能模块的主要功能有三个:一、设置键盘键值;二、扫描键盘,采集按键信息;三、获取用户输入信息并进行处理。
1.3.2 矩阵式键盘原理图
矩阵键盘是单片机外部设备中所使用的排布类似于矩阵的键盘组,在键盘中按键数量较多时,为了减少I/O口的占用,通常将按键排列成矩阵形式。在矩阵式键盘中,每条水平线和垂直线在交叉处不直接连通,而是通过一个按键加以连接。这样,一个8脚端口就可以构成4*4=16个按键,比之直接将端口线用于键盘多出了一倍,而且线数越多,区别越明显,比如再多加一条线就可以构成20键的键盘,而直接用端口线则只能多出一键(共计9键)。由此可见,在需要的键数比较多时,采用矩阵法来做键盘是合理的。
矩阵式结构的键盘显然比直接法要复杂一些,识别也要复杂一些,例如,行线通过电阻接正电源,并将列线所接的单片机的I/O口作为输出端,而行线所接的I/O口则作为输入。这样,当按键没有按下时,所有的输入端都是高电平,代表无键按下。列线输出是低电平,一旦有键按下,则输入线就会被拉低,这样,通过读入输入线的状态就可 得知是否有键按下了。
矩阵键盘来确定键盘上何键被按下本实验采用的是“行扫描法”。行扫描法又称为逐行(或列)扫描查询法,是一种常见的按键识别方法,如图3.6所示键盘,介绍过程如下。
(1) 首先判断键盘中有无按键按下。驱动程序中采用中断的方式来判断有无按键按下,将矩阵键盘横行各I/O引脚与中断函数关联,当有按键按下时,程序自动跳转到中断函数中,每一行配置了特有了中断号和中断名称,从而可以识别是哪一行的按键被按下;如果没有按键按下,程序将继续等待,不会向下运行。
(2) 然后判断按键所在列的位置,在初始时刻列线所在端口为输出状态且均为低电平,按键按下后,逐列将其所对应的I/O引脚电平置为高电平,然后读取已确定的行端口电平,若为低电平,则不是该列的按键按下;若为高电平,则可以判断按键的位置就在该列。
图1.6 矩阵键盘与中央处理器的连线图
图1.6是本实验箱的矩阵键盘与中央处理器的连线图,其中键盘的纵列(col)从左到右依次连接EINT15、EINT5、EINT4和EINT3,对应芯片的引脚依次为GPG7、GPF5、GPF4和GPF3;同理在横向(irq)从上到下依次连接EINT2、EINT1、EINT13和EINT14,对应的芯片引脚为GPF2、GPF1、GPG5和GPG6。
1.3.3 矩阵式键盘测试代码分析
int main(void)
{
int buttons_fd;
char buttons[2] = {‘0’, ‘0’};
char first_flag = 0;
buttons_fd = open(“/dev/dial_key”, 0);//打开设备
if (buttons_fd < 0) { //打开设备失败
perror(“open device buttons failed!”);
exit(1);
}
for (;;) {//持续检测键盘输入
int i;
char current_buttons[2];
char realchar;
if (read(buttons_fd, current_buttons, sizeof current_buttons) != sizeof current_buttons) {//等待键盘读入数据
perror(“read buttons:”);
exit(1);
}
if(first_flag == 0)//这里first_flag没有用,没有大括号会直接往下运行
first_flag = 1;
else
{ //判断是否和上一个按键相同
if(buttons[0]!=current_buttons[0]||
buttons[1]!=current_buttons[1])
{
buttons[0] = current_buttons[0];
buttons[1] = current_buttons[1];
switch(buttons[0])//识别按键行号
{
case 51:
switch(buttons[1])//识别按键列号
{
case 51:realchar=’1’;break;
case 50:realchar=’2’;break;
case 49:realchar=’3’;break;
case 48:realchar=’A’;break;
}break;
case 50:
switch(buttons[1])
{
case 51:realchar=’4’;break;
case 50:realchar=’5’;break;
case 49:realchar=’6’;break;
case 48:realchar=’B’;break;
}break;
case 49:
switch(buttons[1])
{
case 51:realchar=’7’;break;
case 50:realchar=’8’;break;
case 49:realchar=’9’;break;
case 48:realchar=’C’;break;
}break;
case 48:
switch(buttons[1])
{
case 51:realchar=’*’;break;
case 50:realchar=’0’;break;
case 49:realchar=’#’;break;
case 48:realchar=’D’;break;
}break;
}
printf(“key(%c) is pressed!\n”, realchar);
}
}
}
close(buttons_fd);
return 0;
}
1.4 系统显示模块的设计与实现
本系统使用一块七英寸LCD液晶屏作为主要显示,并且系统基于Linux的帧缓冲对LCD进行图形界面的设计。
1.4.1 显示系统界面设计
设计图形界面时本文把系统分为功能选择界面和具体操作界面。
功能选择界面图
功能操作界面图
1.4.2 LCD设备显示原理
Framebuffer是Linux为图形设备提供的一个抽象接口,它允许上层应用程序在图形模式下直接对显示缓冲区进行读写操作。由于Linux工作在保护模式,所以用户态进程无法像DOS那样使用显卡BIOS里提供的中断调用来实现直接写屏,Linux抽象出Framebuffer这个设备来供用户态进程实现直接写屏。在使用Framebuffer时,Linux是将显卡置于图形模式下的。Framebuffer就是模仿显卡的功能,相当于抽象的显卡硬件结构,实现了通过Framebuffer的读写直接对显存进行操作。用户可以将Framebuffer看成是显示内存的一个映像,将其映射到进程地址空间之后,就可以直接进行读写操作,而写操作可以立即反映在屏幕上。这种操作是抽象的、统一的。用户不必关心物理显存的位置、换页机制等具体细节,因为这些都是有Framebuffer设备驱动来完成的。Framebuffer设备还提供了若干ioctl命令,通过这些命令,可以获得显示设备的一些固定信息与显示模式有关的可变信息等[6]。
实现在LCD显示的关键是Framebuffer驱动程序的使用,利用驱动程序将LCD的显存与实验板的内存映射到一起,向映射的内存中写东西,然后刷新显存,LCD就会按照计算的位置在LCD屏幕上显示写入的数据信息。
要实现LCD的显示,首先需要清楚相关的硬件信息,包括屏幕的大小、能够映射的内存空间大小。其次,需要知道显示缓存与显示点的对应关系,而要搞清楚它们之间的关系必需弄明白液晶显示屏在当前显示状态下的显示灰度。灰度级别能够控制LCD显示的色彩信息,并且能够确定每一个像素是由多少位来控制的,如果1级灰度,那么在此情况下LCD只可以显示黑白两种颜色,一个bit位就可以一个像素点,也就是说一个内存单元可以容纳8个像素点的信息,只有了解了这些才能在写显存的时候确定好像素的颜色和位置等信息[7]。
1.4.3 字符的显示实现
设计思路是使用字模提取软件将A-Z等字模提取出来,然后调用自定义的wirte_single函数通过Framebuffer的方式显示在屏幕的指定位置。显示代码如下:
struct fb_var_screeninfo vinfo;
struct fb_fix_screeninfo finfo;
char *fbp = 0;
void wirte_single(int lenth,int height,char c[][1])
{
int x = 0, y = 0,z = 0;
long int location = 0;
int b,g,r;
vinfo.xoffset = lenth; // (计算屏幕图像在屏幕中间一块区域显示)Where we are going to put the pixel (x坐标偏移量:111) 要分清一块屏有宽和高,宽即用x坐标表示,高用y表示,和直角坐标系一样
vinfo.yoffset = height; // (y坐标偏移量:70) 即该区域的左上角的像素点坐标为(x,y)=(110,70),右下角的坐标为(x,y)= (110+420,70+340)
b = 0; // 即blue : 0000 0010
g = 0; // A little green 即green: 0000 0100
r = 255; // A lot of red 即red : 0000 0100
// Figure out where in memory to put the pixel
for ( y = 0; y < 16; y++ ) // 行扫描
for ( x = 0; x < 1; x++ ) { // 列扫描
for(z=0;z<8;z++) {
if(c[y][x]&(0x80>>z)) {
location = (x*8+z+vinfo.xoffset) * (vinfo.bits_per_pixel/8) + // 定位到具体哪一行的第几个像素
(y+vinfo.yoffset) * finfo.line_length; //定位到哪一行(即该行的第一个像素的地址) 这两句即是实现求某一个像素的地址的功能
if ( vinfo.bits_per_pixel == 32 ) { //
*(fbp + location) = b; // Some blue
*(fbp + location + 1) = g; // A little green
*(fbp + location + 2) = r; // A lot of red
*(fbp + location + 3) = 0; // No transparency
} else { //16bpp: r:g:b=5:6:5 //assume 16bpp
unsigned short int t = r<<11 | g << 5 | b;
*((unsigned short int*)(fbp + location)) = t;
}
}
}
}
}
int Show_title_in_LCD(char title[],int length,int height)//显示字符函数
{
int fbfd = 0,i=0;
char c;
struct fb_cmap cmapinfo;
long int screensize = 0;
//定义a-z,_,
char zimu[27][16][1]={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x38,0x44,0x0C,0x34,0x44,0x4C,0x36,0x00,0x00, 0x00,0x00,0x00,0x00,0xC0,0x40,0x40,0x58,0x64,0x42,0x42,0x42,0x64,0x58,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1C,0x22,0x40,0x40,0x40,0x22,0x1C,0x00,0x00, 0x00,0x00,0x00,0x00,0x06,0x02,0x02,0x3E,0x42,0x42,0x42,0x42,0x46,0x3B,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3C,0x42,0x42,0x7E,0x40,0x42,0x3C,0x00,0x00, 0x00,0x00,0x00,0x00,0x0C,0x12,0x10,0x7C,0x10,0x10,0x10,0x10,0x10,0x7C,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3E,0x44,0x44,0x38,0x40,0x3C,0x42,0x42,0x3C, 0x00,0x00,0x00,0x00,0xC0,0x40,0x40,0x5C,0x62,0x42,0x42,0x42,0x42,0xE7,0x00,0x00, 0x00,0x00,0x00,0x30,0x30,0x00,0x00,0x70,0x10,0x10,0x10,0x10,0x10,0x7C,0x00,0x00, 0x00,0x00,0x00,0x0C,0x0C,0x00,0x00,0x1C,0x04,0x04,0x04,0x04,0x04,0x04,0x44,0x78, 0x00,0x00,0x00,0x00,0xC0,0x40,0x40,0x4E,0x48,0x50,0x70,0x48,0x44,0xEE,0x00,0x00, 0x00,0x00,0x00,0x10,0x70,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x7C,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFE,0x49,0x49,0x49,0x49,0x49,0xED,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xDC,0x62,0x42,0x42,0x42,0x42,0xE7,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3C,0x42,0x42,0x42,0x42,0x42,0x3C,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xD8,0x64,0x42,0x42,0x42,0x64,0x58,0x40,0xE0, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1A,0x26,0x42,0x42,0x42,0x26,0x1A,0x02,0x07,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xEE,0x32,0x20,0x20,0x20,0x20,0xF8,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3E,0x42,0x40,0x3C,0x02,0x42,0x7C,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x10,0x10,0x7C,0x10,0x10,0x10,0x10,0x12,0x0C,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xC6,0x42,0x42,0x42,0x42,0x46,0x3B,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xEE,0x44,0x44,0x28,0x28,0x10,0x10,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xDB,0x89,0x4A,0x5A,0x54,0x24,0x24,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x76,0x24,0x18,0x18,0x18,0x24,0x6E,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE7,0x42,0x24,0x24,0x18,0x18,0x10,0x10,0x60, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7E,0x44,0x08,0x10,0x10,0x22,0x7E,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF};
// Open the file for reading and writing
fbfd = open(“/dev/fb0”, O_RDWR); // 打开Frame Buffer设备
if (fbfd < 0) {
printf(“Error: cannot open framebuffer device.%x\n”,fbfd);
exit(1);
}
printf("The framebuffer device was opened successfully.\n");
// Get fixed screen information
if (ioctl(fbfd, FBIOGET_FSCREENINFO, &finfo)) { // 获取设备固有信息
printf("Error reading fixed information.\n");
exit(2);
}
// Get variable screen information
if (ioctl(fbfd, FBIOGET_VSCREENINFO, &vinfo)) { // 获取设备可变信息
printf("Error reading variable information.\n");
exit(3);
}
printf("%dx%d, %dbpp\n", vinfo.xres, vinfo.yres, vinfo.bits_per_pixel );
// Figure out the size of the screen in bytes
screensize = vinfo.xres * vinfo.yres * vinfo.bits_per_pixel / 8;
// Map the device to memory 通过mmap系统调用将framebuffer内存映射到用户空间,并返回映射后的起始地址
fbp = (char *)mmap(0, screensize, PROT_READ | PROT_WRITE, MAP_SHARED,
fbfd, 0);
if ((int)fbp == -1) {
printf("Error: failed to map framebuffer device to memory.\n");
exit(4);
}
printf("The framebuffer device was mapped to memory successfully.\n");
memset(fbp,0,screensize);//clear screen
//
int temp_l=length,temp_h=height;
for(i=0;(c=title[i])!=’\0’;i++)
{
if(c==’ ‘)
temp_l+=8;
else if(c==’/’)
{
temp_h+=16;
temp_l=length;
}
else if(c==’_’)
{
wirte_single(temp_l,temp_h,zimu[26]);
temp_l+=8;
}
else
{
wirte_single(temp_l,temp_h,zimu[c-‘a’]);
temp_l+=8;
}
}
munmap(fbp, screensize);
close(fbfd);
return 0;
}
2 娱乐助手系统的测试
测试任何产品都有两种方法:如果已经知道了产品应该具有的功能,可以通过测试来检验是否每个功能都能正常使用;如果知道产品内部工作过程,可以通过测试来检验产品内部动作是否按照规格说明书的规定正常进行。前一个方法称为黑盒测试,又称为功能性测试,后一个方法称为白盒测试[12]。
本章节对已经实现的娱乐助手系统进行测试,主要采用功能性测试方法进行测试。用户对娱乐助手的请求有多种可能性,所以我们选取以下测试用例来对系统进行测试。
(1) 选取测试条件为用户选择猜数字游戏,LED能够按照随机出的数字显示,并且程序可以按照规则对按键所按下的数字进行大小正确的判断,猜数进行10次后程序自动返回功能选择界面。
(2) 选取测试条件为用户选择MP3播放,在实验箱耳机插口插上耳机后,能够听到程序内置的歌曲,按Ctrl+C可以退出播放。
(3) 选取测试条件为用户选择蜂鸣器歌曲,选择后蜂鸣器可以响起内置的旋律和节奏,按Ctrl+C可以退出播放。
(4) 选取测试条件为用户选择键盘弹钢琴,选择后按下1-7号键蜂鸣器可以发出不同频率的声音,按下“*”可以退出弹奏。