2011年6月30日星期四

  Android周学习Step By Step(5)--常用widget组件

首先要说的是Android中对widget组件的一些列操作都是通过查找该组件的ID后的操作,和.NET中的双击直接编辑事件过程的傻瓜似的操作大大不同,但是这并不妨碍我们对Android的学习。

l  Button、ImageButton

外观控制方面:与.NET中的Button相比前者更加傻瓜化,Android在配置文件中控制Button的外观方面的参数,.NET则直接通过拖拖拽拽就可以方便的经行界面控制。

时间响应方面:Android同过监听实现对事件的响应,而.NET则是通过委托(在VB6中则直接通过过程函数名来实现对事件的响应)。至于ImageButton在事件响应方面和Button是一样,只不过多了增加图片的功能而已,也就是说Android中把Button分的更细了。

其他方面大同小异。

l  TextView

和.NET中的Label大致相同,明显的区别在于获得其文本值和对其赋值的方法不一样。Android中取出TextView的文本采用getText方法,而为TextView赋值则采用setText方法。

l  EditText

和.NET中的TextBox类似,同TextView一样取得EditText的文本内容和对其赋值同样用getText和setText两个方法,其他方面和.NET中的大致一致,可以照搬过来用。

l  CheckBox

和.NET中的CheckBox没有什么大的区别,还是在获得其文本内容以及设置文本内容的时候用getText以及setText两个方法而已。

l  RadioGroup

Android中的RadioGroup和.NET中的GroupBox以及RadioButton配合起来是一样的,都是提供单选的控件,没有明显的区别。

l  Spinner

和.NET中的ComboBox类似都是通过下拉菜单进行选择,都支持既能在程序代码中加载数据也能在布局文件中加载数据,用起来没有太大区别。

l  AutoCompleteTextView

自动完成文本控件是.NET中所没有的,如果.NET要实现这种效果的话比Android要费尽http://topic.csdn.net/u/20100909/09/0dd742c7-0e4c-4e55-8386-47d016bc3231.html

l  DatePicker、TimePicker

以上两个控件是用于选择日期和时间的在.NET中的DateTimePicker是一样的,只不过在Android中把日期和时间分开了而已。

l  ScrollView

滚动视图控件在.NET中也是分为两种,水平滚动和垂直滚动。功能一样都是在父窗体不够完全显示的时候通过滚动增加显示内容,但有些其他控件是自带滚动条的(比如TextBox控件)这样的话就不用人为的增加滚动条了。

l  ProgressBar

这个控件和.NET中的进度条用法是一致的,最大的不同是Android中有圆形的进度条,而.NET中貌似没有圆形的进度条。

l  SeekBar

拖动条控件更像是.NET中的TrackBar,通过拖动然后获取当前的值进行相关操作。

l  RatingBar

在.NET中貌似没有与其对应的控件。

l  ImageView

Android中的图片视图控件和.NET中的pictureBox非常类似,都是用于图片的显示没有太大的区别。

l  ImageSwitcher&Gallery以及GridView

这些个控件貌似就是为类似相册一类的应用设计的,用户体验非常棒。

image

image

在.NET中要实现这个效果恐怕需要不少代码,因为.NET中只提供了最基本的一些个图片的显示功能,但是在这里Android为我们封装好了,只要拿过来用就可以了。

l  Tab

标签在.NET中也有对应的控件,最大的差别在于Android需要为每个标签绑定(getTabHost)模板,而.NET中则直接用控件拖拽就可以了。

以上只是对Android自带的一些组件和.NET中的控件进行简单的对照,他们大同小异。通过对比对他们有个宏观的认识,具体详细用法还要参照Android的文档才行。

  高速PCB 的过孔设计(转)

摘要:在高速PCB 设计中,过孔设计是一个重要因素,它由孔、孔周围的焊盘区和POWER 层隔离区组成,通常分为盲孔、埋孔和通孔三类。在PCB 设计过程中通过对过孔的寄生电容和寄生电感分析,总结出高速PCB 过孔设计中的一些注意事项。
关键词:过孔;寄生电容;寄生电感;非穿导孔技术
  目前高速PCB 的设计在通信、计算机、图形图像处理等领域应用广泛,所有高科技附加值的电子产品设计都在追求低功耗、低电磁辐射、高可靠性、小型化、轻型化等特点,为了达到以上目标,在高速PCB 设计中,过孔设计是一个重要因素。
1、过孔
  过孔是多层PCB 设计中的一个重要因素,一个过孔主要由三部分组成,一是;二是孔周围的焊盘区;三是POWER 层隔离区。过孔的工艺过程是在过孔的孔壁圆柱面上用化学沉积的方法镀上一层金属,用以连通中间各层需要连通的铜箔,而过孔的上下两面做成普通的焊盘形状,可直接与上下两面的线路相通,也可不连。过孔可以起到电气连接,固定或定位器件的作用。过孔示意图如图1 所示。

过孔一般又分为三类:盲孔、埋孔和通孔
盲孔,指位于印刷线路板的顶层和底层表面,具有一定深度,用于表层线路和下面的内层线路的连接,孔的深度与孔径通常不超过一定的比率。
埋孔,指位于印刷线路板内层的连接孔,它不会延伸到线路板的表面。
  盲孔与埋孔两类孔都位于线路板的内层,层压前利用通孔成型工艺完成,在过孔形成过程中可能还会重叠做好几个内层。
通孔,这种孔穿过整个线路板,可用于实现内部互连或作为元件的安装定位孔。由于通孔在工艺上更易于实现,成本较低,所以一般印制电路板均使用通孔。过孔的分类如图2 所示。

2、过孔的寄生电容
  过孔本身存在着对地的寄生电容,若过孔在铺地层上的隔离孔直径为D2,过孔焊盘的直径为D1,PCB的厚度为T,板基材介电常数为ε,则过孔的寄生电容大小近似于:
C =1.41εTD1/(D2-D1)
    过孔的寄生电容会给电路造成的主要影响是延长了信号的上升时间,降低了电路的速度,电容值越小则影响越小。
3、过孔的寄生电感
  过孔本身就存在寄生电感,在高速数字电路的设计中,过孔的寄生电感带来的危害往往大于寄生电容的影响。过孔的寄生串联电感会削弱旁路电容的作用,减弱整个电源系统的滤波效用。若L指过孔的电感,h是过孔的长度,d是中心钻孔的直径,
过孔的寄生电感近似于:
L=5.08h[ln(4h/d)+1]
  从式中可以看出,过孔的直径对电感的影响较小,而对电感影响最大的是过孔的长度。
4、非穿导孔技术
非穿导孔包含盲孔埋孔
  在非穿导孔技术中,盲孔和埋孔的应用,可以极大地降低PCB的尺寸和质量,减少层数,提高电磁兼容性,增加电子产品特色,降低成本,同时也会使得设计工作更加简便快捷。在传统PCB设计和加工中,通孔会带来许多问题。首先它们占居大量的有效空间,其次大量的通孔密集一处也对多层PCB内层走线造成巨大障碍,这些通孔占去走线所需的空间,它们密集地穿过电源与地线层的表面,还会破坏电源地线层的阻抗特性,使电源地线层失效。且常规的机械法钻孔将是采用非穿导孔技术工作量的20倍。
  在PCB设计中,虽然焊盘、过孔的尺寸已逐渐减小,但如果板层厚度不按比例下降,将会导致通孔的纵横比增大,通孔的纵横比增大会降低可靠性。随着先进的激光打孔技术、等离子干腐蚀技术的成熟,应用非贯穿的小盲孔和小埋孔成为可能,若这些非穿导孔的孔直径为0.3mm,所带来的寄生参数是原先常规孔的1/10左右,提高了PCB的可靠性。
  由于采用非穿导孔技术,使得PCB上大的过孔会很少,因而可以为走线提供更多的空间。剩余空间可以用作大面积屏蔽用途,以改进EMI/RFI性能。同时更多的剩余空间还可以用于内层对器件和关键网线进行部分屏蔽,使其具有最佳电气性能。采用非穿导孔,可以更方便地进行器件引脚扇出,使得高密度引脚器件(如BGA 封装器件)很容易布线,缩短连线长度,满足高速电路时序要求。
5、普通PCB 中的过孔选择
  在普通PCB 设计中,过孔的寄生电容和寄生电感对PCB 设计的影响较小,对1-4层PCB 设计,一般选用0.36mm/0.61mm/1.02mm(钻孔/ 焊盘/POWER 隔离区)的过孔较好,一些特殊要求的信号线(如电源线、地线、时钟线等)可选用0.41mm/0.81mm/1.32mm 的过孔,也可根据实际选用其余尺寸的过孔。
6、高速PCB 中的过孔设计
  通过上面对过孔寄生特性的分析,我们可以看到,在高速PCB 设计中,看似简单的过孔往往也会给电路的设计带来很大的负面效应。为了减小过孔的寄生效应带来的不利影响,在设计中可以尽量做到:
(1)选择合理的过孔尺寸。对于多层一般密度的PCB 设计来说,选用0.25mm/0.51mm/0.91mm(钻孔/ 焊盘/ POWER 隔离区)的过孔较好;对于一些高密度的PCB 也可以使用0.20mm/0.46mm/0.86mm 的过孔,也可以尝试非穿导孔;对于电源或地线的过孔则可以考虑使用较大尺寸,以减小阻抗;
(2)POWER 隔离区越大越好,考虑PCB 上的过孔密度,一般为D1=D2+0.41
(3)PCB 上的信号走线尽量不换层,也就是说尽量减少过孔;
(4)使用较的PCB 有利于减小过孔的两种寄生参数;
(5)电源和地的管脚要就近过孔,过孔和管脚之间的引线越短越好,因为它们会导致电感的增加。同时电源和地的引线要尽可能粗,以减少阻抗;
(6)在信号换层的过孔附近放置一些接地过孔,以便为信号提供短距离回路。
  当然,在设计时还需具体问题具体分析。从成本和信号质量两方面综合考虑,在高速PCB 设计时,设计者总是希望过孔越小越好,这样板上可以留有更多的布线空间,此外,过孔越小,其自身的寄生电容也越小,更适合用于高速电路。在高密度PCB设计中,采用非穿导孔以及过孔尺寸的减小同时带来了成本的增加,而且过孔的尺寸不可能无限制地减小,它受到PCB 厂家钻孔和电镀等工艺技术的限制,在高速PCB 的过孔设计中应给以均衡考虑。
**************************我是分割线*********************************
PCB设计中过孔的概念和分类 作者:mhpcbsix
过孔(via)是多层PCB的重要组成部分之一,钻孔的费用通常占PCB制板费用的30%到40%。简单的说来,PCB上的每一个孔都可以称之为过孔。
从作用上看,过孔可以分成两类:一是用作各层间的电气连接;二是用作器件的固定或定位。
从工艺制程上来说,这些过孔一般又分为三类,即盲孔(blind via)埋孔(buried via)通孔(through via)盲孔位于印刷线路板的顶层和底层表面,具有一定深度,用于表层线路和下面的内层线路的连接,孔的深度通常不超过一定的比率(孔径)。埋孔是指位于印刷线路板内层的连接孔,它不会延伸到线路板的表面。上述两类孔都位于线路板的内层,层压前利用通孔成型工艺完成,在过孔形成过程中可能还会重叠做好几个内层。第三种称为通孔,这种孔穿过整个线路板,可用于实现内部互连或作为元件的安装定位孔。由于通孔在工艺上更易于实现,成本较低,所以绝大部分印刷电路板均使用它,而不用另外两种过孔。
从设计的角度来看,一个过孔主要由两个部分组成,一是中间的钻孔(drill hole),二是钻孔周围的焊盘区,小决定了过孔的大小。很显然,在高速,高密度的PCB设计时,设计者总是希望过孔越小越好,这样板上可以留有更多的布线空间,此外,过孔越小,其自身的寄生电容也越小,更适合用于高速电路。但孔尺寸的减小同时带来了成本的增加,而且过孔的尺寸不可能无限制的减小,它受到钻孔(drill)和电镀(plating)等工艺技术的限制:孔越小,钻孔需花费的时间越长,也越容易偏离中心位置;且当孔的深度超过钻孔直径的6倍时,就无法保证孔壁能均匀镀铜。比如,如果一块正常的6层PCB板的厚度(通孔深度)为50Mil,那么,一般条件下PCB厂家能提供的钻孔直径最小只能达到8Mil。随着激光钻孔技术的发展,钻孔的尺寸也可以越来越小,一般直径小于等于6Mils的过孔,我们就称为微孔。在HDI(高密度互连结构)设计中经常使用到微孔,微孔技术可以允许过孔直接打在焊盘上(Via-in-pad),这大大提高了电路性能,节约了布线空间。过孔在传输线上表现为阻抗不连续的断点,会造成信号的反射。一般过孔的等效阻抗比传输线低12%左右,比如50欧姆的传输线在经过过孔时阻抗会减小6欧姆(具体和过孔的尺寸,板厚也有关,不是绝对减小)。但过孔因为阻抗不连续而造成的反射其实是微乎其微的,其反射系数仅为:(44-50)/(44+50)=-0.06,过孔产生的问题更多的集中于寄生电容和电感的影响。

  j2se 数组的创建

The JavaTM Virtual Machine Specification Second Edition
5.3.3 Creating Array Classes
The following steps are used to create the array class C denoted by N using class loader L. Class loader L may be either the bootstrap class loader or a user-defined class loader.
If L has already been recorded as an initiating loader of an array class with the same component type as N, that class is C, and no array class creation is necessary. Otherwise, the following steps are performed to create C:

If the component type is a reference type, the algorithm of this section (§5.3) is applied recursively using class loader L in order to load and thereby create the component type of C.

The Java virtual machine creates a new array class with the indicated component type and number of dimensions. If the component type is a reference type, C is marked as having been defined by the defining class loader of the component type. Otherwise, C is marked as having been defined by the bootstrap class loader. In any case, the Java virtual machine then records that L is an initiating loader for C (§5.3.4). If the component type is a reference type, the accessibility of the array class is determined by the accessibility of its component type. Otherwise, the accessibility of the array class is public.
http://java.sun.com/docs/books/jvms/second_edition/html/ConstantPool.doc.html#79473

1.类加载器检查数组类是否已经创建了。没有,则需要创建数组类。否则,就无需创建了。
2.如果数组元素是引用类型,那么类加载器会先加载数组类。
3.JVM根据元素类型和维度,创建相应的数组类。     (解释:数组类由维度和定义类型所决定,即维度不同类型相同则不属于同一个类,维度相同类型相同(即使长度不一样)才是同一个类,(注意:长度不是这个类的成员变量,虽然看上去很像,我们可以通过反射的机制检查下))

1.为什么获取数组的长度用.length(成员变量的形式),而获取String的长度用.length()(成员方法的形式)     class loader加载完数组类后,都是调用 arraylength,对于String本身是一个新的不同于数组类的对象,暴露的length()方式也是arraylength的调用
2.数组对象的类是什么?  不是java.util.Arrays,首先需要明确和这个没有关系      数组类由维度和定义类型所决定,即维度不同类型相同则不属于同一个类,维度相同类型相同(即使长度不一样)才是同一个类,(注意:长度不是这个类的成员变量,虽然看上去很像,通过反射的机制检查下)....这个类是一个非法命名类,估计是为了避免和其他自定义类产生冲突,java.lang.Class  550行中有描述
3.JVM没有为数组类生成length这个成员变量,那么Array.length这样的语法如何通过编译执行的?需要阅读字节码

int[] xxx = new int[1];
Class clazz = xxx.getClass();
//class [I      没有任何package的非法类名
//数组的类名由若干个'['和数组元素类型的内部名称组成,'['的数目 代表了数组的维度
System.out.println(xxx.getClass().getName());
System.out.println(clazz.getDeclaredFields().length);
System.out.println(clazz.getDeclaredMethods().length);
System.out.println(clazz.getDeclaredConstructors().length);
System.out.println(clazz.getDeclaredAnnotations().length);
System.out.println(clazz.getDeclaredClasses().length);
System.out.println(clazz.getSuperclass());

[I
0
0
0
0
0
class java.lang.Object

public class Test {
public static void main(String[] args) {
int ab[] = new int[3]; int ii = ab.length;
}
}

public static void main(java.lang.String[]);
  Code:
   Stack=1, Locals=3, Args_size=1
   0:   iconst_3   //将int型常量3压入操作数栈
   1:   newarray int  //常量3出栈,初始化维度为1个int数值,引用压栈
   3:   astore_1   //引用出栈,保存索引到1位置(即将数组引用赋值给ab)
   4:   aload_1    //将ab压栈(ab.length调用)
   5:   arraylength  //ab出栈,获取数组长度(具体长度获取由jvm获取),将数组长度压栈
   6:   istore_2   //变量存贮数组长度到索引2位置
   7:   return
  LineNumberTable:
   line 3: 0
   line 4: 7

}

编译器也可以对Array.length()这样的语法做特殊处理,直接编译成arraylength指令。这样的话,我们就可 以使用方法调用的风格获取数组的长度了,这样看起来貌似也更加OO一点。那为什么不使用Array.length()的语法呢?也许是开发Java的那帮 天才对.length有所偏爱,或者抛硬币拍脑袋随便决定的吧。 形式不重要,重要的是我们明白了背后的机理。

