【STM32外设系列】GPS定位模块(ATGM336H)

🎀 文章作者:二土电子

🌸 关注公众号获取更多资料!

🐸 期待大家一起学习交流!


文章目录

  • 一、GPS模块简介
  • 二、使用方法
    • 2.1 引脚介绍
    • 2.2 数据帧介绍
    • 2.3 关于不同的启动方式
  • 三、前置知识
    • 3.1 strstr函数
    • 3.2 memset函数
    • 3.3 memcpy函数
    • 3.4 strtod函数
  • 四、程序设计
    • 4.1 串口初始化程序
    • 4.2 串口1接收中断服务函数
    • 4.3 帧信息解析
    • 4.4 经纬度数据解析转换

一、GPS模块简介

  我们在做一些项目时有时会需要进行GPS定位,获取自身的经纬度信息,这里使用的是中科微电子的GPS模块ATGM336H,带陶瓷天线。

ATGM336H模块
  该模块可以实现GPS定位,返回定位点的UTC时间和经纬度信息。

  UTC时间是全世界公用的时间,我们用的北京时间比UTC时间快8个小时。

  在使用时必须注意以下几点

  • 必须在室外空旷地带才能进行定位;
  • 定位是陶瓷天线上的小圆点必须朝上,上方不要有遮挡物;
  • 楼间距也可能会影响定位,楼间距较小的地方可能定位失败;

二、使用方法

2.1 引脚介绍

  ATGM336H使用3.3V或者5V供电,利用串口将GPS信息发送出来,我们简单地介绍一下它的几个引脚

引脚功能
VCC电源正极(3.3V或5V)
GND电源负极
TXD串口发送
RXD串口接收

  PPS引脚这里没有用到,就不再做介绍了,有兴趣的小伙伴可以自行搜索一下它的用途。

2.2 数据帧介绍

  ATGM336H利用串口发送定位信息给主控芯片,串口波特率为9600,我们最开始可以先用USB-TTL连接ATGM336H,到空旷地带观察一下它的输出信息,定位成功时接收到的信息如下

定位成功时的接收信息

  我们可以看到,ATGM336H一次会返回许多信息,其实我们只需要关注其中的“GNRMC”这条信息即可,我们简单看一下这条信息。

$GNRMC,015135.000,A,4159.65553,N,12136.79345,E,0.52,0.00,191123,,,A*7F
  • 消息ID —— $GNRMC
  • 定位点的UTC时间 —— 015135.000
  • 定位状态 —— A:定位;V:导航(我们进行定位时,如果该位为A表示数据有效,该位为V表示数据无效)
  • 纬度 —— 4159.65553
  • 纬度方向 —— N
  • 经度 —— 12136.79345
  • 经度方向 —— E

  需要注意的是,这里的经纬度并不能直接拿来在地图上搜索定位,而是需要进行数据转换之后才可以,关于数据抓换,后续的程序设计会详细介绍。

2.3 关于不同的启动方式

  ATGM336H有三种不同的启动模式,不同模式定位成功所需的时间是不同的,我们这里来简单描述一下这几种启动模式

  • 冷启动
    冷启动是指在一个陌生的环境下启动 GPS 直到 GPS 和周围卫星联系并且计算出坐标的启动过程。比如我们初次使用,电池耗尽导致星历信息丢失,或者关机状态下将接收机移动 1000 公里以上距离再启动都属于冷启动,冷启动大概需要等待1~2分钟才能定位成功。
  • 温启动
    温启动是指距离上次定位时间超过 2 个小时的启动,搜星定位时间介于冷启动和热启动之间。
  • 热启动
    热启动是指在上次关机的地方没有过多移动启动 GPS,但距离上次定位时间必须小于 2 个小时。

三、前置知识

  我们在介绍程序设计之前先介绍一下一些必备的前置知识,关于STM32串口的配置这里就不再详细介绍了,具体可以到博主的STM32俗称笔记专栏串口篇查看。这里着重介绍几个C语言中的函数。

3.1 strstr函数

  strstr函数原型为

char *strstr( const char *str1, const char *str2 );

  该函数是在字符串str1中查找是否含有字符串str2,如果存在,返回str2在str1中第一次出现的地址(指针);否则返回NULL。使用strstr函数时需要包含头文件<string.h>。

  值得注意的是,实际输入变量都是指针,如果我们稍加设计,能得到循环查找分隔符的效果,具体可以看后面在解析接收帧信息时的程序设计,这里只介绍一下它的基本用法。

