2017-07-11 15:12:07 qq_35945759 阅读数 1437
  • 51单片机综合小项目-第2季第4部分

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

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

这次期末的课程设计做了一个智能灯光控制系统,系统整体的功能不在此赘述,系统主要是要实现下位机同上位机的通信,上位机选用的是Android手机端,下位机是52单片机,通过蓝牙模块实现通信。虽然系统很简单,但还是第一次完成的走完从下位机数据采集,数据传输,再到上位机的处理这个流程,故在这里做一个记录,也希望能够帮到有需要的人。

一、下位机通信

下位机选用的是52单片机,数据来自几个传感器,传感器采集到数据后通过串口发送到蓝牙模块,然后蓝牙模块发送到上位机。因代码量较大,所以只在这里贴出传输有关的函数。

//利用串口发送一个字符
void SendOneByte(unsigned char c) 
{
    SBUF = c;
    while(!TI);
    TI = 0;
}
//重写putchar函数,就可以直接调用printf()函数向串口发送数据,程序自动将printf()中的数据转换成char调用putchar发送
char putchar(char ch) 
{
	ES=0;
	SBUF=ch;
	while(!TI);
	TI=0;
	ES=1;
	return 0;
}
//初始化串口
void InitUART() 
{
	TMOD = 0x20;
	PCON = 0x00;
	SCON = 0x50;    
	TH1 = 0xFD;
	TL1 = 0xFD;
	TR1 = 1;
	ES = 1;
	EA = 1;
}
//串口中断
void UARTInterrupt() interrupt 4
{
    //RI为1则表示串口接收到数据
    if(RI)
    {
    	RI = 0;
    	r_buf = SBUF;
	//价格SBUF中的数据赋给r_buf,然后就可以对数据进行处理		
    }
}
void main()
{
	InitUART();
	while(1)
	{
	
	}
}

、蓝牙模块

蓝牙模块我选用的是HC-05,这个模块我之前也没用使用过,查询了一些资料后就能够上手了,感觉还是很好用。模块有六个引脚,如果用的是带一个小按钮的HC-05,EN就不用接;然后VCC和GND分别接电源和地;TXD 和 RXD在配置AT指令的时候分别接单片机的TXD和RXD,但是在正常使用时,HC-05的TXD和RXD分别接单片机的RXD和TXD,这个需要注意;还有一个引脚是state,当有蓝牙连接的时候会置1,将其随意连接到单片机的引脚上。

使用前先利用AT指令集配置模块,设置波特率和主从模式等,然后就可以连线使用。连接后蓝牙模块会进入快闪模式,进入AT指令集后会进入慢闪模式,当有蓝牙设备连接后会进入双闪模式。


三、Android端程序

Android端主要就是接受数据,做出一定处理,还需发送指令给单片机。我用的代码也是在网上找的然后又做了一些修改,源代码出处找不到了。主要代码如下:

1、DeveiceListActivity类

public class DeviceListActivity extends Activity {
    // 调试用
    private static final String TAG = "DeviceListActivity";
    private static final boolean D = true;

    // 返回时数据标签
    public static String EXTRA_DEVICE_ADDRESS = "设备地址";

    // 成员域
    private BluetoothAdapter mBtAdapter;
    private ArrayAdapter<String> mPairedDevicesArrayAdapter;
    private ArrayAdapter<String> mNewDevicesArrayAdapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // 创建并显示窗口
        requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);  //设置窗口显示模式为窗口方式
        setContentView(R.layout.device_list);

        // 设定默认返回值为取消
        setResult(Activity.RESULT_CANCELED);

        // 设定扫描按键响应
        Button scanButton = (Button) findViewById(R.id.button_scan);
        scanButton.setOnClickListener(new OnClickListener() {
            public void onClick(View v) {
                doDiscovery();
                v.setVisibility(View.GONE);
            }
        });

        // 初使化设备存储数组
        mPairedDevicesArrayAdapter = new ArrayAdapter<String>(this, R.layout.device_name);
        mNewDevicesArrayAdapter = new ArrayAdapter<String>(this, R.layout.device_name);

        // 设置已配队设备列表

        ListView pairedListView = (ListView) findViewById(R.id.paired_devices);
        pairedListView.setAdapter(mPairedDevicesArrayAdapter);
        pairedListView.setOnItemClickListener(mDeviceClickListener);

        // 设置新查找设备列表
        ListView newDevicesListView = (ListView) findViewById(R.id.new_devices);
        newDevicesListView.setAdapter(mNewDevicesArrayAdapter);
        newDevicesListView.setOnItemClickListener(mDeviceClickListener);

        // 注册接收查找到设备action接收器
        IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
        this.registerReceiver(mReceiver, filter);

        // 注册查找结束action接收器
        filter = new IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
        this.registerReceiver(mReceiver, filter);

        // 得到本地蓝牙
        mBtAdapter = BluetoothAdapter.getDefaultAdapter();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();

        // 关闭服务查找
        if (mBtAdapter != null) {
            mBtAdapter.cancelDiscovery();
        }

        // 注销action接收器
        this.unregisterReceiver(mReceiver);
    }

    public void OnCancel(View v){
        finish();
    }
    /**
     * 开始服务和设备查找
     */
    private void doDiscovery() {
        if (D)
            Log.d(TAG, "doDiscovery()");

        // 在窗口显示查找中信息
        setProgressBarIndeterminateVisibility(true);
        setTitle("查找设备中...");

        // 显示其它设备(未配对设备)列表
        findViewById(R.id.title_new_devices).setVisibility(View.VISIBLE);

        // 关闭再进行的服务查找
        if (mBtAdapter.isDiscovering()) {
            mBtAdapter.cancelDiscovery();
        }
        //并重新开始
        mBtAdapter.startDiscovery();
    }

    // 选择设备响应函数
    private OnItemClickListener mDeviceClickListener = new OnItemClickListener() {
        public void onItemClick(AdapterView<?> av, View v, int arg2, long arg3) {
            // 准备连接设备,关闭服务查找
            mBtAdapter.cancelDiscovery();

            // 得到mac地址
            String info = ((TextView) v).getText().toString();
            String address = info.substring(info.length() - 17);

            // 设置返回数据
            Intent intent = new Intent();
            intent.putExtra(EXTRA_DEVICE_ADDRESS, address);

            // 设置返回值并结束程序
            setResult(Activity.RESULT_OK, intent);
            finish();
        }
    };

    // 查找到设备和搜索完成action监听器
    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();

            // 查找到设备action
            if (BluetoothDevice.ACTION_FOUND.equals(action)) {
                // 得到蓝牙设备
                BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
                // 如果是已配对的则略过,已得到显示,其余的在添加到列表中进行显示
                if (device.getBondState() != BluetoothDevice.BOND_BONDED) {
                    mNewDevicesArrayAdapter.add(device.getName() + "\n" + device.getAddress());
                }else{  //添加到已配对设备列表
                    mPairedDevicesArrayAdapter.add(device.getName() + "\n" + device.getAddress());
                }
                // 搜索完成action
            } else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) {
                setProgressBarIndeterminateVisibility(false);
                setTitle("选择要连接的设备");
                if (mNewDevicesArrayAdapter.getCount() == 0) {
                    String noDevices = "没有找到新设备";
                    mNewDevicesArrayAdapter.add(noDevices);
                }
            }
        }
    };
}