java要比c/c++中的数组更安全,Java使用特定的指令访问数组的元素,这些指令都会对数组的长度(arraylenth)进行检查,java.lang.ArrayIndexOutOfBoundsException

汇编书籍推荐< >王爽

  Android周学习Step By Step(7)--Activity简介

Activity可以算是Android开发者遇到最频繁,也是最基本的模块之一。在Android的程序当中,Activity一般代表手机屏幕的一屏。如果把手机比作一个浏览器,那么Activity就相当于一个网页。在Activity当中,我们可以添加一些Button元素,或者Check box当。可以看到Activity之间可以进行互相跳转,例如,按下一个Button按钮后,可能会跳转到其他的Activity。和网页跳转稍微有些不一样的是,Activity之间的跳转有可能返回值,例如,从Activity A跳转到Activity B,那么当Activity B运行结束的时候,可能会给Activity A一个返回值。这样做在很多时候是相当方便了。

当打开一个新的屏幕时,之前一个屏幕会被置为暂停状态,并且压入历史堆栈中。用户可以通过回退操作返回到以前打开过的屏幕。我们可以选择性的一处一些没有必要保留的屏幕,应为Android会把每个应用的开始到当前的每个屏幕保存在堆栈中。

Activity是由Android系统进行维护的,它也有自己的生命周期,即它的一个产生、运行、销毁的一个周期……了解Activity的生命周期对程序的设计编写是非常有益的。

---Android 开发与实战

image

1、onCreate()

当Activity创建时被调用。

该函数在Acitvity生命周期中只被调用一次。

拥有一个参数,或者为null,或者是通过函数onSaveInstanceState()预先保存的状态信息。

2、onStart()

在Activity即将显示给用户时被调用。

3、onResume()

当Activity可以与用户交互时被调用。

如果需要播放音乐或者动画,该函数最佳。

4、onPause()

当Activity将要进入后台时被调用。

通常是一个新的Activity启动并显示时,这时需要保存程序持久化数据,比如正在编辑的数据记录。

5、onStop()

当Activity不在可见并且在一段时间内不会需要时调用。

如果内存紧张,该函数可能永远不会调用,系统会直接结束掉进程。

6、onRestart()

当Activity由Stop状态重新转为可见状态时被调用。

7、onDestroy()

当Activity被销毁前被调用。

内存紧张时,该函数可能永远不会被调用,系统会直接结束掉进程。

  PHP date与gmdate的区别及如何修改PHP的默认时区

PHP中的时间有2个格式化函数:date()和gmdate(),在官方的文档中的描述为:

date — 格式化一个本地时间/日期

gmdate — 格式化一个 GMT/UTC 日期/时间,返回的是格林威治标准时(GMT)。

举个例子,我们现在所在的时区是+8,那么服务器运行以下脚本返回的时间应该是这样的:

当前时间假定是2007-03-14 12:15:27

echo date(‘Y-m-d H:i:s’, time()); 输出为:2007-03-14 12:15:27

echo gmdate(‘Y-m-d H:i:s’, time()); 输出为:2007-03-14 04:15:27

但是这只是在Linux+Apache下运行PHP所得的结果,如果在Windows下运行,则2个函数返回都是:2007-03-14 04:15:27。

所以,我们应该给一个兼容性的写法,统一使用gmdate,并手工设置当前时区,写法改进如下:

echo gmdate(‘Y-m-d H:i:s’, time() + 3600 * 8);

这样不管在Linux+Apache下还是Windows下都得到了正确的结果,当然这样写还有一个好处,当网站是面向全世界的时候,那么网站用户只要设置所在的时区,程序自动根据用户设置的时区进行时间计算,数据库中信息发布时间只存当前的time()所生成的时间,那么在中国+8时区看到的发布时间是:2007-03-14 12:15:27,那么在欧洲+2时区用户看到这个信息的发布时间是:2007-03-14 06:15:27,这样信息的时间就全部对应正确了。

====================================================

修改PHP的默认时区

   每个地区都有自己的本地时间,在网上以及无线电通信中,时间的转换问题就显得格外突出。整个地球分为二十四个时区,每个时区都有自己的本地时间。在国际无线电或网络通信场合,为了统一起见,使用一个统一的时间,称为通用协调时(UTC,Universal Time Coordinated),是由世界时间标准设定的全球标准时间。UTC原先也被称为格林威治标准时间(GMT,Greenwich Mean Time),都与英国伦敦的本地时间相同。

PHP默认的时区设置是UTC时间,而北京正好位于时区的东八区,领先UTC八个小时。所以在使用PHP中像time()等获取当前时间的函数时,得到的时间总是不对,表现是和北京时间相差八个小时。如果希望正确的显示北京时间,就需要修改默认的时区设置,可以通过以下两种方式完成。

如果使用的是独立的服务器,有权限修改配置文件,设置时区就可以通过修改php.ini中的date.timezone属性完成。我们可以将这个属性的值设置为”Asia/Shang”、”Asia/Chongqing”、”Etc/GMT-8″或PRC等中的一个,再在PHP脚本中获取的当前时间就是北京时间。修改PHP的配置文件如下所示:

date.timezone = Etc/GMT-8       
//在配置文件中设置默认时区为东8区(北京时间)

如果您使用的是共享服务器,没有权限修改配置文件php.ini,并且PHP版本又在5.1.0以上,也可以在输出时间之前调用date_default_timezone_set()函数设置时区。该函数需要提供一个时区标识符作为参数,和配置文件中date.timezone属性的值相同。该函数的使用如下所示:

date_default_timezone_set(‘PRC’);          
//在输出时间之前设置时区,PRC为中华人民共和国 echo date(‘Y-m-d H:i:s’, time());        
//输出的当前时间为北京时间

测试:

代码

date_default_timezone_set(‘Etc/GMT’);     
//在输出时间之前设置时区,Etc/GMT为时间标准时间
echo(time()); 
echo(‘
’);
echo date(‘Y-m-d H:i:s’, time());        
echo(‘
’);
echo gmdate (“Y-m-d H:i:s”,  time());
echo(‘
’);

date_default_timezone_set(‘PRC’);     
//在输出时间之前设置时区,PRC为中华人民共和国 
echo(time()); 
echo(‘
’);
echo date(‘Y-m-d H:i:s’, time());        
echo(‘
’);
echo gmdate (“Y-m-d H:i:s”,  time());

?>

结果:

1276257131
2010-06-11 11:52:11
2010-06-11 11:52:11
1276257131
2010-06-11 19:52:11
2010-06-11 11:52:11

结论:

time() :标准时间戳

  指返回自从 Unix 纪元(格林威治时间 1970 年 1 月 1 日 00:00:00)到当前时间(指的是当前GMT标准时间)的秒数,它的值跟php系统设置的时区无关
date():返回的是当前 GMT标准时间的本地化时间的“自定义格式”时间,跟php系统设置的时区有关。

gmdate():返回的是当前 GMT标准时间的“自定义格式”时间,跟php系统设置的时区无关。

=====================================================

unix时间戳:指的是当前(GMT标准)时间离unix纪元时间(1970-01-01 00:00:00)的秒数;

代码

date_default_timezone_set(‘Etc/GMT’);     
//在输出时间之前设置时区,Etc/GMT为时间标准时间
echo(strtotime(’1970-01-01 08:00:00′));

echo(‘
’);
date_default_timezone_set(‘PRC’);     
//在输出时间之前设置时区,PRC为中华人民共和国

echo(strtotime(’1970-01-01 08:00:00′));

?>

测试结果:

28800
0

strtotime(a):当前时间的GMT标准时间的unix时间戳,如果当前php系统的设置的时区不是GMT标准时区,则在应用strtotime(a)时,系统会自动把时间a折算成相应的GMT标准时间,然后计算这个时间的unix时间戳。跟php系统设置的时区有关

  Android周学习Step By Step(8)--Intent之启动新的Activity

Intent的官方定义:An intent is an abstract description of an operation to be performed。一个Intent就是一次对将要执行的操作的抽象描述。

l  启动一个特定的Activity,代码如下:

   1:  Intent intent=new Intent(CurrentActivity.this,OtherActivity.class);
   2:  startActivity (intent);

其中CurrentActivity是当前的Activity,OtherActivity是要启动的那个Activity,这就类似于窗体之间的跳转,只不过和.NET不同的是指明了谁在新“窗体”之前(或者说谁产生了新“窗体”)。

l  启动一个未指明的Activity,代码如下:

   1:  Intent intent=new Intent(Intent.ACTION_DIAL,Uri.parse("tel:115-1345"));
   2:  startActivity(intent);

以上代码没有指明要启动那个Activity只是把要启动的那个Activity的描述放在了Intent中,让系统去查找符合这个描述的Activity,然后启动并执行。

l  处理一个Activity的返回值,界面设计以及代码如下:

ActivityMain:

image
   1:  package com.eoeAndroid.activity;
   2:  import android.app.Activity;
   3:  import android.content.Intent;
   4:  import android.net.Uri;
   5:  import android.os.Bundle;
   6:  import android.view.View;
   7:  import android.view.View.OnClickListener;
   8:  import android.widget.Button;
   9:  public class ActivityMain extends Activity
  10:  {
  11:      OnClickListener listener1=null;
  12:      OnClickListener listener2=null;
  13:      Button button1;
  14:      Button button2;
  15:      static final int REQUEST_CODE=1;
  16:      @Override
  17:      public void onCreate(Bundle savedInstanceState)
  18:      {
  19:          super.onCreate(savedInstanceState);
  20:          listener1=new OnClickListener()
  21:          {            
  22:              @Override
  23:              public void onClick(View v)
  24:              {
  25:                  Intent intent1=new Intent(ActivityMain.this,Activity1.class);
  26:                  intent1.putExtra("activityMain", "数据来自activityMain");
  27:                  startActivityForResult(intent1, REQUEST_CODE);
  28:              }
  29:          };        
  30:          listener2 =new OnClickListener()
  31:          {            
  32:              @Override
  33:              public void onClick(View v)
  34:              {
  35:                  setTitle("这是在ActivityMain");
  36:                  Intent intent2=new Intent(ActivityMain.this,Activity2.class);
  37:                  startActivity(intent2);
  38:              }
  39:          };
  40:          setContentView(R.layout.main);
  41:          button1=(Button)findViewById(R.id.button1);
  42:          button1.setOnClickListener(listener1);
  43:          button2=(Button)findViewById(R.id.button2);
  44:          button2.setOnClickListener(listener2);
  45:          setTitle("ActivityMain");
  46:      }
  47:      @Override
  48:      protected void onActivityResult(int requestCode,int resuleCode,Intent data)
  49:      {
  50:          if (requestCode==REQUEST_CODE)
  51:          {
  52:              if (resuleCode==RESULT_CANCELED)
  53:                  setTitle("取消");
  54:              else if (resuleCode==RESULT_OK)
  55:              {
  56:                  String temp=null;
  57:                  Bundle extras=data.getExtras();
  58:                  if (extras!=null)
  59:                  {
  60:                      temp=extras.getString("stroe");
  61:                  }
  62:                  setTitle(temp);            
  63:              }        
  64:          }
  65:      }
  66:  }

Activity1:

image

   1:  package com.eoeAndroid.activity;
   2:  import android.app.Activity;
   3:  import android.content.Intent;
   4:  import android.os.Bundle;
   5:  import android.view.View;
   6:  import android.view.View.OnClickListener;
   7:  import android.widget.Button;
   8:  public class Activity1 extends Activity {
   9:      OnClickListener listener1 = null;
  10:      Button button1;
  11:      /** Called when the activity is first created. */
  12:      @Override
  13:      public void onCreate(Bundle savedInstanceState) {
  14:          super.onCreate(savedInstanceState);
  15:          setContentView(R.layout.activity1);
  16:          listener1 = new OnClickListener() {
  17:              public void onClick(View v) {
  18:                  Bundle bundle = new Bundle();
  19:                  bundle.putString("store", "数据来自Activity1");
  20:                  Intent mIntent = new Intent();
  21:                  mIntent.putExtras(bundle);
  22:                  setResult(RESULT_OK, mIntent);
  23:                  finish();
  24:              }
  25:          };
  26:          button1 = (Button) findViewById(R.id.button3);
  27:          button1.setOnClickListener(listener1);
  28:          String data=null;
  29:           Bundle extras = getIntent().getExtras();
  30:              if (extras != null) {
  31:                   data = extras.getString("activityMain");
  32:              }
  33:          setTitle("现在是在Activity1里:"+data);
  34:      }
  35:  }

Activity2:

image
   1:  package com.eoeAndroid.activity;
   2:  import android.app.Activity;
   3:  import android.os.Bundle;
   4:  import android.view.View;
   5:  import android.view.View.OnClickListener;
   6:  import android.widget.Button;
   7:  public class Activity2 extends Activity {
   8:      OnClickListener listener = null;
   9:      Button button;
  10:      /** Called when the activity is first created. */
  11:      @Override
  12:      public void onCreate(Bundle savedInstanceState) {
  13:          super.onCreate(savedInstanceState);
  14:          setContentView(R.layout.activity2);
  15:          listener = new OnClickListener() {
  16:              public void onClick(View v) {
  17:                  finish();
  18:              }
  19:          };
  20:          button = (Button) findViewById(R.id.button4);
  21:          button.setOnClickListener(listener);
  22:          setTitle("现在是在Activity2里");
  23:      }
  24:  }

  Android周学习Step By Step(9)--Intent之广播(完)

通过broadcast Intent机制可以将一个Intent发送给任何对这个Intent感兴趣的BroadcastReceiver。

image

 

通过new Intent(ACTION_1)新建Action为ACTION_1的Intent。

通过sendBroadcast(intent)将这个intent进行广播。代码如下

ActivityMain代码:

   1:  package com.eoeandroid.broadcastReceiver;
   2:  import android.app.NotificationManager;
   3:  import android.content.BroadcastReceiver;
   4:  import android.content.Context;
   5:  import android.content.Intent;
   6:  public class EoeAndroidReceiver2 extends BroadcastReceiver {
   7:      Context context;
   8:      @Override
   9:      public void onReceive(Context context, Intent intent) {
  10:          // TODO Auto-generated method stub
  11:          this.context = context;
  12:          DeleteNotification();
  13:      }
  14:      private void DeleteNotification() {        
  15:          NotificationManager notificationManager = (NotificationManager) context
  16:                  .getSystemService(android.content.Context.NOTIFICATION_SERVICE);
  17:          notificationManager.cancel(EoeAndroidReceiver1.NOTIFICATION_ID);
  18:      
  19:      }
  20:  }

当单击MENU的第一项后,程序执行到EoeAndroidReceiver1,通过OnRecievie方法将一个Notification显示在了状态栏中。其中showNotification()负责显示一个Notification。代码如下:

EoeAndroidReceiver1代码

   1:  package com.eoeandroid.broadcastReceiver;
   2:  import android.app.Notification;
   3:  import android.app.NotificationManager;
   4:  import android.app.PendingIntent;
   5:  import android.content.BroadcastReceiver;
   6:  import android.content.Context;
   7:  import android.content.Intent;
   8:  public class EoeAndroidReceiver1 extends BroadcastReceiver {
   9:      Context context;
  10:      public static int NOTIFICATION_ID = 21321;
  11:      @Override
  12:      public void onReceive(Context context, Intent intent) {
  13:          this.context = context;
  14:          showNotification();
  15:      }
  16:      private void showNotification() {
  17:          NotificationManager notificationManager = (NotificationManager) context
  18:                  .getSystemService(android.content.Context.NOTIFICATION_SERVICE);
  19:          Notification notification = new Notification(R.drawable.icon,
  20:                  "在EoeAndroidReceiver1中", System.currentTimeMillis());
  21:          PendingIntent contentIntent = PendingIntent.getActivity(context, 0,
  22:                  new Intent(context, ActivityMain.class), 0);
  23:          notification.setLatestEventInfo(context, "在EoeAndroidReceiver1中", null,
  24:                  contentIntent);
  25:          notificationManager.notify(NOTIFICATION_ID, notification);
  26:      }
  27:  }

单击第二个按钮后程序开始广播,这个广播被EoeAndroidReceiver2截获然后开始执行EoeAndroidReceiver2里的OnReceive方法,其中DeleteNotification()方法负责将刚才生成的Notification从状态栏中删除。代码如下:

EoeAndroidReceiver2代码

   1:  package com.eoeandroid.broadcastReceiver;
   2:  import android.app.NotificationManager;
   3:  import android.content.BroadcastReceiver;
   4:  import android.content.Context;
   5:  import android.content.Intent;
   6:  public class EoeAndroidReceiver2 extends BroadcastReceiver {
   7:      Context context;
   8:      @Override
   9:      public void onReceive(Context context, Intent intent) {
  10:          // TODO Auto-generated method stub
  11:          this.context = context;
  12:          DeleteNotification();
  13:      }
  14:      private void DeleteNotification() {        
  15:          NotificationManager notificationManager = (NotificationManager) context
  16:                  .getSystemService(android.content.Context.NOTIFICATION_SERVICE);
  17:          notificationManager.cancel(EoeAndroidReceiver1.NOTIFICATION_ID);    
  18:      }
  19:  }

