2017-02-02 18:53:41 qq122627018 阅读数 5725
  • 串口通信和RS485-第1季第13部分

    本课程是《朱有鹏老师单片机完全学习系列课程》第1季第13个课程,主要讲解了串行通信UART及其扩展RS485。本课程很重要,因为串口通信是我们接触的早也简单的通信方式,是后续继续学习SPI、I2C甚至USB、网络通信等的基础,大家务必认证对待完全掌握。

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

源码传送门

前言

本篇文章将围绕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 雷达数据高位 雷达数据低位 发送雷达数据

协议的解析

未完待续

2020-02-07 16:22:28 jun8086 阅读数 280
  • 串口通信和RS485-第1季第13部分

    本课程是《朱有鹏老师单片机完全学习系列课程》第1季第13个课程,主要讲解了串行通信UART及其扩展RS485。本课程很重要,因为串口通信是我们接触的早也简单的通信方式,是后续继续学习SPI、I2C甚至USB、网络通信等的基础,大家务必认证对待完全掌握。

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

基于51单片机与Android手机通信的android蓝牙小车

(班门弄斧,请各位大神批评指正,烦请提出宝贵意见。)
整车图片

#一、材料:

1.stc51单片机最小系统板(最好带上拉电阻);

2.HC-02蓝牙芯片;

3.减速电机,电机驱动;

4.充电宝(感谢刘帆同学赞助的充电宝);

5.车架是自己设计,3D打印出来的,打印件下载

5.Android手机及app,APP下载

部分零件

减速电机

二、硬件连接

1.把小车电机驱动上的四根控制线接在单片机4个IO口上;

2.把HC-02的rxd接在单片机的txd上,txd接在单片机的rxd上;

3.把单片机、HC-02、小车电机驱动的电源线接在充电宝输出上;

4.把码盘(用于测速)上的GND、VCC连上电源,输入连在51单片机的IO 口上;

4.给单片机烧程序;

5.打开Android手机扫描蓝牙,连接测试;

6.手机控制小车。
#三、调试过程及相关代码

1.调试HC-02蓝牙芯片(买的时候店家会送调试程序及教程);

2.调试单片机蓝牙通信;

(1).扫描,连接,键盘模式。

(2).右上角三点,设定IO模式,接收:hex字符串,发送:hex字符串。

(3).配置键盘值(发送给蓝牙芯片的字符串就是这个);

(4).前进(1A),长按(1A),释放(DD)

(5).左转(1B),长按(1B),释放(DD)

(6).后退(1F),长按(1F),释放(DD)

(7).右转(1C),长按(1C),释放(DD)

(8).刹车(DD),长按(DD),释放(DD)
3.相关代码

/*
程序名称: 蓝牙手机遥控小车
功能原理: 利用智能手机app连接HC-02蓝牙芯片实现蓝牙串口通信,
           利用stc51单片机控制电机驱动模块驱动小车 。
扩展功能:循迹、避障等。
*/
#include "reg52.h"    //此文件中定义了单片机的一些特殊功能寄存器
#define time_0 5
typedef unsigned int u16;	  //对数据类型进行声明定义
typedef unsigned char u8;

sbit output_date1=P2^7;
sbit output_date2=P2^6;
sbit output_date3=P2^5;
sbit output_date4=P2^4;
sbit STA=P1^0;

#define GO_STAIGHT  {output_date1=1,output_date2=0,output_date3=1,output_date4=0;}	        //前进
#define GO_BACK     {output_date1=0,output_date2=1,output_date3=0,output_date4=1;}			//后退
#define TURN_RIGHT	{output_date1=0,output_date2=1,output_date3=1,output_date4=0;}		    //右转
#define TURN_LEFT   {output_date1=1,output_date2=0,output_date3=0,output_date4=1;}		    //左转
#define STOP_CAR    {output_date1=0,output_date2=0,output_date3=0,output_date4=0;}			//停车


#define USART_MAX_RECV_LEN	40
u8 USART_RX_BUF[USART_MAX_RECV_LEN];
u16 USART_RX_STA=0; 

void delay(u16 n);
void UsartInit();
void USART_SendData();



void delay(u16 n)   //1ms   (误差 -0.651041666667us)
{
   u16 i ;
    unsigned char a,b;
	for(i=1;i<=n;i++)
	{
    for(b=102;b>0;b--)
        for(a=3;a>0;a--);
	}
}


