一、蓝牙模块的测试
1、连接3.3V、GND、TX、RX在对应的USB转TTL模块调试工具上
2、串口调试助手选择对应串口,收发测试手册中的AT指令,查看示例效果
3、记录保存ASCII与hex收发效果
二、单片机发送AT指令
经常会将蓝牙模块集成在产品中,所以后期是无法再通过串口工具直接对蓝牙模块进行初始化设置。此时,要将蓝牙模块初始化时使用到的AT指令进行封装,单片机上电后运行,通过芯片对蓝牙模块进行初始化。
1、初始化单片机与蓝牙连接的串口。
(蓝牙模块本质为做转发的串口,接收到的数据只处理AT指令,其余做转发)
单片机 串口发送两部分:1、封装好的蓝牙AT指令 发送给蓝牙模块
2、自定义需要通过蓝牙透传的数据 发送给蓝牙模块
蓝牙模块 串口的接收两部分:1、单片机自身发送过来的AT指令
2、单片机发送的需要透传的数据
蓝牙模块 串口的发送两部分:1、响应AT指令(无论是单片机的发送还是串口助手测试时的发送)
发给单片机或串口助手
2、单片机发送的需要透传的数据 发送给其他平台
其他平台 接收:蓝牙转发的 单片机发送的需要透传的数据
其他平台 发送:自定义的数据 发送给蓝牙或蓝牙接收后转发给单片机
(单片机串口与蓝牙模块串口相连,接收到的数据其实相同,但是接收有时间差,重点是谁做解析去响应该内容。蓝牙模块可能是AT指令与数据透传二选一,一旦连接其他平台可能就不能再响应AT指令了,等到无连接或断开连接响应AT指令。)
2、编写AT指令通过串口发送。
(不同蓝牙模块AT指令示例不同)
可以使用sprintf 函数将AT指令字符与波特率十进制拼接起来。
(实际在传输指令时AT指令全为字符,使用sprintf函数 也变相将十进制数转换为字符串。不用考虑十进制与ASCII转换。十六进制等也可以考虑此方法。)
//1、将需要发送的AT指令拼接好
//2、放入串口发送多字节
//注意AT指令后是否要增加回车换行符
//建议在buf后单独赋值 0x0D 0x0A
//否则判断到回车换行可能提前结束导致0x0D 0x0A未发送
sprintf(Tx_buf, "AT+BAUD%d", Baud);
Tx_buf[strlen(Tx_buf)+1] = 0x0D;
Tx_buf[strlen(Tx_buf)+2] = 0x0A;
//多字节发送函数
//data 为指针指向需要发送数据数组
//size 为需要发送的数据个数;回车换行发送接收时均算入字节数
void fun_sendbuf(u8* data, u8 size)
{
u8 i = 0;
for(i=0; i<size; i++)
{
while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
USART_SendData(USART1, data[i]);
}
}
3、通过串口接收到蓝牙回应的数据,进行解析。
(单片机接收到蓝牙回复的指令时,根据每条指令接收内容的不同进行区别解析。可以采用strstr函数或strncmp函数。注意增加头文件。当单片机接收蓝牙数据有延迟时,单片机发送AT指令较快,可能单片机会连续接收好几条指令的回复,这个时候可以采用strstr函数。如果是一收一发可以采用strncmp函数,相比于strstr速度较快。)
//头文件:#include <string.h>
//功能:strstr函数是在字符串str1中查找是否含有字符串str2,
//返回值:如果存在,返回str2在str1中第一次出现的地址;否则返回NULL
//声明:
char *strstr( const char *str1, const char *str2 );
//头文件:#include <string.h>
//功能:比较str1与str2的前n个字符
//返回值:str1 = str2的前n个字符相同,则返回0,
//不同时:str1 > str2 则返回大于0的值
// str1 < str2 则返回小于0的值
//声明:
int strncmp(const char* str1, const char* str2, size_t num)
//解析函数示例
//Rx_buf 为接收数据缓存区
//Baud 为蓝牙波特率以便后续处理
static void BlthCmd_analysis(u8* Rx_buf)
{
if(strstr(Rx_buf,"ERROR") != NULL)//回复有ERROR
{
return;
}
if(strstr(Rx_buf,"OK+BAUD9600") != NULL) Baud = 9600;
else if(strstr(Rx_buf,"OK+BAUD115200") != NULL) Baud = 115200;
}
4、通过串口助手检查单片机发送AT指令格式是否正确以及蓝牙模块是否正确响应。(参考之前记录保存的指令测试)
此时,蓝牙模块和单片机正常通讯完成。
三、蓝牙模块数据透传
蓝牙模块进入数据透传模式,即只做数据的转发,其实就是一个无线的UART串口(主要看使用的蓝牙模块采用什么方式传输)。此时单片机无论发什么数据都将被蓝牙模块转发给其他终端(例如连接此蓝牙的手机),包括上文蓝牙会解析并回复的AT指令。
单片机通过UART发送数据
采用 fun_sendbuf 函数,将要透传的数据通过USART1发送即可。
(积极使用指针等帮助完成数组或结构体的赋值,使用指针更加灵活,也方便整体赋值操作。)
四、蓝牙的服务和属性
1、READ
平台向蓝牙模块进行读取数据的通道,有读取蓝牙名称的UUID。
2、WRITE
平台用来向蓝牙模块写出数据的通道
3、NOTIFY
监听,这是一个通知的通道,蓝牙向平台传输数据的话,平台可以通过监听接收。
一般接收数据通过 NOTIFY 关键字,像蓝牙写数据用 WRITE 关键字。使用特征值必须先进服务注册。(具体参考蓝牙模块手册)
五、手机端与蓝牙通信
可以使用微信小程序 “谷雨蓝牙” ,以下图片仅作参考。
使用过程中遇到的问题
1、搜索不到蓝牙模块
可能原因:
电路设计问题,蓝牙没有工作。(检测方法:万用变直接量蓝牙模块3.3V、GND两端电压是否达到蓝牙工作的正常电压)(5V转3.3V电路中,3.3V使用了10uF电容,较小,直接上电后,测得蓝牙模块电压只有0.8V左右,用镊子触碰蓝牙模块区域的电阻,电压恢复到3.3V,蓝牙模块正常工作,将该电容改为22uF的电容,问题解决。)
蓝牙模块名称奇奇怪怪没有发现。(手机搜索到蓝牙,对应排查设备,对蓝牙模块上电断电比较周围搜索到设备的变化。可以换另个模块测试,看是否都搜索不到。一般情况下,模块会有固定有含义的名称。)
2、串口调试工具发送AT指令无法接收蓝牙返回信息
可能原因:
USB转TTL模块本质其实是在模拟单片机的作用,即和单片机一样属于主机,蓝牙模块为从机。收发具体要看不同的USB转TTL模块。
可以尝试交换TX与RX的连接,看是否接线有问题。也可以通过示波器或逻辑分析仪抓取蓝牙发送引脚的波形,判断蓝牙模块是否有正确数据的发送。
检查串口调试助手的串口设置,波特率、数据位、停止位、校验位是否与蓝牙模块默认值一样。蓝牙模块可能设置的与默认值不同,可以切换其他波特率发送指令测试。
3、单片机发送AT指令蓝牙模块不执行
可能原因:
电路设计影响。可以将单片机与蓝牙模块电路断开(将通讯电路的电阻或其他元器件焊下来),单独检查单片机发送的指令格式。
单片机发送的指令格式不对。部分模块要求按照字符格式发送,且最后要加回车换行,可以在keil中调试查看发送时0x0D与0x0A是否发送。
接线问题。蓝牙模块与单片机TX\RX接反,或线路断路,检查是否有虚焊或连线断开等问题。(测试时,无论使用USB转TTL模块,或单片机收发,尽量避免其他元器件和导线的影响。USB转TTL模块中的GND一定要连接。)
4、单片机发送了AT指令,蓝牙处理了,但是单片机没有接收
可能原因:
单片机运行程序速度快于单片机接收蓝牙数据。在单片机还未接收到蓝牙模块发送的数据时,程序已经运行结束,所以单片机并未执行接收到数据后应该执行的响应。可以考虑在发送蓝牙命令后加50-100ms的延时,具体时长不定。或者将单片机的收发改为一问一答模式(较麻烦)。或者将接收处理函数放在定时器中,去定时的做检查是否有接收。
5、更换电容后问题一重复复现。
可能原因:
蓝牙模块作为较大负载,上电后应缓冲一段时间,如果初始化完成后,立即执行蓝牙相关操作,可能造成蓝牙未成功启动。(解决方法:初始化完成后,增加50-100ms延时,具体延时时间自己测试。)
一些收获
1、蓝牙模块使用。
2、研发时,想清楚逻辑思路,流程图;待完成的功能做记录。