3.2 memset函数

  memset函数的函数原型为

void *memset(void *s, int c, size_t n); 

  memset是一个初始化函数,作用是将某一块内存中的全部设置为指定的值。

  • s指向要填充的内存块。
  • c是要被设置的值。
  • n是要被设置该值的字符数。
  • 返回类型是一个指向存储区s的指针。

3.3 memcpy函数

  memcpy函数的函数原型为

void *memcpy(void*dest, const void *src, size_t n);

  该函数的功能是将由src指向地址为起始地址的连续n个字节的数据复制到以destin指向地址为起始地址的空间内。

3.4 strtod函数

  strtod函数的函数原型为

double strtod(const char *nptr, char **endptr);

  strtod函数会检查输入的nptr字符串,跳过前面的空格字符,直到遇上数字或正负符号才开始做转换,到出现非数字或字符串结束时(‘\0’)才结束转换,并将结果返回,返回值是一个double型数值。

  若endptr不为NULL,则会将遇到不合条件而终止的nptr中的字符指针由endptr传回。参数nptr字符串可包含正负号、小数点或E(e)来表示指数部分。

  使用该函数时需要包含头文件。

四、程序设计

  下面我们来进行程序设计,我们用串口1来接收ATGM336H发送来的信息,用串口2将我们解析处理后的GPS信息发送给上位机。

4.1 串口初始化程序

  这里用到了两个串口,我们封装一个串口初始化函数用来初始化串口

/*
 *==============================================================================
 *函数名称:uart_init
 *函数功能:初始化USART
 *输入参数:UARTx:串口几;bound:波特率
 *返回值:无
 *备  注:可以修改成输入初始化哪个USART
 *==============================================================================
*/
void uart_init(UART_TypeDef UARTx,u32 bound)
{
	// 相关结构体定义
	GPIO_InitTypeDef GPIO_InitStructure;
	USART_InitTypeDef USART_InitStructure;
	NVIC_InitTypeDef NVIC_InitStructure;
	
	switch (UARTx)
	{
		case 0:
			// 使能USART1,GPIOA时钟
			RCC_APB2PeriphClockCmd (RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA, ENABLE);	

			// USART1_TX   GPIOA.9
			GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;   // PA.9
			GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
			GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;   // 复用推挽输出
			GPIO_Init(GPIOA, &GPIO_InitStructure);   // 初始化GPIOA.9

			// USART1_RX	  GPIOA.10初始化
			GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;   // PA10
			GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;   // 浮空输入
			GPIO_Init(GPIOA, &GPIO_InitStructure);   // 初始化GPIOA.10  

			// Usart1 NVIC 配置
			NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
			NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ;   // 抢占优先级3
			NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;   // 子优先级3
			NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;   // IRQ通道使能
			NVIC_Init(&NVIC_InitStructure);   // 根据指定的参数初始化VIC寄存器

			// USART 初始化设置
			USART_InitStructure.USART_BaudRate = bound;   // 串口波特率
			USART_InitStructure.USART_WordLength = USART_WordLength_8b;   // 字长为8位数据格式
			USART_InitStructure.USART_StopBits = USART_StopBits_1;   // 一个停止位
			USART_InitStructure.USART_Parity = USART_Parity_No;   // 无奇偶校验位
			// 无硬件数据流控制
			USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
			USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;   // 收发模式
			USART_Init(USART1, &USART_InitStructure);   // 初始化串口1
			
			USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);   // 开启串口接收中断
			USART_ITConfig(USART1, USART_IT_IDLE, ENABLE);   // 使能空闲中断
			
			USART_Cmd(USART1, ENABLE);   // 使能串口1
			
			break;
			
		case 1:
			// 使能USART2,GPIOA时钟
			RCC_APB1PeriphClockCmd (RCC_APB1Periph_USART2 | RCC_APB2Periph_GPIOA, ENABLE);	

			// USART2_TX   GPIOA.2
			GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;   // PA.2
			GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
			GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;   // 复用推挽输出
			GPIO_Init(GPIOA, &GPIO_InitStructure);   // 初始化GPIOA.2

			// USART2_RX	  GPIOA.3初始化
			GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;   // PA3
			GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;   // 浮空输入
			GPIO_Init(GPIOA, &GPIO_InitStructure);   // 初始化GPIOA.3 

			// Usart2 NVIC 配置
			NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;
			NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ;   // 抢占优先级3
			NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;   // 子优先级3
			NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;   // IRQ通道使能
			NVIC_Init(&NVIC_InitStructure);   // 根据指定的参数初始化VIC寄存器

			// USART2 初始化设置
			USART_InitStructure.USART_BaudRate = bound;   // 串口波特率
			USART_InitStructure.USART_WordLength = USART_WordLength_8b;   // 字长为8位数据格式
			USART_InitStructure.USART_StopBits = USART_StopBits_1;   // 一个停止位
			USART_InitStructure.USART_Parity = USART_Parity_No;   // 无奇偶校验位
			// 无硬件数据流控制
			USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
			USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;   // 收发模式
			USART_Init(USART2, &USART_InitStructure);   // 初始化串口2
			
			USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);   // 开启串口接收中断
			USART_ITConfig(USART2, USART_IT_IDLE, ENABLE);   // 使能空闲中断
			
			USART_Cmd(USART2, ENABLE);   // 使能串口2
			
			break;
			
			default:
				break;
	}
}