2、Client类

public class BTClient extends Activity {

    private final static int REQUEST_CONNECT_DEVICE = 1;    //宏定义查询设备句柄

    private final static String MY_UUID = "00001101-0000-1000-8000-00805F9B34FB";   //SPP服务UUID号

    private InputStream is;    //输入流,用来接收蓝牙数据
    private EditText edit0;    //发送数据输入句柄
    private TextView lightSwitch;
    private TextView lightStrength;
    private TextView lightMode;
    private TextView lightPower;
    private String switchMsg= "";
    private String strengthMsg= "";
    private String modeMsg= "";

    BluetoothDevice _device = null;     //蓝牙设备
    BluetoothSocket _socket = null;      //蓝牙通信socket
    boolean bRun = true;
    boolean bThread = false;
    boolean timesign=false;

    private BluetoothAdapter _bluetooth = BluetoothAdapter.getDefaultAdapter();    //获取本地蓝牙适配器,即蓝牙设备

    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);   //设置画面为主画面 main.xml

        edit0 = (EditText)findViewById(R.id.Edit0);   //得到输入框句柄
        lightSwitch = (TextView) findViewById(R.id.lightSwitch);
        lightStrength=(TextView) findViewById(R.id.lightStrength);
        lightMode=(TextView) findViewById(R.id.lightMode);
        lightPower=(TextView) findViewById(R.id.lightpower);

        lightSwitch.setText("关闭");
        lightStrength.setText("8");
        lightMode.setText("时间调节模式");
        lightPower.setText("无数据");

        //如果打开本地蓝牙设备不成功,提示信息,结束程序
        if (_bluetooth == null){
            Toast.makeText(this, "无法打开手机蓝牙,请确认手机是否有蓝牙功能!", Toast.LENGTH_LONG).show();
            finish();
            return;
        }

        // 设置设备可以被搜索
        new Thread(){
            public void run(){
                if(_bluetooth.isEnabled()==false)
                {
                    _bluetooth.enable();
                }
            }
        }.start();
    }

    //发送按键响应
    public void onSendButtonClicked(View v){
        try{
            OutputStream os = _socket.getOutputStream();   //蓝牙连接输出流
            modeMsg=edit0.getText().toString();
            if(modeMsg.equals("1")||modeMsg.equals("2")||modeMsg.equals("3"))
            {
                if(modeMsg.equals("1")) {
                    lightMode.setText("手动调节模式");
                    lightPower.setText("无数据");
                    timesign=false;
                }
                else if(modeMsg.equals("2")) {
                    lightMode.setText("自动调节模式");
                    timesign=false;
                }
                else if(modeMsg.equals("3")) {
                    lightMode.setText("时间调节模式");
                    lightPower.setText("无数据");
                }
            }
            if(timesign)
            {
                final int timec = Integer.valueOf(modeMsg).intValue() * 1000;
//                CountDownTimer cdt = new CountDownTimer(timec, timec) {
//                    @Override
//                    public void onTick(long millisUntilFinished) {
//                    }
//
//                    @Override
//                    public void onFinish() {
//                        edit0.setText("3");
//                    }
//                };
//                cdt.start();
                try{
                    Thread.currentThread().sleep(timec);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                edit0.setText("3");
            }
            if(modeMsg.equals("3")&& !timesign)
                timesign=true;
            byte[] bos = edit0.getText().toString().getBytes();
            edit0.setText("");
            os.write(bos);
        }catch(IOException e){
        }
    }

    //接收活动结果,响应startActivityForResult()
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        switch(requestCode){
            case REQUEST_CONNECT_DEVICE:     //连接结果,由DeviceListActivity设置返回
                // 响应返回结果
                if (resultCode == Activity.RESULT_OK) {   //连接成功,由DeviceListActivity设置返回
                    // MAC地址,由DeviceListActivity设置返回
                    String address = data.getExtras()
                            .getString(DeviceListActivity.EXTRA_DEVICE_ADDRESS);
                    // 得到蓝牙设备
                    _device = _bluetooth.getRemoteDevice(address);

                    // 用服务号得到socket
                    try{
                        _socket = _device.createRfcommSocketToServiceRecord(UUID.fromString(MY_UUID));
                    }catch(IOException e){
                        Toast.makeText(this, "连接失败!", Toast.LENGTH_SHORT).show();
                    }
                    //连接socket
                    Button btn = (Button) findViewById(R.id.Button03);
                    try{
                        _socket.connect();
                        Toast.makeText(this, "连接"+_device.getName()+"成功!", Toast.LENGTH_SHORT).show();
                        btn.setText("断开");
                    }catch(IOException e){
                        try{
                            Toast.makeText(this, "连接失败!", Toast.LENGTH_SHORT).show();
                            _socket.close();
                            _socket = null;
                        }catch(IOException ee){
                            Toast.makeText(this, "连接失败!", Toast.LENGTH_SHORT).show();
                        }

                        return;
                    }

                    //打开接收线程
                    try{
                        is = _socket.getInputStream();   //得到蓝牙数据输入流
                    }catch(IOException e){
                        Toast.makeText(this, "接收数据失败!", Toast.LENGTH_SHORT).show();
                        return;
                    }
                    if(bThread==false){
                        ReadThread.start();
                        bThread=true;
                    }else{
                        bRun = true;
                    }
                }
                break;
            default:break;
        }
    }

    //接收数据线程
    Thread ReadThread=new Thread(){
        public void run(){
            int num = 0;
            byte[] buffer = new byte[1024];
            bRun = true;
            //接收线程
            while(true){
                try{
                    while(is.available()==0){
                        while(bRun == false){}
                    }
                    while(true){
                        num = is.read(buffer);         //读入数据
                        String s=new String(buffer,0,num);
                        switchMsg=s;
                        strengthMsg=s;
                        if(is.available()==0)break;  //短时间没有数据才跳出进行显示
                    }
                    //发送显示消息,进行显示刷新
                    handler.sendMessage(handler.obtainMessage());
                }catch(IOException e){
                }
            }
        }
    };

    //消息处理队列
    Handler handler= new Handler(){
        public void handleMessage(Message msg){
            super.handleMessage(msg);
        }
    };

    //关闭程序掉用处理部分
    public void onDestroy(){
        super.onDestroy();
        if(_socket!=null)  //关闭连接socket
            try{
                _socket.close();
            }catch(IOException e){}
    }

    //连接按键响应函数
    public void onConnectButtonClicked(View v){
        if(_bluetooth.isEnabled()==false){  //如果蓝牙服务不可用则提示
            Toast.makeText(this, " 打开蓝牙中...", Toast.LENGTH_LONG).show();
            return;
        }
        //如未连接设备则打开DeviceListActivity进行设备搜索
        Button btn = (Button) findViewById(R.id.Button03);
        if(_socket==null){
            Intent serverIntent = new Intent(this, DeviceListActivity.class); //跳转程序设置
            startActivityForResult(serverIntent, REQUEST_CONNECT_DEVICE);  //设置返回宏定义
        }
        else{
            try{
                //关闭连接socket
                _socket.close();
                is.close();
                _socket = null;
                bRun = false;
                btn.setText("连接");
            }catch(IOException e){}
        }
        return;
    }

    //退出按键响应函数
    public void onQuitButtonClicked(View v){
        finish();
    }
}

