水比赛专用-蓝牙调试器


做比赛的时候免不了要做一些页面方面的展示,亦或者一些遥控什么的方面的远程启动,常见的无线通信方式如蓝牙,wifi等是很多大学生竞赛中的常客,因此这里我就把我之前用的很熟的一款蓝牙调试器给分享下,同时也算是做个记录吧!

该调试器是某大佬做的,我只是应用,这里特别感谢这位大佬,提供了这么好用的工具,yyds!,原文链接如下:https://www.jianshu.com/p/1a8262492619

1、蓝牙调试器介绍

此蓝牙调试器为上面提到的大佬一个月开发的,其基于安卓设备,通过安卓设备的蓝牙通信功能实现单片机的无线调试。编写这款软件的目的主要是为了盈利, 嗯,当然是为了广大的单片机开发爱好者,拯救他们于繁琐的调试步骤之中,常用的logo如下:
在这里插入图片描述

这个我们要用的时候直接去应用市场下载即可,经过我个人总结,该调试器支持的功能如下所示:

  • 1、普通的串口收发显示
  • 2、类似普通蓝牙调试器的按钮收发
  • 3、支持自定义功能的界面开发,界面支持摇杆,按钮,开关等常见的设备
  • 4、支持对话框等页面

基本就是有的东西都有了,可以当成一些遥控来用了,这里再次感谢作者!

2、功能体验

首先是这个蓝牙连接这里,这里有个老大的bug,这是我对这个软件唯一不满的地方了,这里要打开定位才能用,对于我一个平时不怎么开定位的人来说,真是太难受了!,这里一定要注意,不然不太能搜到设备!
在这里插入图片描述

下面来看下一个基本的蓝牙调试器应该有的页面普通按钮页面,这里也就是比较常规
在这里插入图片描述
然后是对话框,对话框也是很基本的功能了
在这里插入图片描述
好,下面就是重点这个可以自己编辑的页面了,下面是我之前做的一个项目的页面,基本就是托快快来实现这个页面的编辑,然后就是一些绑定数据了,所以在进行这个页面的编辑之前最重要的还是先设置我们的数据

在这里插入图片描述
这里作者将数据按照类型来分类,因为他们所占的字节不一样,作者这样设置就可以方便的处理数据了,其中数据包的构成作者在这个下图的数据包结构设置一栏中已经进行了说明,是可以很清楚的看到的。
在这里插入图片描述
当然就是接收数据包也是需要编辑的,接收数据包也是跟发送数据包一样的布局设置
在这里插入图片描述
然后这里作者还指出了蓝牙收发一个特点就是这里讲到的延时,蓝牙数据量确实是比较小的,所以发送大量数据自然就会产生一定的延时,这里作者已经提到了,所以这个系统不太适合就是需要迅速反馈的系统的!
在这里插入图片描述
同时作者还提供了一些例程代码,主要是包含轮询,中断还有DMA三种方式进行的收发,源码在我上面贴的作者的链接中可以找到这里不再进行赘述,下面我将用HAL库的方式对他进行移植,以方便我们后续的开发设计!

3、程序移植

首先我们需要定义好用到的串口,串口参数这里保持默认即可!
这里注意:

  • 蓝牙是用AT指令开发的,默认的波特率是9600,可以先用串口助手对波特率进行相应的修改
  • 蓝牙的AT指令类似esp8266的,基本上去淘宝店的资料页都能找到怎么用AT指令进行设置
  • HC-05,HC-06这种的都有一个按键,设置AT指令的时候别忘了按下那个按键