Android学习到此告一段落,经过一周的学习(严格的说学习的时间外加写论文的时间是一周,总结、写博客的时间远远大于一周)只能对Android有个大致的了解,还有很多方面由于时间的原因不能够深入学习,甚至很多都没有接触到,以后有时间再学习吧。

Android,有缘我们还会再见!

  Windows下搭建Android开发平台

参考资料:http://www.cnblogs.com/ginohuo/archive/2010/05/06/1728896.html

1. 下载Android SDK.

我下载的是installer_r11-windows.exe,下载页面地址为:http://developer.android.com/sdk/index.html

9

2. 下载JDK

http://www.oracle.com/technetwork/java/javase/downloads/jdk-6u26-download-400750.html

3.Eclipse IDE

下载地址:http://www.eclipse.org/downloads/。这里使用的 Eclipse IDE for Java EE Developers

4. 安装jdk-6u26-windows-i586.exe(JDK)

5. 安装installer_r11-windows.exe。这个是推荐下载的,还有一个android-sdk_r11-windows.zip压缩包,用这个安装sdk还得配置环境,不推荐。安装完android-sdk以后,更新一些包,如下图所示

2

1

6. Eclipse  配置

点击“Add”

4 

出现如下界面

5

在Localion中添加如下地址:https://dl-ssl.google.com/android/eclipse/。Name可以随意填,我填的是Android

7.配置 Android Plugin

Eclipse->Windows->Preferences,出现如下界面。

10

其中:D:\Program Files\J2EE\Android\android-sdk是sdk的安装地址,我这里的api比较多,有从1.5到3.1版本的,这个在开发应用程序的时候可以自己选。

8.创建应用程序

File->new->project,选择Android

6

穿件一个HelloWorld应用程序

13

程序代码:

12

9.创建AVD

14

15

配置:Run As->Run Configurations

7

8

总结:

run工程比较漫长,需要耐心等待。

  CI(CodeIgniter) 实现网站在线自动发送邮件

     在平时,我们在网络上注册很多服务的时候,都会收得到相应网站自动发过来的如确认邮件等实时邮件。极大的方便网站实时,准确抓住有效用户。

看起来感觉门槛很高,好像很大型的网站的技术团队才能实现到。其实不然,就算我们是小团队,甚至一个人,也可以轻松的实现这样的功能。

在这里,我介绍的是使用 PHP 代码实现的,这主要得益于 PHP 的 Email 函数

是我们可实现在本地调用远程服务器发送邮件,其实也就是本地将内容按预定的规则封装好传送到服务器的特定端口,服务器解析、再投送到指定的邮箱。

好的, 我在这里使用的不是纯 PHP 自己写出来的代码实现,而是在 PHP 的一个框架 CI ( CodeIgniter ) 上实现。

CI 是一个非常轻巧并功能强大的 PHP 框架,其封装好了许多的 PHP 函数和用法,再通过其简单的语法去实现你想要的功能,非常适合想快速开放 PHP 网站的新手。

这里,整个代码如下:

 

public function send_email()   // 写在 Controller 里边。
    {
        $this->load->library('email');
       
        $config['protocol'] = 'smtp';
        $config['smtp_host'] = 'smtp.163.com'; // given server
        $config['smtp_user'] = 'your email ';
        $config['smtp_pass'] = 'email password';
        $config['smtp_port'] = '25'; // given port.
        $config['smtp_timeout'] = '5';
        $config['newline'] = "\r\n";
        $config['crlf'] = "\r\n";
        $config['charset']='utf-8';  // Encoding type
        
        $this->email->initialize($config);  //initialize the config
       
       
        $this->email->from('vip@xx.com', 'sender name');  // show in the reciever email box
        $this->email->to($_POST['email']);
        //$this->email->cc('another@another-example.com'); 
        //$this->email->bcc('them@their-example.com');
        $diy_subject='Auto reply';  // Email title
        $diy_msg='Dear '.$_POST['username'].'Test the content whether is right.';  // Email content
        
        $this->email->subject($diy_subject);
        $this->email->message($diy_msg);
        $this->email->send();   //Send out the email.
        //echo $this->email->print_debugger();
        $data['username']=$_POST['username'];
        $data['email']=$_POST['email'];
        $this->load->view('send_email_ok',$data);
    }

具体的 Email 类说明在 http://codeigniter.org.cn/user_guide/libraries/email.html 可以找到。

这样就可以实现在线自动发邮件给用户了。赶快去自定义自己的信件内容呗。

  【转】PHP版本VC6和VC9、Non Thread Safe和Thread Safe的区别

引用地址:http://www.itbkc.com/tb.php?sc=87cc86&id=15

想更新个 PHP 的版本, PHP 的 windows 版本已经分离出来了,见 http://windows.php.net/download/ ,但是上面有很多不同的版本,包括 VC9, VC6,  x86 Non Thread Safe, x86 Thread Safe, 好像没有 x64 版本的,(现在特别喜欢用 64 位的软件),版本有点多,主要的区别和如何选择不同的版本如下:

VC6 版本是使用 Visual Studio 6 编译器编译的,如果你是在 windows 下使用 Apache+PHP 的,请选择 VC6 版本。

VC9 版本是使用 Visual Studio 2008 编译器编译的,如果你是在 windows 下使用 IIS+PHP 的,请选择 VC9 版本。

Non Thread Safe 就是非线程安全,在执行时不进行线程( Thread )安全检查;
Thread Safe 是线程安全,执行时会进行线程( Thread )安全检查,以防止有新要求就启动新线程的 CGI 执行方式而耗尽系统资源;

Windows 下的 PHP 主要有两种执行方式: ISAPI 和 FastCGI 。

      ISAPI 执行方式是以 DLL 动态库的形式使用,可以在被用户请求后执行,在处理完一个用户请求后不会马上消失,所以需要进行线程安全检查,这样来提高程序的执行效率,所以如果是以 ISAPI 来执行 PHP ,建议选择 Thread Safe 版本;

      FastCGI 执行方式是以单一线程来执行操作,所以不需要进行线程的安全检查,除去线程安全检查的防护反而可以提高执行效率,所以,如果是以 FastCGI 来执行 PHP ,建议选择 Non Thread Safe 版本。

官方并不建议你将 Non Thread Safe 应用于生产环境。

知识补充:

      ISAPI 缩写词为 Internet Server Application Programming Interface 为 Microsoft 所提的 Internet server 的 API 。 ISAPI 服务器扩展是可以被 HTTP 服务器加载和调用的 DLL 。 Internet 服务器扩展也称为 Internet 服务器应用程序 (ISA) ,用于增强符合Internet 服务器 API (ISAPI) 的服务器的功能。 ISA 通过浏览器应用程序调用,并且将相似的功能提供给通用网关接口 (CGI)应用程序。

      FastCGI 是一个程序接口,它能加速公共网关接口( CGI ), CGI 是一种用最常见的方式使 Web 服务器调用应用程序的Web 应用程序。按一个 FastCGI 工具来看,用户要求进入一个网站并使用一个专门的应用软件的话,使用 FastCGI 能够快 3到 30 倍。 FastCGI 是 Web 服务器的一种 插件。为了获得良好的性能,它要求对现有服务器应用程序(比如 Perl 、 Tcl 脚本和 C 、 C++ 程序)做细小的改动。

基本上, FastCGI 是一个在单一步骤中管理多重 CGI 请求的程序,为每个请求减少了许多程序指令。没有 FastCGI 的话,每当用户请求某一服务时都会导致 Web 服务器打开 一个新的能控制和执行这项服务的程序,然后关闭它。有了 FastCGI 的话,一个步骤的耗费会被所有当前正处理的请求所分担。与 CGI 不同,有了 FastCGI 的话,每个步骤是独立于 Web 服务器运行的, 这样就提供了更多的安全。 FastCGI 是独立代码的。它的版权属于 Open Market 公司,该公司提供 FastCGI 的免费使用并且将其作为一个公开标准。 FastCGI 提供了唯一一个可以跨平台和在任何 Web 服务器上使用的 无知识产权的方法。

  深刻反思

第4步独立(不看原来的例子)实现是最关键的一步。因为达到了这个目标才能证明你真正理解了,达到了这个目标也一定能理解了。事实证明没有做这一步的后果时间一长,或者在有些变化自己就很费劲,甚至想不起来。

第2步和第5步中可以分为目前能够推演的和不能推演的两部分,第6步能用熟悉的知识推演的要吧推演的展现给别人。暂时不能推演(一定能推演只是我还没找到)的列在一起,讲清楚起做用。有时间就去推演第二部分。

还有很重要的一点,这样学习是很需要时间的,尤其在第1步,需要注意花费很多时间学习新东西。同时要注意每一个步骤的目的,只要完成了当前的目标不要把不同阶段混在一起,尤其是第1、2两个步骤。

  8.3.2 构建筑组合行为

8.3.2 构建筑组合行为

 

    在清单 8.11 中,验证最低收入和当前工作最小年份的检查和报表函数中,有明显的重复。这些检查有类似的结构:都检查客户的某一属性是否小于最小的允许值。

    找出共性是只删除重复第一步。下一步是要看 checkJobYears 和 checkIncome 函数(连同其报表函数)的哪部分是不同的:

    ■ 检查不同的属性。

    ■ 使用不同的最小值。

    ■ 有略微不同的消息。

    要更简洁地写出代码,我们可以创建一个函数,取这三个不同的部件作为其参数值,并返回 ClientTest 记录。当我们创建检查列表时,用不同的参数值调用这个新函数两次,创建两个类似的检查。清单 8.13 显示额外的函数(lessThanTest),以及创建检查列表的新方法。

 