2018-09-18 17:07:04 u010898329 阅读数 2656
  • 51单片机综合小项目-第2季第4部分

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

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

(1)安卓手机与蓝牙模块联合调试(一)—— 蓝牙模块的串口通讯 

(2)安卓手机与蓝牙模块联合调试(二)—— 单片机蓝牙控制LED灯亮灭(上)

(3)安卓手机与蓝牙模块联合调试(三)—— 单片机蓝牙控制LED灯亮灭(下)

(4)安卓手机与蓝牙模块联合调试(四)—— 单片机数据上传至蓝牙(STC89C52 + DS18b20)

(5)安卓手机与蓝牙模块联合调试(五)-- 编写自己的蓝牙控制界面控制单片机(上篇,Android 代码实现)

   本教程的项目地址:1989Jiangtao/BluetoothSCM: 安卓手机通过蓝牙与单片机通信-发送指令/接收数据


接着上篇继续,本篇主要是完善单片机端的代码部分。废话不多说,开始飙车了。 

1.看下初步的演示效果

 

2.主要代码部分,main.c

 (1)单片机端的代码主要是在之前的代码基础上做了修改,多增加了几条指令。

/****************************************
**       蓝牙串口接收数据
** 
**   作者:江涛
**   时间:2018/08/31
**   描述:串口发送数据兼用OLED显示
****************************************/
#include "STC89C5xRC_RDP.h"
#include "string.h"     // 要使用字符串对比函数,需要引入该头文件
#include "OLED.h"       // OLED显示屏头文件
#include "DS18b20.h"

