2016-07-04 12:01:58 qq_29918347 阅读数 4707
  • 51单片机综合小项目-第2季第4部分

    本课程是《朱有鹏老师单片机完全学习系列课程》第2季第4个课程,也是51单片机学完之后的一个综合小项目,该项目运用了开发板上大多数外设设备,并将之结合起来实现了一个时间、温度显示以及报警功能、时间调整功能等单片机控制常见的功能,有一定代码量,需要一定调试技巧和编程能力来完成,对大家是个很好的总结和锻炼,并且能拓展项目经验。

    3395 人正在学习 去看看 朱有鹏

前段时间一直在加班,单身狗的周末也搭进去了…只是为了解决一个莫名其妙的bug,不过最终bug还是拜倒在了朕的强大气场下,哈哈哈,现在就在这里好好吐槽下软件遇上硬件的坑;

先简单说说情景:软件需要给硬件设备的单片机发送一串byte数组,用于打开软件与硬件设备之间的通讯通道,软件命令发送完,接着就打开一个线程用于接收从硬件设备回传的数据,然后开始做其他的一些操作。但是现在出现的问题是命令发送了,会出现偶尔接收不到数据,刚开始还以为是硬件部门的问题,请硬件部门的人过来协助,结果发现软件发送的指令单片机也是接收到了的,那么是我写的程序有问题?NO~

中间的各种脑残调试,这里就不细说了,说说最终得到的分析结果:从软件发送指令到单片机接收到指令,其实是需要时间的,至于这个时间大概在两百毫秒以内,而软件在发送了这段指令之后,就直接执行后面的接收操作,但是这个时候单片机有可能还没有接收到软件发送的指令,通道没有打开,而我的程序已经在开始读取数据了(读个鬼啊,什么都没有),这样就造成了我的操作界面一直处于等待的状态…那为什么有时候又可以读取到数据了?因为从发送数据到单片机接收数据,这个时间不可控的,为什么不可控,这里我猜测跟系统资源及单片机内的程序有关,这种情况就好像当电脑内存紧张时,软件运行速度会降低一样(当然这种情况要毕竟很少),而单片机程序部分如果接收到指令后还做了其他耗时操作这个也是有可能的…知道了原因,那么在程序发送命令后,就简单粗暴的强制线程睡眠一秒Thread.sleep(200),其实这种方法,依然存在问题,正确的最好的办法是让单片机在接收到我发出的命令后返回一个响应(但是要跟硬件部门沟通了~~~)

2017-02-02 18:53:41 qq122627018 阅读数 5680
  • 51单片机综合小项目-第2季第4部分

    本课程是《朱有鹏老师单片机完全学习系列课程》第2季第4个课程,也是51单片机学完之后的一个综合小项目,该项目运用了开发板上大多数外设设备,并将之结合起来实现了一个时间、温度显示以及报警功能、时间调整功能等单片机控制常见的功能,有一定代码量,需要一定调试技巧和编程能力来完成,对大家是个很好的总结和锻炼,并且能拓展项目经验。

    3395 人正在学习 去看看 朱有鹏

源码传送门

前言

本篇文章将围绕App与单片机的蓝牙通信来说说lz最近进行开发的一些方案与思考
此文分为三部分:

  • 单片机的PWM与串口通信
  • Android的蓝牙开发
  • 单片机与Android App的通信方案

预览

这里写图片描述
这里写图片描述

视频观看:

http://v.youku.com/v_show/id_XMjQ5MTgyMTAwMA==.html

环境

单片机

  • 单片机:STC89C52
  • c语言
  • 编写环境:Keil uVision4
  • 烧录:stc-isp-15xx-v6.82

Android

  • android sdk 16+
  • Android studio 1.0+

单片机的PWM与串口通信

PWM

我相信PWM的概念大家都应该,如果还很模糊也可以去查查,可以看看这篇文章

http://www.eepw.com.cn/article/275890.htm

那么我们这里要说的,就是怎么通过程序去模拟pwm信号(有些单片机自带pwm处理,就无需写程序),从程序的方面讲,我们要模拟PWM,就是让高电平持续一小段时间,然后再让低电平持续一段时间,也就是改变占空比。
那么再单片机中,这种关于频率的事情一般都是通过定时器来实现的,那么我的方案是这样的:
设置一个全局变量t,PWM_T,每当定时器中断的时候使t自增1,当t等于100的时候,使之高电平,并让t等于0,当t等于PWM_T的时候,使之低电平,这样,我们就可以通过改变PWM_T的值来改变占空比,从而实现通过目标的电压,使之达到调节的效果(例如调节led灯的亮度,调节电机的速度等)

