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

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

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

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

 

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

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

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

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

源码传送门

前言

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

协议的解析

未完待续

2019-12-18 09:53:55 qq_43566257 阅读数 7
  • 串口通信和RS485-第1季第13部分

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

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

我的项目适用于android和单片机蓝牙通信,以完成功能。直接上代码,两个 activity,清单文件加布局文件需要修改。本人学识短浅,只负责代码搬运,实测有效可完成通讯
##Git地址: https://github.com/BSHead/BLTAndroid/tree/master

2016-01-07 09:53:00 sky_918 阅读数 12054
  • 串口通信和RS485-第1季第13部分

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

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

android 使用串口Modbus协议和单片机进行通信

 我所使用的232串口,使用485串口的请慎重,这232串口和485还是不一样的,这里所说的是232串口通信。

首先你需要导入架包和一些必要文件,在最后会附上图片和源码,这里简单说一下通信的步骤。

  1、使用SerialPortOpt创建对象,然后设置相关数据的初始值:串口号、波特率、数据位,校验位等(来这里看资料的,相信对这些名词有了一定了解,这里不做赘述);

  

serialPort=new SerailPortOpt();
		serialPort.mDevNum=0;
		serialPort.mDataBits=8;
		serialPort.mSpeed=9600;
		serialPort.mStopBits=1;
		serialPort.mParity='n';

 2、打开串口,设置相关参数,并获取输入输出流;

serialPort.openDev(serialPort.mDevNum);
			//需要先打开串口在设置相关参数
			serialPort.setSpeed(serialPort.mFd, serialPort.mSpeed);
			serialPort.setParity(serialPort.mFd,serialPort.mDataBits,
					serialPort.mStopBits, serialPort.mParity);
			//获得输入、输出流
			mInputStream = serialPort.getInputStream();
			mOutputStream = serialPort.getOutputStream();

3、开启线程接收数据;