4.2 串口1接收中断服务函数

  在进行数据接收时,我们一帧一帧地接收,直到接收到我们需要的帧之后将接接收缓冲区的数据复制到我们定义好的接收数据结构体中。

/*
 *==============================================================================
 *函数名称:USART1_IRQHandler
 *函数功能:串口1中断服务函数
 *输入参数:无
 *返回值:无
 *备  注:无
 *==============================================================================
*/
void USART1_IRQHandler (void)
{
	u8 recContent;   // 存储接收内容
	
	// 如果串口接收到内容
	if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) 
	{
		recContent = USART_ReceiveData(USART1);   // 存储接收内容
		
		// 如果接收到的是$($是一帧信息的开始)
		// 保证每接收到新的一帧信息就从缓冲区起始位置开始存储
		if(recContent == '$')
		{
			gReceCunt = 0;   // 清零帧信息计数变量
		}
		
		// 存储接收到的帧信息
		gUart1RcecBuf[gReceCunt ++] = recContent;
		
		// 确定是否收到"GPRMC/GNRMC"这一帧数据
		if(gUart1RcecBuf[0] == '$' && gUart1RcecBuf[4] == 'M' && gUart1RcecBuf[5] == 'C')
		{
			// 接收到换行(接收完了一帧信息)
			if(recContent == '\n')									   
			{
				memset(receDataFrame.Frame_Buffer, 0, Frame_Buffer_Length);   // 初始化接收帧信息数组
				memcpy(receDataFrame.Frame_Buffer, gUart1RcecBuf, recContent);   // 保存GPRMC/GNRMC这帧的数据
				receDataFrame.isGetData = TRUE;   // 接收成功
				recContent = 0;   // 清零接收帧信息接收计数变量
				memset(gUart1RcecBuf, 0, UART1RX_MAX_LENGTH);   // 清空串口1接收Buf				
			}		
		}
		
		// 如果接收内容超出最大长度,不再继续接收
		if(recContent >= UART1RX_MAX_LENGTH)
		{
			recContent = UART1RX_MAX_LENGTH;
		}
	}
}

4.3 帧信息解析

  存储到需要的帧信息后,下一步就是对它进行解析,解析时我们利用每一个数据间的逗号作为分隔符,利用strstr函数寻找逗号对应的地址,将两个逗号之间的信息存储到我们提前定义好的解析信息存储数组中。仔细分析一下“$GNRMC”帧会发现,我们只需要找到七个逗号的地址,提取他们两个相邻逗号中间的字符串就可以得到GPS信息,思路介绍完了,我们来看一下程序设计。