在这里插入图片描述
之后是开启DMA,前面已经说过了,要移植肯定是移植DMA的,移植常规轮询方法和中断方法的都没什么意思,所以这里首选DMA,这才是最高效的方案!
在这里插入图片描述
下面我嫩来认识下.h文件,这个文件其实就是每次修改工程要改的东西了,其实挺重要的就是,这里作者提供了宏参数供我们修改,这里的宏参数要和设置的一样,这样才能确保我们通信的正常,因为这个决定了我们每次数据包的大小!
在这里插入图片描述
下面就是数据接收函数了,数据接收函数就是要注意这个接收函数的这里计算数据长度的这个变量!
在这里插入图片描述
数据发送函数,直接使用官方的库函数即可,不用这么啰嗦,因为CUBEMX都帮我们配置好了
在这里插入图片描述
之后就可以在主函数中调用来实现效果了!

4、实现效果

在初始化中我们需要开启DMA接收这个参数,这样才可以接受DMA的数据,因为DMA开的是循环,所以我们后面就可以不用关注他了
在这里插入图片描述
这里我们要读取数据,需要定义数据结构体,并周期性的读取数据,官方给的要求是每秒不低于十次,这里可以根据自己的需要进行设置!
在这里插入图片描述
将程序下载到开发板,并根据我们之前在手机上位机中设置的摇杆,开关灯,移动摇杆开关等,就可以看到数据变化了!
在这里插入图片描述

5、源码

注意本源码适用于串口2,需要修改为其他串口请根据我前面提到的方法去修改!

valuepack.h

/*
 * valuepack.c
 *
 *  Created on: Mar 31, 2022
 *      Author: LX
 */


#include "valuepack.h"
#include "dma.h"
#include "usart.h"


//DMA下进行的数据传输

unsigned char bits[] = {0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80};

const unsigned int VALUEPACK_INDEX_RANGE = VALUEPACK_BUFFER_SIZE << 3;
//const unsigned short TXPACK_BYTE_SIZE = ((TX_BOOL_NUM + 7) >> 3) + TX_BYTE_NUM + (TX_SHORT_NUM << 1) + (TX_INT_NUM << 2) + (TX_FLOAT_NUM << 2);
//const unsigned short RXPACK_BYTE_SIZE = ((RX_BOOL_NUM + 7) >> 3) + RX_BYTE_NUM + (RX_SHORT_NUM << 1) + (RX_INT_NUM << 2) + (RX_FLOAT_NUM << 2);
#define TXPACK_BYTE_SIZE ((TX_BOOL_NUM + 7) >> 3) + TX_BYTE_NUM + (TX_SHORT_NUM << 1) + (TX_INT_NUM << 2) + (TX_FLOAT_NUM << 2)
#define RXPACK_BYTE_SIZE ((RX_BOOL_NUM + 7) >> 3) + RX_BYTE_NUM + (RX_SHORT_NUM << 1) + (RX_INT_NUM << 2) + (RX_FLOAT_NUM << 2)

unsigned short rx_pack_length = RXPACK_BYTE_SIZE + 3;

long rxIndex = 0;
long rdIndex = 0;
//发送和接收缓冲区
unsigned char vp_rxbuff[VALUEPACK_BUFFER_SIZE];
unsigned char vp_txbuff[TXPACK_BYTE_SIZE + 3];

//extern UART_HandleTypeDef huart2;
//extern DMA_HandleTypeDef hdma_usart2_rx;
//extern DMA_HandleTypeDef hdma_usart2_tx;


//变量标志
unsigned short this_index = 0;
unsigned short last_index = 0;
unsigned short rdi, rdii, idl, idi;
uint32_t idc;
unsigned int err = 0;
unsigned char sum = 0;
unsigned char isok;