// 定义系统时钟和串口波特率
#define FOSC 11059200L      // 系统时钟
#define BAUD 9600           // 串口波特率

/******变量声明*********/ 
char RECEIVED_CMD[10] ;       	// 暂定为10字节的指令
char RECEIVED_INDEX ;         	// 数组指示索引,当接收到一个数据之后,索引会跟随增加
unsigned char flag = 0 ;      	// 数据接收的标志位
unsigned char power_flag = 0 ;  // 电源开关的标志位

/******命令常量*******/
code const char* LED_ON = "ON\r\n" ;					// 电源开指令
code const char* LED_OFF = "OFF\r\n" ;				// 电源关指令
code const char* LED_01_ON = "L1ON\r\n" ;			// 灯组01开指令
code const char* LED_01_OFF = "L1OFF\r\n" ;		// 灯组01关指令
code const char* LED_02_ON = "L2ON\r\n" ;			// 灯组02开指令
code const char* LED_02_OFF = "L2OFF\r\n" ;		// 灯组02关指令
code const char* FAN_ON = "FANON\r\n" ;				// 风扇开指令,使用RGB灯循环来模拟风扇工作
code const char* FAN_OFF = "FANOFF\r\n" ;			// 风扇关指令
code const char* FAILD = "power_off\r\n" ;		// 返回失败原因,电源关闭了

extern unsigned int tvalue;			//温度值
extern unsigned char tflag;			//温度正负标志
unsigned char disdata[7]; 			// 温度数据,使用8字节数组来存储

char color_table[8][3] = { // 颜色表
	{0,0,0},{0,0,1},{0,1,0},{0,1,1},{1,0,0},{1,0,1},{1,1,0},{1,1,1}
};

/*******函数声明*********/
void Init_UART(); // 初始化串口
void UART_SendData(char dat); // 串口发送数据
void UART_SendStr(char* str); // 串口发送字符串
void RGB_Display(int index);  // RGB灯显示

//void split(char str[],char delims[]); // 字符串截取函数

void ds1820disp(); 	// 温度显示

void test_Fan(char flag);		// 模拟测试风扇运行
void Delay1ms();						//@11.0592MHz

/***********端口定义*************/
sbit LED_R = P0^0 ;
sbit LED_G = P0^1 ;
sbit LED_B = P0^2 ;

/*******程序入口*********/
void main() 
{

    unsigned int temperature , old ; // 保存温度数值
	
	Init_UART();  // 串口初始化
	
	LCD_Init();  // OLED 初始化
	LCD_CLS();   // 清屏
	
	LCD_P8x16Str(0 , 0 , "TEMP:");  // 温度开始位置
	
	temperature = ReadTemperature(); 
	old = temperature ; 
	ds1820disp(); // 显示温度
	UART_SendStr(disdata); // 向串口发送数据
	LCD_P8x16Str(5*8 , 0 , disdata); // 显示温度
	
	while(1)
	{
			
	temperature=ReadTemperature();  // 读取一次新的温度
    if (temperature != old )	  
	  {	 
			old = temperature;
			ds1820disp(); // 显示温度
			UART_SendStr(disdata); // 向串口发送数据
			LCD_P8x16Str(5*8 , 0 , disdata); // 显示温度

	  }
		
		if(flag) // 接收数据完毕一次,就会进入中断一次
		{
			flag = 0 ; // 将标志位还原,使得串口又可以重新接收数据
					
			if(strcmp(RECEIVED_CMD , LED_ON) == 0)
			{
				P2 = 0xFF ; // P2口全亮
				power_flag = 1 ; // 标志电源打开
			}
			else if(strcmp(RECEIVED_CMD , LED_OFF) == 0)
			{
				P2 = 0x00 ; // P2口全灭
				power_flag = 0 ;// 标志电源关闭
			}
			else if(strcmp(RECEIVED_CMD , LED_01_ON) == 0)
			{
				if(power_flag) 		// 如果电源开关是关闭的,就不执行以下操作
					P2 = 0x55 ; 		// ‭01010101‬ 为1位置的灯是亮着的
				else
					UART_SendStr(FAILD); // 向串口发送失败原因
			}
			else if(strcmp(RECEIVED_CMD , LED_01_OFF) == 0)
			{
				P2 = P2^0x55 ; // P2口01010101相应位置的灯要全灭,所以使用异或操作
			}
			else if(strcmp(RECEIVED_CMD , LED_02_ON) == 0)
			{
				if(power_flag) 		// 如果电源开关是关闭的,就不执行以下操作
					P2 = 0xAA ; 		// ‭10101010‬ 为1位置的灯是亮着的
				else
					UART_SendStr(FAILD); // 向串口发送失败原因
			}
			else if(strcmp(RECEIVED_CMD , LED_02_OFF) == 0)
			{
				P2 = P2^0xAA ; // P2口10101010相应位置的灯要全灭,所以使用异或操作
			}
			else if(strcmp(RECEIVED_CMD , FAN_ON) == 0)
			{
				test_Fan(1);
			}
			else if(strcmp(RECEIVED_CMD , FAN_OFF) == 0)
			{
				test_Fan(0);
			}			

			
			// 用完之后要记得数组清零处理
      RECEIVED_INDEX = 0 ;        // 数组指引复位
      memset(RECEIVED_CMD,0,10);  // 清0数组
		}
	}
}

