当前位置: 首页 > news >正文

细说ARM MCU的串口接收数据的实现过程

目录

一、硬件及工程

1、硬件

2、软件目的

3、创建.ioc工程

二、 代码修改

1、串口初始化函数MX_USART2_UART_Init()

(1)MX_USART2_UART_Init()串口参数初始化函数

(2)HAL_UART_MspInit()串口功能模块初始化函数

2、串口中断的执行过程

3、启动串口接收中断

4、自动生成main函数

5、启动串口接收函数HAL_UART_Receive_IT()

6、修改main.c函数

7、从定义回调函数 

8、修改while(1)循环

三、下载运行

1、安装串口助手

2、验证串口通讯结果


一、硬件及工程

1、硬件

        本文旨在以实例说明STM32单片机通过串口接收数据的实现过程。本文中使用ST的开发板NUCLEO-G474RE,板上MCU型号为STM32G474RET6。配套的扩展板为作者根据说明书自己设计制造,链接:

2、软件目的

        实例运行后,通过串口通讯助手发送0x10时开发板上的LD2灯亮,其它数据灯灭。

3、创建.ioc工程

        配置GPIO→配置时钟源和DEBUG→配置外部中断→配置串口→配置串口中断→配置系统时钟→build生成。

       将PA4、PA5配置为输出GPIO_Output,PP,Pull up;PC13配置为中断模式GPIO_EXTI13,上升沿触发,用于检测按键B1的状态;PA2、PA3分别配置为串口USART2_TX和USART2_RX;外部时钟;Debug设置为Serial Wire;USART2的基本参数,波特率115200,数据长度8bit,无校验,停止位1;PA4别名BUZ,PA5别名LED,PC13别名KEY;串口中断抢占优先级1,外部中断抢占优先级4;

二、 代码修改

        硬件配置完毕后,启动代码生成,IDE自动将配置好的硬件信息转换成代码。

        自动生成的代码有些需要了解(比如初始化函数),而有些是需要修改的(比如while(1)循环里需要增加的代码,一些注释对里需要增加的代码),还有一些需要重写(比如,重写弱函数)。

1、串口初始化函数MX_USART2_UART_Init()

(1)MX_USART2_UART_Init()串口参数初始化函数

        MX_USART2_UART_Init()函数主要完成对USART2的模式和参数配置,如波特率、数据位、停止位等。因为串口模块要比GPIO复杂,所以配置参数也更多。