//接收数据包解析
unsigned char readValuePack(RxPack *rx_pack_ptr)
{
	isok = 0;
	this_index = VALUEPACK_BUFFER_SIZE - DMA1_Channel6->CNDTR;
	if (this_index < last_index)
		rxIndex += VALUEPACK_BUFFER_SIZE + this_index - last_index;
	else
		rxIndex += this_index - last_index;
	while (rdIndex < (rxIndex - ((rx_pack_length))))
		rdIndex += rx_pack_length;
	while (rdIndex <= (rxIndex - rx_pack_length))
	{

		rdi = rdIndex % VALUEPACK_BUFFER_SIZE;
		rdii = rdi + 1;
		if (vp_rxbuff[rdi] == PACK_HEAD)
		{
			if (vp_rxbuff[(rdi + RXPACK_BYTE_SIZE + 2) % VALUEPACK_BUFFER_SIZE] == PACK_TAIL)
			{
				//  计算校验和
				sum = 0;
				for (short s = 0; s < RXPACK_BYTE_SIZE; s++)
				{
					rdi++;
					if (rdi >= VALUEPACK_BUFFER_SIZE)
						rdi -= VALUEPACK_BUFFER_SIZE;
					sum += vp_rxbuff[rdi];
				}
				rdi++;
				if (rdi >= VALUEPACK_BUFFER_SIZE)
					rdi -= VALUEPACK_BUFFER_SIZE;
				if (sum == vp_rxbuff[rdi])
				{
//  提取数据包数据 一共有五步, bool byte short int float
// 1. bool
#if RX_BOOL_NUM > 0

					idc = (uint32_t)rx_pack_ptr->bools;
					idl = (RX_BOOL_NUM + 7) >> 3;
					for (idi = 0; idi < idl; idi++)
					{
						if (rdii >= VALUEPACK_BUFFER_SIZE)
							rdii -= VALUEPACK_BUFFER_SIZE;
						(*((unsigned char *)idc)) = vp_rxbuff[rdii];
						rdii++;
						idc++;
					}
#endif
// 2.byte
#if RX_BYTE_NUM > 0
					idc = (uint32_t)(rx_pack_ptr->bytes);
					idl = RX_BYTE_NUM;
					for (idi = 0; idi < idl; idi++)
					{
						if (rdii >= VALUEPACK_BUFFER_SIZE)
							rdii -= VALUEPACK_BUFFER_SIZE;
						(*((unsigned char *)idc)) = vp_rxbuff[rdii];
						rdii++;
						idc++;
					}
#endif
// 3.short
#if RX_SHORT_NUM > 0
					idc = (uint32_t)(rx_pack_ptr->shorts);
					idl = RX_SHORT_NUM << 1;
					for (idi = 0; idi < idl; idi++)
					{
						if (rdii >= VALUEPACK_BUFFER_SIZE)
							rdii -= VALUEPACK_BUFFER_SIZE;
						(*((unsigned char *)idc)) = vp_rxbuff[rdii];
						rdii++;
						idc++;
					}
#endif
// 4.int
#if RX_INT_NUM > 0
					idc = (uint32_t)(&(rx_pack_ptr->integers[0]));
					idl = RX_INT_NUM << 2;
					for (idi = 0; idi < idl; idi++)
					{
						if (rdii >= VALUEPACK_BUFFER_SIZE)
							rdii -= VALUEPACK_BUFFER_SIZE;
						(*((unsigned char *)idc)) = vp_rxbuff[rdii];
						rdii++;
						idc++;
					}
#endif
// 5.float
#if RX_FLOAT_NUM > 0
					idc = (uint32_t)(&(rx_pack_ptr->floats[0]));
					idl = RX_FLOAT_NUM << 2;
					for (idi = 0; idi < idl; idi++)
					{
						if (rdii >= VALUEPACK_BUFFER_SIZE)
							rdii -= VALUEPACK_BUFFER_SIZE;
						(*((unsigned char *)idc)) = vp_rxbuff[rdii];
						rdii++;
						idc++;
					}
#endif
					err = rdii;
					rdIndex += rx_pack_length;
					isok = 1;
				}
				else
				{
					rdIndex++;
					err++;
				}
			}
			else
			{
				rdIndex++;
				err++;
			}
		}
		else
		{
			rdIndex++;
			err++;
		}
	}
	last_index = this_index;
	return isok;
}