Listing 8.13 Creating similar tests using a single function (F# Interactive)

 

> let lessThanTest readFunc minValue propertyName =
     let report client =
       printfn "Checking '%s' of '%s' failed (less than %d)!"
                   propertyName client.Name minValue
     { Check = (fun client -> readFunc(client) < minValue)
      Report = report };;
val lessThanTest : (Client -> int) -> int -> string –> ClientTest

> let tests =
     [ (lessThanTest (fun client -> client.Income) 30000 "income")
       (lessThanTest (fun client -> client.YearsInJob)
                             2 "years in the job")
(* more tests... *) ];;
val tests : ClientTest list

 

    像往常一样,这个类型签名告诉我们很多关于这个函数的信息。LessThanTest 函数返回值为 ClientTest,包含了检查和报表函数。检查是用三个参数值生成的:

    1 读客端的一个数字属性

    2 指定必需的最小值 (在本例中,表示收入或年数)

    3 描述属性 (用于报表检查)

    这段代码首先声明一个嵌套函数 report,取客户作为参数值,打印检查未通过的原因。这个函数还使用 lessThanTest 函数的参数值,这意味着,当 report 作为结果的一部分返回时,所有这些参数将捕获在一个闭包中。当构建一个将返回的记录值时,我们指定 report 作为这个函数值的一部分,第二部分写成内嵌的,使用 lambda 函数。

    使用函数的元组或记录,在函数式编程中很常见,它反映了 F# 的开发风格。但在 C# 中,我们将使用不同的方法来实现这个示例。让我们看一下,在开发过程中,还要考虑如何在 C# 中实现这个示例,并改善当前的 F# 版本。

  星空之翼游戏的设计与实现

星空之翼游戏的设计与实现

摘要  本毕业设计开发的《星空之翼》是一款纵版飞行射击类游戏。游戏采用组件式架构。整个游戏由主程序模块、游戏引擎模块、地图编辑器模块和声音引擎模块4个相对独立的模块组成,其中声音引擎采用的是一个开源声音引擎irrklang。本毕业设计用到的技术有多线程编程、嵌入式数据库(SQLite)编程和XML。

     《星空之翼》游戏采用C#语言编写,设计时采用模块化的设计思想,模块间相对独立,耦合度小,便于今后的并行开发。实现时充分考虑了代码的重用性和功能的灵活性和可扩展性。玩家可以通过地图编辑器制作自己的关卡,自定义敌人和游戏背景;游戏的程序和游戏数据分离,使用户可以很方便的修改游戏素材;同时通过替换其中的游戏引擎模块就可以方便的实现游戏升级。

关键词 游戏引擎,模块化,多线程,XML,嵌入式数据库

The Design and Implementation of “Star Wings”

Abstract

"Star Wings" is a vertical version of flight shooting game. We design the project with component-based architecture. The project consists of four relative dependent modules: main program module, the game engine module, the map editor module and sound engine module. The sound engine here uses an open source sound engine, named irrklang. The technologies involved in our project include: multi-thread programming, embedded database (SQLite) programming and XML.

“Star Wings” is developed by C#. The whole project adopts modularizing design method, the modules are relative independent, coupling degree of the modules is relative low, it is feasible for parallel developing in the future. We full considered the code reusability and flexibility and expansibility of functions. Players can use map-editor to create custom levels, custom enemies and custom game background. The game logic separates from the data, by which players can change their favorite materials, like planes, without rewriting the codes. At the same time, game update can be done just by replacing the game engine module.

Keywords game engine, modularity, multi-thread, XML, embedded database

目录

第一章 概述

1.1 课题背景

1.2背景知识

1.2.1 游戏引擎

1.2.2 多线程

1.2.3 嵌入式数据库sqlite介绍

1.2.4 XML

1.2.5单例模式(singleton)

1.2.6 碰撞检测

1.2.7 游戏循环体

1.2.8静态类

1.3 毕设意义

第二章 游戏的总体分析与设计

2.1 设计构想

2.1.1 游戏定位

2.1.2 游戏需求

2.1.3 游戏扩展

2.2 模块划分

2.3 开发工具

第三章 游戏的详细设计

3.1 游戏引擎模块

3.2 地图编辑器模块

3.3 主程序模块

3.4 游戏主循环

3.5 碰撞检测

3.6 地图格式与精灵图片处理

3.7声音引擎

3.8 模块间的关系

结论

毕业设计总结

未来的工作

  1. 第一章 概述

在信息技术高速发展的今天,电子游戏已经深入人们的日常生活,成为老少皆宜的娱乐方式。人类爱玩的天性是驱动电脑游戏硬件和软件技术不断向前发展的根本动力之一。电脑发展经历了半个多世纪,电子游戏也产生了半个多世纪,各种各样的游戏出现在历史的舞台上,一个新游戏在展现完自己辉煌一刻后,又被新的游戏推进尘封在历史的角落里。新游戏像雨后春笋般涌现,随着游戏技术的不断发展,游戏的容量越来越大,使用的技术越来越复杂,但游戏开发最基本的技术是相同的,本设计通过C#开发一款纵版飞行射击类游戏,来研究游戏程序的架构,试图编写一个能够代表飞行射击类游戏的通用软件,可以完全改变游戏的内容,而不用重复开发,游戏的架构能够很好的支持游戏的不断地修正和改变。

游戏开发向来都是C和C++的天下,是因为C 和C++是直接编译派系,在操作系统上运行很快。而java和C#是中间编译派系,运行速度慢,但是可以方便平台移植。随着计算机硬件技术的快速发展,因运算速度导致的差距不断减小,java和C#也可以开发一些游戏,毫不疑问未来java和C#会在游戏开发市场占有一席之位,根据TIOBE开发语言排行榜2011年5月的排名来看最新的前五名分别是Java、C、C++、C#、PHP,其中java同比增长了0.2%,C#同比增长了2.75%,而C降低了2.02%,C++降低了1.23%,其中C#更是取得新的历史高点,C#语言从2000年6月发布以来占有率不断缓慢上升,作为微软全力推行的语言,C#的占有率相信会不断缓慢上升,他来源于C和C++,同时借鉴了Visual Basic、Java、Delphi的特性。微软为C#推出了XNA和Xbox360游戏引擎,相信用C#开发的游戏会不但涌现。

1.1 课题背景

    《星空之翼》属于射击类游戏,这里所说的射击类,并非是类似《CS》的模拟射击(枪战),而是指纯的飞机射击,由玩家控制各种飞行物(主要是飞机)完成任务或过关的游戏。具体属于科幻飞行模拟游戏(Science-Simulation Game)。游戏的内容为非现实的科幻的,游戏视觉版面为纵版。其中最经典的是《雷电》, 因操作简单,节奏明快,受到了广大玩家的欢迎,可以说是老少皆宜的游戏了。《雷电》在电子游戏中有这相当数量的变种,有着庞大的爱好者,在3D游戏网页游戏流行的今天,《雷电》还是被不断的变种,最新的变种是《雷电OL》和《QQ雷电》。最初的《雷电》有个缺点是只有固定的关卡,游戏玩家无法进行真正意义上DIY(Do It Yourself) ,如果玩家通关后,对该游戏的热情就会大大降低。尽管官方提供了地图编辑器,那个地图编辑器只能采用默认提供的精灵控件。

      网络上有大量的《雷电》变种游戏的源代码,其中主要是用C语言和C++语言编写的,少量的是用VB语言和Java语言编写的,用C#语言编写的源代码几乎没有,唯一找到一个是北风网网友编写《勇者斗恶龙》源码,《勇者斗恶龙》游戏仅仅是一个Demo,可以这么说连Demo都不是,太过于简易。

    《星空之翼》游戏采用C#语言编写,使用面向对象和模块化的编程思想。玩家可以通过地图编辑器制作自己的关卡,游戏的程序和游戏数据分离,通过替换其中的模块组件就可以实现游戏的升级,《星空之翼》还提供了默认的关卡,默认的游戏剧情小说。《星空之翼》结合默认的游戏剧情和高度自定义地图地图编辑器,不但给玩家娱乐享受,还可以充分发挥玩家的主动性,玩家可以编写出比默认游戏剧情更好的游戏。

     此次毕业设计通过对《星空之翼》游戏的设计与实现,深入学习游戏的内部架构,模块化的编程思想,应用程序可扩展性,多线程编程,嵌入式数据库和XML的使用。

1.2背景知识

1.2.1 游戏引擎

      游戏引擎是执行游戏中公有任务的程序代码,以前的游戏的容量很小,大都以兆(M)计,每款游戏开发都是从头编写代码,期间存在着大量的重复劳动,耗时耗力。随着技术的发展,开发人员总结出一些规律,在同类题材的游戏中,有大量的相似的代码可以重复使用。为了减少游戏的开发周期和开发费用,慢慢将这些通用的代码进行模块化,方便重用,简化游戏的开发过程。

     游戏引擎根据代码的组织方式分为不同的游戏引擎架构,游戏引擎架构是游戏引擎的骨架,常见的三种引擎架构方法为:

(1) 基类根形式构架。基类根形式构架是以基本的几个抽象服务提供接口类为基础,并以接口与实现相分离为原则,来进行引擎实现,提供的实现类在引擎内部创建,而将接口供给用户使用。这种构架模式的优点是结构清晰,实现灵活,可以适应比较大的变化,适合于做一些较大且有跨平台需求的引擎,但是该架构依赖于基本的抽象类的继承关系,导致引擎内部类继承了无用的功能而变得虚大,况且接口的通用性使得实现的效率不高,导致整体效率偏低。

(2) 结构化构架。结构化构架采用的方法是将游戏引擎内部的模块以结构化的形式组合,并以API的形式提供接口,这种构架模式的优点是效率高,接口简单清晰,适合于做一些速度要求比较高的引擎,但是缺点是其模块之间耦合度高,不易修改。

(3) 组件形式构架。组件形式构架是把不同功能的模块做成互相独立的系统,模块内部可以使用任何构架方式,只需要提供相应的接口即可。引擎以模块管理器为核心,支持插件形式组件增加方式,可以将新增的功能组件以插件的形式来插入系统工作,这种构架方式极其灵活,且模块内效率比较高,所以某些大型商用游戏引擎采用这种方式,但这种方式设计比较困难,实现比较复杂,需要大量的开发经验。

1.2.2 多线程

由于游戏的特殊性,游戏对实时性,操作性,画面质量有较高的要求特别是实时性,如果一个游戏不能实时处理,那么基本可以断定一个游戏失败了。

什么是进程?

当一个程序开始运行时,它就是一个进程,进程包括运行中的程序和程序所使用到的内存和系统资源。而一个进程又是由多个线程所组成的。

什么是线程?

线程是程序中的一个执行流,每个线程都有自己的专有寄存器(栈指针、程序计数器等),但代码区是共享的,即不同的线程可以执行同样的函数。

什么是多线程?

多线程是指程序中包含多个执行流,即在一个程序中可以同时运行多个不同的线程来执行不同的任务,也就是说允许单个程序创建多个并行执行的线程来完成各自的任务。

多线程的好处:

可以提高CPU的利用率。在多线程程序中,一个线程必须等待的时候,CPU可以运行其它的线程而不是等待,这样就大大提高了程序的效率。

多线程的不利方面:

线程也是程序,所以线程需要占用内存,线程越多占用内存也越多; 多线程需要协调和管理,所以需要CPU时间跟踪线程;线程之间对共享资源的访问会相互影响,必须解决竞用共享资源的问题;线程太多会导致控制太复杂,最终可能造成很多Bug;

在《星空之翼》游戏中采用多线程来提高游戏运行速度。

1.2.3 嵌入式数据库sqlite介绍

SQLite是一款轻型的开源数据库。它的设计目标是嵌入式的,在很多嵌入式产品中使用了它,它占用资源非常的低。SQLite能够支持Windows/Linux/Unix等等主流的操作系统,同时能够跟很多程序语言相结合,比如 Tcl、C#、PHP、Java等,还有ODBC接口,同样比起Mysql、PostgreSQL这两款开源世界著名的数据库管理系统来讲,它的处理速度比他们都快。

轻便、无需计算机服务支持和跨平台的SQLite有着广泛的应用,谷歌浏览器(Chrome)就使用了SQlite来存储用户信息和操作。

《星空之翼》游戏中采用SQLite来存储游戏数据,方便游戏各模块之间的的通信。同时便于游戏的扩充。

1.2.4 XML

XML(Extensible Markup Language)即可扩展标记语言,它与HTML一样,都是SGML(Standard Generalized Markup Language,标准通用标记语言)。Xml是Internet环境中跨平台的,依赖于内容的技术,是当前处理结构化文档信息的有力工具。

《星空之翼》游戏中采用XML对游戏程序的各项参数进行自定义配置。

1.2.5单例模式(singleton)

单例模式(singleton)顾名思义,就是只有一个实例。单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。这个类称为单例类。

单例模式的要点有三个:

(1) 是某个类只能有一个实例;

(2) 是它必须自行创建这个实例;

(3) 是它必须自行向整个系统提供这个实例。

在《星空之翼》游戏中盟友的管理,敌人的管理,盟友武器的管理,敌人武器的管理和冲突检测的管理都必须确保只有一个实例。

1.2.6 碰撞检测

《星空之翼》游戏中碰撞是最关键的技术,因为飞行射击类游戏说白了就是碰撞的游戏——躲避敌人的子弹或飞机,同时用自己的子弹去碰撞敌人。游戏中的碰撞检测方式有很多,不同的算法之间主要是在精度和速度之间权衡。碰撞检测的算法归纳为3类:

(1) 范围检测:将游戏中的物体划分为规则的几何范围,如果两物体的范围相交,则判断两物体相撞。

(2) 颜色检测:取出物体中所有像素点的颜色和当前物体所在区域的像素点的颜色,两者进行AND运算,判断结果。

(3) 路线检测:通过行进路线来检测是否发生碰撞。使用向量来进行判断。

在《星空之翼》游戏中采用范围检测中的矩形检测方法。

1.2.7 游戏循环体

在游戏设计和开发中,尤其是引擎开发中,逻辑循环是一个重要组成部分,循环决定了游戏的基础逻辑和运行方式。常见的游戏循环方式有五种:

(1) 自身式循环:自身式循环比较容易理解,比如一个精灵控件,自己内部实现一个循环,来不停的检测和执行逻辑,开发者都不需要去单独做什么,只需要new出来它们自己就会执行逻辑了,这种方式非常便捷和方便,开发起来也相对容易,互相之间没有任何关系,此时需要借助单例之类的设计模式来解决互相的结合问题。

(2) 链条式循环:自身逻辑存在各自的循环消耗问题,为了减少消耗,可以将各自的循环逻辑统一到一个循环中,基本的原理是将各个循环体放入到一个大循环中,然后从第一个开始执行循环逻辑,只执行一次,然后下一个,到底以后回来继续执行。

(3) 子树式循环:子树循环顾名思义,使用树状结构来处理循环逻辑,我们实际应用中还有可以分为:活动子树式循环和固定子树循环.

(4) 区间式循环:区间式循环原理是将游戏系统各个部分拆分开,挂入不同的循环结构中,在区间式循环中,分拆最消耗性能的部分,到另外一个线程(或循环结构)中完成循环。

(5) 组合式循环:组合式循环是组合前四种循环方式,实际应用都是结合前四种方式一起使用。

1.2.8静态类

《星空之翼》游戏中有很多数据需要在各个类之间共享,静态类可以满足类之间的数据共享。静态类是不能实例化的,我们直接使用它的属性与方法,静态类最大的特点就是共享。

静态类就是在class关键字前加入了static关键字,基本语法是static Class ClassName{} 。静态类有几个特点:

(1) 静态类只能包括静态成员

(2) 类本身无法被实例化,也就是不能创建静态类的对象

(3) 静态类无法被继承

(4) 无法包含实例构造方法

创建静态类的时候,必须注意到以上四点。静态类所提供的数据成员和方法成员具有唯一性,就如同使用了全局变量,所以比较适合封装公用的成员。

1.3 毕设意义

本次毕业设计通过对《星空之翼》游戏的设计与实现,掌握制作一个游戏软件的方法,以及制作游戏软件的整个流程,制作游戏软件的步骤。

在开发《星空之翼》游戏中,对游戏架构进行分析、探索和实践。学习了游戏开发设计思想,程序架构,数据结构和算法,特别是对程序的扩展型有了直观的认识。本毕业设计体现面向对象的编程思想和模块化设计思想,本毕业设计用到多线程编程、嵌入式数据库(SQLite)编程和XML技术。本次毕业设计不但对大学四年所学的专业基础知识进行了复习、巩固和提高。而且学习了新的技术知识和思考解决问题的方法。

本次毕业设计对软件可扩展性和灵活性进行了探索和研究,《星空之翼》游戏能够代表飞行射击这一类的游戏,完全可以通过更改其中的精灵素材和设置,将《星空之翼》改变成为另一个的纵版飞行射击类游戏。

第二章 游戏的总体分析与设计

2.1 设计构想

由于游戏的特殊性,游戏的设计总是在开发的过程中被不断地修正和改变,因此游戏开发时常做无用功,把自己已经写好的东西推倒重来。为了避免过多的无用功,必须使代码模块强内聚、松耦合。在本设计中一定程度体现面向对象的思想和模块化编程带来的好处。在代码编写中,极力避免在面向对象的开发环境中进行面向过程的代码编写。

随着游戏开发的不断推进,延续的变更会使游戏的整体结构日渐紊乱,使程序难以理解和维护,所以在游戏的设计中,特别是在前期的设计中,我给游戏的核心给予足够的重视,多角度的全面分析,不要贪多求快,盲目过早进入具体的外部效果设计。

在本设计中游戏的整体采用组件式构架。不同功能的模块做成互相独立的系统,模块内部可以使用任何构架方式,只需要提供相应的接口即可。

2.1.1 游戏定位

《星空之翼》是一款纵向卷轴的飞行射击类的2D游戏。《星空之翼》的游戏定位是游戏DIY爱好者,游戏玩家可以自行控制游戏的各种元素。可以修改整个战场的背景、敌人、自己的战机等等,还可以使用自己的图片制作游戏精灵,支持游戏中的精灵元素更改。并提供扩展支持。采用模块设计,可以多人同时完成游戏的开发。地图文件采用嵌入式数据库SQLite,可以对地图文件进行加密。游戏窗口的大小游戏的地图决定,如果当前游戏的窗口与地图窗口不一致,游戏会自动更改游戏的窗口。游戏的各种界面参数保存在XML配置文件中。

2.1.2 游戏需求

《星空之翼》是一个纵向卷轴的2D游戏,游戏玩家的操作有6个按键,分别是 上、下、左、右、机枪、导弹回车键暂停/继续。游戏运行环境为.NET Framework2.0。星空之翼的关卡是采用提前读取的方式,不采用动态提取的方法。玩家飞机在开火,吃奖品,或者被击毁的时候有相应的提示音,游戏完成后会根据游戏玩家的信息显示历史积分榜。游戏的关卡与程序独立,通过配置文件来决定关卡的进行。通过地图编辑器完成关卡的内容。

2.1.3 游戏扩展

游戏的扩展性决定游戏能否长期被玩家追捧。在本设计中最大可能提供游戏的扩展性,如提供地图编辑器,玩家可以自己设计优秀的地图文件。玩家可以将自行设计的精灵图片放入的资源文件中从而改变游戏的敌人和背景。关卡文件采用的是嵌入式数据库SQLite,在后续的开发中可以添加新的游戏元素,从而完成游戏的升级。游戏主程序与地图编辑器是独立的两个模块,他们通过地图文件进行通信。游戏的内核是单独的DLL文件,在独立的工程里完成设计,可以不断修改其中的算法从而使游戏的运行速度更快,效果更好。

2.2 模块划分

《星空之翼》游戏的模块分为:

(1) 游戏引擎模块:负责游戏精灵的管理、游戏世界的计数器、游戏的冲突检测、图像渲染和粒子系统。

(2) 地图编辑器模块:负责游戏地图的创建和修改。

(3) 主程序模块:负责游戏的流程的控制和玩家的交互。

(4) 声音引擎模块:负责播放游戏的各种声音。

2.3 开发工具

本毕业设计采用的开发工具是Microsoft Visual Studio 2005

游戏开发使用的语言是C#

游戏需要运行的环境是.net framework 2.0

第三章 游戏的详细设计

3.1 游戏引擎模块

31

图3.1 游戏精灵类图

游戏引擎采用的是基类根形式构架。游戏引擎包括游戏精灵的管理、游戏世界的计数器、游戏的冲突检测、图像渲染和粒子系统。

游戏引擎最基本的类是精灵类(sprite)。敌人(Enemy)、盟友(Ally)、敌人武器(EnemyWeapons)、盟友武器(AllyWeapons)都从精灵类继承。

精灵动画类(Movie)封装类精灵的图片动画。

游戏引擎中有很多设计成单例模式的类。

(1) 对于所有的敌机,设计一个敌机管理类(EnermyManagement),由于整个游戏中只有敌机管理类,类设计采用单例模式。

(2) 对于所有的盟友,设计一个盟友管理类(AllyManagement),类设计采用单例模式。

(3) 对于所有的敌机武器,设计一个敌机武器管理类(EnermyWeaponsManagement),类设计采用单例模式。

(4) 对于所有的主角武器,设计一个主角武器管理类(AllyWeaponsManagement),类设计采用单例模式。

32

图3.2 游戏引擎中的管理类

(5) 设计一个碰撞检测类(CollisionSystem),类设计采用单例模式。

在游戏引擎中定义了一个GameConfig的静态类,用于共享游戏中的公共数据。

public static class GameConfig

{ public static int GameWordWidth = 800; //游戏世界的视觉宽度

public static int GameWordHeight = 600; //游戏世界的视觉高度

public static int GameWordTime = 1; //游戏世界的时间

public static int GameMode =0; //游戏世界的状态

}

3.2 地图编辑器模块

地图编辑器,顾名思义,就是用来编辑地图的工具,如果玩过《魔兽争霸》或《帝国时代2》、《英雄无敌》的话,应该会知道这几款游戏都附有地图编辑工具,可提供玩家自行编辑地图关卡,地图编辑器就具有类似的功能。

按部就班的按游戏设计者的安排进行游戏,固然会获得一些乐趣。但是否曾经想过玩自己设计的游戏呢?《星空之翼》的地图编辑器给了我们机会,不需要复杂的编程,只要有创意,短时间内就能学会并获得玩自己设计的纵向卷轴的飞行射击类游戏的乐趣。

地图编辑器模块最重要的是确定地图文件的存储格式,在探索地图文件存储格式中经历了4种方案,分别为:自定义二进制文件、XML文件、类的序列化和数据库。最后结合游戏要求和自身技术水平选择了嵌入式数据库(SQLite)。

(1) 自定义二进制文件:由于本身技术水平限制,不能很好的存储变化较大的二进制文件,最后放弃了这种地图文件组织方式。

(2) XML文件:由于XML对玩家过于透明,玩家很容易修改地图文件,导致地图文件极其不稳定,因此放弃了自定义二进制文件

(3) 类的序列化:类的序列化可以很好的组织地图文件,但是考虑到以后的地图文件的存储格式会发生改变,扩展性不强,每次修改都要修改类的定义

(4) 数据库:考虑到随着游戏开发的不断推进,延续的变更会使游戏的整体结构日渐紊乱,使程序难以理解和维护,游戏地图是游戏主程序与地图编辑器联系的纽带,要延续地图的稳定性,采用了数据库可以一定程度上,保持游戏地图的稳定性。最开始选择的数据库是access,可是考虑到许多玩家的电脑不一定安装了access的驱动,因此放弃access,很高兴找到SQLite数据库,SQLite太好了,只要一个System.Data.SQLite.dll就可以了使用该数据库。

目前地图文件主要存储的信息有两个,一个是静态的地图背景,另一个是动态的敌人。还包括一些配置信息。地图编辑器大体上分为两区域,一个是游戏地图的控件菜单区,另一个是游戏地图编辑区。在控件菜单区可以选择背景和精灵两种类别,通过背景玩家可以加载自己喜欢的背景Tile,然后添加到地图中。通过精灵可以添加自定义的敌人,可以设置敌人的运动方式、速度、攻击力,生命值,攻击模式。

 

33 图3.3 添加背景和精灵

在地图编辑器模块为了提高图形资源的复用,利用一小块一小块的图形(也就是tile)拼成一个大的完整的地图。在本设计中采用的Tile是矩形。在地图编辑器中定义了Sprite控件、Tile类、Map类。

(1) Sprite控件:是一个自定义的不规则控件。

(2) Tile类:Tile类中定义了该Tile在整个地图世界的坐标(X,Y)、大小(Width, Height)和所代表的精灵图片。

34

图3.4 添加精灵

(3) Map类:Map类定义了整个游戏世界尺寸(Width,Height)、Tile链表MapTiles和一系列地图操作方法。

在整个地图编辑器中最重要的3个难点是Sprite控件,游戏世界尺寸的动态变化和地图的读取和存储。

(1) Sprite控件:制作的方法是定义一个方法CalculateControlGraphicsPath来获得图片的边界。然后在Sprite_Load方法中设定Sprite控件的背景图片,设定Region属性为背景图片的边界。就可以得到不规则的控件了。

(2) MapTiles链表:由于地图的尺寸需要不断改变,因此采用一维的List 来存储游戏世界的二维可变Tile。

(3) 地图文件的读取和存储:由于地图文件采用的是SQLite数据库,因此地图的存储和读取也就是数据库的存储与读取。其中重要一点的是精灵图片采用的是GIF格式,要对普通的Gif文件处理,将不需要的部分镂空。镂空的方法是用PhotoShop的魔棒工具去除需要镂空的部分。

3.3 主程序模块

游戏主程序模块是整个游戏的核心,游戏主程序负责游戏的流程的控制和玩家的交互。在游戏主程序模块的重点有:

1. C#多线程编程

2. XML配置游戏信息

3. 全局键盘

4. 玩家交互

5. 地图管理

游戏的主程序采用了多线程,不同的子线程完成不同的工作。不同的线程又不同的调用周期。主程序创建了4个子线程,分别为warTimethread(战场计数器线程)、paintthread(战场打印线程)、managementthread(战场管理线程)、updatethread(战场更新线程)。

1. 战场计数器线程:主要完成整个游戏世界时间的记录。

2. 战场打印线程:完成对整个游戏所有的打印工作,包括游戏背景、敌人、盟友、敌人武器、盟友武器、爆炸效果和粒子系统图像的打印。

3. 战场管理线程:主要完成的是游戏的碰撞检测和游戏精灵的运动。

4. 战场更新线程:主要完成的是游戏世界游戏精灵的清理和再生。

游戏主程序采用XML配置游戏的名称、版本、游戏界面的高度和宽度。在《星空之翼》游戏中定义了一个SystemConfig类来操作游戏配置文件。

《星空之翼》提供了老板键Ctrl+F2。老板键用的技术使用了键盘钩子。在《星空之翼》游戏中定义了一个HookDll类,HookDll用于处理全局键盘事件。在游戏运行时是先安装了键盘钩子,就可以使用Ctrl+F2隐藏显示游戏界面的窗口。游戏退出后卸载钩子。

玩家可以根据自己的喜好设定玩家战机的初始装备和操作方法。

35

图3.5 自定义玩家信息

游戏玩家可以将自行设计的游戏地图加载到游戏中,游戏主程序会根据地图的尺寸改变自身的大小。游戏玩家的可以中途修改游戏的操作。

36

图3.6 游戏界面

游戏的基本流程是:游戏初始化—读取配置文件—选择游戏模式--开始游戏—结束游戏—保存配置

3.4 游戏主循环

37

图3.7 游戏主循环

游戏主程序核心是一个游戏主循环(Game Loop),游戏主循环(Game Loop)是每个游戏中最重要的一部分。游戏都是由更新状态、处理数据、播放音乐、更换地图和处理动画来构成。最能解释游戏主循环的就是固定的FPS(每秒帧数),例如,每次刷帧,地图向下移动1个像素,人物向上移动2个像素。而能引起这种的更新的一般是由两种行为引起的:

(1) 事件驱动(用户输入)

(2) 固定时间的FPS(每秒帧数)

《星空之翼》游戏采用的游戏主循环为组合式的游戏循环。在程序中玩家交互循环是事件驱动引起的,在游戏启动的情况下,游戏每隔33毫秒执行一次战场打印,每隔1毫秒执行一次战场计数,每隔10毫秒执行一次碰撞检测,每隔150执行一次游戏更新。游戏各子线程完成各自任务,相互配合,共同完成游戏的主循环。

3.5 碰撞检测

《星空之翼》采用最简单的碰撞检测算法矩形碰撞检测。最开始采用的是一个非常简单的算法A,经过多次测试,发现有些情况不能检测出碰撞,最后采用算法B.

(1) 算法A

38

图3.8 碰撞检测算法A

x1 + w1 > x2 限定第一个矩形的右边线在第二个矩形的左边线右边

x1 < x2 + w2 限定第一个矩形的左边线在第二个矩形的右边线左边

这样下来,就限定了两个矩形在X方向上投影必然相交。同理

y1 + h1 > y2 限定第一个矩形的下边线在第二个矩形的上边线下面

y1 < y2 + h2 限定第一个矩形的上边线在第二个矩形的下边线上边

这样下来,就限定了两个矩形在Y方向上投影必然相交。综合起来就可以简单进行碰撞检测了。

判断(x1 + w1 > x2 && x1 < x2 + w2 && y1 + h1 > y2 && y1 < y2 + h2)是否为真,如果为真,则发生了碰撞。

(2) 算法B

图3.9碰撞检测算法B

39

算法B能够检测出倾斜矩形间的碰撞检测,在碰撞检测的精度上提高了。可以采用的方法有两个,一个是采用的方法是依次判断矩形A的四边是否与矩形B相交。另一个是依次判断矩形的四个点在另一个矩形里。《星空之翼》采用的是第一种方式。

if (((p1.X - q1.X) * (q2.Y - q1.Y) - (q2.X - q1.X) * (p1.Y - q1.Y)) * ((q2.X - q1.X) * (p2.Y - q1.Y) - (p2.X - q1.X) * (q2.Y - q1.Y)) >= 0)

{

if (((q1.X - p1.X) * (p2.Y - p1.Y) - (p2.X - p1.X) * (q1.Y - p1.Y)) * ((p2.X - p1.X) * (q2.Y - p1.Y) - (q2.X - p1.X) * (p2.Y - p1.Y)) >= 0)

{ return true; }

}

return false;

3.6 地图格式与精灵图片处理

在《星空之翼》游戏中地图文件格式为*.map,实际上的地图文件格式为*.db,一个地图文件就是一个SQLite数据库文件,目前每个地图文件包括3个表Level,Sprite,LevelSprite。

(1) Level:描述了当前游戏关卡的游戏界面的尺寸、背景图片、故事剧情信息、地图尺寸等信息。

(2) Sprite:存储了当前关卡里所有的精灵的详细信息。

(3) Levelsprite:存储了当前关卡敌人、陨石和动态背景的具体的详细信息,包括生命值、攻击力、运动速度、运动模式、攻击模式、出现地点等详细信息。

在地图编辑器生成地图时,会将Level、Sprite和LevelSprite表的信息加入到地图文件中,然后在游戏主程序读取地图信息。

《星空之翼》精灵采用的原始图片为GIF格式,由于精灵通常都是不规则的图片,因此要对原始GiF处理,一种处理的方法是用Photoshop的魔棒工具去除透明背景。

将GIF文件加载到Photoshop中,选择魔棒工具,用魔棒工具点击GIF背景部分,魔棒工具会自动选择出背景部分,按DEL键删除背景,透明背景处理完成后另存为Gif格式,就可以的到透明的GIF背景图片。也可以使用专门的GIF透明背景处理小工具Ulead GIF Animator。

3.7声音引擎

Irrlicht是一款开源免费的声音引擎,Irrlicht引擎是一个用C++书写的高性能实时的3D引擎,可以应用于C++程序或者.NET语言中。在c#中使用Irrlicht很简单,引用irrKlang.NET.dll就可以了使用Irrlicht声音引擎了。使用的方法很简单,两行代码就可以播放声音了。

ISoundEngine engine = new ISoundEngine(); //定义引擎

engine.play2D("one.mp3"); //播发声音

在《星空之翼》游戏中主要用Irrlicht声音引擎播放游戏中的各种声音。

3.8 模块间的关系

整个游戏的组织方式是组件形式构架,游戏引擎、地图编辑器、游戏主程序、声音引擎是互相独立的系统,模块内部可以使用任何构架方式。这样的组织方式是比较灵活,可以同时进行开发,便于后续的开发。游戏主程序是整个模块的核心。

(1) 游戏主程序和地图编辑器:游戏主程序与地图编辑器之间用通过地图文件,地图编辑器创建修改地图文件,游戏主程序读取地图文件。

(2) 游戏主程序与游戏引擎:游戏主程序与游戏引擎的关系是最密切的,游戏主程序读取地图内容放进游戏引擎相关的类中,游戏引擎将处理的结果返回到游戏主程序中在显示出来。

(3) 游戏主程序与声音引擎:游戏主程序根据游戏引擎返回的结果,播放相应的游戏声音。

结论

毕业设计总结

《星空之翼》游戏开发时采用模块化的设计思想主要体现在:游戏引擎、游戏主程序和地图编辑器可以同时进行开发;对游戏引擎的算法进行优化,不会影响游戏主程序的运行;只要游戏地图格式不变,对地图编辑器的任何更改都不会影响游戏的运行;

《星空之翼》游戏开发时充分考虑了代码的重用性、功能的灵活性和可扩展性主要表现在:《星空之翼》的游戏引擎包含了纵版飞行射击类游戏所必须需的公用代码,只要是纵版飞行射击类游戏都可以直接引用《星空之翼》的游戏引擎;游戏主程序可以自行根据地图尺寸修改游戏本身的配置,玩家可以自行设置操作方法和游戏初始状态。地图编辑器可以设置游戏精灵攻击力、生命值、运动速度、运动模式和攻击模式等信息;游戏引擎采用面向对象体系结构设计原则,有很好的扩展性。《星空之翼》游戏地图采用的是嵌入式数据库SQLite,未来可以根据游戏的需求,改变游戏地图格式。因此地图格式有很好的扩展性。

未来的工作

由于时间紧迫,整个游戏开发有许多不如人意的地方,未来有许多需要改进的地方。下面例举出存在的不足和未来需要改进的地方:

(1) 在游戏引擎中,需要完善各个类之间的访问权限,需要丰富相应的构造函数满足不同条件下的需求。

(2) 在游戏引擎中,需要优化碰撞检测算法和粒子系统算法。

(3) 在游戏引擎中,需要为游戏精灵添加人工智能,让游戏精灵在游戏中运行得更好。

(4) 在地图编辑器中,需要解决地图文件读取的时间过长,需要减少地图文件占用的空间过大的问题。

(5) 在地图编辑器中,为地图文件添加密码选项,满足不同DIY爱好者的需求。

(6) 完善游戏主程序占用资源过高的问题,提高整个游戏的视觉效果和声音效果。

本文相关源码与论文下载  作者其他源码下载

  android文件的上传操作

在进行文件上传下载时,要注意使用的编码格式。

比方说下面一段上传代码:

 
//注意在android上传文件操作需要用到commons-net架包。我用的是commons-net-3.0.1.jar
//并导入下面的两个类.
import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPClient;
 
//上传代码:
FTPClient ftpClient = null;
/*
如果在下面不能创建ftpClient,可以尝试:将commons-net jar文件解压缩,然后导入到
android工程的src代码里面。
*/
ftpClient = new FTPClient();
 
//ftpClient.connect("ftp服务器的url",端口号);
ftpClient.connect("192.168,18",21);
ftpClient.login("userName","password");//用户名和密码
 

//ftpClient.setFileType(FTP.BINARY_FILE_TYPE)这句话一定要有。

//在此前写这段代码的时候,漏掉这句话,下载的文件,总比实际文件大,每上传1024

//个字节多4个字节。原来是ftp把文件默认的以文本文件传送的。

ftpClient.setFileType(FTP.BINARY_TILE_TYPE);
 
 

  Explicit Congestion Notification

ECN是一个IP协议和TCP协议的扩展,详细定义请见RFC 3168 (2001)

ECN允许端到端的网络拥塞通告,不同于通常我们使用的方式,即通过丢包来判断拥塞。ECN作为一个可选项,需要通信双方同时支持。当ECN被成功的协商后,一个支持ECN的路由器可以在IP头设置一个代替丢包的位来预警即将发生的拥塞。需要注意的是,有些老的网络设备会丢弃带有ECN位的包而不是仅仅忽略这个位。

IP中ECN的操作方式

ECN使用IP头中的两个比特的区分服务位(DifServ)来表明四种不同的代码点。
00代表Non ECN-Capable Transport -- Noe-ECT; 10代表ECN Capable Transport – ECT(0); 01代表ECN Capable Transport – ECT(1); 11代表Congestion Encountered – CE

当通信双方支持ECN时,他们将ECN位置为ECT(0)或ECT(1)。如果数据包穿过一个动态队列管理的队列(AQM),例如使用RED的队列,它们是支持ECN的,设备将改变代码点为CE来代替丢包。这个操作是为了告诉接收端即将出现拥塞。在接收端这个拥塞的通知被上层协议处理(TCP协议),然后需要被回馈给发送方以此来降低发送速率。

由于CE需要被TCP来处理,ECN必须联合TCP共同使用:需要支持拥塞控制;需要有方法将CE回馈给发送方。

TCP中ECN的操作方式

TCP支持ECN通过TCP头的两个标志位。这两个位被用来回馈可能出现的拥塞同时也确认拥塞的通告已经被成功接收。两个标志位分别叫做ECN-Echo(ECE)和Congestion Window Reduced(CWR)

因为ECN是可选的,如果要使用ECN需要在SYN和SYN-ACK中进行协商。

实现

Microsoft Windows:Windows Server 2008, Windows 7, and Windows Vista均支持ECN for TCP,但是默认是关闭状态。通过下面命令开启:netsh interface tcp set global ecncapability=enabled

Apple Macintosh: Mac OS X 10.5和10.6支持ECN for TCP。使用下面的sysctl变量来控制:net.inet.tcp.ecn_negotiate_in(enable ECN on incoming connections that already have ECN flags set), net.inet.tcp.ecn_initiate_out (try to initiate outgoing connections with ECN enabled). Both variables default to 0, but can be set to 1 to enable the respective behavior: sysctl -w net.inet.tcp.ecn_negotiate_in=1 and sysctl -w net.inet.tcp.ecn_initiate_out=1
To make the settings persistent, put following lines in /etc/sysctl.conf:
net.inet.tcp.ecn_initiate_out=1
net.inet.tcp.ecn_negotiate_in=1

Linux:Linux Kernal支持三种状态的ECN for TCP,分别是 no ECN(0),Use ECN(1), Support ECN/Server Mode(2)。如果对方支持ECN,默认被设置为支持。在大多数系统可以通过下面查看支持情况:sysctl net.ipv4.tcp_ecn=1

  List and Map 学习笔记

编写泛型类要注意:
1) 在定义一个泛型类的时候,在 “<>”之间定义形式类型参数,例如:“class TestGen<K,V>”,其中“K” , “V”不代表值,而是表示类型。
2) 实例化泛型对象的时候,一定要在类名后面指定类型参数的值(类型),一共要有两次书写。例如:
TestGen<String,String> t=new TestGen<String,String>();
3) 泛型中<K extends Object>,extends并不代表继承,它是类型范围限制。

  很多地方用到LIST,Map这两个接口,现在把它的基本用法归纳起来(部分实现类的用法):