/*** @brief USART2 Initialization Function* @param None* @retval None*/
static void MX_USART2_UART_Init(void)
{/* USER CODE BEGIN USART2_Init 0 *//* USER CODE END USART2_Init 0 *//* USER CODE BEGIN USART2_Init 1 *//* USER CODE END USART2_Init 1 */huart2.Instance = USART2;huart2.Init.BaudRate = 115200;huart2.Init.WordLength = UART_WORDLENGTH_8B;huart2.Init.StopBits = UART_STOPBITS_1;huart2.Init.Parity = UART_PARITY_NONE;huart2.Init.Mode = UART_MODE_TX_RX;huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE;huart2.Init.OverSampling = UART_OVERSAMPLING_16;huart2.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;huart2.Init.ClockPrescaler = UART_PRESCALER_DIV1;huart2.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;if (HAL_UART_Init(&huart2) != HAL_OK){Error_Handler();}if (HAL_UARTEx_SetTxFifoThreshold(&huart2, UART_TXFIFO_THRESHOLD_1_8) != HAL_OK){Error_Handler();}if (HAL_UARTEx_SetRxFifoThreshold(&huart2, UART_RXFIFO_THRESHOLD_1_8) != HAL_OK){Error_Handler();}if (HAL_UARTEx_DisableFifoMode(&huart2) != HAL_OK){Error_Handler();}

        MX_USART2_UART_Init()自动生成于main.c中;

        该函数内部出现的HAL_UART_Init(UART_HandleTypeDef *huart)在stm32g4xx_hal_uart.c中。

(2)HAL_UART_MspInit()串口功能模块初始化函数

        对于串口来说,针对引脚等参数的配置,是在文件stm32g4xx_hal_msp.c中。msp是MCU support package的缩写,指的是MCU相关的支持包。关于串口有3个支持包:

HAL_MspInit(void)
HAL_UART_MspInit(UART_HandleTypeDef *huart)
HAL_UART_MspDeInit(UART_HandleTypeDef *huart)

        由函数名可见,其中都带有MspInit字样。这类函数的作用是进行MCU功能模块(譬如串口、定时器、ADC等)的初始化。在固件库中,通常是采用这种方式将MCU的模块初始化代码集中起来,以方便代码在不同型号的MCU上移植。

        上述函数中,第一个是初始化全局Msp。后面两个函数的参数完全一样,函数名也很类似;区别是后一个函数名中多了两个字母“De”,是Default的缩写。

        HAL_UART_MspInit()函数可以对串口硬件初始化、配置引脚模式以及设置中断优先级并使能中断,与对GPIO进行初始化的MX_GPIO_Init()函数所完成的功类似。

        HAL_UART_MspDeInit()函数可以把串口复位成初始值,关闭串口并关闭串口中断。

        HAL_UART_MspInit()函数是由函数HAL_UART_Init()(在stm32g4xx_hal_uart.c文件中定义)调用的。而HAL_UART_Init()是由MX_USART2_UART_Init()函数调用的(在if语句的条件表达式中调用)。

2、串口中断的执行过程

        由于配置了串口的中断功能,所以当中断发生后就会调用相应的中断服务函数来完成一定的任务。

void HAL_UART_IRQHandler(UART_HandleTypeDef *huart)
{... .../*if no error occurs */errorflags = (isrflags & (uint32_t)(USART_ISR_PE|USART_ISR_FE|USART_ISR_ORE|USART_ISR_NE));if(errorflags == 0U){/*------UART in mode Receiver-------*/if(((isrflags & USART_ISR_RXNE_RXFNE) != 0U) && (((crlits & USART_CR1_RXNEIE_RXFNEIE) != 0U)||((cr3its & USART_CR3_RXFTIE) != 0U))){if(huart->RxISR != NULL){huart->RxISR(huart);}return;}}
... ...
}

        当程序执行到huart →RxISR(huart)时,会调用UART_RxISR_8BIT()函数(如果配置数据字长为7位或8位,则调用此函数;如果数据字长为9位,则会调用另一函数UART_RxISR_16BIT),并且在该函数中会调用回调函数HAL_UART_RxCpltCallback()。这个回调函数是在stm32g4xx_hal_ uart.c中定义的弱函数。用户需要重写该函数,可以写在main.c中。

3、启动串口接收中断

        在使用中断之前,还要用到函数HAL_UART_Receive_IT()。该函数的格式如下:

HAL_UART_Receive_IT(UART_HandleTypeDef *huart,uint8_t *pData,uint16_t Size)

        该函数是给将要接收的数据定义一个缓冲区pData,并指定接收数据的长度为Size(也就是要接收的字节数)。这个Size决定了调用回调函数的频率。如果Size大于1,则不会每次中断都调用回调函数,而是到Size次之后,才会调用一次回调函数。此外,这个函数还有开启接收中断的功能,所以需要在main函数的初始化代码中调用一次HAL_UART_Receive_IT()函数。这样就可以确保开启接收中断。在执行一次回调函数时,接收中断会关闭,所以还需要再次开启接收中断。这个再次开启中断的动作,也可以在回调函数中通过调用HAL_UART_Receive_IT()函数来实现。

4、自动生成main函数

        完成上面的硬件配置,并自动生成代码后,然后在main.c中的初始化部分调用HAL_UART_ Receive _IT()函数设置参数并开启接收中断,然后写回调函HAL_UART_RxCpltCallback(),以便对接收的数据进行处理。删除了一些注释对。

#include "main.h"
UART_HandleTypeDef huart2;
/* USER CODE BEGIN PV */
/* USER CODE END PV */
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_USART2_UART_Init(void);
int main(void)
{HAL_Init();SystemClock_Config();MX_GPIO_Init();MX_USART2_UART_Init();/* USER CODE BEGIN 2 *//* USERCODE END 2 */while(1){}
}

        main函数中出现了一个串口初始化函数MX_USART2_UART_Init()。此外,在main.c中,首先定义了一个全局变量huart2,类型为UART_HandleTypeDef。huart2是一个结构体变量,通常也称为串口句柄。这个结构体是关于UART的,它的成员有很多,有的成员本身也是结构体类型。这个结构体有些复杂。在串口初始化函数MX_USART2_UART_Init中,使用了huart2变量。

static void MX_USART2_UART_Init(void)
{huart2.Instance = USART2;huart2.Init.BaudRate = 115200;huart2.Init.WordLength = UART_WORDLENGTH_8B;huart2.Init.StopBits = UART_STOPBITS_1;huart2.Init.Parity = UART_PARITY_NONE;huart2.Init.Mode = UART_MODE_TX_RX;huart2.Init.HwFlowCtl =UART_HWCONTROL_NONE;... ...
}

        由此可见,在MX_USART2_UART_Init函数中,第一句huart2.Instance =USART2,就将前面配置的USART2与结构体变量huart2关联了起来。

5、启动串口接收函数HAL_UART_Receive_IT()

        要实现串口接收中断,需要在主程序的初始化代码中调用HAL_UART_Receive_IT()函数。该函数的结构如下:

HAL_UART_Receive_IT(UART_HandleTypeDef *huart,uint8_t *pData,uint16_t Size)

        该函数有三个参数,第一个参数的类型就是UART_HandleTypeDef,所以要将该参数与USART2关联起来。因此,HAL_UART_Receive_IT()函数的调用要可将该函数放到MX_USART2 _UART_Init()函数之后的注释对中。

        HAL_UART_Receive_IT()函数的第二个参数是设置接收数据的缓冲区,可以定义一个长度为RXBUFFERSIZE的数组RxBuffer [RXBUFFERSIZE],当然这个数组以及RXBUFFERSIZE都需要另外定义(后面会将它们定义为全局变量)。

        HAL_UART_Receive_IT()函数的第三个参数用于指定接收数据的长度,这个数据长度可以与接收缓冲区的长度相同,即RXBUFFERSIZE。

        将RxBuffer[RXBUFFERSIZE]定义为全局变量(需要放到注释对中),并将对HAL_UART_ Receive_IT()函数的调用放置到MX_USART2_UART_Init()语句之后的注释对/* USER CODE BEGIN 2 */与/* USER CODE END 2 */中。

6、修改main.c函数

#include "main.h"
UART_HandleTypeDef huart2;
/* USER CODE BEGIN PV */
/* 直接使用了变量RXBUFFERSIZE */
uint8_t RxBuffer[RXBUFFERSIZE];
/* USER CODE END PV */
/*Private function prototypes */
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_USART2_UART_Init(void);
int main(void)
{HAL_Init();SystemClock_Config();MX_GPIO_Init();MX_USART2_UART_Init();/* USER CODE BEGIN2 */HAL_UART_Receive_IT(&huart2,(uint8_t*)RxBuffer,RXBUFFERSIZE);/* USER CODE END2 */while(1){}
}

        上面直接使用了变量RXBUFFERSIZE。对该变量的定义可以放到main.h头文件中,可以用define宏(也需放置到注释对中):

/* USER CODE BEGIN Private defines */
#define RXBUFFERSIZE 1 //接收缓冲区的长度
/* USER CODE END Private defines */

        将RXBUFFERSIZE定义为1,也就是1字节。

7、从定义回调函数 

        重定义串口中断接收的回调函数HAL_UART_RxCpltCallback()。这个函数已经在stm32g4xx_ hal_uart.c中有定义,只不过被定义为弱函数,实际就是一个空函数。需要重写它。与写EXTI的回调函数类似,也将该函数写在main.c中。

        串口有数据送来,会执行中断服务函数USART2_IRQHandler(),然后该函数又会调用函数HAL_UART_IRQHandler()。调用一定次数的HAL_UART_IRQHandler()函数后,就会自动执行回调函数HAL_UART_RxCpltCallback()。这里的“一定次数”是由HAL_UART_Receive_IT()函数的第三个参数决定的,也就是前面在主程序中用到的常量RXBUFFERSIZE。由于把RXBUFFERSIZE定义为1,所以串口收到1字节的数据后,会调用一次回调函数HAL_UART_RxCpltCallback()。当调用回调函数之时,1字节的数据已经放到了接收缓冲区中,也就是放到前面定义的数组RxBuffer中。 

        调用HAL_UART_Receive_IT()函数,不但实现了定义缓冲区并设置接收数据长度的功能,而且还有开启串口中断接收的功能。因此,在接收完指定长度的数据之后,需要重新开启接收中断的功能,否则后面就不会再进入中断了。可以在回调函数HAL_UART_RxCpltCallback()中调用一下HAL_UART_Receive_IT()函数,重新开启接收中断。对该函数的调用,可以连同EXTI的回调函数HAL GPIO_EXTI_Callback()一起写在main.c后面的注释对中:

/*USER CODE BEGIN 4 */
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{HAL_UART_Receive_IT(&huart2,(uint8_t *)RxBuffer,RXBUFFERSIZE);
}
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{HAL_GPIO_WritePin(BUZ_GPIO_Port,BUZ_Pin,GPIO_PIN_RESET);HAL_Delay(100);	//延时HAL_GPIO_WritePin(BUZ_GPIO_Port,BUZ_Pin,GPIO_PIN_SET);
}
/*USER CODE END 4 */

        在EXTI的回调函数中使用了中断的方式实现:当按键按下时,让蜂鸣器响一声。

8、修改while(1)循环

        根据串口送来的数据,控制发光二极管的亮灭。当接收到的数据为0x10(十六进制)时,点亮LD2;当接收到的数据不是0x10时,熄灭LD2。

/*USER CODE BEGIN WHILE */
while(1)
{/*USER CODE BEGIN 3 */if(RxBuffer[0] == 0x10)HAL_GPIO_WritePin(LED_GPIO_Port,LED_Pin,GPIO_PIN_SET);elseHAL_GPIO_WritePin(LED_GPIO_Port,LED_Pin,GPIO_PIN_RESET);
}
/*USER CODE END 3 */

三、下载运行

1、安装串口助手

        好用的 Win10 串口调试助手 + 网口调试 - 知乎  https://zhuanlan.zhihu.com/p/109941792?eqid=a328954a0002e745000000066477efb6&utm_id=0

2、验证串口通讯结果

        开启串口通讯,发送0x10,开大坂上的LD2亮,发送其它内容,比如0x20,LD2灭。

相关文章:

细说ARM MCU的串口接收数据的实现过程

目录 一、硬件及工程 1、硬件 2、软件目的 3、创建.ioc工程 二、 代码修改 1、串口初始化函数MX_USART2_UART_Init() (1)MX_USART2_UART_Init()串口参数初始化函数 (2)HAL_UART_MspInit()串口功能模块初始化函数 2、串口…...

000-基于sklearn的机器学习入门:工作环境搭建与配置

本专栏将介绍基于Scikit-learn(简称sklearn)的机器学习入门知识。包括但不一定限于,机器学习基本知识、sklearn库简介,基于Sklearn库的机器学习实践。 这是本专栏的第000篇,将介绍如何安装和配置sklearn环境,不仅包括Sklearn库的…...

就业班 第四阶段(k8s) 2401--6.5 day3 Yaml语法解析+钩子函数

Yaml语法解析 今天学的都是在pod里面操作的 格式 这个文件要创建的资源对象是什么 kind: Pod 这个资源对象所在的api版本是什么 apiVersion: v1 元数据 metadata: 对这个pod中的镜像的描述 spec: 字典无序 同一级可以调换顺序 :比如kind和apiVersion …...

电脑开机出现英文字母,如何解决这个常见问题?

电脑开机时出现英文字母的情况通常意味着系统在启动过程中遇到了问题。这些英文字母可能是错误信息、系统提示或BIOS设置问题。通过理解这些信息并采取适当的措施,您可以解决大多数启动问题。本文将介绍三种解决电脑开机出现英文字母问题的方法,帮助您恢…...

一张试卷

目录 问题 1: 1.时间 题目描述1 输入1 输出1 样例输入1 样例输出1 提示1 代码1 问题 2: 超酷的电话号码 题目描述2 输入2 输出2 样例输入2 样例输出2 提示2 代码2 问题 3:3.爸爸的数学题 题目描述3 输入3 输出3 样例输入3 样例输出3 提示3 代码3 问题 4: 4. 营养膳食 题目描述4…...

记一次 .NET某游戏币自助机后端 内存暴涨分析

一:背景 1. 讲故事 前些天有位朋友找到我,说他们的程序内存会偶发性暴涨,自己分析了下是非托管内存问题,让我帮忙看下怎么回事?哈哈,看到这个dump我还是非常有兴趣的,居然还有这种游戏币自助机…...

计算机考研|哪些985/211院校不歧视双非二本生?

说句残酷的话,能对某一专业做出贡献,那么你的水平肯定是很高的。如果普通学生,普通本科的话可能很难做到这一点。这也是现在考研风气比较强的原因,一部分专业能力不突出的学生来选择深造3年。 对于基础较差想要考计算机研究生的同…...

Spring Boot:简化 Java 应用开发的艺术

Spring Boot 是一种用于快速开发、运行和管理 Java 应用程序的开源框架。它简化了基于 Spring 的应用程序的配置和部署,使得开发者能够更加专注于业务逻辑的实现。本文将介绍 Spring Boot 的核心特性、优势以及如何在项目中使用 Spring Boot。 一、核心特性 自动配…...

elasticsearch安装与使用(2)-基于term匹配的简单搜索引擎搭建

把一篇pdf论文解析后,放入es数据库中,建立倒排索引表,并实现简单搜索。 1、pdf论文解析(英文) 安装pdf解析包 pip install pdfminer.sixdef extract_text_from_pdf(filename, page_numbersNone, min_line_length1):从pdf文件中提取文字:pa…...

速盾:ddos防护与高防ip区别?

在网络安全领域,DDOS 防护和高防 IP 都是重要的防护手段,但它们之间存在着一些明显的区别。 DDOS 防护是一种针对分布式拒绝服务攻击的防御策略。它通过多种技术和方法来识别和抵御 DDOS 攻击。常见的 DDOS 防护手段包括流量清洗、连接限制、协议分析等。…...

Java中StringBulider详解

StringBuilder 是 Java 中一个用来创建可变字符串的类。与 String 类不同,StringBuilder 对象能够被修改,不会创建新的对象,因此在需要进行大量字符串操作时,StringBuilder 更高效。以下是 StringBuilder 的详细讲解。 创建 Stri…...

基于springboot高校就业招聘系统的设计

管理员账户功能包括:系统首页,个人中心,就业咨询管理,毕业去向管理,简历管理,管理员管理,基础数据管理 辅导员账户功能包括:系统首页,个人中心,就业咨询管理…...

嵌入式C语言编码规范要点

1.函数命名方法 骆驼命名法(Camel) 帕斯卡命名法((pascal),也叫大驼峰命名法(Upper Camel Case) 匈牙利命名法 下划线命名法(也称为蛇形命名法) 详述见之前文…...

Python中的全局解释器锁:深入解析与应对策略

1. 引言 在Python的世界里,全局解释器锁(GIL)是一个经常被讨论的话题。它既是Python并发编程中的一个重要概念,也是许多开发者感到困惑的源头。本文将深入探讨GIL的工作原理、它对Python程序性能的影响 2. 全局解释器锁的历史背…...

【java计算机毕设】图书商城管理系统MySQL springboot vue html maven送文档

1项目功能介绍 【java计算机毕设】图书商城管理系统 Java Spring Boot vue HTML MySQL 赠送文档 PPT 2项目简介 系统功能: 图书商城管理系统包括管理员和用户两种角色。 管理员的功能包括在个人中心修改个人信息,以及在基础数据管理中管理会员等级类型和…...

【Java刷题】二叉树

相同的树 public boolean isSameTree(TreeNode p, TreeNode q) {if(p null && q null) {return true;} else if(p ! null && q ! null) {if(p.val ! q.val) {return false;} else {return isSameTree(p.left, q.left) && isSameTree(p.right, q.rig…...

【Linux】程序地址空间之动态库的加载

我们先进行一个整体轮廓的了解,随后在深入理解细节。 在动态库加载之前还要说一下程序的加载,因为理解了程序的加载对动态库会有更深的理解。 轮廓: 首先,不管是程序还是动态库刚开始都是在磁盘中的,想要执行对应的可…...

LabVIEW处理大量数据时,怎样确保数据的准确性和完整性?

在LabVIEW处理中,确保大量数据的准确性和完整性至关重要。以下是详细的多角度分析和建议,以确保在LabVIEW中处理大量数据时,数据的准确性和完整性: 1. 数据采集阶段 1.1 高精度硬件选择 选择高精度的数据采集硬件,如…...

容器是什么?

概念 容器可以被看作是一种轻量级的虚拟化技术。与传统虚拟化技术相比,容器不需要为每个应用程序提供单独的操作系统,它们共享宿主机的操作系统内核。这使得容器更加轻便和高效。 想象一下,容器就像是一艘艘可以在海洋中独立航行的货轮&…...

#15 从Stable Diffusion生成的艺术中寻找灵感

文章目录 前言1. Stable Diffusion简介2. 寻找灵感的途径2.1 深入探索主题2.2 结合多种艺术风格2.3 实验不同的创意组合 3. 灵感应用3.1 艺术创作3.2 设计项目3.3 故事讲述 4. 实践建议4.1 记录和迭代4.2 开放实验4.3 结合个人风格 结论 前言 在当今的数字时代,人工…...

Linux应用开发之网络套接字编程(实例篇)

服务端与客户端单连接 服务端代码 #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <pthread.h> …...

[2025CVPR]DeepVideo-R1:基于难度感知回归GRPO的视频强化微调框架详解

突破视频大语言模型推理瓶颈,在多个视频基准上实现SOTA性能 一、核心问题与创新亮点 1.1 GRPO在视频任务中的两大挑战 ​安全措施依赖问题​ GRPO使用min和clip函数限制策略更新幅度,导致: 梯度抑制:当新旧策略差异过大时梯度消失收敛困难:策略无法充分优化# 传统GRPO的梯…...

iOS 26 携众系统重磅更新,但“苹果智能”仍与国行无缘

美国西海岸的夏天&#xff0c;再次被苹果点燃。一年一度的全球开发者大会 WWDC25 如期而至&#xff0c;这不仅是开发者的盛宴&#xff0c;更是全球数亿苹果用户翘首以盼的科技春晚。今年&#xff0c;苹果依旧为我们带来了全家桶式的系统更新&#xff0c;包括 iOS 26、iPadOS 26…...

微软PowerBI考试 PL300-选择 Power BI 模型框架【附练习数据】

微软PowerBI考试 PL300-选择 Power BI 模型框架 20 多年来&#xff0c;Microsoft 持续对企业商业智能 (BI) 进行大量投资。 Azure Analysis Services (AAS) 和 SQL Server Analysis Services (SSAS) 基于无数企业使用的成熟的 BI 数据建模技术。 同样的技术也是 Power BI 数据…...

深入理解JavaScript设计模式之单例模式

目录 什么是单例模式为什么需要单例模式常见应用场景包括 单例模式实现透明单例模式实现不透明单例模式用代理实现单例模式javaScript中的单例模式使用命名空间使用闭包封装私有变量 惰性单例通用的惰性单例 结语 什么是单例模式 单例模式&#xff08;Singleton Pattern&#…...

OkHttp 中实现断点续传 demo

在 OkHttp 中实现断点续传主要通过以下步骤完成&#xff0c;核心是利用 HTTP 协议的 Range 请求头指定下载范围&#xff1a; 实现原理 Range 请求头&#xff1a;向服务器请求文件的特定字节范围&#xff08;如 Range: bytes1024-&#xff09; 本地文件记录&#xff1a;保存已…...

均衡后的SNRSINR

本文主要摘自参考文献中的前两篇&#xff0c;相关文献中经常会出现MIMO检测后的SINR不过一直没有找到相关数学推到过程&#xff0c;其中文献[1]中给出了相关原理在此仅做记录。 1. 系统模型 复信道模型 n t n_t nt​ 根发送天线&#xff0c; n r n_r nr​ 根接收天线的 MIMO 系…...

JAVA后端开发——多租户

数据隔离是多租户系统中的核心概念&#xff0c;确保一个租户&#xff08;在这个系统中可能是一个公司或一个独立的客户&#xff09;的数据对其他租户是不可见的。在 RuoYi 框架&#xff08;您当前项目所使用的基础框架&#xff09;中&#xff0c;这通常是通过在数据表中增加一个…...

AI+无人机如何守护濒危物种?YOLOv8实现95%精准识别

【导读】 野生动物监测在理解和保护生态系统中发挥着至关重要的作用。然而&#xff0c;传统的野生动物观察方法往往耗时耗力、成本高昂且范围有限。无人机的出现为野生动物监测提供了有前景的替代方案&#xff0c;能够实现大范围覆盖并远程采集数据。尽管具备这些优势&#xf…...

站群服务器的应用场景都有哪些?

站群服务器主要是为了多个网站的托管和管理所设计的&#xff0c;可以通过集中管理和高效资源的分配&#xff0c;来支持多个独立的网站同时运行&#xff0c;让每一个网站都可以分配到独立的IP地址&#xff0c;避免出现IP关联的风险&#xff0c;用户还可以通过控制面板进行管理功…...