void sendBuffer(unsigned char *p, unsigned short length)
{
//	DMA_DeInit(DMA1_Channel4);
//	dma.DMA_DIR = DMA_DIR_PeripheralDST;
//	dma.DMA_M2M = DMA_M2M_Disable;
//	dma.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
//	dma.DMA_PeripheralBaseAddr = (uint32_t) & (USART1->DR);
//	dma.DMA_Priority = DMA_Priority_High;
//	dma.DMA_BufferSize = length;
//	dma.DMA_MemoryBaseAddr = (uint32_t)p;
//	dma.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
//	dma.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
//	dma.DMA_MemoryInc = DMA_MemoryInc_Enable;
//	dma.DMA_Mode = DMA_Mode_Normal;
//	DMA_Init(DMA1_Channel4, &dma);
//	DMA_Cmd(DMA1_Channel4, ENABLE);

	HAL_UART_Transmit_DMA(&huart2,p,length);
}
unsigned short loop;
unsigned char valuepack_tx_bit_index;
unsigned char valuepack_tx_index;
void sendValuePack(TxPack *tx_pack_ptr)
{
	int i;
	vp_txbuff[0] = 0xa5;
	sum = 0;
	//  由于结构体中不同类型的变量在内存空间的排布不是严格对齐的,中间嵌有无效字节,因此需要特殊处理
	valuepack_tx_bit_index = 0;
	valuepack_tx_index = 1;
#if TX_BOOL_NUM > 0
	for (loop = 0; loop < TX_BOOL_NUM; loop++)
	{
		if (tx_pack_ptr->bools[loop])
			vp_txbuff[valuepack_tx_index] |= 0x01 << valuepack_tx_bit_index;
		else
			vp_txbuff[valuepack_tx_index] &= ~(0x01 << valuepack_tx_bit_index);

		valuepack_tx_bit_index++;
		if (valuepack_tx_bit_index >= 8)
		{
			valuepack_tx_bit_index = 0;
			valuepack_tx_index++;
		}
	}
	if (valuepack_tx_bit_index != 0)
		valuepack_tx_index++;
#endif
#if TX_BYTE_NUM > 0

	for (loop = 0; loop < TX_BYTE_NUM; loop++)
	{
		vp_txbuff[valuepack_tx_index] = tx_pack_ptr->bytes[loop];
		valuepack_tx_index++;
	}

#endif
#if TX_SHORT_NUM > 0
	for (loop = 0; loop < TX_SHORT_NUM; loop++)
	{
		vp_txbuff[valuepack_tx_index] = tx_pack_ptr->shorts[loop] & 0xff;
		vp_txbuff[valuepack_tx_index + 1] = tx_pack_ptr->shorts[loop] >> 8;
		valuepack_tx_index += 2;
	}
#endif
#if TX_INT_NUM > 0
	for (loop = 0; loop < TX_INT_NUM; loop++)
	{
		i = tx_pack_ptr->integers[loop];

		vp_txbuff[valuepack_tx_index] = i & 0xff;
		vp_txbuff[valuepack_tx_index + 1] = (i >> 8) & 0xff;
		vp_txbuff[valuepack_tx_index + 2] = (i >> 16) & 0xff;
		vp_txbuff[valuepack_tx_index + 3] = (i >> 24) & 0xff;

		valuepack_tx_index += 4;
	}
#endif
#if TX_FLOAT_NUM > 0
	for (loop = 0; loop < TX_FLOAT_NUM; loop++)
	{
		i = *(int *)(&(tx_pack_ptr->floats[loop]));

		vp_txbuff[valuepack_tx_index] = i & 0xff;
		vp_txbuff[valuepack_tx_index + 1] = (i >> 8) & 0xff;
		vp_txbuff[valuepack_tx_index + 2] = (i >> 16) & 0xff;
		vp_txbuff[valuepack_tx_index + 3] = (i >> 24) & 0xff;

		valuepack_tx_index += 4;
	}
#endif
	for (unsigned short d = 1; d <= TXPACK_BYTE_SIZE; d++)
		sum += vp_txbuff[d];
	vp_txbuff[TXPACK_BYTE_SIZE + 1] = sum;
	vp_txbuff[TXPACK_BYTE_SIZE + 2] = 0x5a;
	sendBuffer(vp_txbuff, TXPACK_BYTE_SIZE + 3);
}