1, List

   JDK1.5.0说明:

(

   public interface List
extends Collection

有序的 collection(也称为序列)。此接口的用户可以对列表中每个元素的插入位置进行精确地控  制。用户可以根据元素的整数索引(在列表中的位置)访问元素,并搜索列表中的元素。

与 set 不同,列表通常允许重复的元素。更正式地说,列表通常允许满足 e1.equals(e2) 的元素对 e1 和 e2,并且如果列表本身允许 null 元素的话,通常它们允许多个 null 元素。难免有人希望通过在用户尝试插入重复元素时抛出运行时异常的方法来禁止重复的列表,但我们希望这种用法越少越好。)

   1)Create List

   // Create the list
    List list = new LinkedList();    // Doubly-linked list
    list = new ArrayList();          // List implemented as growable array
    // Append an element to the list
    list.add("a");
    // Insert an element at the head of the list
    list.add(0, "b");
    // Get the number of elements in the list
    int size = list.size();          // 2
    // Retrieving the element at the end of the list
    Object element = list.get(list.size()-1);   // a
    // Retrieving the element at the head of the list
    element = list.get(0);                      // b
    // Remove the first occurrence of an element
    boolean b = list.remove("b");      // true
    b = list.remove("b");              // false
    // Remove the element at a particular index
    element = list.remove(0);          // a

   2)  Sorting a List

    // Create a list
String[] strArray = new String[] {"z", "a", "C"};
List list = Arrays.asList(strArray);

// Sort
Collections.sort(list);
// C, a, z

// Case-insensitive sort
Collections.sort(list, String.CASE_INSENSITIVE_ORDER);
// a, C, z

// Reverse-order sort
Collections.sort(list, Collections.reverseOrder());
// z, a, C