/****************************************************
               定时器0中断模拟PWM
               调节led的亮度
****************************************************/
int t = 0;
int PWM_T = 0;   //占空比控制变
void main()
{
	TMOD = 0x22;   //定时器0,工作模式2,8位定时模式
	TH0=210;     //写入预置初值(取值1-255,数越大PWM频率越高)
	TL0=210;     //写入预置值 (取值1-255,数越大PWM频率越高)
	TR0=1;       //启动定时器
	ET0=1;       //允许定时器0中断
	EA=1;        //允许总中断
	P1=0xff; 	 //初始化P1,输出端口
	PWM_T=30;
	while(1)      
	{   	
		if(!up)   //当up按键按下的时候
		{
			if(PWM_T<100)
			{
				PWM_T+=1;
			}
			delay_1ms(20);
		}
		if(!down)  //当down按键按下的时候
		{
			if(PWM_T>0)
			{
				PWM_T-=1;
			}
			delay_1ms(20);
		}
	 }  
}

timer0() interrupt 1  
{ 
	t++;    //每次定时器溢出加1
	if(t==100)   //PWM周期 100个单位
	{
		t=0;  //使t=0,开始新的PWM周期
		P1=0x00;  //输出端口,使之低电平
	} 
	if(PWM_T==t)  //按照当前占空比切换输出为高电平
	{  
		P1=0xff;    //输出端口,使之高电平    
	}
}

串口通信

上面我们说了PWM调速,那么要达到app实时显示速度,就必须要单片机把速度传输给手机(在这里先用占空比模拟实时速度,道理是一样的,春节快递停了,测速模块还没到),那么我的首选方案肯定是单片机通过蓝牙串口发送给app,app接收并进行显示,这里我的蓝牙模块是hc-06。串口通信很容易,但在这个过程中我发现难的地方是数据格式的定义和数据的解析,也就是说要统一使用16进制,还是10进制,数据的头节点和尾节点的定义,或者说数据每一位所代表的参数,在这里先埋个伏笔,文章的后面会对我自己的方案进行介绍.

Android蓝牙开发

那么android为我们提供的关于蓝牙的api其实已经很强大了,通常的步骤为:

  1. 打开蓝牙
  2. 搜索蓝牙设备
  3. 进行配对
  4. 连接
  5. 数据的发送与接收

开启蓝牙

private BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
/**
* 打开蓝牙
*/
public static void openBluetooth(@NonNull Activity activity) {
	if (INSTANCE.bluetoothAdapter == null) {
            // 设备不支持蓝牙
            Toast.makeText(INSTANCE.context.getApplicationContext(), "您的设备似乎不支持蓝牙", Toast.LENGTH_SHORT).show();
            return;
	}
	Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
	activity.startActivityForResult(enableBtIntent, 6);
}

/**
* 关闭蓝牙
*/
public static void closeBluetooth() {
	if (INSTANCE.bluetoothAdapter != null) {
		INSTANCE.bluetoothAdapter.disable();
	}
}

/**
* 判断蓝牙是否打开
* @return boolean 蓝牙是否打开
*/
public static boolean isBluetoothOpen() {
	return INSTANCE.bluetoothAdapter != null && INSTANCE.bluetoothAdapter.isEnabled();
}

搜索附近的蓝牙设备

那么搜索蓝牙设备当然也是调用系统的api即可,然后系统通过广播接收者的方式告诉你,我找到设备了,下面po出代码

/**
* 搜索蓝牙设备
*/
public static void searchDevices() {
	INSTANCE.bluetoothDevices.clear();
	if (INSTANCE.bluetoothAdapter != null) {
		// 寻找蓝牙设备,android会将查找到的设备以广播形式发出去
		INSTANCE.bluetoothAdapter.startDiscovery();
	}
}

下面是所要接收的广播


    /**
     * 初始化过滤器
     */
    private void initIntentFilter() {
        // 设置广播信息过滤
        IntentFilter intentFilter = new IntentFilter();
        //搜索到设备
        intentFilter.addAction(BluetoothDevice.ACTION_FOUND);
        intentFilter.addAction(BluetoothAdapter.ACTION_DISCOVERY_STARTED);
        intentFilter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
        //蓝牙状态改变
        intentFilter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
        //绑定状态改变
        intentFilter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
        // 注册广播接收器,接收并处理搜索结果
        registerReceiver(receiver, intentFilter);
    }

