【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, gReceCunt);   // 保存GPRMC/GNRMC这帧的数据receDataFrame.isGetData = TRUE;   // 接收成功gReceCunt = 0;   // 清零接收帧信息接收计数变量memset(gUart1RcecBuf, 0, UART1RX_MAX_LENGTH);   // 清空串口1接收Buf				}		}// 如果接收内容超出最大长度,不再继续接收if(gReceCunt >= UART1RX_MAX_LENGTH){gReceCunt = 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.rhkb.cn/news/278660.html

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

相关文章

Java实现简单的通讯录

每日一言 泪眼问花花不语&#xff0c;乱红飞过秋千去。 —欧阳修- 简单的通讯录实现&#xff0c;跟写Java实现图书管理系统差不多&#xff0c;用到的知识也差不多&#xff0c;就当个小练习&#xff0c;练习一下写Java程序的手感。 Java实现图书管理系统 关于通讯录的代码都写…

Docker部署TeamCity来完成内部CI、CD流程

使用TeamCity来完成内部CI、CD流程 本篇教程主要讲解基于容器服务搭建TeamCity服务&#xff0c;并且完成内部项目的CI流程配置。至于完整的DevOps&#xff0c;我们后续独立探讨。 一个简单的CI、CD流程 以下分享一个简单的CI、CD流程&#xff08;仅供参考&#xff09;&#…

AR/MR产品设计(二):如何用一双手完成与虚拟对象的自然交互

AR/MR产品设计&#xff08;二&#xff09;&#xff1a;如何用一双手完成与虚拟对象的自然交互 - 知乎 手是我们与现实世界交互最重要的方式&#xff0c;同样在虚实混合的世界中是最重要的交互方式 在AR/MR/VR的交互中&#xff0c;手势交互会作为XR的重要交互动作&#xff0c;因…

自然语言处理: 第十七章RAG的评估技术RAGAS

论文地址&#xff1a;[2309.15217] RAGAS: Automated Evaluation of Retrieval Augmented Generation (arxiv.org) 项目地址: explodinggradients/ragas: Evaluation framework for your Retrieval Augmented Generation (RAG) pipelines (github.com) 上一篇文章主要介绍了R…

Spring boot2.7整合jetcache方法缓存

