武煜

F0903001

5090309130

单片机程序编写和调试及报告撰写

吴佳峻

F0903001

5090309131

手机程序编写和调试

陈景逸

F0903001

5090309132

手机程序编写和调试

摘要

本设计实现的是基于手机安卓平台通过蓝牙对小车进行控制。控制系统由车载控制设备和手机设备组成,车载控制设备以STC系列的STC11L02系列51单片机芯片和蓝牙芯片为核心,手机软件使用以Eclipse搭建的安卓应用程序。使用该软件通过蓝牙发送指令到单片机,再由单片机处理控制信号并对电机进行相应的控制,使小车完成指令动作。

关键词:

安卓手机,单片机,蓝牙,Eclipse

第一章 项目介绍 1

1.1开发背景 1

1.2项目任务 1

第二章 设计思路 2

2.1遥控小车改装 2

2.2 控制思路 2

第三章 硬件电路实现 4

3.1 小车的选择与改造 4

3.2 小车遥控器的改造 4

3.3控制信号的解调的单片机电路 4

3.4与单片机相连的蓝牙模块 6

3.5我们在调试硬件电路时遇到的问题 7

第四章 软件部分 8

4.1 安卓手机软件部分 8

4.1.1编译与调试环境 8

4.1.2  安卓平台用户界面搭建 10

4.2 单片机平台部分 14

4.2.1编译环境与功能实现 14

4.2.2 程序烧录 14

4.3软件设计中我们遇到的问题及解决方法 15

第五章 项目总结与心得 17

第六章 致谢 18

第七章 参考资料 19

第八章 附录 (拓展部分程序)20

第一章   项目介绍

1.1开发背景

作为电子信息学院的科技创新活动,为了将所学的各种理论知识应用到实际中同时现在,手机作为一种基本的生活工具,成为人们生活中不可缺少的部分。手机平台也相应的成为了各生产厂家争相开发的目标,好的平台能够成全用户自主开发新功能,这里我们实现就是用手机平台来控制小车。  

Android支持使用Java作为编程语言来开发应用程序,而AndroidJava开发方面从接口到功能,都有层出不穷的变化。考虑到Java虚拟机的效率和资源占用,谷歌重新设计了AndroidJava,以便能提高效率和减少资源占用,因而与J2ME等不同。  Android结构

其中Activity等同于J2MEMIDlet,一个 Activity 类(Class)负责创建视窗(Windows),一个活动中的Activity就是在 foreground(前景)模式,背景运行的程序叫做Service。两者之间通过由ServiceConnectionAIDL连结,达到复数程序同时运行效果。如果运行中的 Activity 全部画面被其他 Activity 取代时,该 Activity 便被停止(Stopped),甚至被系统清除(Kill)。   View等同于J2MEDisplayable,程序人员可以通过 View 类与“XML layout”档将UI放置在视窗上,Android 1.5的版本可以利用 View 打造出所谓的 Widgets,其实Widget只是View的一种,所以可以使用xml来设计layoutHTCAndroid Hero手机即含有大量的widget。至于ViewGroup 是各种layout 的基础抽象类(abstract class),ViewGroup之内还可以有ViewGroupView的构造函数不需要再Activity中调用,但是Displayable的是必须的,在Activity 中,要通过findViewById()来从XML 中取得ViewAndroidView类的显示很大程度上是从XML中读取的。View 与事件(event)息息相关,两者之间通过Listener 结合在一起,每一个View都可以注册一个event listener,例如:当View要处理用户触碰(touch)的事件时,就要向Android框架注册View.OnClickListener。另外还有BitMap等同于J2MEImage

C/C++开发方面:早期的Android开发只支持Java作为编程语言开发应用程序,因而使得其他语言开发者只能望而却步。20104月,谷歌正式对开发者发布了Android NDKNDK允许开发者使用C/C++作为编程语言来为Android开发应用程序,初版的NDK使得开发者看到了C/C++Android开发中的希望。   但是,当前版本的NDK在功能上还有很多局限性:NDK并没有提供对应用程序生命周期的维护;NDK也不提供对Android系统中大量系统事件的支持;对于作为应用程序交互接口的UI API,当前版本的NDK中也没有提供。但是相对于初版的NDK,现在的NDK已经进行了许多重大的功能改进。   由此可见,NDK仍然需要完善和发展,相信未来随着NDK的发展,NDK可以做得更多更好。

1.2项目任务

本次项目的任务为,改造一辆小车,用户能够对它进行控制,实现途径为通过手机、嵌入式开发板(9B96)、自制开发板、手机开发软件的模拟器等作为控制媒介,优先考虑无线模式。与此同时,可以附加其他功能,如:可以预先设定一条多边形封闭路径,让其自动循环行走;在用户界面上直接修改或输入运动路径,并使小车完成规定动作等。

第二章  设计思路

2.1遥控小车改装

出于我们老师的建议,我们是选择的改装市场上出售的遥控小车。老师给我们的小车的主要部件有一个完好无损的控制小车和改装的遥控器。如图2.1所示,是我们选择的小车在运动时的样子,比较遗憾的是,在我们制作的过程中,我们没有拍下相应的照片。从图中可以看出完整的小车和它的遥控器。途中是用绿线缆将遥控器绑在小车尾部的。

2. 1 遥控小车与遥控器

2.2 控制思路

遥控小车的控制思路是通过遥控器发射一定频率的控制信号,由小车上的接受器来接收信号,并处理信号,然后将信号里的控制信息转换为对马达的控制和对前轮方向的控制。而遥控器上的输入信号则是由人来控制的,一般为按钮输入,当按下某个键时,则会形成通路或断路,从而产生特定的信号发射出去,在这里我们不是人为按钮控制的,所以需要对遥控器进行改装,即将单片机输出分别对应接入遥控器的四个控制回路,通过单片机发出的信号来直接代替人的控制工能。如图2.2所示,为老师给我们的以改装过的遥控器。

2.2  改装的遥控器