/*
 *==============================================================================
 *函数名称:Uart_Rece_Pares
 *函数功能:解析串口接收内容
 *输入参数:无
 *返回值:无
 *备  注:无
 *==============================================================================
*/
void Uart_Rece_Pares(void)   // 串口接收内容解析函数
{
	// 注意变量类型
	char *point = 0;   // 逗号的地址指针
	char *nextPoint = 0;   // 下一个逗号的地址指针
	u8 tempVar = 0;   // 临时循环变量
	
	// 如果数据接收成功
	if (receDataFrame.isGetData)
	{
		receDataFrame.isGetData = 0;   // 清除接收成功标志位
		
		// for循环解析接收帧
		// 总共需要找到7个逗号
		for (tempVar = 0;tempVar < 7;tempVar ++)
		{
			// 第一次循环
			if (tempVar == 0)
			{
				// 寻找第一个逗号
				if ((point = strstr(receDataFrame.Frame_Buffer,",")) == NULL)
				{
					printf ("Prase Errpr!\r\n");   // 解析错误
				}
			}
			else
			{
				point ++;   // 防止重复找到同一个逗号
				
				// 寻找下一个逗号
				// 注意strstr函数的输入变量,是从上一个逗号之后开始找下一个逗号
				if ((nextPoint = strstr(point,",")) != NULL)
				{
					// 存储信息
					switch (tempVar)
					{
						case 1:   // UTC时间
							memcpy(receDataFrame.UTCTime,point,nextPoint - point);
							break;
						
						case 2:   // 数据有效标识
							memcpy(receDataFrame.UsefullFlag,point,nextPoint - point);
							break;
						
						case 3:   // 纬度
							memcpy(receDataFrame.latitude,point,nextPoint - point);
							break;
						
						case 4:   // 纬度方向
							memcpy(receDataFrame.N_S,point,nextPoint - point);
							break;
						
						case 5:   // 经度
							memcpy(receDataFrame.longitude,point,nextPoint - point);
							break;
						
						case 6:   // 经度方向
							memcpy(receDataFrame.E_W,point,nextPoint - point);
							break;
					}
					
					point = nextPoint;   // 更新上一个逗号地址指针
					
					receDataFrame.isParseData = TRUE;   // 数据解析完成
					
					// 数据有效
					if (receDataFrame.UsefullFlag[0] == 'A')
					{
						printf ("Data is usefull!\r\n");
					}
					else if (receDataFrame.UsefullFlag[0] == 'V')
					{
						printf ("Data is invalid!\r\n");
					}
				}
				else
				{
					printf ("Prase Errpr!\r\n");   // 解析错误
				}
			}
		}
	}
}

4.4 经纬度数据解析转换

  解析出经纬度信息后,我们需要将它转换成我们需要的格式,可以直接在地图中输入定位,拿上面的例子来介绍一下转换方法

  上面接收到地经纬度信息为

4159.65553,N,12136.79345,E

  换算方法和结果为

41 + 59.65553 / 60 = 41.994
121 + 36.79345 / 60 = 121.613

  下面我们来展示一下程序实现

