本项目代码仓库的网页:董昊旻/rt_nano_bluetoothhttps://gitee.com/donghaomin/rt_nano_bluetooth
目录
一,任务来源
清明节在家呆着无聊就看了一个猪八戒发布的一个简单的任务,但是不会操作那些程序外包的东西,就把这个任务作为一个练手的小项目,打发一下时间,代码的思考,撰写和调试应该花了12个小时,昨天晚上5点到半夜1点(代码写到兴奋的睡不着觉,在地上站着玩了一个半小时手机才睡着),第二天的早上8点到12点。很大一部分时间花在了调试stm32的引脚,中断和串口上了,大学毕业之后就没用过stm32了,也没用过stm32clubMx,所以学习这个工具花了一点时间。
蓝牙模块我手里没有,因为长春疫情的原因我也没办法买,啥时候疫情结束我也不知道,就对着大夏龙雀的蓝牙文档盲写了。
硬件如下
二,任务描述
下边这个图就是他发布的任务,这个东西仁者见仁智者见智,我感觉难度还行,就是比较费时间。
三,代码编写思路
这个要是我大学的时候写,也就是三个小时之内,现在至少需要20个小时,哈哈。不是能力下降了,就是思考的问题不一样了。大学的时候一切以能用主义,写上去能用就OK,现在开始思考代码的可读性,可维护性,稳定性等等...
言归正传,既然思考的问题不一样,所以我们就先分割一下这个项目的功能。
1.带id的数据存储,还可以删除数据(我的理解是不用存储到norflash上,暂存到sram即可)
2.一个下降沿中断的GPIO
3.一路串口(我用的是UART2)
这个是硬件上的基础需求,然后我们聊一下软件的代码逻辑:
(一)数据存储,删减
我没有采取链表的结构来存储数据,第一是没必要这么复杂,它只要几十个数据用不了那么多,写着也麻烦。我就用一个机构体存储了温度数据和连接状态。DATA_SUM是40,这么一写的话就能存储40条数据了,还能存储蓝牙硬件初始化的完成状况和蓝牙连接的状态。
数据增加,你一按动按键的时候就将中断中的flag置1,在主循环的while(1)将数据加进去一个结构体中,将结构体中的useDataCount加一,超出40就报错,让他清空。
删除的话就更简单了,直接让这个大结构体memset为0,就OK了。
typedef enum UsrState {
DATA_UN_USED_Sta = 0x00, // 温度值不正确(不存在)
DATA_USED_Sta = 0x01, // 温度值正确(存在)
DATA_SUM_Sta
}usrsta;
typedef struct D_USR_DATA{
unsigned char id; // 计数标签
float value; // 温度值
usrsta state; // 状态值,温度值是否正确
}d_data;
typedef struct Dong_BLUE_TEMP{
unsigned int blueTooth_state; // 蓝牙启动的状态位
unsigned int blueTooth_connect_state; // 蓝牙连接的状态位,是否断了连接
unsigned char usedDataCount; // 当前存储了多少个温度数据
d_data data[DATA_SUM]; // 温度数据
}d_bule_temp;
(二)蓝牙初始化,蓝牙发送过来的数据进行解析
蓝牙模块初始化:蓝牙模块是通过"AT"指令进行配置的,为了避免有人用没配置过的蓝牙模块进行连接,所以我们就需要每次初始化的时候初始化,避免出现蓝牙模块配置的问题出现的BUG。
蓝牙数据解析:蓝牙发过来的数据是串口发送与接收的,我设置串口的缓冲BUFFER为10Byte,对这10Byte进行解析我们就能知道要做的事情。我用一个结构体来存储这10个Byte。代码里有注释,我就不详细描述了。
有了这个结构体,我们就可以做一个类似于"诊断"的接口,因此代码运行的逻辑就是,接收到串口的数据,在主函数中判断结构体的第一个数据really是不是0x01,是0x01的话就判断function id,在进入到不同得一级分支,在根据sub function id进入到二级分支,再根据datalenght来判断data的6Byte用了几个。具体这个6个Byte表达了那些内容就由,二级分支自己决定了。
/**
* 一级主功能ID
* */
typedef enum FUNCTION_ID {
BLUE_TOOTH_IMG = 0x00, // 蓝牙信息获取
TEMP_DATA_OPT = 0x01, // 温度信息获取,删除
DIAG_CODE_OPT = 0x02, // 诊断ID
FUNCTION_ID_SUM // ID总数
}d_function_id;
typedef struct Dong_BLUETOOTH_RECIVE_CMD{
unsigned char really; // 判断是否当前有数据(你也可以理解为器件ID),有数据的话就是0x01,没有就是0x00,也就是说蓝牙发送数据的时候第一位必须是0x01
d_function_id function_id; // 蓝牙接收数据的主功能id
unsigned char sub_function_id; // 蓝牙接收数据的二级功能id
unsigned char datalenght; // 蓝牙发送过来的数据长度
unsigned char data[6]; // 蓝牙发送过来的数据6个byte
}d_bule_revcmd;
蓝牙模块AT指令的判断我写了一个代码觉得很有趣下边贴出来,加了一个超时的处理。
// 蓝牙状态运行检测
errornum bluetooth_AT_OK(void) {
errornum ret = DIAG_OK;
unsigned int overtime = 0;
while(d_revbuffer.really == 0x00) {
HAL_UART_Transmit_IT(&huart2,(uint8_t *)AT_OK_ACK,strlen(AT_OK_ACK));
rt_thread_mdelay(500);
overtime++;
if(overtime > OVERTIME_TIMES) {
ret = DIAG_OVERTIM_ERR;
rt_kprintf("AT_OK time out\n");
return ret;
}
}
memcpy(locat_data,&d_revbuffer,sizeof(locat_data));
int dong = memcmp(locat_data,AT_OK_ANS,strlen(AT_OK_ANS));
if(dong!=0){
rt_kprintf("AT False\n");
ret = DIAG_BLUE_ERR;
}
else {
rt_kprintf("AT OK\n");
ret = DIAG_OK;
}
return ret;
}
运行的LOG,用串口模拟蓝牙,主机主动发AT,从机在10次请求内发OK,主机就认为蓝牙没问题。
我的代码结构如下图:
diag负责分析和处理蓝牙接收的串口数据。
bluetooth负责蓝牙模块的初始化,检测蓝牙信号强度,连接状态等的函数(连接状态函数我会放到一个一秒周期的task中,连接上了blueTooth_connect_state就置0x01,掉了就0x00),蓝牙模块正不正常的话我就用AT指令测试,能回OK就将blueTooth_connect_state置0x02,不能就0x03,类似于这个意思推之后的逻辑。
hardware就是硬件初始化相关的函数,我初始化了UART2,PC13(LED),PA11(KEY)。
urs_data就是存储温度和其他基础结构体的头文件,初始化函数。
四,总结
代码目前还没写完,目前处于一个初版本demo的状况,但是能用!你可以直接把代码下载下来,用RT Thread Studio编译下载到你的开发板,你也不用担心,开发板不兼容,因为我用的是核心板。