/******************
** 初始化串口
*******************/
void Init_UART()
{
	SCON = 0x50;                     //设置8位数据位
	TMOD = 0x20;                     //8位自动重载
	TH1 = TL1 = -(FOSC/12/32/BAUD);  //设置重载值
	TR1 = 1;                         //使能时钟
	ES = 1;                          //使能串口中断
	EA = 1;                          //开中断开关
}

/********************
** 串口中断处理
*********************/
void UART_Isr() interrupt 4 using 1
{
	// 串口接收中断处理
	if(RI) 
	{
		RI = 0 ;                              // 清除中断标志位
		RECEIVED_CMD[RECEIVED_INDEX] = SBUF ; // 保存串口接收的数据
		if(RECEIVED_CMD[RECEIVED_INDEX] == 0x0A ){ // 遇到了结束符号
			 flag = 1 ;           // 接收结束,到循环中处理接收的数据
		}else {
			 RECEIVED_INDEX ++ ;   // 继续接收数据
		}
	}

	// 串口发送中断处理
	if(TI)
	{
		TI = 0 ;  // 清发送中断标志位
	}
		
}

/**************************
** 通过串口发送一位数据
***************************/
void UART_SendData(char dat)
{
	ES = 0 ;      // 串口工作的时候禁止中断
	SBUF = dat ;  // 待发送的数据放到SBUF中
	while(!TI) ;  // 等待发送完毕
	TI = 0 ;      // 清TI中断
	ES = 1 ;      // 打开中断
}

/*****************************
**  通过串口发送字符串
******************************/
void UART_SendStr(char *str)
{
	do
	{
		UART_SendData(*str);
	}while(*str ++  != '\0' ); // 一直到字符串结束
}

/****************************
**  显示RGB灯的颜色
*****************************/
void RGB_Display(int index)
{
	LED_R = color_table[index%8][0];
	LED_G = color_table[index%8][1];
	LED_B = color_table[index%8][2];
}

void Delay1ms()		//@11.0592MHz
{
	unsigned char i, j;

	_nop_();
	i = 2;
	j = 199;
	do
	{
		while (--j);
	} while (--i);
}

/****************
** 模拟风扇运行
****************/
void test_Fan(char flag)
{
	unsigned int t , count = 500 ;

	if(!flag) return ; // 如果传入的是0,表示停止,不往下继续进行
	
	for(t=0 ; t<8 ; t++)
	{
		RGB_Display(t);		// 风扇
		for( ; count > 0 ; count --)
			Delay1ms();
	}	
}

///***********************************
//** 字符串截取函数
//***********************************/
//void split(char str[],char delims[])
//{
//	char *result = NULL; 
//	result = strtok( str, delims );  
//	while( result != NULL ) {  
//		 result = strtok( NULL, delims );  
//	}
//} 

/***************************
** 温度值显示
***************************/
void ds1820disp()
{ 	
	  unsigned char flagdat;
	
		if(tflag==0)
//			flagdat=0x20;//正温度不显示符号
		  flagdat=0x2b;//正温度显示符号
		else
		  flagdat=0x2d;//负温度显示负号:-
		
//		disdata[0] = flagdat; //符号位
	
		disdata[1]=tvalue/1000+0x30;//百位数
		disdata[2]=tvalue%1000/100+0x30;//十位数
		disdata[3]=tvalue%100/10+0x30;//个位数
		disdata[4]= 0x2E ;//小数点
		disdata[5]=tvalue%10/1+0x30;//小数位

		if(disdata[1]==0x30) // 如果百位为0
		{
			disdata[0]= 0x20; // 第一位不显示
			disdata[1]= flagdat; // 百位显示符号
			if(disdata[2]==0x30) //如果百位为0,十位为0
			{
				disdata[1]=0x20; // 百位不显示符号
				disdata[2]=flagdat; // 10位显示符号
			}
		}
}

代码中有部分逻辑处理还需要完善,其中有个电源开关和两组灯控制部分的逻辑,理论上电源关闭的时候,操作两个灯是无效的,为了演示用我将灯关闭的消息发送给了安卓端,让安卓端进行提示,但是单片机端这部分的代码逻辑还有要完善的地方。

(2)安卓端修改的部分

    @Override
    public void onClick(View v) {

        // 当蓝牙有连接,并且MAC地址存在,两个UUID都不为空的情况下,点击按钮才有效
        // 以下只要有一个条件不满足,就不让点击按钮发送数据
        if(!MyApp.getBluetoothClient().isBleSupported()
                || TextUtils.isEmpty(MAC)
                || TextUtils.isEmpty(serviceUuid.toString())
                || TextUtils.isEmpty(characterUuid.toString())){
            Toast.makeText(MainActivity.this , "请先检查蓝牙设备与手机是否连接正常",Toast.LENGTH_SHORT).show();
            return;
        }

        switch (v.getId()){
            case R.id.sw_lamp_01: // 灯组01
                lamp01.switchState();
                lamp01Name.setText(lamp01.isIconEnabled() ? "灯组1开" : "灯组1关");
                writeCmd(MAC , serviceUuid , characterUuid , lamp01.isIconEnabled() ? "L1ON\r\n" :"L1OFF\r\n");
                break;
            case R.id.sw_lamp_02: // 灯组02
                lamp02.switchState();
                lamp02Name.setText(lamp02.isIconEnabled() ? "灯组1开" : "灯组1关");
                writeCmd(MAC , serviceUuid , characterUuid , lamp02.isIconEnabled() ? "L2ON\r\n" :"L2OFF\r\n");
                break;
            case R.id.sw_power: // 电源
                powerSw.switchState();
                powerName.setText(powerSw.isIconEnabled() ? "电源开" : "电源关");
                writeCmd(MAC , serviceUuid , characterUuid , powerSw.isIconEnabled() ? "ON\r\n" :"OFF\r\n");
                break;
            case R.id.sw_fan: // 风扇
                fanSw.switchState();
                fanName.setText(fanSw.isIconEnabled() ? "风扇开" : "风扇关");
                writeCmd(MAC , serviceUuid , characterUuid , fanSw.isIconEnabled() ? "FANON\r\n" :"FANOFF\r\n");
                break;
        }
    }