那么当接收到广播的时候,只需调用BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE)就可以取出对应的搜索的蓝牙设备

蓝牙配对

 /**
     * 绑定设备
     *
     * @param device BluetoothDevice对象
     * @return 是否绑定成功
     */
    public static boolean bondDevice(BluetoothDevice device) {
        if (INSTANCE.bluetoothAdapter == null) {
            return false;
        }
        // 取消蓝牙设备搜索
        INSTANCE.bluetoothAdapter.cancelDiscovery();
        try {
            if (device.getBondState() == BluetoothDevice.BOND_NONE) {
                // 设备未配对,进行配对操作
                Method method = BluetoothDevice.class.getMethod("createBond");
                method.invoke(device);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }

蓝牙连接

	BluetoothSocket socket;
	//获取一个uuid值
	UUID uuid = uuidCandidates.get(candidate++);
	//根据android不同sdk调用不同的api
	if (secure) {
		socket= device.createRfcommSocketToServiceRecord(uuid);
	} else {
		socket= device.createInsecureRfcommSocketToServiceRecord(uuid);
	}

数据的发送与接收

参考了网上很多关于蓝牙数据通信的做法,好多都是每发送一次数据都关闭socket,但是那样我觉得并不好,因为socket的开启与关闭都是比较耗费资源的,那么我的方案是开启一个线程保持socket连接进行蓝牙数据的接收与发送。

public class TouchMsgThread extends Thread {
    private  BluetoothSocket socket;
    private  InputStream inputStream;
    private  OutputStream outputStream;
    private  Handler handler;
    public TouchMsgThread(BluetoothSocket socket, Handler handler) {
        this.socket = socket;
        InputStream input = null;
        OutputStream output = null;
        this.handler = handler;
        try {
            input = socket.getInputStream();
            output = socket.getOutputStream();
        } catch (IOException e) {
            e.printStackTrace();
        }
        this.inputStream = input;
        this.outputStream = output;
    }
    public void run() {
        while (true) {
            try {
                int count = 5;
                byte[] bytes = new byte[count];
                int readCount = 0; // 已经成功读取的字节的个数
	              while (readCount < count) {
                    readCount += inputStream.read(bytes, readCount, count - readCount);
               }
                int s = BinaryToHexString(bytes);
                Message message=handler.obtainMessage();
                message.what = 333;
                message.obj=s;
                handler.sendMessage(message);
            } catch (Exception e) {
                e.printStackTrace();
                break;
            }
        }
    }
    public void write(byte[] bytes) {
        try {
            byte[] b = {-1,1,2,3,-1};
            outputStream.write(b);
            outputStream.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    public void cancel() {
        try {
            if(outputStream!=null){
                outputStream.close();
                outputStream = null;
            }
            if(inputStream!=null){
                inputStream.close();
                inputStream = null;
            }
            //socket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    //数据的检验,这里暂时先忽略
    public static int BinaryToHexString(byte[] bytes) {
        int result = 0;
        String temp = "";
        for(int i=0;i<5;i++){
            byte b = bytes[i];
            if(i==2){
                temp = Integer.toHexString((b & 0xff));
            }
            if(i==3){
                String hex = Integer.toHexString((b & 0xff));
                result = Integer.parseInt(temp+hex, 16);
            }
        }
        return result;
    }
}

单片机与Android的通信方案

制定协议

那么上面我们已经讲了单片机与Android怎么样通过蓝牙进行信息交互了,但是在实际应用中,二者之间传递的信息类型太多了,比如实时速度,电量,还有车子灯光打开,或者修改车子密码等等信息,那么单片机或者app要怎么去判断传递过来的是哪种信息呢?那么我们就必须去制定一套数据协议,这里看看我的方案,协议规定:

包头 类型位 数据位 数据位 结束位
0xFF 0x** 0x** 0x** 0xFF

那么我们的数据位可以分别代表高二位和低二位,那么通常情况下这种方案就可以满足我们的需求了。举个例子:

类型位 数据位 数据位 功能
0X00 0X02 0X00 前进
0X00 0X01 0X00 后退
0X00 0X03 0X00 左转
0X00 0X04 0X00 右转
0X00 0X00 0X00 停止
0X02 0x00 0X01 车灯亮
0X02 0x00 0X02 车灯灭
0X03 雷达数据高位 雷达数据低位 发送雷达数据

协议的解析

未完待续

2018-08-24 14:46:00 dbmmn64000 阅读数 2
  • 51单片机综合小项目-第2季第4部分

    本课程是《朱有鹏老师单片机完全学习系列课程》第2季第4个课程,也是51单片机学完之后的一个综合小项目,该项目运用了开发板上大多数外设设备,并将之结合起来实现了一个时间、温度显示以及报警功能、时间调整功能等单片机控制常见的功能,有一定代码量,需要一定调试技巧和编程能力来完成,对大家是个很好的总结和锻炼,并且能拓展项目经验。

    3395 人正在学习 去看看 朱有鹏

1、单片机实验板功能设计

  为验证数据通信内容,让单片机板上的四个按键与android手机客户端上的四个LED灯相互控制;为达到上述基本实验要求,采用单字符传输数据即可,硬件需设计两块相同的单片机电路板,包含单片机最小系统,四路输入,四路输出,MAX232模块,九针插口,由于Zigbee模块与蓝牙模块即插即用,即分别设计四路插口即可。单片机的功能图如下图所示:

实际单片机效果图为:

单片机模块通过蓝牙模块与安卓客户端连接,进而进行数据的传输。

单片机写入的程序如下:

#include<reg52.h>

sbit key1=P2^0;//
sbit key2=P2^2;//
sbit key3=P2^1;//
sbit key4=P2^3;//

sbit led1=P0^0;//
sbit led2=P0^3;//
sbit led3=P0^1;//
sbit led4=P0^2;//


void delay(unsigned int xms)
{
    unsigned int i,j;
    for(i=xms;i>0;i--)
      for(j=110;j>0;j--);
}

void Send_bit(unsigned char b)
{
    SBUF=b;
    while(!TI);//判断是否发送完成
    TI=0;
}
    
void init(void)
{
    SCON=0x50;
    TMOD|=0x20;
    TH1=0xFA;
    TR1=1;
    EA=1;
}

    
void main()
{
    init();
    ES=1;
    key1=1;//初始化定义按键和小灯泡最开始的值
    key2=1;
    key3=1;
    key4=1;
    led1=1;
    led2=1;
    led3=1;
    led4=1;
    while(1)
     {
        if(!key1)//第一个灯的控制上
        {
            delay(10);
            if(key1==0)
            {
                Send_bit(1);
            }
        }
        
        if(!key2)//第二个灯的控制下
        {
            delay(10);
            if(key2==0)
            {
                Send_bit(2);
            }
        }
        
        if(!key3)//第三个灯的控制左
        {
            delay(10);
            if(key3==0)
            {
        Send_bit(3);
            }
        }
        
        if(!key4)//第四个灯的控制右
        {
            delay(10);
            if(key4==0)
            {
         Send_bit(4);
            }
        }
    }


}

void Serial_INT(void)interrupt 4
{
    int a;
    if(RI)//如果有接受到字符
    {
        RI=0;
        a=SBUF;
        if(a=='1')
        {
            led1=!led1;
        }
        if(a=='2')
        {
            led2=!led2;
        }
        if(a=='3')
        {
            led3=!led3;
        }
        if(a=='4')
        {
            led4=!led4;
        }
      if(TI)//判断与此同时是否有发送,在中断过程中只接受而不发送
      {
        TI=0;
      }
    }    
}

2、android手机客户端

选用安卓手机,编写安卓程序。设计界面完成“连接”、“断开”和“上”、“下”、“左”、“右”四个方向按键,以及读取连接状态的显示。

安卓软件编写环境:

Android Studio 3.1.2

JRE:1.8.0_152-release-1024-b02 amd64

JVM: OpenJDK 64-Bit Server VM by JetBrains s.r.o

通过编程完成各按键功能。界面及功能如下图所示:

 

下面为android端接收单片机传来的数据,实现截图中间四个模拟led灯亮灭的代码,以及读写数据的代码:

// 该Handler从BluetoothChatService中获取信息
    private final Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MESSAGE_STATE_CHANGE:
                    if (D)
                    Log.i(TAG, "MESSAGE_STATE_CHANGE: " + msg.arg1);


                    switch (msg.arg1)
                    {
                        case BluetoothChatService.STATE_CONNECTED:
                         mTitle.setText(R.string.title_connected_to);
                         mTitle.append(mConnectedDeviceName);
                         mConversationView.setText(null);
                         break;

                        case BluetoothChatService.STATE_CONNECTING:
                        mTitle.setText(R.string.title_connecting);
                        break;

                        case BluetoothChatService.STATE_LISTEN:
                        case BluetoothChatService.STATE_NONE:
                        mTitle.setText(R.string.title_not_connected);
                        break;
                   }
                break;

            case MESSAGE_WRITE:
                byte[] writeBuf = (byte[]) msg.obj;
                // 自动发送
                if (auto == true) {

                        // 自动发送模块
                    mHandler.postDelayed(runnable, 1000);
                    } else if (auto == false) {
                        mHandler.removeCallbacks(runnable);
                }
                // 发送计数
                if (outhex == true) {
                    String writeMessage = Data_syn.Bytes2HexString(writeBuf);
                    countout += writeMessage.length() / 2;
                    outcount.setText("" + countout);
                } else if (outhex == false) {
                    String writeMessage = null;
                    try {
                        writeMessage = new String(writeBuf, "GBK");
                    } catch (UnsupportedEncodingException e1) {
                        e1.printStackTrace();
                    }
                    countout += writeMessage.length();
                    outcount.setText("" + countout);
                }
                break;
            case MESSAGE_READ:

                byte[] readBuf = (byte[]) msg.obj;

                  //检错误码计算函数

                if (inhex == true) {
                    String readMessage = " "
                            + Data_syn.bytesToHexString(readBuf, msg.arg1);
                    fmsg += readMessage;
                    mConversationView.append(readMessage);
                    // 接收计数,更显UI
                    countin += readMessage.length() / 2;
                    incount.setText("" + countin);




                } else if (inhex == false) {
                    String readMessage = null;
                    try {
                        readMessage = new String(readBuf, 0, msg.arg1, "GBK");
                    } catch (UnsupportedEncodingException e) {
                        e.printStackTrace();
                    }
                    fmsg += readMessage;
                    mConversationView.append(readMessage);
                    // 接收计数,更新UI
                    countin += readMessage.length();
                    incount.setText("" + countin);

                    //将字符型的readmessage转换为int型的
                    int messAge=Integer.valueOf(readMessage);


                    //对接收到的控制指令进行计数,分别表示上下左右的次数

                    if(messAge==1){
                        countI=countI+1;
                        //Toast.makeText(BluetoothChat.this,"I:"+countI,Toast.LENGTH_SHORT).show();

                    }else {
                        if(messAge==2){
                            countJ=countJ+1;
                            //Toast.makeText(BluetoothChat.this,"J:"+countJ,Toast.LENGTH_SHORT).show();
                        }else {
                            if (messAge==3){
                                countK=countK+1;
                                //Toast.makeText(BluetoothChat.this,"K:"+countK,Toast.LENGTH_SHORT).show();
                            }else {
                                countL=countL+1;
                                //Toast.makeText(BluetoothChat.this,"L:"+countL,Toast.LENGTH_SHORT).show();
                            }
                        }
                    }

                    //根据上面的次数,进行背景的变换,即模拟led亮灭
                    switch (messAge){
                        case 1: {
                            if (countI % 2 == 1) {
                                upled.setImageResource(R.drawable.circle);//更换背景
                            } else {
                                upled.setImageResource(R.drawable.circleline);
                            }
                        }

                        case 2: {
                            if (countJ % 2 == 1) {
                                downled.setImageResource(R.drawable.circle);//更换背景
                            } else {
                                downled.setImageResource(R.drawable.circleline);
                            }
                        }
                        case 3: {
                            if (countK % 2 == 1) {
                                leftled.setImageResource(R.drawable.circle);//更换背景
                            } else {
                                leftled.setImageResource(R.drawable.circleline);
                            }
                        }
                        case 4:{
                            if(countL % 2==1) {
                                rightled.setImageResource(R.drawable.circle);//更换背景
                            }else {
                                rightled.setImageResource(R.drawable.circleline);
                            }
                        }
                    }





                }
                break;
            case MESSAGE_DEVICE_NAME:
                // 保存已连接设备的名称
                mConnectedDeviceName = msg.getData().getString(DEVICE_NAME);
                Toast.makeText(getApplicationContext(),
                        "连接到 " + mConnectedDeviceName, Toast.LENGTH_SHORT)
                        .show();
                break;
            case MESSAGE_TOAST:
                Toast.makeText(getApplicationContext(),
                        msg.getData().getString(TOAST), Toast.LENGTH_SHORT)
                        .show();
                break;
            }
        }
    };

android端点击上下左右控制单片机端小灯亮灭的代码实现,即点击按钮,发送不同的字符01、02、03、04控制亮灭。

 3、功能演示部分截图

转载于:https://www.cnblogs.com/jlutiger/p/9529820.html

2018-05-07 18:17:02 qq_41570228 阅读数 11854
  • 51单片机综合小项目-第2季第4部分

    本课程是《朱有鹏老师单片机完全学习系列课程》第2季第4个课程,也是51单片机学完之后的一个综合小项目,该项目运用了开发板上大多数外设设备,并将之结合起来实现了一个时间、温度显示以及报警功能、时间调整功能等单片机控制常见的功能,有一定代码量,需要一定调试技巧和编程能力来完成,对大家是个很好的总结和锻炼,并且能拓展项目经验。

    3395 人正在学习 去看看 朱有鹏

  51单片机是市面上比较普及的单片机种类,同时也深受广大电子爱好者的青睐。在51单片机的众多功能中串口通信占据了重要的地位,通过串口通信,我们可以实现51单片机与其他设备的互联,增加了学习的趣味性。

  HC-05蓝牙模块为主从一体的蓝牙模块,并可与手机实现实时的通信。将单片机的串口通信通过HC-05进行扩展从而实现无线通信,更可使用手机来控制单片机完成工作。

 要完成单片机与Android手机的无线通信,首先我们要在写好单片机的控制程序,因为HC-05只是对串口通信功能进行延伸, 并不能独立完成通信。

 编写串口通信的程序中首先我们要对串口的配置进行初始化

void UartConfigurationInit()
{
    TMOD=0x20;  //设置定时器1工作方式为方式2   
    TH1=0xfd;  	  //波特率9600
    TL1=0xfd;  
    TR1=1;      //启动定时器1     
    SM0=0;SM1=1;      //串口方式1         
    REN=1;      //允许接收   
    PCON=0x00;  //关倍频   
    ES=1;       //开串口中断   
    EA=1;       //开总中断
}

配置好串口后,在编写发送函数前我们需要对串口发送数据的原理有个了解:

转码过程:

发送十进制数:查询ASCII码表后发送相应的字符 ,如SBUF=65;则会收到字符‘A’;

发送字符:原样输出

发送字符串:强行对SBUF传递字符串会造成输出乱码,得不到想要的结果。想要发送字符串则需要运用指针,每次发送时发送指针指向的那个字符,因为发送频率很快,最后输出时组合起来还是一条字符串。

实现整数转为字符串发送:

   0x30对应的字符‘0’,当我们要发送一个整数时,需要将该整数按最高位到最低位得到顺序依次发送,发送时需要在原始数据上加上0x30或者48,则会输出相应的数字字符。


字符发送函数:

void PostChar(uchar character)  
{     
	    SBUF=character;   //发送单个字符
	    while(!TI);TI=0; //发送完成标志 
}

字符串的发送需要依靠字符发送函数来实现:

  这里加了一个延时函数delay(),因为在实际与手机通信中我发现发送速度过快会产生严重的数据丢失,因此加上一个延时可以较好的保证数据的准确度,具体延时数据根据大家实际需要调整。

void  PostString(uchar *p)  
{  
	    while(*p)  	   //若指针指向的地址为空,则跳出循环
	    {  
	        PostChar(*p); //指针第一次默认指向首地址
			delay(20);  //延时,作用为提高发送准确度
	        p++;  
	    }	  
}

十进制数据发送:

 因为十进制数据不能直接发送,所以要先加上0x30或者48后再转为字符型。

void IntConversionChar(uint number)
{
       uint model;	//模
       uint numberByte; //十进制数据不同位上的值

     for(model=1000;model>=1;model=model/10) //model的取值根据实际需要选择,选择过大会浪费资源

      {
            numberByte = number/model;	//相除取整
			//当取到整数数据的最高位时,将其加上0x30或者48并转为字符型后发送
            if(numberByte >=1 ) PostChar((uchar)(numberByte%10+0x30));
      		//if(numberByte >=1 ) PostChar((unsigned char)(numberByte%10+48));
	  }	
}

以上就是51单片机使用HC-05蓝牙模块与Android手机通信的程序的要点和原理。

完整程序:

https://download.csdn.net/download/qq_41570228/10398277



2014-01-05 12:04:42 jason0539 阅读数 37701
  • 51单片机综合小项目-第2季第4部分

    本课程是《朱有鹏老师单片机完全学习系列课程》第2季第4个课程,也是51单片机学完之后的一个综合小项目,该项目运用了开发板上大多数外设设备,并将之结合起来实现了一个时间、温度显示以及报警功能、时间调整功能等单片机控制常见的功能,有一定代码量,需要一定调试技巧和编程能力来完成,对大家是个很好的总结和锻炼,并且能拓展项目经验。

    3395 人正在学习 去看看 朱有鹏

之前两篇都是在说与手机的连接,连接方法,和主动配对连接,都是手机与手机的操作,做起来还是没问题的,但是最终的目的是与单片机的蓝牙模块的通信。

 

下面是到目前为止尝试的与单片机的通信方法,没有成功,但是从思路上来说没有问题,最大的问题是与单片机配对的时候,单片机的蓝牙模块的PIN配对码是写死的,固定为1234,

而手机这边连接配对都是自动生成的PIN配对码,这种方式在手机与手机配对的时候是极为方便的,但是在这里与单片机连接却成了最大的问题,因为手机自动生成而且每次都不一样,所以没法与单片机蓝牙模块的1234相同也就没法陪对了。下面只是介绍的到目前为止我们的大题思路,具体代码很多,而且涉及到项目也就没有贴。

如果关于上面的问题哪位同学有思路或者做过类似的项目还请指点。

 

首先,如何开启蓝牙设备和设置可见时间:

private void search() {
        BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
        if (!adapter.isEnabled()) {
            adapter.enable();
        }
        Intent enable = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
        enable.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 3600); //3600为蓝牙设备可见时间
         startActivity(enable);
        Intent searchIntent = new Intent(this, ComminuteActivity.class);
        startActivity(searchIntent);
    }


正式开始与蓝牙模块进行通信

public class ComminuteActivity extends Activity {
    private BluetoothReceiver receiver;
    private BluetoothAdapter bluetoothAdapter;
    private List<String> devices;
    private List<BluetoothDevice> deviceList;
    private Bluetooth client;
    private final String lockName = "YESYOU";
    private String message = "000001";
    private ListView listView;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.search_layout);

        listView = (ListView) this.findViewById(R.id.list);
        deviceList = new ArrayList<BluetoothDevice>();
        devices = new ArrayList<String>();
        bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
        bluetoothAdapter.startDiscovery();
        IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
        receiver = new BluetoothReceiver();
        registerReceiver(receiver, filter);

        listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                setContentView(R.layout.connect_layout);
                BluetoothDevice device = deviceList.get(position);
                client = new Bluetooth(device, handler);
                try {
                    client.connect(message);
                } catch (Exception e) {
                    Log.e("TAG", e.toString());
                }
            }
        });
    }

    @Override
    protected void onDestroy() {
        unregisterReceiver(receiver);
        super.onDestroy();
    }

    private final Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case Bluetooth.CONNECT_FAILED:
                    Toast.makeText(ComminuteActivity.this, "连接失败", Toast.LENGTH_LONG).show();
                    try {
                        client.connect(message);
                    } catch (Exception e) {
                        Log.e("TAG", e.toString());
                    }
                    break;
                case Bluetooth.CONNECT_SUCCESS:
                    Toast.makeText(ComminuteActivity.this, "连接成功", Toast.LENGTH_LONG).show();
                    break;
                case Bluetooth.READ_FAILED:
                    Toast.makeText(ComminuteActivity.this, "读取失败", Toast.LENGTH_LONG).show();
                    break;
                case Bluetooth.WRITE_FAILED:
                    Toast.makeText(ComminuteActivity.this, "写入失败", Toast.LENGTH_LONG).show();
                    break;
                case Bluetooth.DATA:
                    Toast.makeText(ComminuteActivity.this, msg.arg1 + "", Toast.LENGTH_LONG).show();
                    break;
            }
        }
    };

    private class BluetoothReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            if (BluetoothDevice.ACTION_FOUND.equals(action)) {
                BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
                if (isLock(device)) {
                    devices.add(device.getName());
                }
                deviceList.add(device);
            }
            showDevices();
        }
    }

    private boolean isLock(BluetoothDevice device) {
        boolean isLockName = (device.getName()).equals(lockName);
        boolean isSingleDevice = devices.indexOf(device.getName()) == -1;
        return isLockName && isSingleDevice;
    }

    private void showDevices() {
        ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1,
                devices);
        listView.setAdapter(adapter);
    }
}