void UsartInit()
{
	SCON=0X50;			//设置为工作方式1
	TMOD=0X20;			//设置计数器工作方式2
	PCON=0X80;			//波特率加倍
	TH1=0XFa;			//计数器初始值设置,注意波特率是9600的
	TL1=0XFa;
	ES=1;						//打开接收中断
	EA=1;						//打开总中断
	TR1=1;					//打开计数器
}

void USART_SendData(u8 datbuf[])
{
	u8 i=0;
	for(i=0;i<1;i++)
	{
		SBUF=datbuf[i];//将接收到的数据放入到发送寄存器
		while(!TI);//等待发送数据完成
		TI=0;//清除发送完成标志位	
	}
}


void main()
{	u8	date_get=0;
	STA=0;
	UsartInit();  //串口初始化

	
	while(1)
	{
		
		date_get=USART_RX_BUF[0];
		switch(date_get)
		{
			case 0x1B:				//左转
					 		  
					TURN_LEFT;		
					USART_SendData(USART_RX_BUF);
					 delay(time_0);

					break;	
			case 0x1A:				 //前进		  
				    GO_STAIGHT;					
					USART_SendData(USART_RX_BUF); 
					delay(time_0);
					break;
			case 0x1F:				 //后退
						  
				    GO_BACK;
					USART_SendData(USART_RX_BUF); 
					delay(time_0);
					break;
			case 0x1C:				 //右转			  
					TURN_RIGHT;
					USART_SendData(USART_RX_BUF); 
					delay(time_0);
					break;
			case 0xDD:
							  //停
					STOP_CAR;
					USART_SendData(USART_RX_BUF); 
					delay(time_0); 
					STOP_CAR;
				
		}
	            	STOP_CAR;  
	}		
}

void Usart() interrupt 4		//串口中断
{
	u8 res;
	res=SBUF;
	USART_RX_BUF[0]=res;
    RI=0;//清除接收中断标志位	
								 
}

2014-01-05 12:04:42 jason0539 阅读数 37784
  • 串口通信和RS485-第1季第13部分

    本课程是《朱有鹏老师单片机完全学习系列课程》第1季第13个课程,主要讲解了串行通信UART及其扩展RS485。本课程很重要,因为串口通信是我们接触的早也简单的通信方式,是后续继续学习SPI、I2C甚至USB、网络通信等的基础,大家务必认证对待完全掌握。

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

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

 

下面是到目前为止尝试的与单片机的通信方法,没有成功,但是从思路上来说没有问题,最大的问题是与单片机配对的时候,单片机的蓝牙模块的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(转载请说明出处)

2018-01-15 15:46:50 weixin_38727562 阅读数 496
  • 串口通信和RS485-第1季第13部分

    本课程是《朱有鹏老师单片机完全学习系列课程》第1季第13个课程,主要讲解了串行通信UART及其扩展RS485。本课程很重要,因为串口通信是我们接触的早也简单的通信方式,是后续继续学习SPI、I2C甚至USB、网络通信等的基础,大家务必认证对待完全掌握。

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

今天调试了一下Android的串口程序,能够实现与PC端的串口收发,数据解析均没有问题,同时编写了单片机的串口收发程序,与PC端通信没有问题;

现把单片机的9针串口与Android的9针串口用串口线连接,无法通信,单片机的信号显示一直有数据发送,Android端收不到数据,

麻烦高手给些解决建议。

2020-02-02 19:22:25 weixin_42619971 阅读数 194
  • 串口通信和RS485-第1季第13部分

    本课程是《朱有鹏老师单片机完全学习系列课程》第1季第13个课程,主要讲解了串行通信UART及其扩展RS485。本课程很重要,因为串口通信是我们接触的早也简单的通信方式,是后续继续学习SPI、I2C甚至USB、网络通信等的基础,大家务必认证对待完全掌握。

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

 MIT Appinventor 是编程领域较为受欢迎且适用的编程软件 ,其操作流程和使用方法简单,一种深受广大编程小镇的喜爱今天为大家介绍一种利用它开发手机APP的实战经历 ——手机APP开发之MIT Appinventor详细实战教程(一),利用通过蓝牙控制单片机,以及实现单片机与android设备之间的串口通信。 
  