附上读写安卓端指令的两个方法

    /***
     * 获取温度值并显示到界面上
     * @param address           设备地址
     * @param serviceUuid       服务UUID
     * @param characterUuid     特征UUID
     */
    private void getTemperature(String address , UUID serviceUuid , UUID characterUuid ){
        MyApp.getBluetoothClient().notify(address, serviceUuid, characterUuid, new BleNotifyResponse() {
            @Override
            public void onNotify(UUID service, UUID character, byte[] value) {
                    Log.d("CJT" , "getTemperature -- value2Str -- :" + new String(value));
                    if(new String(value).contains("power_off\r\n")){
                        Toast.makeText(MainActivity.this , "请打开电源开关",Toast.LENGTH_SHORT).show();
                        return;
                    }
                    String hexStr = bytesToHexString(value);
                    Log.d("CJT","getTemperature -- hexStr -- : "+hexStr);
                    if(!hexStr.contains("2e")) return ; // 不包含小数点
                    int beginIndex = hexStr.indexOf("2b") + 2; // 加号开始截取,并且跳过加号
                    int endIndex = hexStr.indexOf("2e") + 4 ;  // 小数点开始截取
                    String validTemp = hexStr.substring(beginIndex , endIndex );
                    Log.d("CJT" , "valid temp = "+validTemp+", hex2Str = "+ new String(hexStringToBytes(validTemp)));

                    // 设置温度值
                    tempView.setAngleWithAnim(Double.valueOf(new String(hexStringToBytes(validTemp))));
            }

            @Override
            public void onResponse(int code) {

            }
        });
    }

    /***
     * 向设备下发指令
     * @param address           设备MAC地址
     * @param serviceUuid       服务UUID
     * @param characterUuid     特征UUID
     * @param cmd               待下发的命令
     */
    private void writeCmd(String address , UUID serviceUuid , UUID characterUuid , String cmd){
        MyApp.getBluetoothClient().write(address, serviceUuid, characterUuid, cmd.getBytes(), new BleWriteResponse() {
            @Override
            public void onResponse(int code) {
                if(code == Constants.REQUEST_SUCCESS){

                }
            }
        });
    }

 注释代码中写得比较清楚了,这里当手机收到的指令中含有单片机发送过来的`"power-off"`指令的时候,表示电源开关关闭,需要提示先打开电源开关,这部分的逻辑需要大家去完善下,我这里就先留个BUG给大家。

3.小结

        最后,单片机通过蓝牙模块和手机通信其实就是通过蓝牙模块的串口通讯,如果熟悉单片机底层的串口通讯,那么底层蓝牙部分就很简单了,手机端的编程需要掌握java和安卓编程才能进行,如果没有太多基础,大家可以找些入门教程看下。

        最后希望该系列的博客能给大家一些启发和建议,行文至此,也了了我(从前的电子工程师,如今的安卓工程师)一个心愿。程序学习的路很漫长,希望大家都能找到自己喜欢的并坚持下去。

 

 

2019-04-11 20:34:41 weixin_43710544 阅读数 1690
  • 51单片机综合小项目-第2季第4部分

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

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

学习HC06蓝牙模块遇到一些问题,特此写下作为记录。

  1. 用USB转TTL连接,插到电脑上,发AT怎么也不回
    USB转TTL与蓝牙模块连接图
    USB转TTL与蓝牙模块连接图
    插到电脑上
    打开串口助手:
    一开始用的是丁丁串口助手,发·AT不回OK。
    后来用的是友善也不回。
    在这里插入图片描述
    原因是要,加回车。
    (光标在蓝色的地方,而不是红色的地方,就算 自动换行 你打 √ 了,还是要加回车)
    我看很多文章中写是发 AT/r/n 不知道为什么,我用了很多串口软件发送都没有用。

    HC06 灯闪烁是AT模式,常亮建立连接。

有一个方法可以确认你的蓝牙有木有问题或者连接有木有问题。
在这里插入图片描述
将蓝牙模块与USB转TLL连接好,插上电脑,打开串口助手打开串口,之后将蓝牙模块的Vcc与USB转TLL的5V连线断开,然后再插上。就会收到+REDAY…

  1. AT指令 可以输入AT+HELP查看
    AT+NAME mingzi
    可以修改蓝牙名称

  2. 单片机与HC06 就是串口通信UART
    (1)写APP时,要注意蓝牙的模式是串口通信
    (2)用51单片机时,要注意晶振。百度:51单片机波特率的计算方法 有详细的解释
    (3)发送的数据都是ACSII码

