【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.9GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;   // PA.9GPIO_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;   // PA10GPIO_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 ;   // 抢占优先级3NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;   // 子优先级3NVIC_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);   // 初始化串口1USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);   // 开启串口接收中断USART_ITConfig(USART1, USART_IT_IDLE, ENABLE);   // 使能空闲中断USART_Cmd(USART1, ENABLE);   // 使能串口1break;case 1:// 使能USART2,GPIOA时钟RCC_APB1PeriphClockCmd (RCC_APB1Periph_USART2 | RCC_APB2Periph_GPIOA, ENABLE);	// USART2_TX   GPIOA.2GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;   // PA.2GPIO_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;   // PA3GPIO_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 ;   // 抢占优先级3NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;   // 子优先级3NVIC_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);   // 初始化串口2USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);   // 开启串口接收中断USART_ITConfig(USART2, USART_IT_IDLE, ENABLE);   // 使能空闲中断USART_Cmd(USART2, ENABLE);   // 使能串口2break;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);}
}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

基于H1ve一分钟搭好CTF靶场

写在前面 ◉ ‿ ◉ 上一篇文章给大家详细介绍了基于H1ve搭建CTF靶场&#xff0c;以及过程中可能遇到的报错及解决方法&#xff0c;那么这篇文章&#xff0c;我总结了一下&#xff0c;将不会遇到报错的方法给到大家&#xff0c;但是前提是你的服务器最好是一个全新的哦~~~ 我…

C++基础从0到1入门编程(四)类和对象

系统学习C 方便自己日后复习&#xff0c;错误的地方希望积极指正 往期文章&#xff1a; C基础从0到1入门编程&#xff08;一&#xff09; C基础从0到1入门编程&#xff08;二&#xff09; C基础从0到1入门编程&#xff08;三&#xff09; 参考视频&#xff1a; 1.黑马程序员匠心…

香蕉派BPI-M4 Zero单板计算机采用全志H618,板载2GRAM内存

Banana Pi BPI-M4 Zero 香蕉派 BPI-M4 Zero是BPI-M2 Zero的最新升级版本。它在性能上有很大的提高。主控芯片升级为全志科技H618 四核A53, CPU主频提升25%。内存升级为2G LPDDR4&#xff0c;板载8G eMMC存储。它支持5G WiFi 和蓝牙, USB接口也升级为type-C。 它具有与树莓派 …

23. 深度学习 - 多维向量自动求导

Hi, 你好。我是茶桁。 前面几节课中&#xff0c;我们从最初的理解神经网络&#xff0c;到讲解函数&#xff0c;多层神经网络&#xff0c;拓朴排序以及自动求导。 可以说&#xff0c;最难的部分已经过去了&#xff0c;这节课到了我们来收尾的阶段&#xff0c;没错&#xff0c;生…

算法通关村第十二关-白银挑战字符串经典题目

大家好我是苏麟 , 今天带来字符串相关的题目 . 大纲 反转问题字符串反转K个一组反转仅仅反转字母反转字符串中的单词 反转问题 字符串反转 描述 : 编写一个函数&#xff0c;其作用是将输入的字符串反转过来。输入字符串以字符数组 s的形式给出。 题目 : LeetCode 344. 反转…

webshell之扩展免杀

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

MySQL中自增id用完怎么办?

MySQL中自增id用完怎么办&#xff1f; MySQL里有很多自增的id&#xff0c;每个自增id都是定义了初始值&#xff0c;然后不停地往上加步长。虽然自然数是没有上限的&#xff0c;但是在计算机里&#xff0c;只要定义了表示这个数的字节长度&#xff0c;那它就有上限。比如&#…

python数据结构与算法-15_堆与堆排序

堆(heap) 前面我们讲了两种使用分治和递归解决排序问题的归并排序和快速排序&#xff0c;中间又穿插了一把树和二叉树&#xff0c; 本章我们开始介绍另一种有用的数据结构堆(heap)&#xff0c; 以及借助堆来实现的堆排序&#xff0c;相比前两种排序算法要稍难实现一些。 最后我…

Linux开发工具(含gdb调试教程)