// Case-insensitive reverse-order sort
Collections.sort(list, String.CASE_INSENSITIVE_ORDER);
Collections.reverse(list);
// z, C, a
   3) Operating on ListsSee 
    // Create the lists
List list1 = new ArrayList();
List list2 = new ArrayList();

// Add elements to the lists ...

// Copy all the elements from list2 to list1 (list1 += list2)
// list1 becomes the union of list1 and list2
list1.addAll(list2);

// Remove all the elements in list1 from list2 (list1 -= list2)
// list1 becomes the asymmetric difference of list1 and list2
list1.removeAll(list2);

// Get the intersection of list1 and list2
// list1 becomes the intersection of list1 and list2
list1.retainAll(list2);

// Remove all elements from a list
list1.clear();

// Truncate the list
int newSize = 2;
list1.subList(newSize, list1.size()).clear();
    List的复制,可以用clone()方法,clone()是Object的方法.
     因此复制一个ArrayList,可以这样做:
  PreList=(List)((ArrayList)PrefixList).clone();
     PreList和Prefixlist都是ArrayListr的实例.
 2.Map
  JDK1.5.0说明:
( 
public interface Map
 

将键映射到值的对象。一个映射不能包含重复的键;每个键最多只能映射一个值。

此接口代替 Dictionary 类,后者完全是一个抽象类,而不是一个接口。

Map 接口提供三种collection 视图,允许以键集、值集合或键-值映射关系集的形式查看某个映射的内容。映射的顺序 定义为迭代器在映射的

collection 视图中返回其元素的顺序。某些映射实现可明确保证其顺序,如 TreeMap 类;某些映射实现则不保证顺序,如 HashMap 类. )

1)Create Map  
   Map map = new LinkedHashMap();
// Add some elements
map.put("1", "value1");
map.put("2", "value2");
map.put("3", "value3");
map.put("2", "value4");

// List the entries
for (Iterator it=map.keySet().iterator(); it.hasNext(); ) {
Object key = it.next();
Object value = map.get(key);
}
// [1=value1, 2=value4, 3=value3]
  2) Operater Map
     Listing the Elements of a Collection
     // For a set or list
for (Iterator it=collection.iterator(); it.hasNext(); ) {
Object element = it.next();
}

// For keys of a map
for (Iterator it=map.keySet().iterator(); it.hasNext(); ) {
Object key = it.next();
}

// For values of a map
for (Iterator it=map.values().iterator(); it.hasNext(); ) {
Object value = it.next();
}

// For both the keys and values of a map
for (Iterator it=map.entrySet().iterator(); it.hasNext(); ) {
Map.Entry entry = (Map.Entry)it.next();
Object key = entry.getKey();
Object value = entry.getValue();
}

  【2011.06.30】 第四次期末总结

    刚刚看了下博客里仅存的几篇帖子,想起自己寒假时候的雄心壮志,比照下当前的萎靡不振,不觉有些汗颜。先习惯性的解释一下吧。博客很少更新,一则是网速太慢以至于更新博客时间变成了便秘一样的事情,额,很纠结;二则是开始学着用OneNote做笔记,这软件很棒,基本上实现了当初所预想的“不动笔墨不读书”,博客也就没有当初所想的那么重要;三则,当计划照进现实,结局总不外乎如此。

    以上算是对自己的一个解释,我很讨厌回顾过去发现当初所预想的事情完全没做,而且没有任何理由时那种挫败感,好强而又奇懒得人,活着真是纠结啊。

    前天儿交了WinCore大作业,老师给的评分很厚道,我觉得忙活了大半个学期,总算是可以摸着胸脯对自己说,“这门课,我学有所成”。现在回顾一下,通过这门课我学会的不仅仅是关于Windows底层的机制、一些乱七八糟的API、一些科普性的编程技巧,最大的收获在于我真的花费了大量的时间与精力去设计、去编码、去测试了一个较为复杂的程序,其中所获得的提高真的是难以言述。之前的我或许开口便是编程语言的优劣,闭口是某某算法的精髓,但是只有我自己最清楚,我心里虚。之前,我不停的问自己,“你学了那么多,什么用?你学了那么多,哪一个你能真正用到实践中去而不是仅仅通过考试?你学了那么多,你真正懂的有几个?”现在,我觉得心里的虚,也可以说怀疑略微少了点,尤其是与别人作比较。这话有些诛心,却是我真心所想。

    再说说逍哥的病毒课。其实这学期我一直很为自己能选到windows跟病毒而感到庆幸,这两门课都涉及到Windows比较底层的机制,都涉及到Win32 SDK编程,区别在于,WinCore走的是康庄大道,而逍哥多在向我们指引一些通幽的曲径。二者相互联系、相互对照,总是有些意料之外的收获。

    至于OS,我只能说当初自己是太天真了。这是一门逻辑清楚、深入浅出、轻重分明的课,李彤院长的风度也确实是一时无两,但是都改变不了这还是一门入门兼清谈课的事实,越往后学,越觉气闷。

    数据库,这应该算是我这学期花费气力最多的课程了。但或许是学不得法,花费多而收益浅,略有些失望。幸而基础还算牢靠,也算安慰奖了。

    挂掉的素选,真心不想多说。资环的孙子们,这事儿没完。思路到这,突然又是一阵郁闷,不写了。改天有空再批判下自己吧

  kmalloc文章

在设备驱动程序中动态开辟内存,不是用malloc,而是kmalloc,或者用get_free_pages直接申请页。释放内存用的是kfree,或free_pages.

   对于提供了MMU(存储管理器,辅助操作系统进行内存管理,提供虚实地址转换等硬件支持)的处理器而言,Linux提供了复杂的存储管理系统,使得进程所能访问的内存达到4GB。

进程的4GB内存空间被人为的分为两个部分--用户空间与内核空间。用户空间地址分布从0到3GB(PAGE_OFFSET,在0x86中它等于0xC0000000),3GB到4GB为内核空间。

内核空间中,从3G到vmalloc_start这段地址是物理内存映射区域(该区域中包含了内核镜像、物理页框表mem_map等等),比如我们使用的 VMware虚拟系统内存是160M,那么3G~3G+160M这片内存就应该映射物理内存。在物理内存映射区之后,就是vmalloc区域。对于 160M的系统而言,vmalloc_start位置应在3G+160M附近(在物理内存映射区与vmalloc_start期间还存在一个8M的gap 来防止跃界),vmalloc_end的位置接近4G(最后位置系统会保留一片128k大小的区域用于专用页面映射)

kmalloc和get_free_page申请的内存位于物理内存映射区域,而且在物理上也是连续的,它们与真实的物理地址只有一个固定的偏移,因此存在较简单的转换关系,virt_to_phys()可以实现内核虚拟地址转化为物理地址:
   #define __pa(x) ((unsigned long)(x)-PAGE_OFFSET)
   extern inline unsigned long virt_to_phys(volatile void * address)
   {
       return __pa(address);
   }
上面转换过程是将虚拟地址减去3G(PAGE_OFFSET=0XC000000)。

与之对应的函数为phys_to_virt(),将内核物理地址转化为虚拟地址:
   #define __va(x) ((void *)((unsigned long)(x)+PAGE_OFFSET))
   extern inline void * phys_to_virt(unsigned long address)
   {
       return __va(address);
   }
virt_to_phys()和phys_to_virt()都定义在include\asm-i386\io.h中。
-------------------------------------------------------------------------------------

1、kmalloc() 分配连续的物理地址,用于小内存分配。

2、__get_free_page() 分配连续的物理地址,用于整页分配。

至于为什么说以上函数分配的是连续的物理地址和返回的到底是物理地址还是虚拟地址,下面的记录会做出解释。

kmalloc() 函数本身是基于 slab 实现的。slab 是为分配小内存提供的一种高效机制。但 slab 这种分配机制又不是独立的,它本身也是在页分配器的基础上来划分更细粒度的内存供调用者使用。也就是说系统先用页分配器分配以页为最小单位的连续物理地址,然后 kmalloc() 再在这上面根据调用者的需要进行切分。

关于以上论述,我们可以查看 kmalloc() 的实现,kmalloc()函数的实现是在 __do_kmalloc() 中,可以看到在 __do_kmalloc()代码里最终调用了 __cache_alloc() 来分配一个 slab,其实

kmem_cache_alloc() 等函数的实现也是调用了这个函数来分配新的 slab。我们按照 __cache_alloc()函数的调用路径一直跟踪下去会发现在 cache_grow() 函数中使用了 kmem_getpages()函数来分配一个物理页面,kmem_getpages() 函数中调用的alloc_pages_node() 最终是使用 __alloc_pages() 来返回一个struct page 结构,而这个结构正是系统用来描述物理页面的。这样也就证实了上面所说的,slab 是在物理页面基础上实现的。kmalloc() 分配的是物理地址。

__get_free_page() 是页面分配器提供给调用者的最底层的内存分配函数。它分配连续的物理内存。__get_free_page() 函数本身是基于 buddy 实现的。在使用 buddy 实现的物理内存管理中最小分配粒度是以页为单位的。关于以上论述,我们可以查看__get_free_page()的实现,可以看到 __get_free_page()函数只是一个非常简单的封状,它的整个函数实现就是无条件的调用 __alloc_pages() 函数来分配物理内存,上面记录 kmalloc()实现时也提到过是在调用 __alloc_pages() 函数来分配物理页面的前提下进行的 slab 管理。那么这个函数是如何分配到物理页面又是在什么区域中进行分配的?回答这个问题只能看下相关的实现。可以看到在 __alloc_pages() 函数中,多次尝试调用get_page_from_freelist() 函数从 zonelist 中取得相关 zone,并从其中返回一个可用的 struct page 页面(这里的有些调用分支是因为标志不同)。至此,可以知道一个物理页面的分配是从 zonelist(一个 zone 的结构数组)中的 zone 返回的。那么 zonelist/zone 是如何与物理页面关联,又是如何初始化的呢?继续来看 free_area_init_nodes() 函数,此函数在系统初始化时由 zone_sizes_init() 函数间接调用的,zone_sizes_init()函数填充了三个区域:ZONE_DMA,ZONE_NORMAL,ZONE_HIGHMEM。并把他们作为参数调用 free_area_init_nodes(),在这个函数中会分配一个 pglist_data 结构,此结构中包含了 zonelist/zone结构和一个 struct page 的物理页结构,在函数最后用此结构作为参数调用了 free_area_init_node() 函数,在这个函数中首先使用 calculate_node_totalpages() 函数标记 pglist_data 相关区域,然后调用 alloc_node_mem_map() 函数初始化 pglist_data结构中的 struct page 物理页。最后使用 free_area_init_core()函数关联 pglist_data 与 zonelist。现在通以上分析已经明确了__get_free_page() 函数分配物理内存的流程。但这里又引出了几个新问题,那就是此函数分配的物理页面是如何映射的?映射到了什么位置?到这里不得不去看下与 VMM 相关的引导代码。

在看 VMM 相关的引导代码前,先来看一下 virt_to_phys() 与phys_to_virt 这两个函数。顾名思义,即是虚拟地址到物理地址和物理地址到虚拟地址的转换。函数实现十分简单,前者调用了__pa( address ) 转换虚拟地址到物理地址,后者调用 __va(addrress ) 将物理地址转换为虚拟地址。再看下 __pa __va 这两个宏到底做了什么。

#define __pa(x) ((unsigned long)(x)-PAGE_OFFSET)
#define __va(x) ((void *)((unsigned long)(x)+PAGE_OFFSET))

通过上面可以看到仅仅是把地址加上或减去 PAGE_OFFSET,而PAGE_OFFSET 在 x86 下定义为 0xC0000000。这里又引出了疑问,在 linux 下写过 driver 的人都知道,在使用 kmalloc() 与

__get_free_page() 分配完物理地址后,如果想得到正确的物理地址需要使用 virt_to_phys() 进行转换。那么为什么要有这一步呢?我们不分配的不就是物理地址么?怎么分配完成还需要转换?如果返回的是虚拟地址,那么根据如上对 virt_to_phys() 的分析,为什么仅仅对 PAGE_OFFSET 操作就能实现地址转换呢?虚拟地址与物理地址之间的转换不需要查页表么?代着以上诸多疑问来看 VMM 相关的引导代码。

直接从 start_kernel() 内核引导部分来查找 VMM 相关内容。可以看到第一个应该关注的函数是 setup_arch(),在这个函数当中使用paging_init() 函数来初始化和映射硬件页表(在初始化前已有 8M内存被映射,在这里不做记录),而 paging_init() 则是调用的pagetable_init() 来完成内核物理地址的映射以及相关内存的初始化。在 pagetable_init() 函数中,首先是一些 PAE/PSE/PGE 相关判断与设置,然后使用 kernel_physical_mapping_init() 函数来实现内核物理内存的映射。在这个函数中可以很清楚的看到,pgd_idx 是以PAGE_OFFSET 为启始地址进行映射的,也就是说循环初始化所有物理地址是以 PAGE_OFFSET 为起点的。继续观察我们可以看到在 PMD 被初始化后,所有地址计算均是以 PAGE_OFFSET 作为标记来递增的。分析到这里已经很明显的可以看出,物理地址被映射到以 PAGE_OFFSET 开始的虚拟地址空间。这样以上所有疑问就都有了答案。kmalloc() 与__get_free_page() 所分配的物理页面被映射到了 PAGE_OFFSET 开始的虚拟地址,也就是说实际物理地址与虚拟地址有一组一一对应的关系,

正是因为有了这种映射关系,对内核以 PAGE_OFFSET 启始的虚拟地址的分配也就是对物理地址的分配(当然这有一定的范围,应该在 PAGE_OFFSET与 VMALLOC_START 之间,后者为 vmalloc() 函数分配内存的启始地址)。这也就解释了为什么 virt_to_phys() 与 phys_to_virt() 函数的实现仅仅是加/减 PAGE_OFFSET 即可在虚拟地址与物理地址之间转换,正是因为了有了这种映射,且固定不变,所以才不用去查页表进行转换。这也同样回答了开始的问题,即 kmalloc() / __get_free_page() 分配的是物理地址,而返回的则是虚拟地址(虽然这听上去有些别扭)。正是因为有了这种映射关系,所以需要将它们的返回地址减去 PAGE_OFFSET 才可以得到真正的物理地址。

另一篇更容易理解的:
kmalloc, vmalloc分配的内存结构 zz
2008-01-20 16:05
进程空间:| <-用户空间-> | <-内核空间-> |
内核空间:| <-物理内存映射区-> | <-vmalloc区域-> |

==============原文================================

   对于提供了MMU(存储管理器,辅助操作系统进行内存管理,提供虚实地址转换等硬件支持)的处理器而言,Linux提供了复杂的存储管理系统,使得进程所能访问的内存达到4GB。

进程的4GB内存空间被人为的分为两个部分--用户空间与内核空间。用户空间地址分布从0到3GB(PAGE_OFFSET,在0x86中它等于0xC0000000),3GB到4GB为内核空间。

内核空间中,从3G到vmalloc_start这段地址是物理内存映射区域(该区域中包含了内核镜像、物理页框表mem_map等等),比如我们使用 的 VMware虚拟系统内存是160M,那么3G~3G+160M这片内存就应该映射物理内存。在物理内存映射区之后,就是vmalloc区域。对于 160M的系统而言,vmalloc_start位置应在3G+160M附近(在物理内存映射区与vmalloc_start期间还存在一个8M的gap 来防止跃界),vmalloc_end的位置接近4G(最后位置系统会保留一片128k大小的区域用于专用页面映射)

kmalloc和get_free_page申请的内存位于物理内存映射区域,而且在物理上也是连续的,它们与真实的物理地址只有一个固定的偏移,因此存在较简单的转换关系,virt_to_phys()可以实现内核虚拟地址转化为物理地址:
   #define __pa(x) ((unsigned long)(x)-PAGE_OFFSET)
   extern inline unsigned long virt_to_phys(volatile void * address)
   {
       return __pa(address);
   }
上面转换过程是将虚拟地址减去3G(PAGE_OFFSET=0XC000000)。

与之对应的函数为phys_to_virt(),将内核物理地址转化为虚拟地址:
   #define __va(x) ((void *)((unsigned long)(x)+PAGE_OFFSET))
   extern inline void * phys_to_virt(unsigned long address)
   {
       return __va(address);
   }
virt_to_phys()和phys_to_virt()都定义在include\asm-i386\io.h中。

而vmalloc申请的内存则位于vmalloc_start~vmalloc_end之间,与物理地址没有简单的转换关系,虽然在逻辑上它们也是连续的,但是在物理上它们不要求连续。

我们用下面的程序来演示kmalloc、get_free_page和vmalloc的区别:
#include
#include
#include
MODULE_LICENSE("GPL");
unsigned char *pagemem;
unsigned char *kmallocmem;
unsigned char *vmallocmem;

int __init mem_module_init(void)
{
//最好每次内存申请都检查申请是否成功
//下面这段仅仅作为演示的代码没有检查
pagemem = (unsigned char*)get_free_page(0);
printk("<1>pagemem addr=%x", pagemem);

kmallocmem = (unsigned char*)kmalloc(100, 0);
printk("<1>kmallocmem addr=%x", kmallocmem);

vmallocmem = (unsigned char*)vmalloc(1000000);
printk("<1>vmallocmem addr=%x", vmallocmem);

return 0;
}