51单片机与蓝牙模块
参考 https://blog.csdn.net/qq_40277973/article/details/79345817
发送什么内容就回什么内容:

#include <reg52.h>
unsigned char tempbuf;	//存储接收到的信息

/*初始化串口*/
void BlueteethInit()
{
	SCON = 0x50;	//串口模式1,允许接收
	TMOD = 0x20;	//T1工作模式为2,自动重装
	PCON = 0x00;	//波特率不倍增
	REN = 1;

	TH1 = 0xfd;		//设置波特率为9600
	TL1 = 0xfd;

	RI = 0;

	EA = 1;
	ES = 1;

	TR1 = 1;
}

void main()
{
	BlueteethInit();
   	TI = 0;
}

void Serial(void) interrupt 4
{
	tempbuf = SBUF;
	RI = 0;	//读标志清零
	SBUF = tempbuf;	//将内容返回到手机端,可在手机查看发送的内容
	while(!TI);
	TI = 0;	//写标志清零
}

4.用电脑串口和手机蓝牙串口APP(接线同上)
手机APP用蓝牙串口,发中文会乱码
自己写的APP,还是中文
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2019-12-07 09:21:31 qq_43421598 阅读数 52
  • 51单片机综合小项目-第2季第4部分

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

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

蓝牙模块

蓝牙转串口(TTL)模块,蓝牙转串口,不需要知道蓝牙协议,只需要知道串口协议就好。我所用的是BT06,只能做从机,蓝牙接收数据。

BT06蓝牙模块是专为智能无线数据传输而打造,遵循V3.0 蓝牙规范。
本模块支持UART接口,并支持SPP蓝牙串口协议,具有成本低、体积小、功耗低、收发灵敏性高等优点,只需配备少许的外围元件就能实现其强大功能。

接线图蓝牙正反面图片

可以USB-TTL模块直接与蓝牙模块相连,但要注意 5V-5V,GND-GND,RXD-TXD,TXD-RXD。
也可以直接用开发板进行蓝牙设置,但注意不要插单片机,否则数据会冲突,因为单片机进行烧录也是用的RX,TX。
开发板连接

状态指示LED:

LED显示 模块状态
长亮 建立连接
均匀慢速闪烁 等待配对

AT指令集

在进行蓝牙与电脑连接上之后,打开串口助手(或者STC下载器自带的)打开串口可以进行AT指令设置,设置它的密码,名称以及波特率等
用户可以通过串口和 BT06蓝牙进行通信,串口使用 Tx, Rx 两根信号线,波特 率 支 持
1200,2400,4800,9600,14400,19200,38400,57600,115200,230400,460800 和921600bps。串口缺省波特率为
9600bps。
(注:发 AT 指令时必须回车换行, AT 指令只能在模块未连接状态下才能生效,一旦蓝牙模块与设备连接上,蓝牙模块即进入数据透传模式。\r\n为直接按电脑回车键,如不能按回车键则加\r\n。AT指令不分大小写)
AT指令集
指令
指令
在这里插入图片描述更改密码
波特率

应用链接

一般默认名称是BT-04,默认密码为1234,手机打开蓝牙,在手机app端控制蓝牙发送数据。

单片机程序

使用蓝牙时接受的数据由串口进行通讯传给MCU,所以使用串口中断。
参考程序:

#include <reg52.h> 
#define uchar unsigned char 

void UART_INIT()
{
 SM0 = 0;
 SM1 = 1;//串口工作方式1
 REN = 1;//允许串口接收
 EA = 1;//开总中断
 ES = 1;//开串口中断
 TMOD = 0x20;//8位自动重装模式
 TH1 = 0xfd;
 TL1 = 0xfd;//9600波特率
 TR1 = 1;//启动定时器1
}
//串口中断
void UART_SER() interrupt 4
{
 if(RI)
 {
  RI = 0;//清除接收标志
  switch(SBUF)
  {
   case 0x01:/*执行*/ ; break;//蓝牙接收1事进行的操作
  }
 }
}

void main()
{
 UART_INIT();//串口初始化
 
 while(1)
 {
 }
}
2018-02-22 12:17:38 qq_40277973 阅读数 62332
  • 51单片机综合小项目-第2季第4部分

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

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

不久前开始学习使用蓝牙模块,在模块与51单片机连接的过程中出现了非常多的问题,我想应该也是很多新手和我一样会遇到这样的问题,因此特地写这篇文章,想分享下在学习过程中遇到的问题以及解决方法。


此次学习用到模块是HC-06蓝牙模块,如下图:

该模块某宝有售,价格约为20RMB。某宝上的HC-06有两种,分别是带引脚不带引脚的,建议新手购买带引脚的。我从试验开始到成功,一共使用了四块蓝牙模块。第一次买的是带引脚的,但是模块本身是坏的;第二次买的是不带引脚的,但是由于自身的焊功有限,导致模块损坏,无法使用;第三次是朋友送的蓝牙4.0,由于某些原因无法使用,在此也特别感谢朋友送我蓝牙;第四次购买,就是上图所示的蓝牙,才最终完成了试验。

总结:在某宝购买时,最好货比三家,虽然模块不值钱,但是在购买过程遇到问题会耽误时间,影响开发,非常麻烦。