/*
	 * 开启线程接收数据
	 */
	private class ReadThread extends Thread{
		byte[] buf = new byte[512];
		@Override
		public void run() {
			// TODO Auto-generated method stub
			super.run();
			if (mInputStream == null){
				return;
			}//判断接收的数据是否为空,若为null之后返回继续接收
			//	mTimer.stopThread();//接收到数据,循环发送的线程stop————————————————————————————
			int size;
			size = serialPort.readBytes(buf);
			if(size>0){
				byte[] dest = new byte[size];
				byteLinkedList.offer(dest);//使用队列接收数据
				/*System.arraycopy(buf, 0, dest, 0, size);//复制数组***********
				System.out.println("在真正数据之前");
				for(byte dest1:dest){
					System.out.println("刚接手到的数据"+dest1);
				}
				 */
				onDataReceived();
			}
		}

4、开启线程发送数据;

/*
	 * 开启发送线程
	 */
	private class SendThread extends Thread{
		@Override
		public void run() {
			// TODO Auto-generated method stub
			super.run();
			sendMsg();
		}
	}

5、关闭串口。

serialPort.closeDev(serialPort.mFd);

  串口中的循环发送线程

/*
	 * 定时连续发送的线程
	 */
	private class TimerSend extends Thread{
		private long m_lTimer = 50;	//default 100ms
		private boolean m_bRunFlag = true;
		@Override
		public void run() {
			// TODO Auto-generated method stub
			super.run();
			while (m_bRunFlag){
				sendMsg();
				if (m_lTimer <= 0){			//must over 0ms
					m_lTimer = 50;
				}
				try {
					Thread.sleep(m_lTimer);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
<span style="font-size:18px;">这些过程中涉及的方法并没有说明,不过可以知道了大体的思路,也算是对串口有了一定的了解,但是这些并没有涉及到Modbus协议,Modbus协议是很重要且很简单的协议,网上有很对介绍,若果有找不到的朋友,我这里附上一份Modbus协议的介绍,中文版的。对于modbus协议,收发机制要明白,一些东西都是和单片机规定好的,需要写死,具体做法我还在摸索中,不过我可以简单做到单片机发送的数据可以读取并解析,这里包括CRC的校验,对发送数据的CRC计算和返回数据的CRC校验,在串口助手中可以试验成功。这些可以看我的源码了。  下面附上开头所说的一些照片,很容易明白了</span>

Modbus协议介绍中文版

我的源码

有错就找我

这里的源码已经删除了,可以去看另一篇文章,里面也有源码,比这感觉更好一些。网址:http://blog.csdn.net/sky_918/article/details/52807436


2014-11-22 15:54:59 a353183177 阅读数 1788
  • 串口通信和RS485-第1季第13部分

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

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

前言:若你你一个电子爱好者,若你酷爱编程,那我分享一个Android BLE application的开发方法,让你可以使用手机和单片机结合,使用手机控制单片机,做一些更有趣的东西,下面开始讲解如何开发一个Android BLE application,参考谷歌官方教程https://developer.android.com/guide/topics/connectivity/bluetooth-le.html

本文为封装之后的教程,可以更方便的更快速的搭建你的Android BLE application.当然我会把我的源码上传分享给大家。也可以参考官方提供的例程sdk/samples/android-18/legacy/BluetoothLeGatt

我的源码下载:http://download.csdn.net/detail/a353183177/8187165

1. 新建安卓工程

    打开EclipseFile->New->Android Application Project,在Application Name编辑框内填入Application名字,如:BleExampleMinimum Required SDK选择API18Android 4.3Target SDK也选择API18:Android 4.3,因为buletooth 4.0必须要Android 4.3及以上版本才能使用,其他默认不变,一直点Next按钮,直到出现Finish按钮,然后点Finish按钮。

2. 添加权限和服务

    在manifest file文件中添加:

<uses-permission android:name="android.permission.BLUETOOTH"/>
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>

<uses-feature android:name="android.hardware.bluetooth_le" android:required="true"/>

<service android:name="com.elecfreaks.ble.BluetoothLeService" android:enabled="true"/>

3. 新建listView item布局文件

用于显示ListView中每一项的内容,我们这里使用自定义,这样可以让ListView中一项显示更多内容,item_list.xml内容如下:

<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

    android:layout_width="match_parent"

    android:layout_height="match_parent"

    android:orientation="vertical"    

    >

    <TextView android:id="@+id/textViewDevName"

            android:layout_width="match_parent"

            android:layout_height="wrap_content"

            android:textSize="24dp"/>

    <TextView android:id="@+id/textViewDevAddress"

            android:layout_width="match_parent"

            android:layout_height="wrap_content"

            android:textSize="12dp"/>

</LinearLayout>

Copy我提供的源码BleExamplcom.elecfreaks.ble包到你的工程src目录里面,然后打开有错误提示的文件,按下shift+ctrl+O键。

4. 修改activity_main.xml增加scanButton按钮和bleDeviceListView

增加内容如下:

<Button

        android:id="@+id/scanButton"

        android:layout_width="fill_parent"

        android:layout_height="wrap_content"

        android:onClick="scanOnClick"

        android:text="scan" />

 

     <ListView

        android:id="@+id/bleDeviceListView"

        android:layout_width="fill_parent"

        android:layout_height="wrap_content"

        android:layout_alignLeft="@+id/scanButton"

        android:layout_below="@+id/scanButton"

        android:layout_above="@+id/sendButton"

         >

    </ListView>

5. 在MainActivity.java中添加scanButton响应事件方法(onClick="scanOnClick")

public void scanOnClick(final View v){

 

}

6. 为MainActivity类添加成员

private Button scanButton;

private ListView bleDeviceListView;

private BLEDeviceListAdapter listViewAdapter;

private BluetoothHandler bluetoothHandler;

private boolean isConnected;

7. 在MainActivity.onCreate中设置成员的值

scanButton = (Button) findViewById(R.id.scanButton);

bleDeviceListView = (ListView)  findViewById(R.id.bleDeviceListView);

listViewAdapter = new BLEDeviceListAdapter(this);

bluetoothHandler = new BluetoothHandler(this);

bluetoothHandler.setOnConnectedListener(new  OnConnectedListener() {

@Override

public void onConnected(boolean isConnected) {

// TODO Auto-generated method stub

setConnectStatus(isConnected);

}

});

bluetoothHandler.setOnRecievedDataListener(new  OnRecievedDataListener() {

@Override

public void onRecievedData(byte[] bytes) {

// TODO Auto-generated method stub

System.out.printf("REC:");

for(byte b:bytes)

System.out.printf("%02X "b);

System.out.printf("\n");

}

});

8. 添加setConnectStatus方法

public void setConnectStatus(boolean isConnected){

this.isConnected = isConnected;

if(isConnected){

showMessage("Connection successful");

scanButton.setText("break");

}else{

bluetoothHandler.onPause();

     bluetoothHandler.onDestroy();

     scanButton.setText("scan");

}

}

private void showMessage(String str){

Toast.makeText(MainActivity.thisstr,  Toast.LENGTH_SHORT).show();

}

9. 在scanOnClick中添加内容

if(!isConnected){ bleDeviceListView.setAdapter(bluetoothHandler.getDeviceListAdapter());

bleDeviceListView.setOnItemClickListener(new OnItemClickListener() {

@Override

public void onItemClick(AdapterView<?> parent, View view,

int positionlong id) {

String buttonText = (String) ((Button)v).getText();

if(buttonText.equals("scanning")){

showMessage("scanning...");

return ;

}

BluetoothDevice device = bluetoothHandler.getDeviceListAdapter().getItem(position).device;

// connect

bluetoothHandler.connect(device.getAddress());

}

});

bluetoothHandler.setOnScanListener(new OnScanListener() {

@Override

public void onScanFinished() {

// TODO Auto-generated method stub

((Button)v).setText("scan");

((Button)v).setEnabled(true);

}

@Override

public void onScan(BluetoothDevice deviceint rssibyte[] scanRecord) {}

});

((Button)v).setText("scanning");

((Button)v).setEnabled(false);

bluetoothHandler.scanLeDevice(true);

}else{

setConnectStatus(false);

}

10. 发送数据

byte[] data = new byte[1];

data[0] = 0x02;

bluetoothHandler.sendData(data);

11. 接收数据

接收到数据时会调用bluetoothHandler.setOnRecievedDataListener()方法中设置的OnRecievedDataListener.onRecievedData(byte[] bytes)方法,其中bytes为接收到的数据

12. 通过协议发送数据给单片机

src目录新建Transmitter.java,添加带两个参数的构造函数,如下:

public Transmitter(Context context,  BluetoothHandler bluetoothHandler){

this.context = context;

this.mBluetoothHandler = bluetoothHandler;

}

添加sendData()方法:

private void sendData(byte[] bytes){

mBluetoothHandler.sendData(bytes);

}

添加sendPackage()方法:

public void sendPakege(byte[] databyte cmd){

byte[] bytes = new byte[data.length+6];

bytes[0] = (byte) 0xFA; // 数据头1

bytes[1] = (byte) 0xFB; // 数据头2

bytes[2] = (byte) 0xFC; // 数据头3

bytes[3] = (bytedata.length; // 数据长度

bytes[4] = cmd; // 指令

System.arraycopy(data, 0, bytes, 5, data.length);

for(int i=0; i<data.lengthi++){

bytes[data.length+5] ^= data[i]; // 校验和

}

sendData(bytes);

}

13. 通过协议接收单片机数据

src目录新建MyArray.java,用于连接两个数组,代码如下:

public class MyArray {

static public byte[] arrayCat(byte[] buf1,byte[] buf2){

    byte[] bufret=null;

    int len1 = 0;

    int len2 = 0;

    if(buf1 != null)

     len1 = buf1.length;

    if(buf2 != null)

     len2 = buf2.length;

    if(len1+len2 > 0)

     bufret = new byte[len1+len2];

    if(len1 > 0)

     System.arraycopy(buf1, 0, bufret, 0, len1);

    if(len2 > 0)

     System.arraycopy(buf2, 0, bufretlen1len2);

    return bufret;

    }

}

Copy我提供例程中的protocol.javasrc目录

添加成员private Protocol protocol;

在onCreate()中删除bluetoothHandler.setOnRecievedDataListener();

添加:

protocol = new Protocol(thisnew Transmitter(thisbluetoothHandler));

protocol.setOnReceivedDataListener(recListener);

在MainActivity中添加成员:

private static final boolean INPUT = false;

private static final boolean OUTPUT = true;

private static final boolean LOW = false;

private static final boolean HIGH = true;

private boolean digitalVal[];

private int analogVal[];

并且在onCreate中初始化:

digitalVal = new boolean[14];

analogVal = new int[14];

private OnReceivedRightDataListener recListener = new OnReceivedRightDataListener() {

@Override

public int onReceivedData(byte[] bytes) {

// TODO Auto-generated method stub

/*System.out.print("REC:");

    for(byte b:bytes)

    System.out.printf("%02X ", b);

    System.out.println("");*/

    

     byte pin;

     int pinValue;

    

     switch(bytes[4]){

     case Transmitter.READ_DATA:

if(bytes[Protocol.MODE_INDEX+5] == Protocol.DIGITAL){

     pin = bytes[Protocol.PIN_INDEX+5];

     pinValue = (short)  (bytes[Protocol.PINVALL_INDEX+5]);

     if(pinValue > 0){

     digitalVal[pin] = HIGH;

     }else{

     digitalVal[pin] = LOW;

     }

     }else if(bytes[Protocol.MODE_INDEX+5] ==  Protocol.ANALOG){

     pin = bytes[Protocol.PIN_INDEX+5];

     int high = bytes[Protocol.PINVALH_INDEX+5] & 0xff;

     int low = bytes[Protocol.PINVALL_INDEX+5] & 0xff;

     pinValue = high<<8 + low;

     System.out.println("low="+high+" high="+low+" val="+pinValue);

     analogVal[pin] = pinValue;

}

     break;

     case Transmitter.WRITE_DATA:

     break;

     default:break;

     }

    

return 0;

}

};

14. 使用协议发送数据

protocol.writeAnalogData(9, 20);

protocol.writeDigitalData(3, 1);

15. 使用协议接收数据

protocol.readAnalogDataCommand(9);

protocol.readDigitalDataCommand(3);

注意: 返回的数据由recListener 接收

16. 单片机端协议(arduino)

参考我提供的示例源码AndroidIOControl

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