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

    不久前开始学习使用蓝牙模块,在模块与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,否则可能出现乱码的情况。

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


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


    展开全文
  • 蓝牙(Bluetooth):是一种无线技术标准,可实现固定设备、移动设备...蓝牙技术最初由电信巨头爱立信公司于1994年创制,当时是作为RS232数据线的替代方案, 蓝牙可连接多个设备,在与单片机连接使用也得到了广泛应用。

    蓝牙(Bluetooth):是一种无线技术标准,可实现固定设备、移动设备和楼宇个人域网之间的短距离数据交换(使用2.4—2.485GHz的ISM波段的UHF无线电波)。蓝牙技术最初由电信巨头爱立信公司于1994年创制,当时是作为RS232数据线的替代方案, 蓝牙可连接多个设备,在与单片机连接使用也得到了广泛应用。

    1、端口连接
    与单片机串口连接时,两者之间 相互可以读写。例如51给HC-05传递数据,即51向HC-05写数据,HC-05从51读取数据,那么串口连接处51的写端P3.1引脚(TXD)就与HC-05读端(RXD)相连,反之蓝牙向51传递数据时,HC-05写端(TXD)T与51的读端P3.0引脚(RXD)相连,所以通常为以下连接方式即可实现数据传送。
    注意:只有正确连接读写端才能正常通信。
    端口连接
    2、电平选择
    一般情况下,蓝牙不能正常工作的原因出在电源这得可能比较小,大多数蓝牙模块电压范围比较大,像HC-05蓝牙模块一般在3.3~6V,单片机电源都在这个范围内。不过不排除部分3.3V蓝牙,所以在连接电源前一定按照技术手册,连接正确电源,并保证正负极不能接反。
    3、蓝牙配置
    设置决定了蓝牙模块自动连接工作后的角色,主角色(Master)会自动搜索配对连接,从角色(Slave)只被动接受连接,不会主动搜索,回环角色(Loopback),属于被动连接,接收远程蓝牙主设备数据并将数据原样返回给远程蓝牙主设备。如果两个HC05模块要建立连接,其中一个必须设置为主角色,另外一个可以设置为从角色或回环角色,如果一个HC05模块和电脑蓝牙或者手机蓝牙通信,一般电脑或手机可以主动建立连接,所以HC05可以使用从角色,出厂默认也是设置为从角色的。
    4、AT指令
    在控制电平信号下,可以对蓝牙的一些特性参数进行查询课更改。
    AT+XXX? //查询参数XXX
    AT+XXX=mmm //设置参数XXX为mmm
    例如: 命令: AT+NAME?\r\n //查询蓝牙名称
    返回:+NAME:ChunyuY19 //蓝牙名称为:ChunyuY19
    命令: AT+NAME=Xidian\r\n //设置蓝牙名称为:Xidian
    返回: OK //返回提示符:OK
    命令: AT+PSWD?\r\n //查询蓝牙配对密码
    返回:+PSWD:1234 //配对密码为:1234
    命令: AT+ROLE?\r\n //查询蓝牙模式
    返回:+ROLE:0 //0:从角色,1:主角色,2:回环角色
    注意!!每行命令必须以更多AT命令\r\n结尾,更多的AT指令一般技术手册都会给出,活在网上查询。
    5、实现基于STC51单片机的蓝牙与手机通信
    首先,给单片机载入串口通信程序,注意!!一般下载程序时单片机与蓝牙断开,避免因蓝牙占用单片机串口导致程序无法烧写。载入程序后,按照上图给出的读写连接方式连接,并给给单片机及HC-05连接合适电源,一般都用单片机板子上电源。手机端需先在浏览器上搜索并下载“蓝牙串口调试助手”。
    上电后,蓝牙指示灯一般进入快闪状态,即等待蓝牙连接(从模式),用手机搜索并连接单片机上的蓝牙,配对密码默认为1234。配对成功就可以发送数据给蓝牙,如下图,至此基于STC51单片机的蓝牙与手机通信成功。

    因为毕业设计需要用到无线传输,第一次接触蓝牙串口通信,芯片用的HC-05。调试了一天,复制了不少例程,一直无解认为是程序问题。直到看到这篇文章才发现自己引脚就接错了……
    一定记住单片机TX接蓝牙RX,单片机RX接蓝牙TX。一定记住单片机TX接蓝牙RX,单片机RX接蓝牙TX。一定记住单片机TX接蓝牙RX,单片机RX接蓝牙TX。重要的事说三次。

    附基于STM32的HC-05串口通信框架代码

    #include "stm32f10x.h"    
    #include "stm32f10x_rcc.h"    
    #include "stm32f10x_gpio.h"    
    #include "stm32f10x_usart.h"   
    #include "stm32f10x_crc.h"  
    #include "system_stm32f10x.h"   
    #include "stdio.h"   
      
    #define PUTCHAR_PROTOTYPE int __io_putchar(int ch)  
      
    void RCC_Configuration(void);    
    void GPIO_Configuration(void);   
    void USART_Configuration(void);   
    void delay_ms(u16 time);  
    void UART_PutChar(USART_TypeDef* USARTx, uint8_t Data);  
    void UART_PutStr (USART_TypeDef* USARTx, uint8_t *str);
    int Putchar(int c);
      
    int main()  
    {  
        SystemInit();  
        RCC_Configuration();    
        GPIO_Configuration();     
        USART_Configuration();
    		GPIO_SetBits(GPIOB,GPIO_Pin_5);		
        while(1)  
        {  
          UART_PutStr(USART1, "hello world!"); 
    			delay_ms(1000);			
        }   
          
    }  
    void RCC_Configuration(void)      
    {       
      RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA|RCC_APB2Periph_AFIO|RCC_APB2Periph_GPIOB,ENABLE);       
    }     
        
    void GPIO_Configuration(void)      
    {      
      GPIO_InitTypeDef GPIO_InitStructure;      
          
      GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;    
      GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;                
      GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;           
      GPIO_Init(GPIOA, &GPIO_InitStructure);                
          
      GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;           
      GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;        
      GPIO_Init(GPIOA, &GPIO_InitStructure);                 
           
      GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;  
      GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;    
      GPIO_Init(GPIOB, &GPIO_InitStructure);     
    }  
    void USART_Configuration(void)
    {    
           
        USART_InitTypeDef USART_InitStructure;                   
      
        USART_InitStructure.USART_BaudRate = 9600;                    
        USART_InitStructure.USART_WordLength = USART_WordLength_8b;   
        USART_InitStructure.USART_StopBits = USART_StopBits_1;       
        USART_InitStructure.USART_Parity = USART_Parity_No;        
        USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; 
        USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;   
        USART_Init(USART1,&USART_InitStructure);                    
        USART_Cmd(USART1,ENABLE);    
    }  
    void delay_ms(u16 time)       
    {      
      u16 i=0;      
      while(time--)       
      {      
        i=12000;      
        while(i--);      
      }      
    }   
    int Putchar(int c)                                             
    {    
        if (c == '\n'){putchar('\r');}                                
        USART_SendData(USART1,c);                                  
    		while(USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET){};
        return c;                                                       
    }   
    void UART_PutChar(USART_TypeDef* USARTx, uint8_t Data)  
    {  
        USART_SendData(USARTx, Data);  
        while(USART_GetFlagStatus(USARTx, USART_FLAG_TC) == RESET){}  
    }  
    void UART_PutStr (USART_TypeDef* USARTx, uint8_t *str)    
    {    
        while (0 != *str)    
        {    
            UART_PutChar(USARTx, *str);    
            str++; 		
        }    
    }  
    
    

    程序员开源交流QQ群 792272915

    展开全文
  • 手机打开蓝牙可以搜索到蓝牙芯片并可以连接连接码为1234。系统开启后数码管全0,继电器低电平,LED灯不亮。 手机连接到系统后,扫描二维码,得到一串数字后,输入该串密码,得以解锁继电器。输入密码,支持断点续...

    一.实验结果

    手机安装HC-PDA-ANDROID.apk软件后,开启系统。手机打开蓝牙可以搜索到蓝牙芯片并可以连接,连接码为1234。系统开启后数码管全0,继电器低电平,LED灯不亮。

    手机连接到系统后,扫描二维码,得到一串数字后,输入该串密码,得以解锁继电器。输入密码,支持断点续传密码,如本次输入“123”,再次输入“456”,即可完成输入“123456”。输入错误可以选择按键S5清空输入后重新输入。单片机暂时内部设置密码为“12345678”。输入密码的过程中,流水灯会展示输入密码的最后一个字符的ASCII码,用于指示传输过程中字符是否正确被接收。如果输入了错误的密码,则会返回一串错误提示消息,并将数码管清空。输入错误的字符后,流水灯全亮。

    输入正确的密码后,继电器高电平,LED构成回路后点亮,返回给手机端计费信息和成功解锁提示。数码管开始计时,每10毫秒变动一次,8个数码管两个一组,分别显示小时、分钟、秒、十毫秒。当处于计费状态时,手机端发送数据,返回无法发送的字样。按下按键S4后,停止计费,返回到手机端计时时长和本次费用,数码管清零,等待下次解锁。

    二.单片机工作原理

    51单片机有P0、P1、P2、P3四个端口。本次实验将其中P0用于数码管的显示数字,P1用于流水灯的显示,P2端口的第1位(P2^1)接在继电器的DIO端,在单片机内控制高低电平。P3端口的第0位连接蓝牙的TX端,是单片机串口的接收端,P3端口的第1位连接蓝牙的RX端,是单片机串口的发送端。按键S4在内部连接P3端口的第2位,为外部中断0。按键S5在内部连接P3端口的第3位,为外部中断1。开启定时器0,设置好计数器的初始值;外部中断0,1打开;串口中断打开,设置好波特率9600,0,0与蓝牙模块一致即可。

    串口的中断触发后,需要软件清除RI(接收中断)与TI(发送中断)值为0,SBUF存放串口接收数据或发送数据,在C语言代码中赋值相同寄存器,在物理上分为发送和接收,每次1字节缓冲。

    定时器0设置工作方式0,13位计数器,计数到8192。晶振为11.0592MHz,每个机器周期需要12个时钟周期,计数5000次,所以每次进入中断的时间为5000*12/11.0592M=0.00543s,所以每次进入中断时间为0.005秒,数码管需要每10ms进入一次,所以每次加到2的时候,数码管变动。计数器初值为(8192-5000),分别存放入TH0与TL0,高低位。

    外部中断直接设置触发方式ITx=0/1低电平触发或下降沿触发后,开启外部中断,EXx=1后,编写相应中断函数即可。

    蓝牙模块首先按住复位键上电,即进入AT指令模式,对它输入AT指令进行设置名字、串口波特率、主从回环等后,连接到单片机上即可使用。

    三.模块工作原理

    二维码扫描APP:

    通过谷歌开源ZXing库开发了一款安卓APP,并安装到手机,扫描出数据后通过蓝牙模块输入到单片机中。

    扫描后为“12345678”。


    蓝牙模块HC-05:

    TX连接单片机P3.0口,RX连接单片机P3.1口。

    在蓝牙模块连接到单片机上前,首先通过USB-TTL转接器,连接到电脑上后,通过串口调试助手调试。首先进入命令调试模式,输入AT指令,设置模块的参数。

    设置蓝牙的名称,用指令

    AT+name=”LiMou”\r\n          设置蓝牙模块名字为LiMou,方便后续查找。

    设置自动连接模式的串口波特率为9600,用指令

    AT+uart=9600,0,0\r\n             设置波特率为9600,停止位1位,无校验位。

    用于手机与单片机之间的通信者,发送密码到密码上,在接入计费系统时,向手机端发送解锁成功标志,并提供计费标准。解锁失败发送解锁失败指示。

    测试问题:

    如果串口收不到数据,换一个模块。

    如果单片机收到数据错误,调整波特率,通过串口助手调节。

    继电器模块:

           继电器电源连接VCC,GND接地,DIO连接单片机P2.1端口,继电器的模块的开闭表示单车的开闭锁。在继电器下接入一个LED的小灯,用它的亮灭来表示继电器是否上电,是否已经开锁。

           LED:

    连接通过继电器构成回路,长脚(正极)接电源,短脚(负极)接继电器常闭端,继电器公共触点引到地,构成回路,点亮小灯(在继电器DIO高电平时)。


    数码管:

           给P1口送编码即可,0-F的编码分别为0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71。

    其中P2^6为段选通,P2^7为位选通。

    用于计时模块和指示通过蓝牙传入的密码数据。

    按键:

           根据不同按键连接的端口触发不同的中断,编写对应的中断函数。在本系统中,外部中断0通过独立键盘模块按键S4实现,低电平触发,在计费状态下,停止计费,并向手机发送计费结果。

           外部中断1通过独立键盘模块按键S5实现,下降沿触发,当在非计费状态下,清空数码管显示为全0。

          

    流水灯:

    流水灯连接到单片机的P1端口,直接对P1口赋值即可,高电平为灭,低电平亮。用于指示接收数据的ASCII码和错误输入提示,全亮。


    51单片机代码:

    #include "reg51.h"
    #include <intrins.h>
    sbit lock = P2 ^ 1;
    sbit dula = P2 ^ 6;
    sbit wela = P2 ^ 7;
    
    unsigned char digit_led_pointer = 0;
    unsigned char count = 0;
    unsigned char opened = 0;
    
    unsigned char code table[] = { 0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d, 0x07,0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71 };	// 数码管0-f表
    
    unsigned char led[] = { 0, 1, 2, 3, 4, 0, 0, 0 };	//存放8个数码管显示的数字
    
    
    void delay(unsigned int i)		//延迟函数
    {
    	while (--i);
    }
    
    void clear_digit(void)			//清空数码管,全变成0
    {
    	unsigned char i = 0;
    	for (; i < 8; ++i)
    		led[i] = 0;
    	digit_led_pointer = 0;
    }
    
    
    void send(unsigned char dat)	//通过串口发送到蓝牙模块上,蓝牙模块发送出去一个字节的字符
    {
    	ES = 0;
    	SBUF = dat;
    	while (!TI);
    	TI = 0;
    	ES = 1;
    }
    
    void send_str(unsigned char * str)	//通过蓝牙模块发送一个字符串
    {
    	while (*str != '\0')
    		send(*str++);
    }
    
    
    void display(unsigned char dig)		//输入密码阶段的显示,在已经输入的密码后续进行输入
    {
    	led[digit_led_pointer++] = dig;
    	if (digit_led_pointer == 8)
    	{
    		//判断如果输入密码为12345678的话,返回解锁成功
    		if (led[7] == 8 && led[6] == 7 && led[5] == 6 && led[4] == 5 && led[3] == 4 && led[2] == 3 && led[1] == 2 && led[0] == 1 && (opened == 0))
    		{
    			//开锁状态置位
    			opened = 1;
    			lock = 1;	//开锁继电器,高电平,点亮LED
    			clear_digit();
    			send_str("price: 1Yuan/min\n");
    			send_str("lock successfully!\n");
    		}
    		else
    		{
    			delay(10000000);	//输入8位密码后,延迟显示错误密码
    			clear_digit();
    			//解锁失败
    			send_str("lock fail!\n");	//向手机发送提示信息
    		}
    		digit_led_pointer = 0;
    	}
    }
    
    
    //计时模块,每次进入该函数,为10毫秒
    void time_add_10ms()
    {
    	led[7]++;
    	//100进位,每次10毫秒
    	if (led[7] == 10)
    	{
    		led[7] = 0; led[6]++;
    	}
    
    	if (led[6] == 10)
    	{
    		led[6] = 0; led[5]++;
    	}
    
    	//60进位,为秒数
    	if (led[5] == 10)
    	{
    		led[5] = 0; led[4]++;
    	}
    
    	if (led[4] == 6)
    	{
    		led[4] = 0; led[3]++;
    	}
    	//分钟数
    	if (led[3] == 10)
    	{
    		led[3] = 0; led[2]++;
    	}
    
    	if (led[2] == 10)
    	{
    		led[2] = 0; led[1]++;
    	}
    	//小时数,如果100个小时清零
    	if (led[1] == 10)
    	{
    		led[1] = 0; led[0]++;
    	}
    
    	if (led[0] == 10)
    		clear_digit();
    }
    
    //定时器0,为1号中断
    void timer0_ISR(void) interrupt 1
    {
    	if (opened == 1)
    	{
    		//晶振为11.0592MHz,每个机器周期需要12个时钟周期,计数5000次,所以每次进入中断的时间为
    		//	5000*12/11.0592M=0.00543s,每次进入add_10ms函数需要计数2次,所以每次count==2的时候,数码管显示+1
    		TL0 = (8192 - 5000) % 32;
    		TH0 = (8192 - 5000) / 32;
    		count++;
    		if (count == 2)
    		{
    			//达到10ms了
    			time_add_10ms();
    			count = 0;
    		}
    	}
    }
    
    
    
    void uart() interrupt 4
    {
    	unsigned char ky;
    
    	if (RI)
    	{
    		RI = 0;		//需要软件清除标志位
    		ky = SBUF;	//接收到的数据保存到
    		if (opened == 1)
    		{
    			send_str("can't stop, please press the button S4!\n");	//计费状态不接受输入,并返回提示信息。
    			return;
    		}
    		ES = 0;
    		P1 = ky;	//data send to Port 1,通过流水灯显示接收到的数据的ASCII码
    
    		//根据收到的数据进行异常处理或数码管输入
    		if (ky >= '0'&&ky <= '9')
    		{
    			display(ky - '0');
    		}
    		else if (ky >= 'a' && ky < 'f')
    		{
    			display((unsigned char)((ky - 'a') + 10));
    		}
    		else
    		{
    			//错误数据,流水灯全亮。
    			P1 = 0x00;
    		}
    		ES = 1;
    	}
    }
    
    void init_uart()
    {
    	TMOD = 0x25;            //定时器1工作方式2,计数器0工作方式1
    	SCON = 0x50;            //串口工作方式1
    	EA = 1;             //开总中断
    	ES = 1;             //开串口中断
    	TH1 = 0xfd;            //串口波特率9600
    	TL1 = 0xfd;
    	TR1 = 1;             //定时器1工作	
    }
    
    void timer0_init(void)
    {
    	TMOD &= 0xF0;	//计数器0方式0
    	TL0 = (8192 - 5000) % 32;	//计数5000次
    	TH0 = (8192 - 5000) / 32;
    	count = 0;
    	ET0 = 1;		//开启计时器和中断
    	TR0 = 1;		
    }
    
    void init_EX(void)
    {
    	EX0 = 1;
    	IT0 = 0;
    	EX1 = 1;		//开启外部中断 0
    	IT1 = 1;          //设置成低电平触发,1为下降沿触发
    }
    
    //把计时费用转换成字符串发送给手机端
    void num2str(int cost)
    {
    	while (1)
    	{
    		if (cost / 10 != 0)
    			send(cost / 10 + '0');
    		else
    		{
    			send(cost + '0');
    			break;
    		}
    		cost %= 10;
    	}
    }
    
    //外部中断0,S4按下停止计费
    void Ex0_IRQ(void) interrupt 0
    {
    	//不在计费状态下没反应
    	if (opened == 1)
    	{
    
    		int time;
    		opened = 0;
    		send_str("duration:");
    		send(led[0] + '0'); send(led[1] + '0'); send(':');
    		send(led[2] + '0'); send(led[3] + '0'); send(':');
    		send(led[4] + '0'); send(led[5] + '0');
    		send('\n');
    		time = ((led[0] + led[1]) * 60 + (led[2] * 10 + led[3]));
    		send_str("cost: RMB "); num2str(time + 1); send('\n');
    		lock = 0;
    		clear_digit();
    	}
    }
    
    //外部中断1,2号中断,S5按下后清空数码管
    void Ex1_IRQ(void) interrupt 2
    {
    	//不在计费状态下才有反应
    	if (opened == 0)
    	{
    		digit_led_pointer = 0;
    		clear_digit();
    	}
    }
    
    void main()
    {
    	unsigned char num = 0;
    	//初始化
    	init_uart();
    	timer0_init();
    	clear_digit();
    	init_EX();
    	lock = 0;
    	while (1)
    	{
    		//显示数码管
    		for (num = 0; num < 8; ++num)
    		{
    			wela = 1;
    			P0 = _crol_(0xfe, num);	//选中数码管,循环左移
    			wela = 0;
    			dula = 1;
    			P0 = table[led[num]];
    			delay(100);
    			dula = 0;
    		}
    	}
    }


    展开全文
  • 我们平时用蓝牙,一般是单片机的串口与蓝牙的串口连接,实现数据传输,同时,也会用到mcu的其他一些功能,比如IIC,比如定时器,ADC等。但对于一些功能相对较少的产品,或者要求小体积的产品,这时候可以考虑把mcu省...

    技术小白,感谢大家阅读和点赞!使用蓝牙模块也有段时间了,更新……

     

    现在市面上用的蓝牙芯片大部分是ble的了,也就是低功耗透传模式。最近用到蓝牙SOC(片上系统),和大家分享下。

    我们平时用蓝牙,一般是单片机的串口与蓝牙的串口连接,实现数据传输,同时,也会用到mcu的其他一些功能,比如IIC,比如定时器,ADC等。但对于一些功能相对较少的产品,或者要求小体积的产品,这时候可以考虑把mcu省略掉,通过蓝牙直接实现需求,这就是soc。

    目前市面上用的比较多的soc包括TI的CC2640, Nordic的nRF52832和nRF52840,高通的CSR102x,各个品牌的功能和价格都大同小异,开发环境稍有不同,一般能满足可穿戴、物联产品的需求,博主最近在使用CSR102X,  使用后分享经验哈!

     

    以下帖子为原内容

    第一次用蓝牙通讯,现在市场上很多蓝牙模块功能都很强大,如果只是使用,不需要过多了解内部结构原理,只需要设置一些自己用到的参数就行了。

    蓝牙分为传统蓝牙和ble蓝牙,现在大部分用到的都是ble低功耗蓝牙。蓝牙分主从模式,主模式是主动连接其它蓝牙设备,作为主模式可同时连接7个从设备,作为从设备只能被一个主设备连接。

    使用蓝牙模块时,看模块支持哪种电平,有的可以直接接单片机的串口(TTL电平),有的需要经过232芯片连接。选定串口,设置好波特率,写好通讯协议,就可以通讯了。

    以上都是很简单的内容,这里需要跟大家分享的惨痛的教训是关于串口和单片机的隔离。

    我采购的SKY369可以直接连在单片机串口上,而且也可以3.3v供电。所以在设计电路时,我直接把蓝牙模块与单片机的某个串口接在一起,同时从模块引出四个排针,分别是vcc、gnd、rx、tx,注意,此时蓝牙模块已经焊在板子上了,按道理,在电路板不供电的情况下,我用usb转ttl线接四个排针,是可以进行AT指令设置的。但是,此处却出现了很多问题,很多问题!

    问题如下图,我用的是友善串口助手,串口设置好了(可以在电脑计算机右键——设备管理——端口处查询自己用的串口号),蓝牙模块都有初始波特率,可以参考蓝牙模块的手册,数据位校验位停止位也是参考手册。发送和接收都是ASCII,然后点击发送,没反应,再点击,还是没反应。

    排查串口波特率、排查串口线电压、排查tx、rx接没接反,最后发现都没问题,总不可能是蓝牙坏了吧,事实证明现在的模块都很稳定,一般不会出现质量问题,芯片坏了的情况基本不要考虑。

    而且,这个现象并不是每次都出现,而且蓝牙的通讯功能完好,只是设置出问题。同时,这个问题不是每次都会出现,有时候发送AT指令,有的可以实现,有的就会出现00 00 00 00……

    这个现象据蓝牙模块厂家说,是供电问题,换了好几个串口工具和线,确认不是此处的原因。

    经历了蓝牙模块返厂等一系列,发现,单片机和蓝牙模块之间在设置模式下,最好不要直接连接,拿一块板子做测试,把单片机与模块间的线割掉,就再也没出现发送数据没反应的现象了。

    串口连接蓝牙模块时,同时也给单片机供电了,发送给串口的数据同时也发给单片机了,这时就混乱了,处理方法是单片机与蓝牙模块之间加跳线帽,设置时拔开,通讯时插上。

     

    经验教训:外接设备与单片机连接时,最好做好隔离,包括编码器、蓝牙模块、wifi模块等。

    另外,做通讯协议时,单片机通过蓝牙收到数据,处理返回数据时,返回处理函数最好清晰,此串口用作接受处理函数后,就不要再定时器里写其他的定时返回函数,否则会很混乱!收发乱七八糟……这也是血的教训。

    例如,用单片机usart5做蓝牙通讯,蓝牙收到00,返回01,同时,蓝牙还要每隔0.01秒返回02,这样就会乱,导致蓝牙发送接收丢数据。

    同时,还要强调,一定要检查自己的电脑com口有没有问题,博主用自己的台式机一直失败,换了个笔记本莫名其妙好了!

    蓝牙篇就到这。

    展开全文
  • 之前两篇都是在说手机的连接连接方法,和主动配对连接,都是手机手机的操作,做起来还是没问题的,但是最终的目的是与单片机蓝牙模块的通信。   下面是到目前为止尝试的与单片机的通信方法,没有成功,但是...

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

     

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

    展开全文
  • 安卓蓝牙连接单片机蓝牙模块,获取蓝牙模块数据,根据数据进行绘图
  •  手机端打开编写的蓝牙通信软件,与单片机蓝牙串口模块连接,而后,手机通过蓝牙发送读数据命令到单片机单片机开始读取传感器信息,将采集到得传感器信息通过蓝牙发送到手机端,手机端软件接收到后,显示。...
  • 蓝牙(Bluetooth):是一种无线技术标准,可实现固定设备、移动设备和楼宇...蓝牙可连接多个设备,在与单片机连接使用也得到了广泛应用。 1、端口连接 单片机串口连接时,两者之间 相互可以读写。例如51给HC-05传递
  • 蓝牙模块与单片机连接,如何用单片机控制蓝牙模块
  • 蓝牙模块HC05和单片机连接的基本设置。源码在下一个文件中。
  • 这次期末的课程设计做了一个智能灯光控制系统,系统整体的功能不在此赘述,系统主要是要实现下位机同上位机的通信,上位机选用的是Android手机端,下位机是52单片机,通过蓝牙模块实现通信。虽然系统很简单,但还是...
  • 单片机与HC-06蓝牙的设计,连接好电路以后如何发送指令呢?如何让手机接收到数据呢?
  • 最近辛辛苦苦的终于把落下了七八个月的Android蓝牙开发搞完了,其中的痛苦不堪回首啊。感谢那些帮了大忙的老师和同学们!辛苦归辛苦,收获总是有的,...而单片机上的蓝牙模块手机蓝牙连接有点不同,它的UUID号需要
  • 蓝牙模块怎么stm32单片机通信,可以通过蓝牙模块发送数据给单片机
  • 手机是安卓系统的,蓝牙模块采用hc-06,单片机用msp430f149.具体要求是手机发数据能通过蓝牙模块传给单片机然后做出相应的反应(如控制LED的亮灭等)。
  • 要想使用HC-05蓝牙模块进行单片机之间通讯或者单片机蓝牙设备之间通讯,首先要配置好HC-05蓝牙模块的参数。设置好蓝牙的名称、密码、波特率等待。 step1: 连接usb转ttl模块和蓝牙模块,把两个模块的VCC口相连...
  • android手机接收并波形图显示单片机通过蓝牙传输的传感器数据
  • 打算利用蓝牙芯片HC06实现手机和单片机之间的简单通信。工具包括淘宝上淘的stc单片机实验板一块,hc-06蓝牙模块一个,杜邦线,win7电脑一部,安卓手机一部,相关软件:单片机小精灵,串口通讯助手,keil以及单片机...
  • 由于小项目的要求,需要做一个关于蓝牙的手机控制。  一、实现的功能包括:  1)BluetoothActivity.java 文件为主... 3) ScanDeviceActivity.java,能够和蓝牙模块HC-06进行通信,实现配对、连接功能  4)Fir
  • 蓝牙模块HC05和单片机连接的设置测试源代码
1 2 3 4 5 ... 20
收藏数 2,548
精华内容 1,019