单片机用了两个,分别是新手常用的开发板还有一个单片机最小模块,两者有什么区别我稍后会说明。

开发板:


单片机最小模块:


我特别标注了两者的晶振,分别为12MHZ11.0594MHZ,就是晶振的不同导致我在学习中问题的发生。以下是学习试验过程。


蓝牙模块的调试:

接线,蓝牙模块的RX接转换模块的TX蓝牙模块的TX接转换模块的RX,如下图所示:


接入电脑,在PC端下载好串口调试助手,软件自搜,此处不再赘述。

附可能会用到的驱动:链接:https://pan.baidu.com/s/1bpYLfCr 密码:yabv

进入到调试助手,其实基本不怎么用调参数了,蓝牙模块基本都默认设置好波特率为9600,因此直接启动软件调试即可。具体调参数的方法可以自行百度其他文章,都有很详细的介绍。

启动串口,成功后左下角显示成功:


发送AT指令,返回OK:


表明串口正常,此时用手机连接蓝牙模块。手机端也是用到调试助手,请自行下载。

搜索蓝牙模块:

备注:我的蓝牙模块此前已经被我改名为Ezio,未改名前默认为HC06。


连接成功:


尝试发送消息hello:


此时在PC端的串口助手上,可以收到来自手机端发送的消息:


在此说明一点,在蓝牙模块上电以后,模块上的LED灯为闪烁状态,此时处于从机模式,与手机成功连接后,LED灯会变为常亮。自此,蓝牙模块调试成功,可以与单片机连接进行试验


蓝牙模块与51单片机接线:

和连接转换模块一样,蓝牙模块的RX连接单片机的TX,蓝牙模块的TX连接单片机的RX,此处说明单片机的RX和TX引脚分别为P3.0和P3.1,如图(图片来自网络):


正确接线后,向单片机中写入程序,程序如下:

#include <reg52.h>

sbit P1_0 = P1^0;	//测试口,可用可不用
sbit P1_3 = P1^3;	//输出口

unsigned char tempbuf;	//存储接收到的信息

/*初始化串口*/
void BlueteethInit()
{
	SCON = 0x50;	//串口模式1,允许接收
	TMOD = 0x20;	//T1工作模式为2,自动重装
	PCON = 0x00;	//波特率不倍增

	REN = 1;

	TH1 = 0xfd;		//设置波特率为9600
	TL1 = 0xfd;

	RI = 0;

	EA = 1;
	ES = 1;

	TR1 = 1;
}

void main()
{
	BlueteethInit();
	P1_0 = 0;
	P1_3 = 0;
	TI = 0;
	while(1)
	{
		if(tempbuf == 0x31)	//可以使用
			P1_3 = 1;
		if(tempbuf == 0)	//不可以使用
			P1_3 = 0;
		if(tempbuf == 'A')	//可以使用
			P1_3 = 1;
		if(tempbuf == 'B')	//可以使用
			P1_3 = 0;
	}
}

void Serial(void) interrupt 4
{
	tempbuf = SBUF;
	RI = 0;	//读标志清零
	SBUF = tempbuf;	//将内容返回到手机端,可在手机查看发送的内容
	while(!TI);
	TI = 0;	//写标志清零
}

该程序为最简单的测试程序,利用蓝牙接收手机发来的信息,控制P1.3口输出高或者低电平,以测试是否正确接收到信息。


第一步,用蓝牙模块与开发板接线,并成功用手机与蓝牙模块连接,尝试发送信息,过程如图所示:


无论是发送数字或者是其他字符,都可以看见返回的是乱码,因此可以知道,单片机接收的也是乱码,故程序中的判断:

while(1)
	{
		if(tempbuf == 0x31)	//可以使用
			P1_3 = 1;
		if(tempbuf == 0)	//不可以使用
			P1_3 = 0;
		if(tempbuf == 'A')	//可以使用
			P1_3 = 1;
		if(tempbuf == 'B')	//可以使用
			P1_3 = 0;
	}

无法正确执行,P1.3口自然也无法根据需要来输出高或者低电平。

第二步,用蓝牙模块与单片机最小模块接线,成功用手机连接收尝试发送信息,如下图所示:


可见,此时返回的内容与发出的内容相同,经测试此时程序也可以正确执行,使用万用表可以检查出P1.3口输出电平的变化,表明此时蓝牙模块可以正常使用。

特别说明:

if(tempbuf == 0x31)	//可以使用
	P1_3 = 1;
if(tempbuf == 0)	//不可以使用
	P1_3 = 0;

当发送数字消息时,应为十六进制,因此在判断时,如接收到1,应判断是否等于0x31,而不是判断是否等于1。此处经过测试,发送1时,判断tempbuf == 0x31,该判断有效;当发送0时,判断tempbuf == 0,判断无效。判断字符加单引号即可。

第三步,为什么使用两个相同的单片机会导致结果不同?这也是困扰了我很久的问题,后来经过检查,才知道原来就是晶振的问题。此处PO一下大神关于晶振的说明,暂时未看懂:https://www.zhihu.com/question/30930577

但可以得出的结论就是,如果使用串口通信,应使用的晶振为11.0594MHZ,否则可能出现乱码的情况。

另附:开发板上的晶振如图:


是可以更换的,某宝也有售,可以根据需要的晶振购买。


51单片机与hc06蓝牙模块

博文 来自: baiyuhancooL
没有更多推荐了,返回首页