朱兆祺ForARM步步为营单片机项目篇:基于MT254x蓝牙4.0(蓝牙4.0驱动程序)
在上一节已经带着大家初识蓝牙4.0模块,这一堂课给大家展示下蓝牙4.0在朱兆祺51单片机学习板的驱动程序。
为了没有接触过蓝牙4.0的工程师照样可以使用蓝牙4.0进行串口透传功能的实现,馒头科技蓝牙4.0的蓝牙4.0模块实现主从一体、串口透传的固件。蓝牙4.0的详细介绍(AT指令集、使用手册等等请查看:https://bbs.elecfans.com/jishu_431223_1_1.html)。
/*
*使用朱兆祺51单片机控制蓝牙4.0的头文件
*/
#include "config.h"
#include "uart.h"
#include "delay.h"
#ifndef __COMMAND_H__
#define __COMMAND_H__
/*
* 注意这个联合体的使用,不要觉得C语言中的结构体和联合体、指针等没有使用,使用这些东西可以大大提高你程序的效率
*/
typedef enum
{
AT_CALL, //非查询非设置命令
AT_GET, //查询命令
AT_SET, //设置命令
} AT_t;
/*
* 使用这个联合体包含基本常用的AT指令
*/
typedef enum
{
AT_TEST,
AT_HELP,
AT_VERS,
AT_NAME,
AT_RENEW,
AT_RESET,
AT_ROLE,
AT_NOTI,
AT_IMME,
AT_START,
AT_TYPE,
AT_BAUD,
AT_FLOW,
AT_PARI,
AT_STOP,
AT_ADVI,
AT_POWE,
AT_SCAN,
AT_SHOW,
AT_CON,
AT_CONN,
AT_CONNL,
AT_ISCON,
AT_DISCON,
AT_CLEAR,
AT_RADD,
AT_SAVE,
AT_PASS,
AT_MAC,
AT_RSSI,
AT_TEMP,
AT_LED,
AT_PDIR,
AT_PDAT,
AT_PWRM,
AT_SLEEP,
AT_WAKE,
AT_BATC,
AT_BATT,
} AT_cmd;
/*
* 变量的声明,注意结构体的使用
*/
struct ATCmdIndex
{
AT_cmd cmd;
uint8 *ucpCmdPrefix;
};
/*
* WK管脚的使用:
* 1.管脚电平的改变可以唤醒
* 2.管脚电平决定当前工作模式,高电平——AT指令;低电平——透传
*/
***it WK = P2^0;
/*
* 模块处于远控模式
* do……while(1)的这个用法我在C语言技术公开课有详细讲解,请查看
*/
#define ATMode()
do
{
WK = 1;
}while(0)
/*
* 模块处于透传模式
*/
#define TTMode()
do
{
WK = 0;
}while(0)
/* AT指令的发送 */
extern int8 ATCmdSend(AT_cmd cmd, AT_t type, uint8 *ucpPara);
/* AT指令数据的接收 */
extern int8 ATGetPara(AT_t type, uint8 **ucppParaAddr, uint8 *ucParaLen);
#endif /* end of file */
我们再来看一下功能的具体实现。
/*************************************************
* 文件名称:command.c
* 文件功能:发送AT指令实现文件
* 文件作者:Lawrence
* 创建时间:2014-04-09
* 修改时间:
* 文件版本:V0.3
*************************************************/
#include "command.h"
/*
** AT指令表
** 索引号 , 指令前缀
*/
code struct ATCmdIndex ATCmdTable[] =
{
/*================常用指令==================*/
{AT_TEST, "AT+"}, //测试
{AT_HELP, "AT+HELP"}, //帮助查询
{AT_VERS, "AT+VERS"}, //软件版本查询
{AT_NAME, "AT+NAME"}, //查询/设置模块名称,最长允许15个字符
{AT_RENEW, "AT+RENEW"}, //恢复出厂设置
{AT_RESET, "AT+RESET"}, //重启模块
{AT_ROLE, "AT+ROLE"}, //查询/设置主从模式
{AT_NOTI, "AT+NOTI"}, //查询/设置是否把当前连接状态通知给用户
{AT_IMME, "AT+IMME"}, //查询/设置模块工作方式
{AT_START, "AT+START"}, //开始工作
{AT_TYPE, "AT+TYPE"}, //查询/设置模块密码验证类型
/*================串口指令==================*/
{AT_BAUD, "AT+BAUD"}, //查询/设置波特率
{AT_FLOW, "AT+FLOW"}, //查询/设置硬件流控
{AT_PARI, "AT+PARI"}, //查询/设置串口校验
{AT_STOP, "AT+STOP"}, //查询/设置停止位
/*================从机指令==================*/
{AT_ADVI, "AT+ADVI"}, //查询/设置广播时间间隔
{AT_POWE, "AT+POWE"}, //查询/设置模块发射功率
/*================主机指令==================*/
{AT_SCAN, "AT+SCAN"}, //搜索可连接模块
{AT_SHOW, "AT+SHOW"}, //查询/设置模块在手动搜索时是否返回名字
{AT_CON, "AT+CON"}, //连接指定蓝牙MAC地址的从模块
{AT_CONN, "AT+CONN"}, //连接搜索返回的模块
{AT_CONNL, "AT+CONNL"}, //连接最后一次连接成功的从模块
/*==============连接相关指令================*/
{AT_ISCON, "AT+ISCON"}, //查询当前模块是否处于连接状态
{AT_DISCON, "AT+DISCON"}, //断开连接
{AT_CLEAR, "AT+CLEAR"}, //清除模块配对信息
{AT_RADD, "AT+RADD"}, //查询成功连接过的远程主机地址
{AT_SAVE, "AT+SAVE"}, //查询/设置模块成功连接后是否保存连接地址
/*============模块信息相关指令==============*/
{AT_PASS, "AT+PASS"}, //查询/设置配对密码
{AT_MAC, "AT+MAC"}, //查询本机MAC地址
{AT_RSSI, "AT+RSSI"}, //读取 RSSI 信号值
{AT_TEMP, "AT+TEMP"}, //查询模块温度
/*===============IO监控指令================*/
{AT_LED, "AT+LED"}, //查询/设置LED输出状态
{AT_PDIR, "AT+PDIR"}, //查询/设置PIO口的输入输出方向
{AT_PDAT, "AT+PDAT"}, //查询/设置PIO口的输入输出状态
/*=============电源管理指令================*/
{AT_PWRM, "AT+PWRM"}, //查询/设置模块进入自动休眠的时间
{AT_SLEEP, "AT+SLEEP"}, //让模块进行休眠状态
{AT_WAKE, "AT+WAKE"}, //将模块从休眠状态唤醒
{AT_BATC, "AT+BATC"}, //查询/设置电量监控开关
{AT_BATT, "AT+BATT"}, //查询电量信息
};
/*******************************************************
* 函数名称:FindCmd
* 函数功能:在AT指令表中查找目标指令
* 入口参数:ucCmd,指令索引号;
* 出口参数:uint8 *,找到的该指令的前缀首地址
* 函数说明:
******************************************************/
static uint8 *FindCmd(AT_cmd cmd)
{
uint8 ucSize = sizeof(ATCmdTable) / sizeof(ATCmdTable[0]);
uint8 i;
for(i = 0; i
{
if(cmd == ATCmdTable.cmd)
{
return ATCmdTable.ucpCmdPrefix; //返回这个AT指令的前缀首地址
}
}
return NULL;
}
/*******************************************************
* 函数名称:ATCmdSend
* 函数功能:发送AT指令
* 入口参数: cmd,指令索引号;
type,指令类型;
ucpPara,指令携带的参数,无参数请设置为NULL;
* 出口参数:__OK,成功;__ERR,失败
* 函数说明:
******************************************************/
int8 ATCmdSend(AT_cmd cmd, AT_t type, uint8 *ucpPara)
{
#define CMDLEN (64)
uint8 ucCmdStr[CMDLEN] = {0}; //AT指令缓冲区
uint8 *ucpSrc;
ucpSrc = FindCmd(cmd); //查找该指令,取得前缀
if(NULL == ucpSrc)
{
return __ERR; //没有找到,命令无效
}
strcpy(ucCmdStr, ucpSrc); //将命令拷贝到缓冲区
if(AT_GET == type)
{
strcat(ucCmdStr, "?"); //查询命令,则在指令后面加上问号
}
else if(AT_SET == type)
{
strcat(ucCmdStr, "["); //设置命令,把要设置的参数填充到缓冲区
strcat(ucCmdStr, ucpPara);
strcat(ucCmdStr, "]");
}
else
{
_nop_(); //非查询非设置命令,不带问号不带参数
}
UARTSend(ucCmdStr, strlen(ucCmdStr)); //将指令通过串口发送给模块
MyBzero(ucCmdStr, CMDLEN);
/*
**在波特率为9600的情况下,传输一个字节需要0.96ms,
**由于串口接收是通过中断来完成,主线程序需要延时等待中断,
**而一般的AT指令的回复的数据量都不大(不超过64),除了AT+SCAN这条指令,
**所以一般情况下延时不需要很长。
*/
UARTStartRecv(); //串口开始接收数据
if(AT_SCAN == cmd)
{
//Delay100ms(40); //扫描过程需要4秒
//Delay1ms(64); //特殊命令延时特殊处理
while(NULL == strstr(ucRecvBuf, "OK+END")); //等待“OK+END”结束回复
}
else
{
//Delay1ms(32);
Delay1ms(100);
}
UARTStopRecv(); //串口停止接收数据
return __OK;
}
/*******************************************************
* 函数名称:ATGetPara
* 函数功能:从串口接收缓冲区取出AT指令的回复参数
* 入口参数: ucType,指令类型;
ucppParaAddr,存储本函数获取到回复参数首地址;
ucParaLen,存储本函数获取到回复参数长度;
* 出口参数:__OK,成功;__ERR,失败
* 函数说明:
******************************************************/
int8 ATGetPara(AT_t type, uint8 **ucppParaAddr, uint8 *ucpParaLen)
{
if((NULL == ucppParaAddr) || (NULL == ucpParaLen))
{
return __ERR;
}
if(AT_CALL == type)
{
*ucppParaAddr = ucRecvBuf;
*ucpParaLen = strlen(ucRecvBuf); //对于CALL指令,直接返回原始数据,不进行预处理
}
else if(AT_GET == type)
{
if(strncmp(ucRecvBuf, "OK+GET:", 7) == 0) //进一步校验回复格式是否正确
{
*ucppParaAddr = ucRecvBuf + 7; //取出para的首地址
*ucpParaLen = strlen(ucRecvBuf) - 7 - 2; //略去OK+GET: 和 rn
}
else
{
return __ERR;
}
}
else if(AT_SET == type)
{
if(strncmp(ucRecvBuf, "OK+SET:", 7) == 0)
{
*ucppParaAddr = ucRecvBuf + 7; //取出para的首地址
*ucpParaLen = strlen(ucRecvBuf) - 7 - 2; //略去OK+SET: 和 rn
}
else if(strncmp(ucRecvBuf, "OK+CONN:", 8) == 0) //考虑AT+CON[para1]和AT+CONN[para1]这两条指令的特殊性,需特殊处理
{
*ucppParaAddr = ucRecvBuf + 8; //取出para的首地址
*ucpParaLen = strlen(ucRecvBuf) - 8 - 2; //略去OK+CONN: 和 rn
}
else
{
return __ERR;
}
}
else
{
return __ERR;
}
return __OK;
}
功能进一步实现请看下一节,精彩在更新。
效果预先看:
|