前面的文章 我们讲了 spring boot 整合 jetcache 做基本字符串数据缓存 但是 我这里有个这样的逻辑 我的 domain 包下 有一个 book 属性类 里面就 id 和 name 属性 设置了 对应的 set get函数 和一个整体的构造函数 package com.example.javadom.domain;public class book {pr…

免费阅读篇 | 芒果YOLOv8改进110:注意力机制GAM:用于保留信息以增强渠道空间互动

&#x1f4a1;&#x1f680;&#x1f680;&#x1f680;本博客 改进源代码改进 适用于 YOLOv8 按步骤操作运行改进后的代码即可 该专栏完整目录链接&#xff1a; 芒果YOLOv8深度改进教程 该篇博客为免费阅读内容&#xff0c;直接改进即可&#x1f680;&#x1f680;&#x1f…

最细致最简单的 Arm 架构搭建 Harbor

更好的阅读体验&#xff1a;点这里 &#xff08; www.doubibiji.com &#xff09; ARM离线版本安装 官方提供了一个 arm 版本&#xff0c;但是好久都没更新了&#xff0c;地址&#xff1a;https://github.com/goharbor/harbor-arm 。 也不知道为什么不更新&#xff0c;我看…

Linux docker3--数据卷-nginx配置示例

一、因为docker部署服务都是以最小的代价部署&#xff0c;所以通常在容器内部很多依赖和命令无法执行。进入容器修改配置的操作也比较麻烦。本例介绍的数据卷作用就是将容器内的配置和宿主机文件打通&#xff0c;之后修改宿主机的配置文件就相当于修改了docker进程的配置文件&a…

【IC设计】Verilog线性序列机点灯案例(四)(小梅哥课程)

文章目录 该系列目录&#xff1a;设计环境设计目标设计思路RTL及Testbench代码RTL代码Testbenchxdc约束 仿真结果 声明&#xff1a;案例和代码来自小梅哥课程&#xff0c;本人仅对知识点做做笔记&#xff0c;如有学习需要请支持官方正版。 该系列目录&#xff1a; Verilog线性…

uniapp微信小程序随机生成canvas-id报错?

uniapp微信小程序随机生成canvas-id报错&#xff1f; 文章目录 uniapp微信小程序随机生成canvas-id报错&#xff1f;效果图遇到问题解决 场景&#xff1a; 子组件&#xff0c;在 mounted 绘制 canvas&#xff1b;App、H5端正常显示&#xff0c;微信小程序报错&#xff1b; 效…

spring-boot-starter-thymeleaf加载外部html文件

在Spring MVC中&#xff0c;我们可以使用Thymeleaf模板引擎来实现加载外部HTML文件。 1.Thymeleaf介绍 Thymeleaf是一种现代化的服务器端Java模板引擎&#xff0c;用于构建漂亮、可维护且易于测试的动态Web应用程序。它适用于与Spring框架集成&#xff0c;并且可以与Spring M…

VSCode下使用github初步

由于各种需要&#xff0c;现在需要统一将一些代码提交搞github&#xff0c;于是有了在VSCode下使用github的需求。之前只是简单的使用git clone&#xff0c;代码提交这些用的是其他源代码工具&#xff0c;于是得学习实操下&#xff0c;并做一记录以备后用。 安装 VSCode安装 …

swagger使用手册

1.导入依赖 <!--引入swagger--><dependency><groupId>io.springfox</groupId><artifactId>springfox-swagger2</artifactId><version>2.7.0</version></dependency><dependency><groupId>io.springfox</…

深度学习面经-part3(RNN、LSTM)

3.RNN 核心思想&#xff1a;像人一样拥有记忆能力。用以往的记忆和当前的输入&#xff0c;生成输出。 RNN 和 传统神经网络 最大的区别:在于每次都会将前一次的输出结果&#xff0c;带到下一次的隐藏层中&#xff0c;一起训练。 RNN应用场景: 1.文本生成 2.语音识别 3.机器翻…

25考研|北大软微会「爆炸」吗?

软微不是已经爆炸了吗&#xff1f; 大家去看看他的录取平均分就知道了&#xff0c;没有实力千万别碰&#xff0c;现在考软微已经不存在捡漏之说。 110408的复试线已经划到了465分&#xff0c;这个人真的不低了&#xff0c;因为有数学一和408两个比较难的专业课&#xff0c;复…

判断闰年(C语言)

一、运行结果&#xff1b; 二、源代码&#xff1b; # define _CRT_SECURE_NO_WARNINGS # include <stdio.h>int main() {//初始化变量值&#xff1b;int year 2000;//执行循环判断&#xff1b;while (year < 2010){//执行流程&#xff1b;//判断能否整除4&#xff1…

云原生(三)、Docker网络

Docker网络 在 Docker 中&#xff0c;不同容器之间的网络访问原理取决于容器所使用的网络模式。下面是 Docker 中常见的两种网络模式下容器间网络访问的原理&#xff1a; 桥接模式&#xff08;Bridge&#xff09;&#xff1a; 在桥接模式下&#xff0c;Docker 使用 Linux 桥接…

CSS案例-3.背景练习

效果1 用背景加入图标 效果2 将图片设为页面背景,图片主体在中间 效果3 鼠标放到导航栏上会变颜色 知识点 CSS背景 属性 描述 取值 background 复合属性 看独立属性 background-color 背景颜色 <color> background-image 背景图像 none | url background-repeat 背景…

计算机网络——物理层(信道复用技术)

计算机网络——物理层&#xff08;信道复用技术&#xff09; 信道复用技术频分多址与时分多址 频分复用 FDM (Frequency Division Multiplexing)时分复用 TDM (Time Division Multiplexing)统计时分复用 STDM (Statistic TDM)波分复用码分复用 我们今天接着来看信道复用技术&am…

Android下的匀速贝塞尔

画世界pro里的画笔功能很炫酷 其画笔配置可以调节流量&#xff0c;密度&#xff0c;色相&#xff0c;饱和度&#xff0c;亮度等。 他的大部分画笔应该是通过一个笔头图片在触摸轨迹上匀速绘制的原理。 这里提供一个匀速贝塞尔的kotlin实现&#xff1a; class EvenBezier {p…