草庐IT

水比赛专用-蓝牙调试器

桃成蹊2.0 2023-04-08 原文

文章目录


做比赛的时候免不了要做一些页面方面的展示,亦或者一些遥控什么的方面的远程启动,常见的无线通信方式如蓝牙,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_ */

有关水比赛专用-蓝牙调试器的更多相关文章

  1. ruby-on-rails - 无法让 rspec、spork 和调试器正常运行 - 2

    GivenIamadumbprogrammerandIamusingrspecandIamusingsporkandIwanttodebug...mmm...let'ssaaay,aspecforPhone.那么,我应该把“require'ruby-debug'”行放在哪里,以便在phone_spec.rb的特定点停止处理?(我所要求的只是一个大而粗的箭头,即使是一个有挑战性的程序员也能看到:-3)我已经尝试了很多位置,除非我没有正确测试它们,否则会发生一些奇怪的事情:在spec_helper.rb中的以下位置:require'rubygems'require'spork'

  2. ruby - JetBrains RubyMine 3.2.4 调试器不工作 - 2

    使用Ruby1.9.2运行IDE提示说需要gemruby​​-debug-base19x并提供安装它。但是,在尝试安装它时会显示消息Failedtoinstallgems.Followinggemswerenotinstalled:C:/ProgramFiles(x86)/JetBrains/RubyMine3.2.4/rb/gems/ruby-debug-base19x-0.11.30.pre2.gem:Errorinstallingruby-debug-base19x-0.11.30.pre2.gem:The'linecache19'nativegemrequiresinstall

  3. ruby-on-rails - 如何调试 cucumber 测试? - 2

    我有:When/^(?:|I)follow"([^"]*)"(?:within"([^"]*)")?$/do|link,selector|with_scope(selector)doclick_link(link)endend我打电话的地方:Background:GivenIamanexistingadminuserWhenIfollow"CLIENTS"我的HTML是这样的:CLIENTS我一直收到这个错误:.F-.F--U-----U(::)failedsteps(::)nolinkwithtitle,idortext'CLIENTS'found(Capybara::Element

  4. ruby - Ruby 是否有类似于 Perl 的 "perl -d"的逐步调试器? - 2

    Ruby是否有逐步调试器,类似于Perl的“perl-d”? 最佳答案 ruby-debug(对于ruby1.8),debugger(对于ruby1.9),byebug(对于ruby​​2.0)以及trepanning系列都有一个-x或--trace选项。在调试器内部,命令setlinetrace将打开或关闭线路跟踪。这是themanualforruby-debug原来的答案已经修改,因为数据噪声文章的链接,唉,不再有效了。还添加了ruby​​-debug的后继者 关于ruby-Ruby

  5. Simulink方法总结和避坑指南(一)——Simulink入门与基本调试方法 - 2

    文章目录一、项目场景二、基本模块原理与调试方法分析——信源部分:三、信号处理部分和显示部分:四、基本的通信链路搭建:四、特殊模块:interpretedMATLABfunction:五、总结和坑点提醒一、项目场景  最近一个任务是使用simulink搭建一个MIMO串扰消除的链路,并用实际收到的数据进行测试,在搭建的过程中也遇到了不少的问题(当然这比vivado里面的debug好不知道多少倍)。准备趁着这个机会,先以一个很基本的通信链路对simulink基础和相关的debug方法进行总结。  在本篇中,主要记录simulink的基本原理和基本的SISO通信传输链路(QPSK方式),计划在下篇记

  6. ruby-on-rails - 使用 Pow 作为服务器在 RubyMine 中调试 - Ruby 2.1.1 + Rails 4 - 2

    我已经开始使用RubyMine6。我正在处理Rails4、Ruby2.1.1项目。我无法找到如何使用Pow作为服务器调试到RubyMine。你能给我指明正确的方向吗? 最佳答案 我能够使用远程调试从RubyMine进行调试。我正在使用RubyMine6、Rails3、Ruby2.1.1。首先创建一个.powenv文件并添加:exportRUBY_DEBUG_PORT=1234exportPOW_WORKERS=1将以下gem添加到您的Gemfile:gem'ruby-debug-ide'gem'debase'创建一个新的初始化器st

  7. ruby-on-rails - 放置调试语句 - 2

    当我编写代码时,我非常频繁地使用“puts”语句进行调试。它允许我查看服务器中发生的情况。在调试代码的时候,不知道是什么原因,我把这些“puts”语句去掉了。这是个好主意还是我应该保留它们以使我的服务器日志更加清晰? 最佳答案 您应该使用记录器而不是puts。使用这种语句:Rails.logger.debug"DEBUG:#{self.inspect}#{caller(0).first}"ifRails.logger.debug?如果您想(几乎)实时查看调试,只需在另一个终端窗口中使用tail命令:tail-Flog/develop

  8. ruby-on-rails - Rails 在记录 200 OK 后在做什么? (调试响应时间慢) - 2

    我试图在我的RubyonRails应用程序中调试一个极其缓慢的请求调用。我已设法根据自己的喜好优化Controller方法,Rails的日志告诉我它已在XX毫秒内完成操作(Completed200OKin5049ms(Views:34.9ms|ActiveRecord:76.3ms)).但是,在加载页面时,在浏览器中实际呈现任何内容之前打印此消息很长;最多约15秒的等待时间。Rackmini-profiler证实了这一点,告诉我GET操作(不计算完成Controller操作所花费的时间)花费了14秒左右。(分析器还确认Controller操作的执行时间约为5秒)。我可以接受Contro

  9. ruby:如何在调试时进入任意函数 - 2

    假设我在调试时停在了点上:defget_databyebug=>@cache||=calculate_dataend而@cache有值,所以step函数calculate_data不会被执行。但是我需要在这个确切的运行时点检查calculate_data内部发生了什么。我可以只执行calculate_data并在控制台输出中查看其结果,但是我可以从调试控制台执行函数并同时进入它?(使用byebug或其他一些调试工具)。目标-是在任意时间检查calculate_data逻辑,特别是当get_data调用时@cache已填充。 最佳答案

  10. ruby-on-rails - 如何在不退出 IRB session 的情况下退出调试器? - 2

    这是一个长期存在的挫败感来源,但也许我遗漏了什么。如果我正在调试,并且我想退出调试器并返回到IRB或Rails控制台,“退出”将不起作用,因为它将退出IRB。“完成”似乎也与继续具有相同的效果。使用“删除”删除断点然后尝试“继续”或“完成”不起作用。有什么想法吗? 最佳答案 至少在byebug中,你可以这样做:evalreturn它具有计算当前函数的return语句的净效果。这有时会奏效,具体取决于调用堆栈的外观。现在虽然这不会删除当前断点....如果您只是想收回控制权,在大多数情况下这会做到这一点,具体取决于您的代码结构。在您的代

随机推荐