这里需要提一下的是,startDiscovery()这个方法和它的返回值,它是一个异步方法,会对其他蓝牙设备进行搜索,持续时间为12秒。

搜索过程其实是在System Service中进行,我们可以通过cancelDiscovery()方法来停止这个搜索。在系统搜索蓝牙设备的过程中,系统可能会发送以下三个广播:ACTION_DISCOVERY_START(开始搜索),

ACTION_DISCOVERY_FINISHED(搜索结束)

和ACTION_FOUND(找到设备)。

ACTION_FOUND这个才是我们想要的,这个Intent中包含两个extra fields:    EXTRA_DEVICE和EXTRA_CLASS,

包含的分别是BluetoothDevice和BluetoothClass

EXTRA_DEVICE中的BluetoothDevice就是我们搜索到的设备对象,从中获得设备的名称和地址。

EXTRA_CLASS中的BluetoothClass是搜索到的设备的类型,比如搜索到的是手机还是耳机或者其他,之后我会写一篇关于它的介绍

在这个上面我现在在想,是否通过判断搜索到的设备类型来识别单片机蓝牙模块与手机蓝牙的不同,采取不一样的配对方式,从而不自动生成配对码。不知是否可行,一会尝试。

 

 搜索到该设备后,我们就要对该设备进行连接和通信。