文章目录 Linux开发工具&#xff08;含gdb调试教程&#xff09;1、Linux 软件包管理器 yum2、Linux开发工具2.1、Linux编辑器 -- vim的使用2.1.1、vim的基本概念2.1.2、vim的基本操作2.1.3、vim正常模式命令集2.1.4、vim末行模式命令集 2.2、vim简单配置 3、Linux编译器 -- gcc…

redis之cluster集群

1、redis-cluster集群&#xff1a;redis3.0引入的分布式存储方案 2、集群&#xff1a;由多个node节点组成&#xff0c;redis数据分布在这些节点之中 &#xff08;1&#xff09;在集群之中也分主节点和从节点 &#xff08;2&#xff09;自带哨兵模式 3、redis-cluster集群的…

腾讯云 小程序 SDK对象存储 COS使用记录,原生小程序写法。

最近做了一个项目&#xff0c;需求是上传文档&#xff0c;文档类型多种&#xff0c;图片&#xff0c;视频&#xff0c;文件&#xff0c;doc,xls,zip,txt 等等,而且文档类型可能是大文件&#xff0c;可能得上百兆&#xff0c;甚至超过1G。 腾讯云文档地址&#xff1a;https://c…

PC端页面进去先出现加载效果

自定义指令v-loading&#xff0c;只需要绑定Boolean即可 v-loading“loading” <el-table :data"list" border style"width: 100%" v-loading"loading"><el-table-column align"center" label"序号" width"5…

开发板启动进入系统以后再挂载 NFS 文件系统, 这里的NFS文件系统是根据正点原子教程制作的ubuntu_rootfs

如果是想开发板启动进入系统以后再挂载 NFS 文件系统&#xff0c;开发板启动进入文件系统&#xff0c;开发板和 ubuntu 能互相 ping 通&#xff0c;在开发板文件系统下新建一个目录 you&#xff0c;然后执行如下指令进行挂载&#xff1a; mkdir mi mount -t nfs -o nolock,nfsv…

日本it就职培训机构,日本IT行业的三种类型

日本的IT产业一直保持增长趋势&#xff0c;市场规模逐年增加&#xff0c;在日本所有产业中占据很大比例。由于日本老龄化严重&#xff0c;日本国内的IT人才无法满足需求&#xff0c;为缓解这一问题&#xff0c;日本将引进外国优秀IT人才作为一项国策&#xff0c;日本IT行业不仅…

Leetcode1410. HTML 实体解析器

Every day a Leetcode 题目来源&#xff1a;1410. HTML 实体解析器 解法1&#xff1a;模拟 遍历字符串 text&#xff0c;每次遇到 ’&‘&#xff0c;就判断以下情况&#xff1a; 双引号&#xff1a;字符实体为 &quot; &#xff0c;对应的字符是 " 。单引号&a…

振弦式土压力计在岩土工程安全监测应用的方案

振弦式土压力计在岩土工程安全监测应用的方案 振弦式土压力计是一种常见的土压力测量仪器&#xff0c;其原理是利用振弦在土中传播的速度与土的应力状态有关的特点测量土压力。在岩土工程安全监测中&#xff0c;振弦式土压力计可以应用于以下方面&#xff1a; 1. 地下连续墙和…

某资产管理机构: IAST提升安全水平,保障资产管理水平稳健增长

某资产管理机构是国内首批成立的资产管理公司之一&#xff0c;坚持“科技金融”、“数字金融”战略&#xff0c;以客户为中心&#xff0c;聚焦用户体验与业务协同&#xff0c;着力推进营销数字化进程和大数据平台建设&#xff0c;助力资产管理高质量发展。 数字科技推动工作效率…

面试题:Java 对象不使用时,为什么要赋值 null ?

文章目录 前言示例代码运行时栈典型的运行时栈Java的栈优化提醒 GC一瞥提醒 JVM的“BUG”总结 前言 最近&#xff0c;许多Java开发者都在讨论说&#xff0c;“不使用的对象应手动赋值为null“ 这句话&#xff0c;而且好多开发者一直信奉着这句话&#xff1b;问其原因&#xff…

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

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

nvm切换版本之后npm用不了

原因是 nvm只给你安了对应的node没给你安装对应的node版本的npm 解决办法如下 1找到你安装的node版本号 然后去官网下载对应的版本包 这个网址就是node官网的版本列表 Index of /download/release/ 2下载后解压 把根目录这俩复制到自己的nvm安装目录下 还有那个node_modul…