根据以上提供的思路,我们是利用蓝牙模块来实现代替人为的控制功能的。那么要实现小车的动作,我们就需要对蓝牙实现控制。在此,我们选择以安卓手机平台为基础的蓝牙模块来实现和小车遥控器上的蓝牙模块来实现互相通信以传递控制信息,然后控制信息由与遥控器相连的单片机来分析从而得出输入给遥控器的控制信息,从而通过遥控器控制小车。控制信息由手机提供,这样,我们并能清楚的得到图2.3所示的控制信号流向图。

2.3 控制信号流向图

第三章硬件电路实现

3.1 小车的选择与改造

小车由老师直接配发,只要满足基本的要求即可,根据前面同学的经验,小车与遥控器绑定在一起,不仅能简化设备,还能促进控制的稳定与及时。随着小车的配置物品如下:

1:电池。包括小车的驱动电池和遥控器的供电电池两部分。考虑到小车耗电过快,我们选择的是四节串联的可充电电池,既减少预算,又能提供足够的工作电压;遥控器的电池选用9V的干电池即可。

2:电池充电器。与充电电池同步配套设备。如图3.1所示即为充电电池和其充电器。

3.1 充电电池和其充电器

3.2 小车遥控器的改造

其实对于小车的遥控器改造很简单,就是将原来的按钮取下来,换上对应的单片机的接口,四个接口均设定为低电平有效。即当接口处于高电平时,对应功能不动作;当处于低电平时,回路导通,小车动作。

3.3控制信号的解调的单片机电路

如图3.2所示,为单片机的硬件电路图。它也是我们所用到的电路板的原理图,因此对它的分析和理解对我们在设计过程中有很大的帮助。当需要对其进行调试时可以知道问题的所在。

                                                           3.2 单片机电路原理图  

所用单片机是ATMEL公司的STC系列的STC11L02系列51单片机芯片,主要性能参数如下:

兼容MCS-51指令系统;

内置通用8CPU8K Flash ROM

1000次可擦写周期;

0~24MHz全静态操作;

3级加密程序存储器;

256字节内部RAM

16个双向I/O口;

16位定时/计数器;

5个中断源;

全双工串行I/O口。

TTL通信线:采用计算机到小车单向串行通信,供烧录与下载程序用,如图3.3所示。

3.3 TTL数据线

3.4与单片机相连的蓝牙模块

单片机模块与蓝牙模块连接在同一PCB版上,使得单片机能够通过蓝牙获取外界指令(来自手机),连接电路图如图3.4所示:

3. 4  单片机与蓝牙模块电路板图(正面

3. 5单片机与蓝牙模块电路板图(反面)

3.5我们在调试硬件电路时遇到的问题

在用以上所说述的硬件设备和电路时,我们遇到了不少问题,经过队员们的努力和老师的帮助,最终克服了这些问题。当然,如果不会碰到,自是好事,但是碰到了,小事也会导致整个设计的的延迟甚至失败。下面我就我们遇到的问题和解决方法做个总计:

1:通信线的选择:开始我们根据以往资料的叙述,直接选择的RS232通信线,以期实现单片机的程序的烧录。然而,这与单片机的配置不符合,导致我们很长一段时间都不能烧入程序,经过老师知道,我们选择了TTL通信线,最终实现了程序的烧录。

2:单片机电路的问题。本来,我们是不应该出现对电路板的怀疑,但是由于电源电压的接入错误,很可能烧坏单片机,因此我们就有必要对单片机进行检测。在接入9V的电源电压后,我们将ON/OFF跳线帽取下,根据原理图,LED2是不会亮的。但是事实上LED2是亮的,用万用表测的LED2两端的电压差为2.11V,小于3.3V。后来对电路做了一些调试,如取下单片机及其他元件,均不能得到合理的解释,但是就是这样,程序依然烧录成功。当然这样的话就得改变操作步骤,提示上电后,再装上9V电池,当然ON/OFF跳线帽一直连着。

3:蓝牙模块的选择。老师给的物品中包含链接在单片机上的蓝牙模块,每个蓝牙模块的MAC地址不同。在开始我们不能将该蓝牙模块与手机蓝牙连接起来。后来,经过摸索,确定连通蓝牙的操作步骤如下:首先通过计算机的蓝牙搜索蓝牙模块,这是会发现搜索到我们的蓝牙模块,查看属性可以得到该蓝牙模块的MAC地址;然后将该MAC地址copy到软件程序中要连接的蓝牙MAC(具体位置请见软件部分的程序);最后开启手机蓝牙,搜索目标蓝牙,连接并输入配置密码,该密码为一般默认的,1234或者0000等,实验成功即可。

第四章软件部分

4.1 安卓手机软件部分

4.1.1编译与调试环境

由于安卓平台软件开发多以JAVA语言编程为主,我们采用的是Eclipse软件来编写程序,如图4.1所示。同时,为了能够直观地在电脑上检视界面效果,我们还采用了Oracle VM Virtual Box虚拟机下搭载的X86Android2.2系统作为试运行环境,如图4.2所示,通过调整虚拟机网络配置器以及IP地址和Eclipse本身的运行调试参数,可以将两个软件联系起来,在Eclipse上完成程序编写并在虚拟机的安卓平台上进行运行。但是要注意的是,在虚拟平台下的安卓界面不能开启蓝牙功能,因此在程序完成编译通过后最好是烧入手机并运行之,通过实际中操作来确定是否实现了预期功能。

对于软件开发环境的配置,只需按照步骤一步一步的做好即可,在此不作过多赘述,具体方法将在参考资料与附录中作出说明。

4. 1  编译环境Eclipse

4.2 虚拟机操作界面

4.3 虚拟机运行成功界面

4.1.2  安卓平台用户界面搭建

1、 窗体架构程序

在基本的功能的基础上的窗体架构:控制小车动作一共需要五个按钮,前进、后退、左转、右转和停止。于是我们在Android工程下的main.xml窗体文件中按十字形安放了5个按钮,等高等宽,位置放置合适即可。图4.4为其实现效果图。

程序如下:

<?xml version="1.0" encoding="utf-8"?>

<AbsoluteLayout xmlns:android="http://schemas.android.com/apk/res/android"

    android:id="@+id/widget0"

    android:layout_width="wrap_content"

    android:layout_height="match_parent" >

<Button

android:id="@+id/btnF"

android:layout_width="100px"

android:layout_height="60px"

android:text="前进"

android:layout_x="130px"

android:layout_y="62px"

>

</Button>

<Button

android:id="@+id/btnL"

android:layout_width="100px"

android:layout_height="60px"

android:text="左转"

android:layout_x="20px"

android:layout_y="152px"

>

</Button>

<Button

android:id="@+id/btnB"

android:layout_width="100px"

android:layout_height="60px"

android:text="后退"

android:layout_x="130px"

android:layout_y="242px"

>

</Button>

<Button

android:id="@+id/btnS"

android:layout_width="100px"

android:layout_height="60px"

android:text="停止"

android:layout_x="130px"

android:layout_y="152px"

>

</Button>

<Button

    android:id="@+id/btnR"

    android:layout_width="100px"

    android:layout_height="60px"

    android:layout_x="165dp"

    android:layout_y="104dp"

    android:text="右转" />

</AbsoluteLayout>

4.4窗体实现效果图

2、 功能实现程序

安卓平台的手机信息传输依靠的是蓝牙通信方式,因此,我们调用了蓝牙配置器类,并且设定将用于通信的变量存放于缓存区message中。五个按钮按下后发送到缓存区的字符分别为:‘1-前进,‘2-左转,‘3-后退,‘4-右转,‘0-停止,并且在屏幕上显示出来短暂的时间。

对于按钮动作,我们将简单的按动动作分为两个部分,其一为按下,其二为松开,具体配置为:当按下某一特定按钮时,向缓存区写入字符‘X’(X为上文事先规定的字符),当松开时,自动向缓存区写入‘0’(表示停止),然后通过蓝牙通信,将写入缓存区的指令信息传送到小车车载单片机上,实现了按下按钮小车动作,松开按钮小车停止的效果,避免了单次触发无停止的不良效果。

按照我们最初的设想,我们所添加的功能是自动检测命令错误。即设想同时按下两个按键,如左转和右转,此时,系统返回COMMOND ERROR,并且自动处于停止状态。由此,我们的思路是先设定一个flag值为0,当检测按键按下时先检测flag是否为0,是则置1,否则返回错误并停止。对于该停止函数,在执行了停止动作后,自动将flag0以实现下一次的可能的正确动作。

但是遗憾的是,当程序编写完成后才发现,安卓系统下并不能同时按下两个键,即是系统默认不会出现命令错误,这是我作为组长的失职,导致了方向的错误,致使工作失败。以下是以前进为例的源程序和命令错误时的停止功能函数:

// 后退

mButtonB = (Button) findViewById(R.id.btnB);

mButtonB.setOnTouchListener(new Button.OnTouchListener() {

public boolean onTouch(View v, MotionEvent event) {

// TODO Auto-generated method stub

String message;

byte[] msgBuffer;

int action = event.getAction();

switch (action) {

case MotionEvent.ACTION_DOWN:

if(flag==1)

{

Toast.makeText(getApplicationContext(), "COMMOND ERROR!WILL STOP!",

Toast.LENGTH_SHORT).show();

stop();

return false;

}

else 

flag=1;

try {

outStream = btSocket.getOutputStream();

catch (IOException e) {

Log.e(TAG"ON RESUME: Output stream creation failed.",

e);

}

message = "3";

msgBuffer = message.getBytes();

try {

outStream.write(msgBuffer);

catch (IOException e) {

Log.e(TAG"ON RESUME: Exception during write.", e);

}

break;

case MotionEvent.ACTION_UP:

                       flag=0;

Toast.makeText(getApplicationContext(), "flag wei o le",

Toast.LENGTH_SHORT).show();

try {

outStream = btSocket.getOutputStream();

catch (IOException e) {

Log.e(TAG"ON RESUME: Output stream creation failed.",

e);

}

message = "0";

msgBuffer = message.getBytes();

try {

outStream.write(msgBuffer);

catch (IOException e) {

Log.e(TAG"ON RESUME: Exception during write.", e);

}

break;

}

return false;

}

});

Stop功能函数:

public void stop() {

try {

outStream = btSocket.getOutputStream();

catch (IOException e) {

Log.e(TAG"ON RESUME: Output stream creation failed.", e);

}

String message = "0";

byte[] msgBuffer = message.getBytes();

try {

outStream.write(msgBuffer);

catch (IOException e) {

Log.e(TAG"ON RESUME: Exception during write.", e);

}

flag=0;

}

4.2 单片机平台部分

4.2.1编译环境与功能实现

我们选择的单片机编译环境为Keil uVision2,通过单片机对引脚进行规定,并且设定接收缓存区用以存放蓝牙通信获取的信息,同时规定信号传输方式为串行输入,设定合适运行参数。在编译完成后,make生成hex文件,这是转换为单片机的可执行文件,是要直接烧入单片机的。

本次共用到四个单片机引脚P3^2P3^3P3^4P3^5,分别对应前进、后退、左转、右转(由焊接时的实际连接决定),由于在安卓平台部分已对指令信号作出了规定,对不同的获得信息作出不同的反应,即对要求的宽口赋“0”即置低电平,对不需要的动作的端口赋“1”即置高电平。

4.2.2 程序烧录

烧录具体步骤请参见相关网页。单片机的程序烧录是基于TTL通信线以及专用烧录软件进行的,USB端接电脑, TxDRxd收发送线连接对应的数据端,Vcc与发送模块Vcc相连, GND与发送模块GND连接。电脑端需使用USBRS232驱动。烧录软件我们选用了宏晶科技的STC-ISP进行单片机握手与程序下载烧录,如图4.5所示。

4.5  STC-ISP烧录软件操作界面

由于程序烧录的成功与否与单片机本身的晶振设置、烧录波特率以及所使用电脑的参数相关,因此我们的烧录是在经过多次修改各项参数的尝试后完成的,具体的参数设置与所用器材罗列如下仅供参考:

1、相关参数:

电脑型号:宏基电脑4930GVISTA操作系统

烧录可用端口:COM6COM7(这个具体视电脑管理界面显示的单片机接口而定)

单片机程序编写环境:Keil μVision2

烧录程序:STC-isp

2、烧录参数设定:

i. MCU Type:STC11L02

ii. COM:COM6(换接口时为COM7

iii. 最高波特率:2400

iv. 最低波特率:2400(如此有利于烧录成功)

v. 下次冷启动后时钟源:内部RC振荡器(首次烧录)/外部晶体或时钟(后续复写烧录)

vi. 振荡器放大增益:Low

vii:打开程序文件位置的hex文件

3、 烧录步骤及注意事项

i. 连接单片机版ON/OFF跳线帽,取下电池

ii. 完成上述参数设定

iii. 点击”下载“

iv. 提示上电(默认开启提示音)后,接入电池,提示正在下载后完全按下接入

v. 在烧录未完成或者未烧录成功情况下,不可直接移除USB接口,否则易导致STC软件无响应、自动退出或者电脑蓝屏死机的情况,如果参数设定错误或者操作步骤出现错误,也会导致蓝屏的情况。

4.3软件设计中我们遇到的问题及解决方法

其实本次设计用到的软件的主体部分为前面的同学设计完成的,再次表示感谢。我们在使用该程序的过程中遇到了些许问题,并经过讨论得到解决。

1:软件使用蓝牙模块的授权命令。开始我们发现并不能使用蓝牙模块,程序会报错。加入授权许可后编译方能通过;

2:运行程序总是显示SOCKET CLOSED,是因为手机蓝牙并没有与目标蓝牙连接成功。如果蓝牙未开,会提示你先打开蓝牙;

3:修改蓝牙mac地址;

4:在编译程序的时候可能由于某些原因,导致程序缺失,这是会出现编译错误,系统会自动提示修改方案,一般请况下,选择默认方案即可;

5:用Eclipse编译产生的众多文件中,bin文件下的apk文件为安卓手机的安装文件。

第五章 项目总结与心得

   

武煜:初次接触到手机安卓平台同时,了解到了一些关于手机程序开发的流程,通过51单片机的编程再次深入的学习到嵌入式编程的方法。同时进一步学到了通过c++进行串口通讯编程的方法。收获良多,感谢期间老师、助教和同学的帮助。

吴佳峻:在最后的附加功能的设计与开发的时候,由于定的方向错误,导致功亏一篑,这是我的失职。但是队友们依然团结在一起,一起刻苦的钻研,为最后的任务奉献自己的力量,非常谢谢你们的工作,在这次科创中收获的不止是知识,还有这一份友情。

陈景逸:巩固了以前练习的基本功:焊接电路板。而新的程序设计部分则给我们了一些小的挑战。科创3C很吸引我的地方就是它对安卓系统的应用。可是,对于安卓系统的全新开发语言,我真的是丈二和尚摸不着头脑。幸好助教有给程序,而我又从图书馆里借了一辆本安卓的入门书,总算是稍微了解了一点程序大概运行的方式,稍加改动终于能让小车听指挥了。

第六章致谢

感谢张士文老师一学期来对项目的支持以及各种答疑解惑;

感谢学长对其中的难点的悉心讲解和技术指导;

感谢实验室助教在系统调试过程中提供的帮助和支持;

感谢历年小组提供的各种参考资料;

第七章参考资料

[1] 科创III-C PPT

[2] 科技创新III-C指导书

[3] 《JAVA编程思想》第三版  Bruce Eckel

[4]c02组报告

第八章附录

程序清单:

安卓部分:

Main.xml   //窗体文件

<?xml version="1.0" encoding="utf-8"?>

<AbsoluteLayout

android:id="@+id/widget0"

android:layout_width="fill_parent"

android:layout_height="fill_parent"

xmlns:android="http://schemas.android.com/apk/res/android"

>

</Button>

<Button

android:id="@+id/btnF"

android:layout_width="100px"

android:layout_height="60px"

android:text="前进"

android:layout_x="130px"

android:layout_y="62px"

>

</Button>

<Button

android:id="@+id/btnL"

android:layout_width="100px"

android:layout_height="60px"

android:text="左转"

android:layout_x="20px"

android:layout_y="152px"

>

</Button>

<Button

android:id="@+id/btnR"

android:layout_width="100px"

android:layout_height="60px"

android:text="右转"

android:layout_x="240px"

android:layout_y="152px"

>

</Button>

<Button

android:id="@+id/btnB"

android:layout_width="100px"

android:layout_height="60px"

android:text="后退"

android:layout_x="130px"

android:layout_y="242px"

>

</Button>

<Button

android:id="@+id/btnS"

android:layout_width="100px"

android:layout_height="60px"

android:text="停止"

android:layout_x="130px"

android:layout_y="152px"

>

</Button>

<SurfaceView

android:id="@+id/viewDraw"

android:layout_width="400px"

android:layout_height="400px"

android:layout_x="20px"

android:layout_y="310px"

>

</SurfaceView>

</AbsoluteLayout>

package firsttest.android;

import android.app.Activity;

import android.os.Bundle;

import android.os.Handler;

import android.os.PowerManager;

import java.io.IOException;

import java.io.OutputStream;

import java.util.UUID;

import java.util.concurrent.Semaphore;

import android.app.Activity;

import android.bluetooth.BluetoothAdapter;

import android.bluetooth.BluetoothDevice;

import android.bluetooth.BluetoothSocket;

import android.content.DialogInterface;

import android.content.DialogInterface.OnClickListener;

import android.graphics.Canvas;

import android.graphics.Color;

import android.graphics.Paint;

import android.graphics.Point;

import android.os.Bundle;

import android.provider.ContactsContract.CommonDataKinds.Event;

import android.util.Log;

import android.view.MotionEvent;

import android.view.SurfaceHolder;

import android.view.SurfaceView;

import android.view.View;

import android.widget.Button;

import android.widget.Toast;

public class MysecondprojiectActivity extends Activity {

private int a = 0;

private static final String TAG = "THINBTCLIENT";

private static final boolean D = true;

private BluetoothAdapter mBluetoothAdapter = null;

private BluetoothSocket btSocket = null;

private OutputStream outStream = null;

Button mButtonF;

Button mButtonB;

Button mButtonL;

Button mButtonR;

Button mButtonS;

Button mButtonBL;

Button mButtonBR;

int flag=0;

SurfaceView mSV;

SurfaceHolder mSH;

Semaphore surface_ready = new Semaphore(0);

PathManager pm = new PathManager();

private static final UUID MY_UUID = UUID

.fromString("00001101-0000-1000-8000-00805F9B34FB");

private static String address = "00:10:04:08:00:30"; // <==要连接的蓝牙设备MAC地址

/** Called when the activity is first created. */

@Override

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.main);

// 前进

mButtonF = (Button) findViewById(R.id.btnF);

mButtonF.setOnTouchListener(new Button.OnTouchListener() {

public boolean onTouch(View v, MotionEvent event) {

// TODO Auto-generated method stub

String message;

byte[] msgBuffer;

int action = event.getAction();

switch (action) {

case MotionEvent.ACTION_DOWN:

if(flag==1)

{

Toast.makeText(getApplicationContext(), "COMMOND ERROR!WILL STOP!",

Toast.LENGTH_SHORT).show();

stop();

return false;

}

else 

flag=1;

try {

outStream = btSocket.getOutputStream();

// Toast.makeText(SchoolFlower2Activity.this,

// String.format("%d",a), Toast.LENGTH_LONG).show();

} catch (IOException e) {

Toast.makeText(MysecondprojiectActivity.this,

e.getMessage(), Toast.LENGTH_LONG).show();

Log.e(TAG, "ON RESUME: Output stream creation failed.",

e);

}

message = "1";

msgBuffer = message.getBytes();

try {

outStream.write(msgBuffer);

Toast.makeText(MysecondprojiectActivity.this, message,

Toast.LENGTH_LONG).show();

} catch (IOException e) {

Toast.makeText(MysecondprojiectActivity.this,

e.getMessage(), Toast.LENGTH_LONG).show();

Log.e(TAG, "ON RESUME: Exception during write.", e);

}

break;

case MotionEvent.ACTION_UP:

                        flag=0;

Toast.makeText(getApplicationContext(), "flag wei o le",

Toast.LENGTH_SHORT).show();

try {

outStream = btSocket.getOutputStream();

} catch (IOException e) {

Toast.makeText(MysecondprojiectActivity.this,

e.getMessage(), Toast.LENGTH_LONG).show();

Log.e(TAG, "ON RESUME: Output stream creation failed.",

e);

}

message = "0";

msgBuffer = message.getBytes();

try {

outStream.write(msgBuffer);

Toast.makeText(MysecondprojiectActivity.this, message,

Toast.LENGTH_LONG).show();

} catch (IOException e) {

Toast.makeText(MysecondprojiectActivity.this,

e.getMessage(), Toast.LENGTH_LONG).show();

Log.e(TAG, "ON RESUME: Exception during write.", e);

}

break;

}

return false;

}

});

// 后退

mButtonB = (Button) findViewById(R.id.btnB);

mButtonB.setOnTouchListener(new Button.OnTouchListener() {

public boolean onTouch(View v, MotionEvent event) {

// TODO Auto-generated method stub

String message;

byte[] msgBuffer;

int action = event.getAction();

switch (action) {

case MotionEvent.ACTION_DOWN:

if(flag==1)

{

Toast.makeText(getApplicationContext(), "COMMOND ERROR!WILL STOP!",

Toast.LENGTH_SHORT).show();

stop();

return false;

}

else 

flag=1;

try {

outStream = btSocket.getOutputStream();

} catch (IOException e) {

Log.e(TAG, "ON RESUME: Output stream creation failed.",

e);

}

message = "3";

msgBuffer = message.getBytes();

try {

outStream.write(msgBuffer);

} catch (IOException e) {

Log.e(TAG, "ON RESUME: Exception during write.", e);

}

break;

case MotionEvent.ACTION_UP:

                       flag=0;

Toast.makeText(getApplicationContext(), "flag wei o le",

Toast.LENGTH_SHORT).show();

try {

outStream = btSocket.getOutputStream();

} catch (IOException e) {

Log.e(TAG, "ON RESUME: Output stream creation failed.",

e);

}

message = "0";

msgBuffer = message.getBytes();

try {

outStream.write(msgBuffer);

} catch (IOException e) {

Log.e(TAG, "ON RESUME: Exception during write.", e);

}

break;

}

return false;

}

});

// 左转

mButtonL = (Button) findViewById(R.id.btnL);

mButtonL.setOnTouchListener(new Button.OnTouchListener() {

public boolean onTouch(View v, MotionEvent event) {

// TODO Auto-generated method stub

String message;

byte[] msgBuffer;

int action = event.getAction();

switch (action) {

case MotionEvent.ACTION_DOWN:

if(flag==1)

{

Toast.makeText(getApplicationContext(), "COMMOND ERROR!WILL STOP!",

Toast.LENGTH_SHORT).show();

stop();

return false;

}

else 

flag=1;

try {

outStream = btSocket.getOutputStream();

} catch (IOException e) {

Log.e(TAG, "ON RESUME: Output stream creation failed.",

e);

}

message = "2";

msgBuffer = message.getBytes();

try {

outStream.write(msgBuffer);

} catch (IOException e) {

Log.e(TAG, "ON RESUME: Exception during write.", e);

}

break;

case MotionEvent.ACTION_UP:

                         flag=0;

Toast.makeText(getApplicationContext(), "flag wei o le",

Toast.LENGTH_SHORT).show();

try {

outStream = btSocket.getOutputStream();

} catch (IOException e) {

Log.e(TAG, "ON RESUME: Output stream creation failed.",

e);

}

message = "0";

msgBuffer = message.getBytes();

try {

outStream.write(msgBuffer);

} catch (IOException e) {

Log.e(TAG, "ON RESUME: Exception during write.", e);

}

break;

}

return false;

}

});

// 右转

mButtonR = (Button) findViewById(R.id.btnR);

mButtonR.setOnTouchListener(new Button.OnTouchListener() {

public boolean onTouch(View v, MotionEvent event) {

// TODO Auto-generated method stub

String message;

byte[] msgBuffer;

int action = event.getAction();

switch (action) {

case MotionEvent.ACTION_DOWN:

if(flag==1)

{

Toast.makeText(getApplicationContext(), "COMMOND ERROR!WILL STOP!",

Toast.LENGTH_SHORT).show();

stop();

return false;

}

else 

flag=1;

try {

outStream = btSocket.getOutputStream();

} catch (IOException e) {

Log.e(TAG, "ON RESUME: Output stream creation failed.",

e);

}

message = "4";

msgBuffer = message.getBytes();

try {

outStream.write(msgBuffer);

} catch (IOException e) {

Log.e(TAG, "ON RESUME: Exception during write.", e);

}

break;

case MotionEvent.ACTION_UP:

                     flag=0;

Toast.makeText(getApplicationContext(), "flag wei o le",

Toast.LENGTH_SHORT).show();

try {

outStream = btSocket.getOutputStream();

} catch (IOException e) {

Log.e(TAG, "ON RESUME: Output stream creation failed.",

e);

}

message = "0";

msgBuffer = message.getBytes();

try {

outStream.write(msgBuffer);

} catch (IOException e) {

Log.e(TAG, "ON RESUME: Exception during write.", e);

}

break;

}

return false;

}

});

// 停止

mButtonS = (Button) findViewById(R.id.btnS);

mButtonS.setOnTouchListener(new Button.OnTouchListener() {

public boolean onTouch(View v, MotionEvent event) {

// TODO Auto-generated method stub

if (event.getAction() == MotionEvent.ACTION_DOWN) {

if(flag==1)

{

Toast.makeText(getApplicationContext(), "COMMOND ERROR!WILL STOP!",

Toast.LENGTH_SHORT).show();

stop();

return false;

}

else 

stop();

flag=1;

}

if (event.getAction() == MotionEvent.ACTION_UP) {

flag=0;

Toast.makeText(getApplicationContext(), "flag wei o le",

Toast.LENGTH_SHORT).show();

}

return false;

}

});

mSV = (SurfaceView) findViewById(R.id.viewDraw);

mSH = (SurfaceHolder) mSV.getHolder();

mSH.addCallback(new SurfaceHolder.Callback() {

public void surfaceDestroyed(SurfaceHolder holder) {

// TODO Auto-generated method stub

}

public void surfaceCreated(SurfaceHolder holder) {

// TODO Auto-generated method stub

// surface_ready.release();

}

public void surfaceChanged(SurfaceHolder holder, int format,

int width, int height) {

// TODO Auto-generated method stub

surface_ready.release();

}

});

mSV.setOnTouchListener(new View.OnTouchListener() {

public boolean onTouch(View v, MotionEvent event) {

// TODO Auto-generated method stub

pm.addPoint(new Point((int) event.getX(), (int) event.getY()));

return false;

}

});

// Redrawing thread

final Handler handler = new Handler();

new Thread(new Runnable() {

public void run() {

// TODO Auto-generated method stub

synchronized (mSH) {

while (true) {

try {

surface_ready.acquire();

} catch (InterruptedException e1) {

// TODO Auto-generated catch block

e1.printStackTrace();

}

handler.post(new Runnable() {

public void run() {

// TODO Auto-generated method stub

try {

MysecondprojiectActivity.this.onDraw();

surface_ready.release();

} catch (Exception e) {

Toast.makeText(

MysecondprojiectActivity.this,

e.toString() + e.getMessage(),

Toast.LENGTH_SHORT).show();

}

}

});

try {

mSH.wait(30);

} catch (InterruptedException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

}

}

}

}).start();

if (D)

Log.e(TAG, "+++ ON CREATE +++");

mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();

if (mBluetoothAdapter == null) {

Toast.makeText(this, "Bluetooth is not available.",

Toast.LENGTH_LONG).show();

finish();

return;

}

if (!mBluetoothAdapter.isEnabled()) {

Toast.makeText(this,

"Please enable your Bluetooth and re-run this program.",

Toast.LENGTH_LONG).show();

// finish();

// return;

}

if (D)

Log.e(TAG, "+++ DONE IN ON CREATE, GOT LOCAL BT ADAPTER +++");

}

public void stop() {

try {

outStream = btSocket.getOutputStream();

} catch (IOException e) {

Log.e(TAG, "ON RESUME: Output stream creation failed.", e);

}

String message = "0";

byte[] msgBuffer = message.getBytes();

try {

outStream.write(msgBuffer);

} catch (IOException e) {

Log.e(TAG, "ON RESUME: Exception during write.", e);

}

flag=0;

}

@Override

public void onStart() {

super.onStart();

if (D)

Log.e(TAG, "++ ON START ++");

}

@Override

public void onResume() {

super.onResume();

a++;

if (D) {

Log.e(TAG, "+ ON RESUME +");

Log.e(TAG, "+ ABOUT TO ATTEMPT CLIENT CONNECT +");

}

Toast.makeText(MysecondprojiectActivity.this, "Resume:Connecting",

Toast.LENGTH_LONG);

BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address);

try {

btSocket = device.createRfcommSocketToServiceRecord(MY_UUID);

pm.btSocket = this.btSocket;

} catch (IOException e) {

Log.e(TAG, "ON RESUME: Socket creation failed.", e);

}

mBluetoothAdapter.cancelDiscovery();

try {

btSocket.connect();

Log.e(TAG,

"ON RESUME: BT connection established, data transfer link open.");

} catch (IOException e) {

try {

btSocket.close();

} catch (IOException e2) {

Log.e(TAG,

"ON RESUME: Unable to close socket during connection failure",

e2);

}

}

// Create a data stream so we can talk to server.

if (D)

Log.e(TAG, "+ ABOUT TO SAY SOMETHING TO SERVER +");

try {

outStream = btSocket.getOutputStream();

} catch (IOException e) {

Log.e(TAG, "ON RESUME: Output stream creation failed.", e);

}

String message = "1";

byte[] msgBuffer = message.getBytes();

try {

outStream.write(msgBuffer);

} catch (IOException e) {

Log.e(TAG, "ON RESUME: Exception during write.", e);

}

}

@Override

public void onPause() {

super.onPause();

if (D)

Log.e(TAG, "- ON PAUSE -");

if (outStream != null) {

try {

outStream.flush();

} catch (IOException e) {

Log.e(TAG, "ON PAUSE: Couldn't flush output stream.", e);

}

}

try {

btSocket.close();

} catch (IOException e2) {

Log.e(TAG, "ON PAUSE: Unable to close socket.", e2);

}

}

@Override

public void onStop() {

super.onStop();

if (D)

Log.e(TAG, "-- ON STOP --");

}

@Override

public void onDestroy() {

super.onDestroy();

if (D)

Log.e(TAG, "--- ON DESTROY ---");

}

public void onDraw() throws Exception {

Canvas canvas = mSH.lockCanvas();

if (canvas == null)

throw new Exception("Canvas is null");

Paint paint = new Paint();

paint.setARGB(255, 230, 230, 230);

canvas.drawColor(paint.getColor());

paint.setColor(Color.BLUE);

Point[] path = new Point[0];

synchronized (pm.path) {

path = pm.path.toArray(path);

}

PathManager.unit_vector dir = pm.curDir;

for (int i = 1; i < path.length; i++) {

canvas.drawLine(path[i - 1].x, path[i - 1].y, path[i].x, path[i].y,

paint);

}

canvas.drawCircle(path[0].x, path[0].y, 10, paint);

paint.setColor(Color.RED);

canvas.drawLine(path[0].x, path[0].y, path[0].x + dir.x * 15.0f,

path[0].y + dir.y * 15.0f, paint);

mSH.unlockCanvasAndPost(canvas);

}

}

PathManager.java  //主程序2

package com.Android.SchoolFlower2;

import java.io.IOException;

import java.util.ArrayList;

import java.util.List;

import java.util.concurrent.Semaphore;

import android.bluetooth.BluetoothSocket;

import android.graphics.Point;

public class PathManager {

public class unit_vector {

float x,y;

public unit_vector(Point st, Point ed) {

x = ed.x - st.x;

y = ed.y - st.y;

float mod = (float) Math.sqrt(x*x+y*y);

x /= mod;

y /= mod;

}

public float angleTo(unit_vector vec) {

// in radian, positive value means clockwise

float angle = (float) Math.acos(x*vec.x + y*vec.y);

if ( x*vec.y > y*vec.x ) {

return angle;

} else {

return -angle;

}

}

}

float distance(Point a, Point b) {

float x = a.x - b.x;

float y = a.y - b.y;

return (float) Math.sqrt(x*x+y*y);

}

public List<Point> path = new ArrayList<Point>();

public unit_vector curDir = new unit_vector(new Point(0,0), new Point(0,-1));

public BluetoothSocket btSocket = null;

private Semaphore points = new Semaphore(0);

public PathManager() {

path.add(new Point(200,200));

new Thread(new Runnable() {

public final float rot_speed = 45.54f;

public final float mov_speed = 10.0f;

@Override

public void run() {

// TODO Auto-generated method stub

try {

while(true) {

points.acquire();

Point st, ed;

synchronized(path) {

st = path.get(0);

ed = path.get(1);

}

unit_vector dir = new unit_vector(st,ed);

float rot = curDir.angleTo(dir);

if ( rot > 0.1f || rot < -0.1f ) {

try {

if ( rot > 0.1f )

btSocket.getOutputStream().write('4'); // Turn right

else

btSocket.getOutputStream().write('2'); // Turn left

} catch(Exception e) {

}

Thread.sleep( Math.round(Math.abs(rot) * rot_speed) );

}

curDir = dir;

float dist = distance(st, ed);

try {

btSocket.getOutputStream().write('1');

} catch (IOException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

Thread.sleep( Math.round(dist * mov_speed) );

try {

btSocket.getOutputStream().write('0');

} catch (IOException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

synchronized(path) {

path.remove(0);

}

}

} catch (InterruptedException e) {

// TODO Auto-generated catch block

}

}

}).start();

}

public void addPoint(Point p) {

synchronized(path) {

path.add(p);

}

points.release();

}

}

单片机部分:

#include <reg51.h>

#include <absacc.h>

unsigned char data cmd = 0x00;

unsigned char cnt = 0;

// Enable bit, set to 0 to run the motor

sbit up = P3^2;

sbit down = P3^3;

sbit right = P3^4;

sbit left = P3^5;

sfr AUXR1 = 0xA2;

// 1 = forward, 0 = back

void global_init();

void serial_init();

void send_back(unsigned char c);

void motor_move(unsigned char command);

void main()

{

global_init();

while(1);

}

void global_init()

{

//left_speed = speed;

//right_speed = speed;

TMOD |= 0x02;

TH0 = 0xec;

TL0 = 0xec;

ET0 = 1;

TR0 = 1;

AUXR1 |= 0x80;

serial_init();

EA = 1;

send_back(0xaa);

}

void serial_init()

{

//请用实际行动支持宏晶STC大陆本土MCU统一全球市场

PCON &= 0x7f; //波特率不倍速

SCON = 0x50; //8位数据,可变波特率

//AUXR &= 0xbf; //定时器1时钟为Fosc/12,12T

//AUXR &= 0xfe; //串口1选择定时器1为波特率发生器

TMOD &= 0x0f; //清除定时器1模式位

TMOD |= 0x20; //设定定时器18位自动重装方式

TL1 = 0xFD; //设定定时初值

TH1 = 0xFD; //设定定时器重装值

ET1 = 0; //禁止定时器1中断

TR1 = 1; //启动定时器1

IE |= 0x90;

PS = 1;

up = 1;

down = 1;

right = 1;

left = 1;

}

void send_back(unsigned char c)

{

ES = 0;

TI = 0;

SBUF = c;

while (TI == 0);

TI = 0;

ES = 1;

}

// Move forward: 0x18

// Move backward: 0x00

// Turn left: 0x10

// Turn Right: 0x08

// Stop: 0x78

/*

void motor_move(unsigned char command)

{

EA = 0;

if ( command & 0x80 )

{

speed = command & 0x3F;

// 1 as its most significant bit 

// indicates setting speed

// Its second most significant bit

// indicates whether setting left motor speed

// or right motor speed

// Its remaining bit is the actual speed value

if ( command & 0x40 )

right_speed = speed;

else

left_speed = speed;

}

else 

{

left_enb = command & 0x40;

right_enb = command & 0x20;

left_dir = command & 0x10;

right_dir = command & 0x8; 

if ( left_enb && right_enb )

left_speed = right_speed = 0;

else

left_speed = right_speed = 0x3F;

}

EA = 1;

}

*/

// Timer based interrupt to set speed

void timer0( void ) interrupt 1 using 1

{

ET0 = 0;

if ( ++cnt == 0x3F )

cnt = 0;

ET0 = 1;

}

/*

void receive_cmd() interrupt 4 using 3

{

if (RI == 1)

{

RI = 0;

cmd = SBUF;

send_back(cmd);

u = 0;

motor_move(cmd);

}

}

*/

void receive_cmd() interrupt 4 using 3

{

if (RI == 1)

{

RI = 0;

cmd = SBUF;

send_back(cmd);

if(cmd == '1'){

up = 0;

}else if (cmd == '3'){

down = 0;

}else if(cmd == '4'){

left = 0;

up = 0;

}else if (cmd == '2'){

right = 0;

up = 0;

}else if (cmd == '5'){

down = 0;

left = 0;

}else if (cmd == '6'){

down = 0;

right = 0;

}else{

   up = 1;

   down = 1;

   left = 1;

   right = 1;

}

    //u = 0;

//stay();

}

/*#include <reg51.h>

sbit up = P3^2;

sbit down = P3^3;

sbit left = P3^4;

sbit right = P3^5;

void main(void)

{

    unsigned char x;

//x = SBUF;

    

    EA = 1;

SCON = 0x52;

TMOD = 0x20;

TH1 = 0xFA;

TR1 = 1;

ES = 1;

    up = 1;

    down =1;

    left = 1;

    right = 1;

    for(;;);

{

  x = SBUF;

     switch(x)

 {

   case '1': up = 0;break;

   case '3': down = 0;break;

   case '2': left = 0;break;

   case '4': right = 0;break;

   case '0': {up = 1;down = 1;left = 1;right = 1;break;};

   }

 //down = 0;

 //up = 0;

 //left = 0;

 //right = 0;};

}

}

serial() interrupt 4

{

static int a = 0;

if (TI) {

RI = 0;

left = 0;

right=1;

}

//TI=0;

else {

right = 0;

RI = 0;

left=1;

}

    //down = 0;

//left = 0;

    //right = 0;

}

*/

package mars.Draw;

import java.io.IOException;

import java.io.OutputStream;

import java.util.ArrayList;

import java.util.List;

import java.util.UUID;

import android.app.Activity;

import android.bluetooth.BluetoothAdapter;

import android.bluetooth.BluetoothDevice;

import android.bluetooth.BluetoothSocket;

import android.os.Bundle;

import android.os.Handler;

import android.util.Log;

import android.view.Gravity;

import android.view.MotionEvent;

import android.view.View;

import android.widget.Button;

import android.widget.TextView;

import android.widget.Toast;

public class DrawActivity extends Activity {

private static final String TAG = "THINBTCLIENT";

private static final boolean D = true;

private BluetoothAdapter mBluetoothAdapter = null;

private BluetoothSocket btSocket = null;

private OutputStream outStream = null;

private static final UUID MY_UUID = UUID

.fromString("00001101-0000-1000-8000-00805F9B34FB");

private static String address = "00:10:04:08:00:09"; // <==要连接的蓝牙设备MAC地址

private TextView tv;

    List<Float> X=new ArrayList<Float>();

    List<Float> Y=new ArrayList<Float>();

private Button f;

float Xm,Ym;

    /** Called when the activity is first created. */

    @Override

    

    public void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.main);

        //TextView myTextView = (TextView)findViewById(R.id.myTextView);

        //Button myButton=(Button)findViewById(R.id.myButton);

        //myTextView.setText("我得第一个TextView");

        //myButton.setText("我的第一个Button");

        tv=(TextView)findViewById(R.id.tv);

        f=(Button)findViewById(R.id.myButton);

        f.setText("完成");

        f.setOnClickListener(new View.OnClickListener() {

public void onClick(View v) {

// TODO Auto-generated method stub

//handler.post(Go);

for(int i=0;i<X.size()-1;i++){

Xm=X.get(i); Ym=Y.get(i);

//停止

String message;

byte[] msgBuffer;

if(i>=X.size()-1) {

//停止

try {

outStream = btSocket.getOutputStream();

} catch (IOException e) {

Log.e(TAG, "ON RESUME: Output stream creation failed.",

e);

}

message = "\111";

msgBuffer = message.getBytes();

try {

outStream.write(msgBuffer);

} catch (IOException e) {

Log.e(TAG, "ON RESUME: Exception during write.", e);

}

}

else {

//右转

if( (X.get(i+1)-Xm)>17 ) {

    tv.setText("RIGHT");

//String message;

//byte[] msgBuffer;

try {

outStream = btSocket.getOutputStream();

} catch (IOException e) {

Log.e(TAG, "ON RESUME: Output stream creation failed.",

e);

}

message = "\026";

msgBuffer = message.getBytes();

try {

outStream.write(msgBuffer);

} catch (IOException e) {

Log.e(TAG, "ON RESUME: Exception during write.", e);

}

}

else if ( (X.get(i+1)-Xm)<-17 ){

//左转

 tv.setText("LEFT");

//String message;

//byte[] msgBuffer;

try {

outStream = btSocket.getOutputStream();

} catch (IOException e) {

Log.e(TAG, "ON RESUME: Output stream creation failed.",

e);

}

message = "\022";

msgBuffer = message.getBytes();