public void connect(final String message) {
        Thread thread = new Thread(new Runnable() {
            public void run() {
                BluetoothSocket tmp = null;
                Method method;
                try {
                    method = device.getClass().getMethod("createRfcommSocket", new Class[]{int.class});
                    tmp = (BluetoothSocket) method.invoke(device, 1);
                } catch (Exception e) {
                    setState(CONNECT_FAILED);
                    Log.e("TAG", e.toString());
                }
                socket = tmp;
                try {
                    socket.connect();
                    isConnect = true;
                } catch (Exception e) {
                    setState(CONNECT_FAILED);
                    Log.e("TAG", e.toString());
                }
	       if (isConnect) {
                    try {
                        OutputStream outStream = socket.getOutputStream();
                        outStream.write(getHexBytes(message));
                    } catch (IOException e) {
                        setState(WRITE_FAILED);
                        Log.e("TAG", e.toString());
                    }
                    try {
                        InputStream inputStream = socket.getInputStream();
                        int data;
                        while (true) {
                            try {
                                data = inputStream.read();
                                Message msg = handler.obtainMessage();
                                msg.what = DATA;
                                msg.arg1 = data;
                                handler.sendMessage(msg);
                            } catch (IOException e) {
                                setState(READ_FAILED);
                                Log.e("TAG", e.toString());
                                break;
                            }
                        }
                    } catch (IOException e) {
                        setState(WRITE_FAILED);
                        Log.e("TAG", e.toString());
                    }
                }

                if (socket != null) {
                    try {
                        socket.close();
                    } catch (IOException e) {
                        Log.e("TAG", e.toString());
                    }
               }
       }
}

 这里包括写入和读取,用法和基本的Socket是一样的,但是写入的时候,需要将字符串转化为16进制:

private byte[] getHexBytes(String message) {
        int len = message.length() / 2;
        char[] chars = message.toCharArray();
        String[] hexStr = new String[len];
        byte[] bytes = new byte[len];
        for (int i = 0, j = 0; j < len; i += 2, j++) {
            hexStr[j] = "" + chars[i] + chars[i + 1];
            bytes[j] = (byte) Integer.parseInt(hexStr[j], 16);
        }
        return bytes;
    }


 

连接设备之前需要UUID,所谓的UUID,就是用来进行配对的,全称是Universally Unique Identifier,是一个128位的字符串ID,用于进行唯一标识。网上的例子,包括谷歌的例子提供的uuid,通用的"00001101-0000-1000-8000-00805F9B34FB"也试过了,在配对的时候都是自动生成了配对码,也无法正常与单片机的蓝牙模块连接,所以,我就利用反射的原理,让设备自己提供UUID尝试。到这里其实我有点怀疑自己对于UUID的理解是否正确了。

            在谷歌提供的例子中,我们可以看到谷歌的程序员的程序水平很高,一些好的编码习惯我们可以学习一下,像是在try..catch中才定义的变量,我们应该在try...catch之前声明一个临时变量,然后再在try...catch后赋值给我们真正要使用的变量。这种做法的好处就是:如果我们直接就是使用真正的变量,当出现异常的时候,该变量的使用就会出现问题,而且很难进行排查,如果是临时变量,我么可以通过检查变量的值来确定是否是赋值时出错。

   

作者:jason0539

微博:http://weibo.com/2553717707

博客:http://blog.csdn.net/jason0539(转载请说明出处)

没有更多推荐了,返回首页