valuepack.h

/*
 * valuepack.h
 *
 *  Created on: Mar 31, 2022
 *      Author: LX
 */

#ifndef VALUEPACK_H_
#define VALUEPACK_H_

#include "main.h"

// 本程序通过DMA和USART 进行数据包的接收和发送
// 接收的数据自动写入到buffer中,通过定时调用readValuePack()函数来解析,定时间隔建议在10ms以内。
// 数据发送也采用DMA

// 1.指定接收缓冲区的大小 --------------------------------------------------------------------------------
// 一般需要512字节以上,需要根据实际接收数据的速度和proc函数的频率考虑。
#define VALUEPACK_BUFFER_SIZE 1024

// 2.指定发送到手机的数据包的结构----------在发送时会自动额外在前后加上包头,包尾和校验和数据,因此会多出3个字节
// 根据实际需要的变量,定义数据包中 bool byte short int float 五种类型的数目

#define TX_BOOL_NUM 0
#define TX_BYTE_NUM 0
#define TX_SHORT_NUM 3
#define TX_INT_NUM 0
#define TX_FLOAT_NUM 1

// 3.指定接收数据包的结构-----------------------------------------------------------------------------------
// 根据实际需要的变量,定义数据包中 bool byte short int float 五种类型的数目

#define RX_BOOL_NUM 0
#define RX_BYTE_NUM 4
#define RX_SHORT_NUM 3
#define RX_INT_NUM 0
#define RX_FLOAT_NUM 0

typedef struct
{
#if TX_BOOL_NUM > 0
	unsigned char bools[TX_BOOL_NUM];
#endif

#if TX_BYTE_NUM > 0
	char bytes[TX_BYTE_NUM];
#endif

#if TX_SHORT_NUM > 0
	short shorts[TX_SHORT_NUM];
#endif

#if TX_INT_NUM > 0
	int integers[TX_INT_NUM];
#endif

#if TX_FLOAT_NUM > 0
	float floats[TX_FLOAT_NUM];
#endif
	char space; // 无意义,只为了不让结构体为空,结构体为空会报错。
} TxPack;
typedef struct
{
#if RX_BOOL_NUM > 0
	unsigned char bools[RX_BOOL_NUM];
#endif

#if RX_BYTE_NUM > 0
	char bytes[RX_BYTE_NUM];
#endif

#if RX_SHORT_NUM > 0
	short shorts[RX_SHORT_NUM];
#endif

#if RX_INT_NUM > 0
	int integers[RX_INT_NUM];
#endif

#if RX_FLOAT_NUM > 0
	float floats[RX_FLOAT_NUM];
#endif
	char space; // 无意义,只为了不让结构体为空,结构体为空会报错。
} RxPack;
// 初始化 valuepack 包括一些必要的硬件外设配置

//void initValuePack(int baudrate);

// 需要保证至少每秒执行10次该函数
// 该函数的主要过程是先解析接收的缓冲区,如果接收到完整的RX数据包,则解析RX数据包中的数据,然后开始串口发送TX数据包 。
// 接收到数据包时 返回 1 ,否则返回 0
unsigned char readValuePack(RxPack *rx_pack_ptr);
// 发送数据包
void sendValuePack(TxPack *tx_pack_ptr);

#define PACK_HEAD 0xa5
#define PACK_TAIL 0x5a

#endif /* VALUEPACK_H_ */