/*
 *==============================================================================
 *函数名称:Data_Transfor
 *函数功能:数据转换
 *输入参数:无
 *返回值:无
 *备  注:无
 *==============================================================================
*/
void Data_Transfor (void)
{
	float latitude = 0;   // 存储纬度信息
	u16 temp1 = 0;   // 临时变量1,存储整数
	float longitude = 0;   // 存储经度信息
	u16 temp2 = 0;   // 临时变量2,存储整数
	
	latitude = strtod(receDataFrame.latitude,NULL);   // 字符串转换成浮点数
	longitude = strtod(receDataFrame.longitude,NULL);   // 字符串转换成浮点数
	
	// 纬度信息处理
	// 五位纬度信息
	if ((latitude - 10000.0) >= 0)
	{
		// 前三位需要单独拿出来组成一个数
		temp1 = (((u16)latitude / 10000) % 10) * 100 + (((u16)latitude / 1000) % 10) * 10 + ((u16)latitude / 100) % 10;
		latitude = latitude - (float)temp1 * 100;
		latitude = (float)temp1 + latitude / 60;
		printf ("latitude:%.3f\r\n",latitude);
	}
	else   // 四位纬度信息
	{
		// 前两位需要单独拿出来组成一个数
		temp1 = (((u16)latitude / 1000) % 10) * 10 + ((u16)latitude / 100) % 10;
		latitude = latitude - (float)temp1 * 100;
		latitude = (float)temp1 + latitude / 60;
		printf ("latitude:%.3f\r\n",latitude);
	}
	
	// 经度信息处理
	// 五位经度信息
	if ((longitude - 10000.0) >= 0)
	{
		// 前三位需要单独拿出来组成一个数
		temp2 = (((u16)longitude / 10000) % 10) * 100 + (((u16)longitude / 1000) % 10) * 10 + ((u16)longitude / 100) % 10;
		longitude = longitude - (float)temp2 * 100;
		longitude = (float)temp2 + longitude / 60;
		printf ("longitude:%.3f\r\n",longitude);
	}
	else   // 四位经度信息
	{
		// 前两位需要单独拿出来组成一个数
		temp2 = (((u16)longitude / 1000) % 10) * 10 + ((u16)longitude / 100) % 10;
		longitude = longitude - (float)temp2 * 100;
		longitude = (float)temp2 + longitude / 60;
		printf ("longitude:%.3f\r\n",longitude);
	}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/180164.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【洛谷算法题】P5714-肥胖问题【入门2分支结构】

&#x1f468;‍&#x1f4bb;博客主页&#xff1a;花无缺 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! 本文由 花无缺 原创 收录于专栏 【洛谷算法题】 文章目录 【洛谷算法题】P5714-肥胖问题【入门2分支结构】&#x1f30f;题目描述&#x1f30f;输入格式&a…

实在智能携“TARS大模型”入选“2023中国数据智能产业AI大模型先锋企业”

近日&#xff0c;由数据猿与上海大数据联盟联合主办的“2023企业数智化转型升级发展论坛”在上海圆满收官。 论坛颁奖典礼上&#xff0c;《2023中国数据智能产业AI大模型先锋企业》等六大榜单正式揭晓&#xff0c;旨在表彰在AI领域为数智化升级取得卓越成就和突出贡献的企业&am…

【Flask使用】全知识md文档,4大部分60页第3篇:Flask模板使用和案例

本文的主要内容&#xff1a;flask视图&路由、虚拟环境安装、路由各种定义、状态保持、cookie、session、模板基本使用、过滤器&自定义过滤器、模板代码复用&#xff1a;宏、继承/包含、模板中特有变量和函数、Flask-WTF 表单、CSRF、数据库操作、ORM、Flask-SQLAlchemy…

深入浅出理解libevent——2万字总结

概述 libevent,libev,libuv都是c实现的异步事件库&#xff0c;注册异步事件&#xff0c;检测异步事件&#xff0c;根据事件的触发先后顺序&#xff0c;调用相对应回调函数处理事件。处理的事件包括&#xff1a;网络 io 事件、定时事件以及信号事件。这三个事件驱动着服务器的运…

中国智能汽车这一年,主打一个“卷”

文丨刘俊宏 “这才刚过去半年多&#xff0c;汽车行业又更新了一轮。”一位车评人在广州车展感叹道。 作为每年最后一个A级车展&#xff0c;广州车展向来被视为中国车市的“风向标”。相比上海车展“拥抱汽车行业新时代”、成都车展“驭见未来”的主题&#xff0c;广州车展“新…

为什么鼠标按键释放后才执行对应的动作?

如果你留心观察的话&#xff0c;你会发现这样一个事实&#xff1a;大部分的软件的用户体验中&#xff0c;一般都是在鼠标按键释放后&#xff0c;才会执行相应的动作&#xff0c;而不是按下的时候。例如&#xff0c;当我们按下开始菜单的时候&#xff0c;不会有任何动作发生&…

数据库基础入门 — SQL运算符

我是南城余&#xff01;阿里云开发者平台专家博士证书获得者&#xff01; 欢迎关注我的博客&#xff01;一同成长&#xff01; 一名从事运维开发的worker&#xff0c;记录分享学习。 专注于AI&#xff0c;运维开发&#xff0c;windows Linux 系统领域的分享&#xff01; 本…

2023 年亚马逊黑色星期五和网络星期一的企业电子商务指南

亚马逊黑色星期五和网络星期一 周末即将到来&#xff01;感恩节于 11 月 23 日举行&#xff0c;紧接着是 24 日黑色星期五和 27 日网络星期一。您的亚马逊业务准备好应对大量涌入了吗&#xff1f; 我们相信您已经准备好黑色星期五优惠并准备好库存&#xff0c;以确保您有足够的…

一键重装系统Win10专业版教程

在电脑操作过程中&#xff0c;用户如果遇到无法解决的系统问题&#xff0c;就可以考虑直接重新系统。现在&#xff0c;用户想要重新安装一下Win10专业版系统&#xff0c;但是不清楚具体的重装操作步骤。接下来小编给大家介绍轻松重装Win10专业版系统的详细步骤。 推荐下载 系统…

怎么快速卸载office365

怎么快速卸载office365 根据官网提供的两种解决方案即点即用或MSIMicrosoft Store 根据官网提供的两种解决方案 官网地址&#xff1a;https://support.microsoft.com/zh-cn/office/%E4%BB%8E-pc-%E5%8D%B8%E8%BD%BD-office-9dd49b83-264a-477a-8fcc-2fdf5dbf61d8#OfficeVersio…

如何在AppLink配置金蝶云星空预算使用单流程

上一篇有提到金蝶云星空如何通过AppLink平台配置销售订单操作&#xff0c;这次来演示下如何“保存预算使用单”、“调拨单定时自动审核”以及“预算使用单反审核后删除”操作。 根据请求数据保存预算使用单 当webhook接收到数据时触发流程 步骤1&#xff1a;根据webhook的请…

监控员工上网有什么软件丨三款好用的员工上网管理软件推荐

监控员工上网行为是企业管理中不可或缺的一部分&#xff0c;因此&#xff0c;选择一款好的监控员工上网的软件至关重要。目前市场上存在多种监控员工上网的软件&#xff0c;它们具有各种特点和功能&#xff0c;但企业需要仔细评估和选择。 一、域之盾软件 这是一款优秀的监控员…

第十七章 Java链接数据库

目录 1.登录MySQL 2.创建库和表 3.使用Java命令查询数据库操作 4.右击——点击“Build Path”——选择第四个——找到包的位置——导入成功 一、创建java项目 1.注册驱动 2.获取链接 3.获取statment对象 4.执行sql语句返回结果集 5.遍历结果集 6.关闭连接释放资源 封装…

给项目快速接入链路追踪

为什么需要链路追踪&#xff1f; 我们程序员在日常工作中&#xff0c;最常做事情之一就是修bug了。如果程序只是运行在单机上&#xff0c;我们最常用的方式就是在程序上打日志&#xff0c;然后程序运行的过程中将日志输出到文件上&#xff0c;然后我们根据日志去推断程序是哪一…

事件溯源模式

概念解释 事件溯源&#xff08;Event Sourcing&#xff09;是一种设计模式&#xff0c;其核心思想是将系统的状态变化表示为一系列不可变的事件&#xff0c;并将这些事件存储在事件日志中。系统的当前状态可以通过重新应用&#xff08;回放&#xff09;这些事件来还原&#xf…

webshell之扩展免杀

由于很多企业为了防止源码泄露&#xff0c;都会使用加密扩展将代码进行加密&#xff0c;那么我们就可以就将计就计&#xff0c;将webshell也利用扩展加密&#xff0c;将特征消除&#xff0c;从而达到免杀的效果 1.php-beast 扩展地址 下载dll&#xff0c;并添加至ext中 在php…

网络安全等级保护收费标准?

不同省份价格会略有不同&#xff0c;二级等保一般不低于5万元;三级等保不低于9万元&#xff0c;个别省份也可能7万也能办理&#xff0c;根据企业实际情况和省市选定的代理机构确定。 等级保护二级? 第二级等保是指信息系统受到破坏后&#xff0c;会对公民、法人和其他组织的合…

Airtest结合Poco对控件实施精准截图,学起来!

1.前言 最近在Q群内发现有个小伙伴提出了一个很有趣的脚本需求&#xff0c;想要实现“通过选择器获取到了控件&#xff0c;然后截图这个控件范围”&#xff0c;根据我们的Airtest的局部截图接口以及poco控件的属性查询接口是可以很快实现的~ 2.接口查找 首先我们需要知道我们…

PHP 正则式 全能匹配URL(UBB)

PHP 正则式 全能匹配URL&#xff08;UBB&#xff09; 语言&#xff1a;PHP 注明&#xff1a;正则式 无语言限制&#xff08;js、PHP、JSP、ASP、VB、.net、C#...&#xff09;一切皆可。 简介&#xff1a;PHP UBB 正则式 全能匹配URL 自动加超级链接。网上找了很多都不匹配或…

短视频矩阵系统源码搭建部署分享

一、 短视频矩阵系统源码搭建部署分享 目录 一、 短视频矩阵系统源码搭建部署分享 二、短视频矩阵系统搭建功能设计 三、 抖音矩阵号矩阵系统功能设计原则 四、 短视频矩阵开发部分源码展示 很高兴能够帮助您&#xff0c;以下是短视频矩阵系统源码搭建部署分享&#xff1a…