(一)前期软件准备和硬件准备

 软件准备主要是keil uvision,stc-isp-15xx-v6.85
 硬件准备主要是有一个蓝牙模块 、一个便宜的单片机 、一个蓝牙可以正常使用的手机和编程电脑 ;  蓝牙模块需要和单片机连接,具体的操作方法:VCC接VCC,GND接GND,TXD接P30,RXD接P31
  


( 二 ) 实现的思路和操作原理

 我们知道手机和单片机是两个互不相连的事物 ,所以要实现他们之间的通信,我们需要一个信使来充当中间媒介的作用 。而那个信使就是上面提到的蓝牙模块 。 这个蓝牙模块一方面可以接收到手机APP发送的数据并将它传达给单片机 ,另一方面单片机也可以将反馈数据通过蓝牙模块进而发送到手机App上 。这就实现了单片机和手机之间的交互通信 。 

( 三) 具体的操作方法


 首先我们需要  MIT Appinventor 制作一个简单的组件页面 ,具体情况如下图所示  :
 这个布局还有待优化 ,实现最基本的功能也足够了 


 接下来是逻辑层面的设计 
 [ 这个可以用来实现手机APP对蓝牙的选择连接 ]
 列表选择框(选择蓝牙)的启用为真,按钮(断开)的启用为假。选择蓝牙时将蓝牙客户端的地址及名称显示出来供用户选择。、
 
 蓝牙所发送的数据相关设置也必不可少 ,本文让蓝牙发送的数据为1或0 ,你来传递给单片机,进而控制LED灯的亮灭。
 ![ 较为简单的逻辑设计 ,这只是一个简单的框架,主要设计的是通过按钮的开关来控制灯的亮灭 ]
 然后是通过发送零或一来使单片机Led灯发亮或暗 的控制的控制逻辑
 [ 在文本框输入1或0,点击确认发送即可完成相应功能 ]
  上面的一系列操作完成后,我们已经可以实现手机单方面对单片机进行控制。 在这里我们可以把手机形象化的比喻为命令的发出者 ,发出者通过蓝牙模块将数据传达给单片机 ,单片机又根据内部的程序,从而实现对LED灯的控制 ( 单片机的相关配合程序下文会详细给出 )下面我们来实现单片机对手机反馈数据 这一流程的操作 ,遵循单片机反向反馈数据的原理 ,和相关知识。我们可以有下面的尝试 
  ![ 通过显示框找到蓝牙接收到的数据显示出来 ]


  **另大一部分便是单片机的配合程序了** 

 

#include <reg52.h> //51头文件

sbit LED1 = P2^0; //位定义 LED1硬件接口
void delay(unsigned int z)//毫秒级延时
{
    unsigned int x,y;
    for(x = z; x > 0; x--)
        for(y = 114; y > 0 ; y--);
}    


 /******************************************************************/
/* 串口中断程序*/
/******************************************************************/
void UART_SER () interrupt 4
{
    unsigned int n;     //定义临时变量

    if(RI)         //判断是接收中断产生
    {
        RI=0;     //标志位清零
        n=SBUF; //读入缓冲区的值

        switch(n)
        {
            case 1:    LED1 = 0;    break;    //亮灯
            case 0:    LED1 = 1;    break;    //灭灯
        }
    }

}

//蓝牙初始化
void boothint(void)
{
   
       SCON = 0x50;     // SCON: 模式1, 8-bit UART, 使能接收 
    TMOD |= 0x20;
    TH1=0xfd;         //波特率9600 初值
    TL1=0xfd;
    TR1= 1;
    EA = 1;        //开总中断
    ES= 1;         //打开串口中断


}
//串口初始化
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;       //开总中断
}
void main()
{
    boothint();
    while(1)
    {
     
        SBUF=9;   //发送单个字符
        while(!TI);TI=0; //发送完成标志
    }
}

 到目前为止,软件设计 和单片机内部程序的设计 的所有细节已经全部讲到   , 然后就是软件下载或者是调试的过程了 ,  在整个界面的上面有一个连接(Connect)选项,推荐使用AI侣,要求手机下载一个Appinventor的app,然后手机和电脑在同一个局域网下,扫描二维码就可以在线调试看效果了。是不是有点小激动呀!  
 

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