void __exit mem_module_exit(void)
{
free_page(pagemem);
kfree(kmallocmem);
vfree(vmallocmem);
}

module_init(mem_module_init);
module_exit(mem_module_exit);

我们的系统上有160MB的内存空间,运行一次上述程序,发现pagemem的地址在0xc7997000(约3G+121M)、kmallocmem 地址在0xc9bc1380(约3G+155M)、vmallocmem的地址在0xcabeb000(约3G+171M)处,符合前文所述的内存布局。

  davinci-pcm.c

davinci-pcm.c

001     /**
002       * ALSA PCM interface for the TI DAVINCI processor
003       *
004       * Author:      Vladimir Barinov, <vbarinov@embeddedalley.com>
005       * Copyright:   (C) 2007 MontaVista Software, Inc., <source@mvista.com>
006       * added SRAM ping/pong (C) 2008 Troy Kisky <troy.kisky@boundarydevices.com>
007       *
008       * This program is free software; you can redistribute it and/or modify
009       * it under the terms of the GNU General Public License version 2 as
010       * published by the Free Software Foundation.
011       */
012     
013     #include <linux/module.h>
014     #include <linux/init.h>
015     #include <linux/platform_device.h>
016     #include <linux/slab.h>
017     #include <linux/dma-mapping.h>
018     #include <linux/kernel.h>
019     
020     #include <sound/core.h>
021     #include <sound/pcm.h>
022     #include <sound/pcm_params.h>
023     #include <sound/soc.h>
024     
025     #include <asm/dma.h>
026     #include <mach/edma.h>
027     #include <mach/sram.h>
028     
029     #include "davinci-pcm.h"
030     
031     #ifdef DEBUG
032     static void print_buf_info( int slot, char *name)
033     {
034          struct edmacc_param p;
035          if (slot < 0)
036              return ;
037          edma_read_slot(slot, &p);
038          printk(KERN_DEBUG "%s: 0x%x, opt=%x, src=%x, a_b_cnt=%x dst=%x\n" ,
039                  name, slot, p.opt, p.src, p.a_b_cnt, p.dst);
040          printk(KERN_DEBUG "    src_dst_bidx=%x link_bcntrld=%x src_dst_cidx=%x ccnt=%x\n" ,
041                  p.src_dst_bidx, p.link_bcntrld, p.src_dst_cidx, p.ccnt);
042     }
043     #else
044     static void print_buf_info( int slot, char *name)
045     {
046     }
047     #endif
048     
049     static struct snd_pcm_hardware pcm_hardware_playback = {
050          .info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER |
051               SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |
052               SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME),
053          .formats = (SNDRV_PCM_FMTBIT_S16_LE),
054          .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
055                SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 |
056                SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |
057                SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 |
058                SNDRV_PCM_RATE_KNOT),
059          .rate_min = 8000,
060          .rate_max = 96000,
061          .channels_min = 2,
062          .channels_max = 2,
063          .buffer_bytes_max = 128 * 1024,
064          .period_bytes_min = 32,
065          .period_bytes_max = 8 * 1024,
066          .periods_min = 16,
067          .periods_max = 255,
068          .fifo_size = 0,
069     };
070     
071     static struct snd_pcm_hardware pcm_hardware_capture = {
072          .info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER |
073               SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |
074               SNDRV_PCM_INFO_PAUSE),
075          .formats = (SNDRV_PCM_FMTBIT_S16_LE),
076          .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
077                SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 |
078                SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |
079                SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 |
080                SNDRV_PCM_RATE_KNOT),
081          .rate_min = 8000,
082          .rate_max = 96000,
083          .channels_min = 2,
084          .channels_max = 2,
085          .buffer_bytes_max = 128 * 1024,
086          .period_bytes_min = 32,
087          .period_bytes_max = 8 * 1024,
088          .periods_min = 16,
089          .periods_max = 255,
090          .fifo_size = 0,
091     };
092     
093     /**
094       * How ping/pong works....
095       *
096       * Playback:
097       * ram_params - copys 2*ping_size from start of SDRAM to iram,
098       *  links to ram_link2
099       * ram_link2 - copys rest of SDRAM to iram in ping_size units,
100       *  links to ram_link
101       * ram_link - copys entire SDRAM to iram in ping_size uints,
102       *  links to self
103       *
104       * asp_params - same as asp_link[0]
105       * asp_link[0] - copys from lower half of iram to asp port
106       *  links to asp_link[1], triggers iram copy event on completion
107       * asp_link[1] - copys from upper half of iram to asp port
108       *  links to asp_link[0], triggers iram copy event on completion
109       *  triggers interrupt only needed to let upper SOC levels update position
110       *  in stream on completion
111       *
112       * When playback is started:
113       *  ram_params started
114       *  asp_params started
115       *
116       * Capture:
117       * ram_params - same as ram_link,
118       *  links to ram_link
119       * ram_link - same as playback
120       *  links to self
121       *
122       * asp_params - same as playback
123       * asp_link[0] - same as playback
124       * asp_link[1] - same as playback
125       *
126       * When capture is started:
127       *  asp_params started
128       */
129     struct davinci_runtime_data {
130          spinlock_t lock;
131          int period;     /** current DMA period */
132          int asp_channel;    /** Master DMA channel */
133          int asp_link[2];    /** asp parameter link channel, ping/pong */
134          struct davinci_pcm_dma_params *params;  /** DMA params */
135          int ram_channel;
136          int ram_link;
137          int ram_link2;
138          struct edmacc_param asp_params;
139          struct edmacc_param ram_params;
140     };
141     
142     /**
143       * Not used with ping/pong
144       */
145     static void davinci_pcm_enqueue_dma( struct snd_pcm_substream *substream)
146     {
147          struct davinci_runtime_data *prtd = substream->runtime->private_data;
148          struct snd_pcm_runtime *runtime = substream->runtime;
149          int link = prtd->asp_link[0];
150          unsigned int period_size;
151          unsigned int dma_offset;
152          dma_addr_t dma_pos;
153          dma_addr_t src, dst;
154          unsigned short src_bidx, dst_bidx;
155          unsigned short src_cidx, dst_cidx;
156          unsigned int data_type;
157          unsigned short acnt;
158          unsigned int count;
159          unsigned int fifo_level;
160     
161          period_size = snd_pcm_lib_period_bytes(substream);
162          dma_offset = prtd->period * period_size;
163          dma_pos = runtime->dma_addr + dma_offset;
164          fifo_level = prtd->params->fifo_level;
165     
166          pr_debug( "davinci_pcm: audio_set_dma_params_play channel = %d "
167              "dma_ptr = %x period_size=%x\n" , link, dma_pos, period_size);
168     
169          data_type = prtd->params->data_type;
170          count = period_size / data_type;
171          if (fifo_level)
172              count /= fifo_level;
173     
174          if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
175              src = dma_pos;
176              dst = prtd->params->dma_addr;
177              src_bidx = data_type;
178              dst_bidx = 0;
179              src_cidx = data_type * fifo_level;
180              dst_cidx = 0;
181          } else {
182              src = prtd->params->dma_addr;
183              dst = dma_pos;
184              src_bidx = 0;
185              dst_bidx = data_type;
186              src_cidx = 0;
187              dst_cidx = data_type * fifo_level;
188          }
189     
190          acnt = prtd->params->acnt;
191          edma_set_src(link, src, INCR, W8BIT);
192          edma_set_dest(link, dst, INCR, W8BIT);
193     
194          edma_set_src_index(link, src_bidx, src_cidx);
195          edma_set_dest_index(link, dst_bidx, dst_cidx);
196     
197          if (!fifo_level)
198              edma_set_transfer_params(link, acnt, count, 1, 0, ASYNC);
199          else
200              edma_set_transfer_params(link, acnt, fifo_level, count,
201                                  fifo_level, ABSYNC);
202     
203          prtd->period++;
204          if (unlikely(prtd->period >= runtime->periods))
205              prtd->period = 0;
206     }
207     
208     static void davinci_pcm_dma_irq(unsigned link, u16 ch_status, void *data)
209     {
210          struct snd_pcm_substream *substream = data;
211          struct davinci_runtime_data *prtd = substream->runtime->private_data;
212     
213          print_buf_info(prtd->ram_channel, "i ram_channel" );
214          pr_debug( "davinci_pcm: link=%d, status=0x%x\n" , link, ch_status);
215     
216          if (unlikely(ch_status != DMA_COMPLETE))
217              return ;
218     
219          if (snd_pcm_running(substream)) {
220              if (prtd->ram_channel < 0) {
221                  /** No ping/pong must fix up link dma data*/
222                  spin_lock(&prtd->lock);
223                  davinci_pcm_enqueue_dma(substream);
224                  spin_unlock(&prtd->lock);
225              }
226              snd_pcm_period_elapsed(substream);
227          }
228     }
229     
230     static int allocate_sram( struct snd_pcm_substream *substream, unsigned size,
231              struct snd_pcm_hardware *ppcm)
232     {
233          struct snd_dma_buffer *buf = &substream->dma_buffer;
234          struct snd_dma_buffer *iram_dma = NULL;
235          dma_addr_t iram_phys = 0;
236          void *iram_virt = NULL;
237     
238          if (buf->private_data || !size)
239              return 0;
240     
241          ppcm->period_bytes_max = size;
242          iram_virt = sram_alloc(size, &iram_phys);
243          if (!iram_virt)
244              goto exit1;
245          iram_dma = kzalloc( sizeof (*iram_dma), GFP_KERNEL);
246          if (!iram_dma)
247              goto exit2;
248          iram_dma->area = iram_virt;
249          iram_dma->addr = iram_phys;
250          memset (iram_dma->area, 0, size);
251          iram_dma->bytes = size;
252          buf->private_data = iram_dma;
253          return 0;
254     exit2:
255          if (iram_virt)
256              sram_free(iram_virt, size);
257     exit1:
258          return -ENOMEM;
259     }
260     
261     /**
262       * Only used with ping/pong.
263       * This is called after runtime->dma_addr, period_bytes and data_type are valid
264       */
265     static int ping_pong_dma_setup( struct snd_pcm_substream *substream)
266     {
267          unsigned short ram_src_cidx, ram_dst_cidx;
268          struct snd_pcm_runtime *runtime = substream->runtime;
269          struct davinci_runtime_data *prtd = runtime->private_data;
270          struct snd_dma_buffer *iram_dma =
271              ( struct snd_dma_buffer *)substream->dma_buffer.private_data;
272          struct davinci_pcm_dma_params *params = prtd->params;
273          unsigned int data_type = params->data_type;
274          unsigned int acnt = params->acnt;
275          /** divide by 2 for ping/pong */
276          unsigned int ping_size = snd_pcm_lib_period_bytes(substream) >> 1;
277          int link = prtd->asp_link[1];
278          unsigned int fifo_level = prtd->params->fifo_level;
279          unsigned int count;
280          if ((data_type == 0) || (data_type > 4)) {
281              printk(KERN_ERR "%s: data_type=%i\n" , __func__, data_type);
282              return -EINVAL;
283          }
284          if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
285              dma_addr_t asp_src_pong = iram_dma->addr + ping_size;
286              ram_src_cidx = ping_size;
287              ram_dst_cidx = -ping_size;
288              edma_set_src(link, asp_src_pong, INCR, W8BIT);
289     
290              link = prtd->asp_link[0];
291              edma_set_src_index(link, data_type, data_type * fifo_level);
292              link = prtd->asp_link[1];
293              edma_set_src_index(link, data_type, data_type * fifo_level);
294     
295              link = prtd->ram_link;
296              edma_set_src(link, runtime->dma_addr, INCR, W32BIT);
297          } else {
298              dma_addr_t asp_dst_pong = iram_dma->addr + ping_size;
299              ram_src_cidx = -ping_size;
300              ram_dst_cidx = ping_size;
301              edma_set_dest(link, asp_dst_pong, INCR, W8BIT);
302     
303              link = prtd->asp_link[0];
304              edma_set_dest_index(link, data_type, data_type * fifo_level);
305              link = prtd->asp_link[1];
306              edma_set_dest_index(link, data_type, data_type * fifo_level);
307     
308              link = prtd->ram_link;
309              edma_set_dest(link, runtime->dma_addr, INCR, W32BIT);
310          }
311     
312          if (!fifo_level) {
313              count = ping_size / data_type;
314              edma_set_transfer_params(prtd->asp_link[0], acnt, count,
315                      1, 0, ASYNC);
316              edma_set_transfer_params(prtd->asp_link[1], acnt, count,
317                      1, 0, ASYNC);
318          } else {
319              count = ping_size / (data_type * fifo_level);
320              edma_set_transfer_params(prtd->asp_link[0], acnt, fifo_level,
321                      count, fifo_level, ABSYNC);
322              edma_set_transfer_params(prtd->asp_link[1], acnt, fifo_level,
323                      count, fifo_level, ABSYNC);
324          }
325     
326          link = prtd->ram_link;
327          edma_set_src_index(link, ping_size, ram_src_cidx);
328          edma_set_dest_index(link, ping_size, ram_dst_cidx);
329          edma_set_transfer_params(link, ping_size, 2,
330                  runtime->periods, 2, ASYNC);
331     
332          /** init master params */
333          edma_read_slot(prtd->asp_link[0], &prtd->asp_params);
334          edma_read_slot(prtd->ram_link, &prtd->ram_params);
335          if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
336              struct edmacc_param p_ram;
337              /** Copy entire iram buffer before playback started */
338              prtd->ram_params.a_b_cnt = (1 << 16) | (ping_size << 1);
339              /** 0 dst_bidx */
340              prtd->ram_params.src_dst_bidx = (ping_size << 1);
341              /** 0 dst_cidx */
342              prtd->ram_params.src_dst_cidx = (ping_size << 1);
343              prtd->ram_params.ccnt = 1;
344     
345              /** Skip 1st period */
346              edma_read_slot(prtd->ram_link, &p_ram);
347              p_ram.src += (ping_size << 1);
348              p_ram.ccnt -= 1;
349              edma_write_slot(prtd->ram_link2, &p_ram);
350              /**
351               * When 1st started, ram -> iram dma channel will fill the
352               * entire iram.  Then, whenever a ping/pong asp buffer finishes,
353               * 1/2 iram will be filled.
354               */
355              prtd->ram_params.link_bcntrld =
356                  EDMA_CHAN_SLOT(prtd->ram_link2) << 5;
357          }
358          return 0;
359     }
360     
361     /** 1 asp tx or rx channel using 2 parameter channels
362       * 1 ram to/from iram channel using 1 parameter channel
363       *
364       * Playback
365       * ram copy channel kicks off first,
366       * 1st ram copy of entire iram buffer completion kicks off asp channel
367       * asp tcc always kicks off ram copy of 1/2 iram buffer
368       *
369       * Record
370       * asp channel starts, tcc kicks off ram copy
371       */
372     static int request_ping_pong( struct snd_pcm_substream *substream,
373              struct davinci_runtime_data *prtd,
374              struct snd_dma_buffer *iram_dma)
375     {
376          dma_addr_t asp_src_ping;
377          dma_addr_t asp_dst_ping;
378          int link;
379          struct davinci_pcm_dma_params *params = prtd->params;
380     
381          /** Request ram master channel */
382          link = prtd->ram_channel = edma_alloc_channel(EDMA_CHANNEL_ANY,
383                        davinci_pcm_dma_irq, substream,
384                        prtd->params->ram_chan_q);
385          if (link < 0)
386              goto exit1;
387     
388          /** Request ram link channel */
389          link = prtd->ram_link = edma_alloc_slot(
390                  EDMA_CTLR(prtd->ram_channel), EDMA_SLOT_ANY);
391          if (link < 0)
392              goto exit2;
393     
394          link = prtd->asp_link[1] = edma_alloc_slot(
395                  EDMA_CTLR(prtd->asp_channel), EDMA_SLOT_ANY);
396          if (link < 0)
397              goto exit3;
398     
399          prtd->ram_link2 = -1;
400          if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
401              link = prtd->ram_link2 = edma_alloc_slot(
402                  EDMA_CTLR(prtd->ram_channel), EDMA_SLOT_ANY);
403              if (link < 0)
404                  goto exit4;
405          }
406          /** circle ping-pong buffers */
407          edma_link(prtd->asp_link[0], prtd->asp_link[1]);
408          edma_link(prtd->asp_link[1], prtd->asp_link[0]);
409          /** circle ram buffers */
410          edma_link(prtd->ram_link, prtd->ram_link);
411     
412          if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
413              asp_src_ping = iram_dma->addr;
414              asp_dst_ping = params->dma_addr; /** fifo */
415          } else {
416              asp_src_ping = params->dma_addr; /** fifo */
417              asp_dst_ping = iram_dma->addr;
418          }
419          /** ping */
420          link = prtd->asp_link[0];
421          edma_set_src(link, asp_src_ping, INCR, W16BIT);
422          edma_set_dest(link, asp_dst_ping, INCR, W16BIT);
423          edma_set_src_index(link, 0, 0);
424          edma_set_dest_index(link, 0, 0);
425     
426          edma_read_slot(link, &prtd->asp_params);
427          prtd->asp_params.opt &= ~(TCCMODE | EDMA_TCC(0x3f) | TCINTEN);
428          prtd->asp_params.opt |= TCCHEN | EDMA_TCC(prtd->ram_channel & 0x3f);
429          edma_write_slot(link, &prtd->asp_params);
430     
431          /** pong */
432          link = prtd->asp_link[1];
433          edma_set_src(link, asp_src_ping, INCR, W16BIT);
434          edma_set_dest(link, asp_dst_ping, INCR, W16BIT);
435          edma_set_src_index(link, 0, 0);
436          edma_set_dest_index(link, 0, 0);
437     
438          edma_read_slot(link, &prtd->asp_params);
439          prtd->asp_params.opt &= ~(TCCMODE | EDMA_TCC(0x3f));
440          /** interrupt after every pong completion */
441          prtd->asp_params.opt |= TCINTEN | TCCHEN |
442              EDMA_TCC(EDMA_CHAN_SLOT(prtd->ram_channel));
443          edma_write_slot(link, &prtd->asp_params);
444     
445          /** ram */
446          link = prtd->ram_link;
447          edma_set_src(link, iram_dma->addr, INCR, W32BIT);
448          edma_set_dest(link, iram_dma->addr, INCR, W32BIT);
449          pr_debug( "%s: audio dma channels/slots in use for ram:%u %u %u,"
450              "for asp:%u %u %u\n" , __func__,
451              prtd->ram_channel, prtd->ram_link, prtd->ram_link2,
452              prtd->asp_channel, prtd->asp_link[0],
453              prtd->asp_link[1]);
454          return 0;
455     exit4:
456          edma_free_channel(prtd->asp_link[1]);
457          prtd->asp_link[1] = -1;
458     exit3:
459          edma_free_channel(prtd->ram_link);
460          prtd->ram_link = -1;
461     exit2:
462          edma_free_channel(prtd->ram_channel);
463          prtd->ram_channel = -1;
464     exit1:
465          return link;
466     }
467     
468     static int davinci_pcm_dma_request( struct snd_pcm_substream *substream)
469     {
470          struct snd_dma_buffer *iram_dma;
471          struct davinci_runtime_data *prtd = substream->runtime->private_data;
472          struct davinci_pcm_dma_params *params = prtd->params;
473          int link;
474     
475          if (!params)
476              return -ENODEV;
477     
478          /** Request asp master DMA channel */
479          link = prtd->asp_channel = edma_alloc_channel(params->channel,
480                  davinci_pcm_dma_irq, substream,
481                  prtd->params->asp_chan_q);
482          if (link < 0)
483              goto exit1;
484     
485          /** Request asp link channels */
486          link = prtd->asp_link[0] = edma_alloc_slot(
487                  EDMA_CTLR(prtd->asp_channel), EDMA_SLOT_ANY);
488          if (link < 0)
489              goto exit2;
490     
491          iram_dma = ( struct snd_dma_buffer *)substream->dma_buffer.private_data;
492          if (iram_dma) {
493              if (request_ping_pong(substream, prtd, iram_dma) == 0)
494                  return 0;
495              printk(KERN_WARNING "%s: dma channel allocation failed,"
496                      "not using sram\n" , __func__);
497          }
498     
499          /** Issue transfer completion IRQ when the channel completes a
500           * transfer, then always reload from the same slot (by a kind
501           * of loopback link).  The completion IRQ handler will update
502           * the reload slot with a new buffer.
503           *
504           * REVISIT save p_ram here after setting up everything except
505           * the buffer and its length (ccnt) ... use it as a template
506           * so davinci_pcm_enqueue_dma() takes less time in IRQ.
507           */
508          edma_read_slot(link, &prtd->asp_params);
509          prtd->asp_params.opt |= TCINTEN |
510              EDMA_TCC(EDMA_CHAN_SLOT(prtd->asp_channel));
511          prtd->asp_params.link_bcntrld = EDMA_CHAN_SLOT(link) << 5;
512          edma_write_slot(link, &prtd->asp_params);
513          return 0;
514     exit2:
515          edma_free_channel(prtd->asp_channel);
516          prtd->asp_channel = -1;
517     exit1:
518          return link;
519     }
520     
521     static int davinci_pcm_trigger( struct snd_pcm_substream *substream, int cmd)
522     {
523          struct davinci_runtime_data *prtd = substream->runtime->private_data;
524          int ret = 0;
525     
526          spin_lock(&prtd->lock);
527     
528          switch (cmd) {
529          case SNDRV_PCM_TRIGGER_START:
530          case SNDRV_PCM_TRIGGER_RESUME:
531          case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
532              edma_resume(prtd->asp_channel);
533              break ;
534          case SNDRV_PCM_TRIGGER_STOP:
535          case SNDRV_PCM_TRIGGER_SUSPEND:
536          case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
537              edma_pause(prtd->asp_channel);
538              break ;
539          default :
540              ret = -EINVAL;
541              break ;
542          }
543     
544          spin_unlock(&prtd->lock);
545     
546          return ret;
547     }
548     
549     static int davinci_pcm_prepare( struct snd_pcm_substream *substream)
550     {
551          struct davinci_runtime_data *prtd = substream->runtime->private_data;
552     
553          if (prtd->ram_channel >= 0) {
554              int ret = ping_pong_dma_setup(substream);
555              if (ret < 0)
556                  return ret;
557     
558              edma_write_slot(prtd->ram_channel, &prtd->ram_params);
559              edma_write_slot(prtd->asp_channel, &prtd->asp_params);
560     
561              print_buf_info(prtd->ram_channel, "ram_channel" );
562              print_buf_info(prtd->ram_link, "ram_link" );
563              print_buf_info(prtd->ram_link2, "ram_link2" );
564              print_buf_info(prtd->asp_channel, "asp_channel" );
565              print_buf_info(prtd->asp_link[0], "asp_link[0]" );
566              print_buf_info(prtd->asp_link[1], "asp_link[1]" );
567     
568              if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
569                  /** copy 1st iram buffer */
570                  edma_start(prtd->ram_channel);
571              }
572              edma_start(prtd->asp_channel);
573              return 0;
574          }
575          prtd->period = 0;
576          davinci_pcm_enqueue_dma(substream);
577     
578          /** Copy self-linked parameter RAM entry into master channel */
579          edma_read_slot(prtd->asp_link[0], &prtd->asp_params);
580          edma_write_slot(prtd->asp_channel, &prtd->asp_params);
581          davinci_pcm_enqueue_dma(substream);
582          edma_start(prtd->asp_channel);
583     
584          return 0;
585     }
586     
587     static snd_pcm_uframes_t
588     davinci_pcm_pointer( struct snd_pcm_substream *substream)
589     {
590          struct snd_pcm_runtime *runtime = substream->runtime;
591          struct davinci_runtime_data *prtd = runtime->private_data;
592          unsigned int offset;
593          int asp_count;
594          dma_addr_t asp_src, asp_dst;
595     
596          spin_lock(&prtd->lock);
597          if (prtd->ram_channel >= 0) {
598              int ram_count;
599              int mod_ram;
600              dma_addr_t ram_src, ram_dst;
601              unsigned int period_size = snd_pcm_lib_period_bytes(substream);
602              if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
603                  /** reading ram before asp should be safe
604                   * as long as the asp transfers less than a ping size
605                   * of bytes between the 2 reads
606                   */
607                  edma_get_position(prtd->ram_channel,
608                          &ram_src, &ram_dst);
609                  edma_get_position(prtd->asp_channel,
610                          &asp_src, &asp_dst);
611                  asp_count = asp_src - prtd->asp_params.src;
612                  ram_count = ram_src - prtd->ram_params.src;
613                  mod_ram = ram_count % period_size;
614                  mod_ram -= asp_count;
615                  if (mod_ram < 0)
616                      mod_ram += period_size;
617                  else if (mod_ram == 0) {
618                      if (snd_pcm_running(substream))
619                          mod_ram += period_size;
620                  }
621                  ram_count -= mod_ram;
622                  if (ram_count < 0)
623                      ram_count += period_size * runtime->periods;
624              } else {
625                  edma_get_position(prtd->ram_channel,
626                          &ram_src, &ram_dst);
627                  ram_count = ram_dst - prtd->ram_params.dst;
628              }
629              asp_count = ram_count;
630          } else {
631              edma_get_position(prtd->asp_channel, &asp_src, &asp_dst);
632              if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
633                  asp_count = asp_src - runtime->dma_addr;
634              else
635                  asp_count = asp_dst - runtime->dma_addr;
636          }
637          spin_unlock(&prtd->lock);
638     
639          offset = bytes_to_frames(runtime, asp_count);
640          if (offset >= runtime->buffer_size)
641              offset = 0;
642     
643          return offset;
644     }
645     
646     static int davinci_pcm_open( struct snd_pcm_substream *substream)
647     {
648          struct snd_pcm_runtime *runtime = substream->runtime;
649          struct davinci_runtime_data *prtd;
650          struct snd_pcm_hardware *ppcm;
651          int ret = 0;
652          struct snd_soc_pcm_runtime *rtd = substream->private_data;
653          struct davinci_pcm_dma_params *pa;
654          struct davinci_pcm_dma_params *params;
655     
656          pa = snd_soc_dai_get_dma_data(rtd->dai->cpu_dai, substream);
657          if (!pa)
658              return -ENODEV;
659          params = &pa[substream->stream];
660     
661          ppcm = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
662                  &pcm_hardware_playback : &pcm_hardware_capture;
663          allocate_sram(substream, params->sram_size, ppcm);
664          snd_soc_set_runtime_hwparams(substream, ppcm);
665          /** ensure that buffer size is a multiple of period size */
666          ret = snd_pcm_hw_constraint_integer(runtime,
667                              SNDRV_PCM_HW_PARAM_PERIODS);
668          if (ret < 0)
669              return ret;
670     
671          prtd = kzalloc( sizeof ( struct davinci_runtime_data), GFP_KERNEL);
672          if (prtd == NULL)
673              return -ENOMEM;
674     
675          spin_lock_init(&prtd->lock);
676          prtd->params = params;
677          prtd->asp_channel = -1;
678          prtd->asp_link[0] = prtd->asp_link[1] = -1;
679          prtd->ram_channel = -1;
680          prtd->ram_link = -1;
681          prtd->ram_link2 = -1;
682     
683          runtime->private_data = prtd;
684     
685          ret = davinci_pcm_dma_request(substream);
686          if (ret) {
687              printk(KERN_ERR "davinci_pcm: Failed to get dma channels\n" );
688              kfree(prtd);
689          }
690     
691          return ret;
692     }
693     
694     static int davinci_pcm_close( struct snd_pcm_substream *substream)
695     {
696          struct snd_pcm_runtime *runtime = substream->runtime;
697          struct davinci_runtime_data *prtd = runtime->private_data;
698     
699          if (prtd->ram_channel >= 0)
700              edma_stop(prtd->ram_channel);
701          if (prtd->asp_channel >= 0)
702              edma_stop(prtd->asp_channel);
703          if (prtd->asp_link[0] >= 0)
704              edma_unlink(prtd->asp_link[0]);
705          if (prtd->asp_link[1] >= 0)
706              edma_unlink(prtd->asp_link[1]);
707          if (prtd->ram_link >= 0)
708              edma_unlink(prtd->ram_link);
709     
710          if (prtd->asp_link[0] >= 0)
711              edma_free_slot(prtd->asp_link[0]);
712          if (prtd->asp_link[1] >= 0)
713              edma_free_slot(prtd->asp_link[1]);
714          if (prtd->asp_channel >= 0)
715              edma_free_channel(prtd->asp_channel);
716          if (prtd->ram_link >= 0)
717              edma_free_slot(prtd->ram_link);
718          if (prtd->ram_link2 >= 0)
719              edma_free_slot(prtd->ram_link2);
720          if (prtd->ram_channel >= 0)
721              edma_free_channel(prtd->ram_channel);
722     
723          kfree(prtd);
724     
725          return 0;
726     }
727     
728     static int davinci_pcm_hw_params( struct snd_pcm_substream *substream,
729                       struct snd_pcm_hw_params *hw_params)
730     {
731          return snd_pcm_lib_malloc_pages(substream,
732                          params_buffer_bytes(hw_params));
733     }
734     
735     static int davinci_pcm_hw_free( struct snd_pcm_substream *substream)
736     {
737          return snd_pcm_lib_free_pages(substream);
738     }
739     
740     static int davinci_pcm_mmap( struct snd_pcm_substream *substream,
741                      struct vm_area_struct *vma)
742     {
743          struct snd_pcm_runtime *runtime = substream->runtime;
744     
745          return dma_mmap_writecombine(substream->pcm->card->dev, vma,
746                           runtime->dma_area,
747                           runtime->dma_addr,
748                           runtime->dma_bytes);
749     }
750     
751     static struct snd_pcm_ops davinci_pcm_ops = {
752          .open =     davinci_pcm_open,
753          .close =    davinci_pcm_close,
754          .ioctl =    snd_pcm_lib_ioctl,
755          .hw_params =    davinci_pcm_hw_params,
756          .hw_free =  davinci_pcm_hw_free,
757          .prepare =  davinci_pcm_prepare,
758          .trigger =  davinci_pcm_trigger,
759          .pointer =  davinci_pcm_pointer,
760          .mmap =     davinci_pcm_mmap,
761     };
762     
763     static int davinci_pcm_preallocate_dma_buffer( struct snd_pcm *pcm, int stream,
764              size_t size)
765     {
766          struct snd_pcm_substream *substream = pcm->streams[stream].substream;
767          struct snd_dma_buffer *buf = &substream->dma_buffer;
768     
769          buf->dev.type = SNDRV_DMA_TYPE_DEV;
770          buf->dev.dev = pcm->card->dev;
771          buf->private_data = NULL;
772          buf->area = dma_alloc_writecombine(pcm->card->dev, size,
773                             &buf->addr, GFP_KERNEL);
774     
775          pr_debug( "davinci_pcm: preallocate_dma_buffer: area=%p, addr=%p, "
776              "size=%d\n" , ( void *) buf->area, ( void *) buf->addr, size);
777     
778          if (!buf->area)
779              return -ENOMEM;
780     
781          buf->bytes = size;
782          return 0;
783     }
784     
785     static void davinci_pcm_free( struct snd_pcm *pcm)
786     {
787          struct snd_pcm_substream *substream;
788          struct snd_dma_buffer *buf;
789          int stream;
790     
791          for (stream = 0; stream < 2; stream++) {
792              struct snd_dma_buffer *iram_dma;
793              substream = pcm->streams[stream].substream;
794              if (!substream)
795                  continue ;
796     
797              buf = &substream->dma_buffer;
798              if (!buf->area)
799                  continue ;
800     
801              dma_free_writecombine(pcm->card->dev, buf->bytes,
802                            buf->area, buf->addr);
803              buf->area = NULL;
804              iram_dma = buf->private_data;
805              if (iram_dma) {
806                  sram_free(iram_dma->area, iram_dma->bytes);
807                  kfree(iram_dma);
808              }
809          }
810     }
811     
812     static u64 davinci_pcm_dmamask = 0xffffffff;
813     
814     static int davinci_pcm_new( struct snd_card *card,
815                     struct snd_soc_dai *dai, struct snd_pcm *pcm)
816     {
817          int ret;
818     
819          if (!card->dev->dma_mask)
820              card->dev->dma_mask = &davinci_pcm_dmamask;
821          if (!card->dev->coherent_dma_mask)
822              card->dev->coherent_dma_mask = 0xffffffff;
823     
824          if (dai->playback.channels_min) {
825              ret = davinci_pcm_preallocate_dma_buffer(pcm,
826                  SNDRV_PCM_STREAM_PLAYBACK,
827                  pcm_hardware_playback.buffer_bytes_max);
828              if (ret)
829                  return ret;
830          }
831     
832          if (dai->capture.channels_min) {
833              ret = davinci_pcm_preallocate_dma_buffer(pcm,
834                  SNDRV_PCM_STREAM_CAPTURE,
835                  pcm_hardware_capture.buffer_bytes_max);
836              if (ret)
837                  return ret;
838          }
839     
840          return 0;
841     }
842     
843     struct snd_soc_platform davinci_soc_platform = {
844          .name =     "davinci-audio" ,
845          .pcm_ops =  &davinci_pcm_ops,
846          .pcm_new =  davinci_pcm_new,
847          .pcm_free =     davinci_pcm_free,
848     };
849     EXPORT_SYMBOL_GPL(davinci_soc_platform);
850     
851     static int __init davinci_soc_platform_init( void )
852     {
853          return snd_soc_register_platform(&davinci_soc_platform);
854     }
855     module_init(davinci_soc_platform_init);
856     
857     static void __exit davinci_soc_platform_exit( void )
858     {
859          snd_soc_unregister_platform(&davinci_soc_platform);
860     }
861     module_exit(davinci_soc_platform_exit);
862     
863     MODULE_AUTHOR( "Vladimir Barinov" );
864     MODULE_DESCRIPTION( "TI DAVINCI PCM DMA module" );
865     MODULE_LICENSE( "GPL" );