[HAL库分析—GPIO] - 指南

news/2026/1/19 22:55:55/文章来源:https://www.cnblogs.com/yangykaifa/p/19503924

文章目录

  • 前言
  • 1. GPIO
    • 1.1. 使能GPIOB时钟
    • 1.2. 配置GPIO工作方式
    • 1.3. 控制GPIOB输出状态
  • 2. 寄存器编程实验
  • 3. 总结

前言

开发板为:野火指南者,基于STM32F103VET6
分析的例程为:指南者\1-程序源码_教程文档\2-[野火]《STM32 HAL库开发实战指南》(HAL库源码)\12-GPIO输出—使用固件库点亮LED灯

1. GPIO

通过HAL库操作GPIOB的关键函数数据结构如下:

// 1、使能GPIO时钟
__HAL_RCC_GPIOB_CLK_ENABLE()
// 2、配置GPIO工作模式
void HAL_GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_Init)
// 3、控制GPIOB输出状态
void HAL_GPIO_WritePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin, GPIO_PinState PinState)

接下来逐行分析上述的3行代码

1.1. 使能GPIOB时钟

__HAL_RCC_GPIOB_CLK_ENABLE()STM32F1xx_HAL_Driver\Inc\stm32f1xx_hal_rcc.h文件中的511行,如下面代码所示

// 1、
#define __HAL_RCC_GPIOB_CLK_ENABLE()   do { \
__IO uint32_t tmpreg; \
SET_BIT(RCC->APB2ENR, RCC_APB2ENR_IOPBEN);\
/* Delay after an RCC peripheral clock enabling */\
tmpreg = READ_BIT(RCC->APB2ENR, RCC_APB2ENR_IOPBEN);\
UNUSED(tmpreg); \
} while(0U)
// 2、展开
#define __HAL_RCC_GPIOB_CLK_ENABLE()  do { \
__IO uint32_t tmpreg; \
(RCC->APB2ENR) |= (RCC_APB2ENR_IOPBEN);\
/* Delay after an RCC peripheral clock enabling */\
tmpreg = (RCC->APB2ENR) & (RCC_APB2ENR_IOPBEN);\
(void)tmpreg;\
} while(0U)
// 3、删掉不重要的代码得到
#define __HAL_RCC_GPIOB_CLK_ENABLE()  (RCC->APB2ENR) |= (RCC_APB2ENR_IOPBEN);
// 4、关于RCC以及RCC_APB2ENR_IOPBEN的定义
typedef struct
{
__IO uint32_t CR;			// 偏移0字节
__IO uint32_t CFGR;			// 偏移4字节
__IO uint32_t CIR;			// 偏移8字节
__IO uint32_t APB2RSTR;	// 偏移12字节
__IO uint32_t APB1RSTR;	// 偏移16字节
__IO uint32_t AHBENR;		// 偏移20字节
__IO uint32_t APB2ENR;	// 偏移24字节, 即偏移0x18字节
__IO uint32_t APB1ENR;
__IO uint32_t BDCR;
__IO uint32_t CSR;
} RCC_TypeDef;
#define RCC  ((RCC_TypeDef *)RCC_BASE)
RCC_BASE = (AHBPERIPH_BASE + 0x00001000UL)
= ((PERIPH_BASE + 0x00020000UL) + 0x00001000UL)
= ((0x40000000UL+ 0x00020000UL) + 0x00001000UL)
RCC_APB2ENR_IOPBEN = (0x1UL << RCC_APB2ENR_IOPBEN_Pos) = (0x1UL << 3U) = 0000 0000 0000 1000 = 0x00000008UL
// 5、把第4部分代码合并到第3部分后得到
RCC->APB2ENR = *(unsigned int *)(((0x40000000UL+ 0x00020000UL) + 0x00001000UL) + 0x00000018UL)
RCC->APB2ENR |= 0x00000008UL

✅通过查询《STM32F10xxx参考手册》的2.3存储器映像章节得知
复位和时钟控制(RCC) 的基地址 = ((0x40000000UL+ 0x00020000UL) + 0x00001000UL)) = 0x40021000UL

在这里插入图片描述
✅通过查询《STM32F10xxx参考手册》的6.3.11 RCC寄存器地址映像章节得知
RCC_APB2ENR的地址 =(((0x40000000UL+ 0x00020000UL) + 0x00001000UL) + 0x00000018UL) = 0x40021018UL
在这里插入图片描述
✅通过查询通过查询《STM32F10xxx参考手册》6.3.7 APB2外设时钟使能寄存器(RCC_APB2ENR)章节得知
RCC_APB2ENRbit3是用于使能GPIOB的时钟
在这里插入图片描述

1.2. 配置GPIO工作方式

void HAL_GPIO_Init(GPIO_TypeDef *GPIOx, GPIO_InitTypeDef *GPIO_Init)STM32F1xx_HAL_Driver\Src\stm32f1xx_hal_gpio.c文件中的178行,函数定义如下

/**
* @brief  Initializes the GPIOx peripheral according to the specified parameters in the GPIO_Init.
* @param  GPIOx: where x can be (A..G depending on device used) to select the GPIO peripheral
* @param  GPIO_Init: pointer to a GPIO_InitTypeDef structure that contains
*         the configuration information for the specified GPIO peripheral.
* @retval None
*/
void HAL_GPIO_Init(GPIO_TypeDef  *GPIOx, GPIO_InitTypeDef *GPIO_Init)
{
uint32_t position = 0x00u;
uint32_t ioposition;
uint32_t iocurrent;
uint32_t temp;
uint32_t config = 0x00u;
__IO uint32_t *configregister; /* Store the address of CRL or CRH register based on pin number */
uint32_t registeroffset;       /* offset used during computation of CNF and MODE bits placement inside CRL or CRH register */
/* Check the parameters */
assert_param(IS_GPIO_ALL_INSTANCE(GPIOx));
assert_param(IS_GPIO_PIN(GPIO_Init->Pin));
assert_param(IS_GPIO_MODE(GPIO_Init->Mode));
/* Configure the port pins */
while (((GPIO_Init->Pin) >> position) != 0x00u)
{
/* Get the IO position */
ioposition = (0x01uL << position);
/* Get the current IO position */
iocurrent = (uint32_t)(GPIO_Init->Pin) & ioposition;
if (iocurrent == ioposition)
{
/* Check the Alternate function parameters */
assert_param(IS_GPIO_AF_INSTANCE(GPIOx));
/* Based on the required mode, filling config variable with MODEy[1:0] and CNFy[3:2] corresponding bits */
switch (GPIO_Init->Mode)
{
/* If we are configuring the pin in OUTPUT push-pull mode */
case GPIO_MODE_OUTPUT_PP:
/* Check the GPIO speed parameter */
assert_param(IS_GPIO_SPEED(GPIO_Init->Speed));
config = GPIO_Init->Speed + GPIO_CR_CNF_GP_OUTPUT_PP;
break;
/* If we are configuring the pin in OUTPUT open-drain mode */
case GPIO_MODE_OUTPUT_OD:
/* Check the GPIO speed parameter */
assert_param(IS_GPIO_SPEED(GPIO_Init->Speed));
config = GPIO_Init->Speed + GPIO_CR_CNF_GP_OUTPUT_OD;
break;
/* If we are configuring the pin in ALTERNATE FUNCTION push-pull mode */
case GPIO_MODE_AF_PP:
/* Check the GPIO speed parameter */
assert_param(IS_GPIO_SPEED(GPIO_Init->Speed));
config = GPIO_Init->Speed + GPIO_CR_CNF_AF_OUTPUT_PP;
break;
/* If we are configuring the pin in ALTERNATE FUNCTION open-drain mode */
case GPIO_MODE_AF_OD:
/* Check the GPIO speed parameter */
assert_param(IS_GPIO_SPEED(GPIO_Init->Speed));
config = GPIO_Init->Speed + GPIO_CR_CNF_AF_OUTPUT_OD;
break;
/* If we are configuring the pin in INPUT (also applicable to EVENT and IT mode) */
case GPIO_MODE_INPUT:
case GPIO_MODE_IT_RISING:
case GPIO_MODE_IT_FALLING:
case GPIO_MODE_IT_RISING_FALLING:
case GPIO_MODE_EVT_RISING:
case GPIO_MODE_EVT_FALLING:
case GPIO_MODE_EVT_RISING_FALLING:
/* Check the GPIO pull parameter */
assert_param(IS_GPIO_PULL(GPIO_Init->Pull));
if (GPIO_Init->Pull == GPIO_NOPULL)
{
config = GPIO_CR_MODE_INPUT + GPIO_CR_CNF_INPUT_FLOATING;
}
else if (GPIO_Init->Pull == GPIO_PULLUP)
{
config = GPIO_CR_MODE_INPUT + GPIO_CR_CNF_INPUT_PU_PD;
/* Set the corresponding ODR bit */
GPIOx->BSRR = ioposition;
}
else /* GPIO_PULLDOWN */
{
config = GPIO_CR_MODE_INPUT + GPIO_CR_CNF_INPUT_PU_PD;
/* Reset the corresponding ODR bit */
GPIOx->BRR = ioposition;
}
break;
/* If we are configuring the pin in INPUT analog mode */
case GPIO_MODE_ANALOG:
config = GPIO_CR_MODE_INPUT + GPIO_CR_CNF_ANALOG;
break;
/* Parameters are checked with assert_param */
default:
break;
}
/* Check if the current bit belongs to first half or last half of the pin count number
in order to address CRH or CRL register*/
configregister = (iocurrent < GPIO_PIN_8) ? &GPIOx->CRL     : &GPIOx->CRH;
registeroffset = (iocurrent < GPIO_PIN_8) ? (position << 2u) : ((position - 8u) << 2u);
/* Apply the new configuration of the pin to the register */
MODIFY_REG((*configregister), ((GPIO_CRL_MODE0 | GPIO_CRL_CNF0) << registeroffset), (config << registeroffset));
/*--------------------- EXTI Mode Configuration ------------------------*/
/* Configure the External Interrupt or event for the current IO */
if ((GPIO_Init->Mode & EXTI_MODE) == EXTI_MODE)
{
/* Enable AFIO Clock */
__HAL_RCC_AFIO_CLK_ENABLE();
temp = AFIO->EXTICR[position >> 2u];
CLEAR_BIT(temp, (0x0Fu) << (4u * (position & 0x03u)));
SET_BIT(temp, (GPIO_GET_INDEX(GPIOx)) << (4u * (position & 0x03u)));
AFIO->EXTICR[position >> 2u] = temp;
/* Configure the interrupt mask */
if ((GPIO_Init->Mode & GPIO_MODE_IT) == GPIO_MODE_IT)
{
SET_BIT(EXTI->IMR, iocurrent);
}
else
{
CLEAR_BIT(EXTI->IMR, iocurrent);
}
/* Configure the event mask */
if ((GPIO_Init->Mode & GPIO_MODE_EVT) == GPIO_MODE_EVT)
{
SET_BIT(EXTI->EMR, iocurrent);
}
else
{
CLEAR_BIT(EXTI->EMR, iocurrent);
}
/* Enable or disable the rising trigger */
if ((GPIO_Init->Mode & RISING_EDGE) == RISING_EDGE)
{
SET_BIT(EXTI->RTSR, iocurrent);
}
else
{
CLEAR_BIT(EXTI->RTSR, iocurrent);
}
/* Enable or disable the falling trigger */
if ((GPIO_Init->Mode & FALLING_EDGE) == FALLING_EDGE)
{
SET_BIT(EXTI->FTSR, iocurrent);
}
else
{
CLEAR_BIT(EXTI->FTSR, iocurrent);
}
}
}
position++;
}
}

✅假设我们执行的代码如下

#define GPIOB  (GPIO_TypeDef *) GPIOB_BASE
GPIO_InitTypeDef  GPIO_InitStruct;
GPIO_InitStruct.Pin = GPIO_PIN_5;
GPIO_InitStruct.Mode  = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull  = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

✅由于GPIO_InitStruct没有配置外部中断,那么我们可以把void HAL_GPIO_Init(GPIO_TypeDef *GPIOx, GPIO_InitTypeDef *GPIO_Init)外部中断的配置代码断言代码switch中非GPIO_MODE_OUTPUT_PP模式的代码删掉,便于分析代码,于是得到

// 1、简化后的HAL_GPIO_Init函数定义
void HAL_GPIO_Init(GPIO_TypeDef  *GPIOx, GPIO_InitTypeDef *GPIO_Init)
{
uint32_t position = 0x00u;
uint32_t ioposition;
uint32_t iocurrent;
uint32_t temp;
uint32_t config = 0x00u;
__IO uint32_t *configregister;
uint32_t registeroffset;
// 遍历并配置GPIO_Init->Pin的每一个引脚,从STM32F1xx_HAL_Driver\Inc\stm32f1xx_hal_gpio.h文件的第83行可以知道
// GPIOB有16个引脚宏,每个引脚对应一个bit位,GPIO_Init->Pi = GPIO_PIN_5 = 0x0010 = 0000 0000 0001 0000对应了bit5
while (((GPIO_Init->Pin) >> position) != 0x00u)
{
// ioposition在while循环中从0000 0000 000 0001 -> 1000 0000 000 010 -> ... -> 1000 0000 000 000
ioposition = (0x01uL << position);
// ioposition作为掩码和GPIO_Init->Pin进行按位与运算,找到需要配置的IO口
iocurrent = (uint32_t)(GPIO_Init->Pin) & ioposition;
// 当iocurrent == ioposition的时候,成功找到一个需要配置的IO口
if (iocurrent == ioposition)
{
// 根据GPIO_Init->Mode对IO口进行工作方式的配置
switch (GPIO_Init->Mode)
{
// 配置成开漏输出
case GPIO_MODE_OUTPUT_PP:
config = GPIO_Init->Speed + GPIO_CR_CNF_GP_OUTPUT_PP;
break;
}
/* Check if the current bit belongs to first half or last half of the pin count number
in order to address CRH or CRL register*/
configregister = (iocurrent < GPIO_PIN_8) ? &GPIOx->CRL     : &GPIOx->CRH;
registeroffset = (iocurrent < GPIO_PIN_8) ? (position << 2u) : ((position - 8u) << 2u);
/* Apply the new configuration of the pin to the register */
MODIFY_REG((*configregister), ((GPIO_CRL_MODE0 | GPIO_CRL_CNF0) << registeroffset), (config << registeroffset));
}
position++;
}
// 2、从1中我们可以看出,配置寄存器的就这4行代码
config = GPIO_Init->Speed + GPIO_CR_CNF_GP_OUTPUT_PP;
configregister = (iocurrent < GPIO_PIN_8) ? &GPIOx->CRL : &GPIOx->CRH;
registeroffset = (iocurrent < GPIO_PIN_8) ? (position << 2u) : ((position - 8u) << 2u);
MODIFY_REG((*configregister), ((GPIO_CRL_MODE0 | GPIO_CRL_CNF0) << registeroffset), (config << registeroffset));
// 3、我们继续
// 所以现在的分析思路要转换一下,我们要找到作者到底要操作哪个寄存器,然后查《STM32F10xxx参考手册》,所以先展开MODIFY_REG
#define READ_REG(REG)  ((REG))
#define MODIFY_REG(REG, CLEARMASK, SETMASK)  WRITE_REG((REG), (((READ_REG(REG)) & (~(CLEARMASK))) | (SETMASK)))
// MODIFY_REG(REG, CLEARMASK, SETMASK)相当于REG = (REG & (~CLEARMASK)) | (SETMASK)
// 4、所以作者要操作的寄存器是REG = (*configregister) = &GPIOx->CRL = (iocurrent < GPIO_PIN_8) ? &GPIOx->CRL : &GPIOx->CRH
// GPIOx = GPIOB,GPIOB的定义如下
typedef struct
{
__IO uint32_t CRL;
__IO uint32_t CRH;
__IO uint32_t IDR;
__IO uint32_t ODR;
__IO uint32_t BSRR;
__IO uint32_t BRR;
__IO uint32_t LCKR;
} GPIO_TypeDef;
#define PERIPH_BASE           0x40000000UL
#define APB2PERIPH_BASE       (PERIPH_BASE + 0x00010000UL)
#define GPIOB_BASE            (APB2PERIPH_BASE + 0x00000C00UL)
#define GPIOB  ((GPIO_TypeDef *)GPIOB_BASE)
// 所以作者要操作的寄存器是
configregister = &GPIOx->CRL = (0x40000000UL + 0x00010000UL) + 0x00000C00UL = 0x40010C00UL
// 5、把MODIFY_REG宏展开后得到
// MODIFY_REG(REG, CLEARMASK, SETMASK)相当于REG = (REG & (~CLEARMASK)) | (SETMASK)
// REG = (*(&GPIOx->CRL)) = (*0x40010C00UL)
// ~CLEARMASK = ((GPIO_CRL_MODE0 | GPIO_CRL_CNF0) << registeroffset)
//            = ((0x3UL | (0x3UL << 2U)) << 20) 
//            = ~((0011  | (1100)) << 20) 
//            = 1111 1111 0000 1111 1111 1111 1111 1111b
// SETMASK = (config << registeroffset)
//         = (0x3UL << 20)
//         = 1111 1111 0011 1111 1111 1111 1111 1111b
// 即把GPIOB pin5 设置为 推挽输出模式,最大速度50MHZ
(*0x40010C00UL) = ((*0x40010C00UL) & (1111 1111 0000 1111 1111 1111 1111 1111b)) | 1111 1111 0011 1111 1111 1111 1111 1111b

✅通过查询《STM32F10xxx参考手册》的2.3存储器映像章节得知
GPIO端口B的基地址 = ((0x40000000UL + 0x00010000UL)+ 0x00000C00UL) = 0x40010C00UL

在这里插入图片描述
✅通过查询《STM32F10xxx参考手册》的8.5 GPIO和AFIO寄存器地址映象章节得知
GPIO端口B的基地址 = ((0x40000000UL + 0x00010000UL)+ 0x00000C00UL) = 0x40010C00UL0偏移时,对应寄存器GPIOx_CRL

在这里插入图片描述✅通过查询《STM32F10xxx参考手册》的8.2.1 端口配置低寄存器(GPIOx_CRL)章节得知
寄存器GPIOx_CRL,负责配置GPIOx的第0-7个引脚,对应手册中y = 0-7,每个引脚占用4位的寄存器
从这里可以推断出为什么registeroffset需要左移2位 ,因为position代表着当前while循环中配置第几个引脚,position << 2相当于position * 4,正好复合手册中每个引脚需要占用GPIOx_CRL的4bit的描述,而由于一共有16个引脚,所以一个32bit的寄存器只能管理8个引脚,从这里可以推断出为什么((position - 8u) << 2u)

registeroffset = (iocurrent < GPIO_PIN_8) ? (position << 2u) : ((position - 8u) << 2u);

在这里插入图片描述

✅在推导出结果后,我们知道void HAL_GPIO_Init(GPIO_TypeDef *GPIOx, GPIO_InitTypeDef *GPIO_Init)函数就是用来配置GPIOx_CRLGPIOx_CRH寄存器的。此时我们回头重新看三行代码,就很清晰了

config = GPIO_Init->Speed + GPIO_CR_CNF_GP_OUTPUT_PP; // config = 0x3UL + 0x00000000u
configregister = (iocurrent < GPIO_PIN_8) ? &GPIOx->CRL : &GPIOx->CRH;
registeroffset = (iocurrent < GPIO_PIN_8) ? (position << 2u) : ((position - 8u) << 2u);
  1. GPIO_Init->Speed:用来配置GPIOx_CRL或GPIOx_CRH的MODEy[1:0]
  2. GPIO_CR_CNF_GP_OUTPUT_PP:用来配置GPIOx_CRL或GPIOx_CRH的CNFy[1:0]
  3. configregister :用来确认当前配置的是GPIOx_CRL还是GPIOx_CRH
  4. registeroffset :用来确认当前配置的是GPIOx_CRL或GPIOx_CRH的哪个引脚

1.3. 控制GPIOB输出状态

HAL_GPIO_WritePin()STM32F1xx_HAL_Driver\Src\stm32f1xx_hal_gpio.c文件中的465行,如下面代码所示

// 1
void HAL_GPIO_WritePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin, GPIO_PinState PinState)
{
/* Check the parameters */
assert_param(IS_GPIO_PIN(GPIO_Pin));
assert_param(IS_GPIO_PIN_ACTION(PinState));
if (PinState != GPIO_PIN_RESET)
{
GPIOx->BSRR = GPIO_Pin;
}
else
{
GPIOx->BSRR = (uint32_t)GPIO_Pin << 16u;
}
}
// 2、简化后得到
void HAL_GPIO_WritePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin, GPIO_PinState PinState)
{
if (PinState != GPIO_PIN_RESET)
GPIOx->BSRR = GPIO_Pin;
else
GPIOx->BSRR = (uint32_t)GPIO_Pin << 16u;
}

✅假设我们执行的代码如下

HAL_GPIO_WritePin(GPIOB, GPIO_PIN_5, GPIO_PIN_SET)

✅关键代码就2行

// 3、操作寄存器的关键代码
// 置位
GPIOx->BSRR = GPIO_Pin;
// 复位
GPIOx->BSRR = (uint32_t)GPIO_Pin << 16u;
// 4、继续展开代码
// GPIOx = GPIOB,GPIOB的定义如下
typedef struct
{
__IO uint32_t CRL;
__IO uint32_t CRH;
__IO uint32_t IDR;
__IO uint32_t ODR;
__IO uint32_t BSRR;
__IO uint32_t BRR;
__IO uint32_t LCKR;
} GPIO_TypeDef;
#define PERIPH_BASE           0x40000000UL
#define APB2PERIPH_BASE       (PERIPH_BASE + 0x00010000UL)
#define GPIOB_BASE            (APB2PERIPH_BASE + 0x00000C00UL)
#define GPIOB  ((GPIO_TypeDef *)GPIOB_BASE)
&GPIOx->BSRR = ((0x40000000UL + 0x00010000UL) + 0x00000C00UL) + 0x10;

✅同样的套路,先看《STM32F10xxx参考手册》的2.3章节找基地址,接着根据地址偏移量0x10找到具体的寄存器,从手册中可以发现,要置位或清零,直接往对应的引脚位写1即可
在这里插入图片描述

2. 寄存器编程实验

不用库函数,点亮LED灯
✅电路原理图如下,我们只要让GPIOB的PIN1,推挽输出模式,输出低电平,即可让LED_B蓝色亮起来
在这里插入图片描述

int main(void)
{
SystemClock_Config();
// 1. RCC_APB2ENR
*(volatile unsigned int*)0x40021018 |= 0x08UL;
// 2. GPIOx_CRL
*(volatile unsigned int*)0X40010C00 = (*(volatile unsigned int*)0X40010C00) & (~0xF0UL);
*(volatile unsigned int*)0X40010C00 |= 0x30UL;
// 3. GPIOx_BSSR
*(volatile unsigned int*)0X40010C10 |= (0x01UL << 17);
}

在这里插入图片描述

3. 总结

GPIO的使用,分3步走:

  1. 使能GPIO的时钟,寄存器:RCC_APB2ENR
  2. 配置GPIO的工作模式,寄存器:GPIOx_CRLGPIOx_CRH
  3. 控制GPIO的输出状态,寄存器:GPIOx_BSSR

这次的分析,已经奠定了分析HAL库的基础,后续的分析中我们会发现

  1. HAL库操作寄存器的宏,来来去去都是这几个(MODIFY_REGSET_BITREAD_REGCLEAR_BIT)
  2. HAL库的结构体和函数的命名是完全按照《STM32F10x-中文参考手册》中的寄存器来命名的,后续分析中可以尝试根据命名猜测出操作的寄存器

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

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

相关文章

Snap联手多所高校突破:静态模型实现动态化动画生成

这项由Snap公司联合伊利诺伊大学厄巴纳-香槟分校、加州大学圣克鲁兹分校、卡内基梅隆大学以及南洋理工大学共同完成的突破性研究&#xff0c;发表于2026年1月的arXiv预印本平台&#xff0c;论文编号为arXiv:2601.06378v1。有兴趣深入了解的读者可以通过该编号查询完整论文。在动…

django计算机毕设之基于大数据+django+网络爬虫的安客居二手房屋信息采集系统的设计与实现(完整前后端代码+说明文档+LW,调试定制等)

博主介绍&#xff1a;✌️码农一枚 &#xff0c;专注于大学生项目实战开发、讲解和毕业&#x1f6a2;文撰写修改等。全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围&#xff1a;&am…

小波变换特征融合优化实战

&#x1f493; 博客主页&#xff1a;借口的CSDN主页 ⏩ 文章专栏&#xff1a;《热点资讯》 小波变换特征融合优化实战&#xff1a;从理论到边缘计算的高效落地目录小波变换特征融合优化实战&#xff1a;从理论到边缘计算的高效落地 引言&#xff1a;小波变换的复兴与边缘AI的机…

Vue2 vs Vue3:核心差异全面解析

核心架构差异Vue2 使用基于 Object.defineProperty 的响应式系统&#xff0c;仅能监听对象的属性变化&#xff0c;无法直接监听数组的索引操作&#xff08;需通过重写数组方法实现&#xff09;。 Vue3 改用 Proxy API 实现响应式&#xff0c;支持监听对象和数组的全量操作&…

最强卸载工具Geek Uninstaller下载安装全流程图解(附免安装中文版资源包) - xiema

相信很多朋友都遇到过这样的烦恼:电脑越用越卡,明明已经卸载了软件,硬盘空间却没怎么释放; 有些软件赖在系统里死活删不掉; 卸载后注册表还残留大量垃圾,导致系统不稳定。Windows自带的"添加或删除程序&quo…

03.01.01.菲力尔FLIR GigE相机 快速开始篇(ADSpinnaker案例 使用Eclise开发工具:创建Makefile项目方式 获取Spinnaker版本)

本页目录 1、配置2、测试 基础知识 02.01.01.环境搭建篇&#xff08;CentOS9系统下 EPICS安装配置&#xff09; 02.01.02.环境搭建篇&#xff08;CentOS9系统下 synApps的areaDetector安装配置&#xff09; 源代码 epics-base/synApps/support/areaDetector-R3-11/ADSpinn…

Vue+Laravel全栈开发实战指南

环境配置确保已安装Node.js&#xff08;用于Vue开发&#xff09;和Composer&#xff08;用于Laravel依赖管理&#xff09;。Laravel项目可通过以下命令创建&#xff1a;composer create-project laravel/laravel project-name前端项目初始化在Laravel项目根目录下初始化Vue2项目…

【场景:识别C2通信】评估出站IP是否为已知恶意地址,方法:IP离线库+威胁情报融合

最近项目组做了一次安全项目&#xff0c;在联动讨论中&#xff0c;我们团队提出攻克一个一直被“模糊处理”的问题&#xff1a;如何在不引入复杂流量解密、不严重影响性能的前提下&#xff0c;更可靠地识别潜在的 C2通信行为。 其实在我看来这个问题并不新&#xff0c;在往常的…

PHP8.2 vs 8.4:关键差异与升级指南

版本状态差异PHP 8.2 是已发布的稳定版本&#xff08;2022年11月发布&#xff09;&#xff0c;而 PHP 8.4 尚未发布&#xff08;截至2024年7月&#xff09;。PHP 8.3 是当前最新稳定版本&#xff08;2023年11月发布&#xff09;&#xff0c;8.4 预计在2024年底发布。需注意版本…

django毕设选题推荐:基于django数据可视化+网络爬虫的安客居二手房屋信息采集系统的设计与实现【附源码、mysql、文档、调试+代码讲解+全bao等】

博主介绍&#xff1a;✌️码农一枚 &#xff0c;专注于大学生项目实战开发、讲解和毕业&#x1f6a2;文撰写修改等。全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围&#xff1a;&am…

IP定位技术:游戏反外挂体系中的精准识别引擎

在游戏运营管理中&#xff0c;工作室批量账号操作是一个棘手问题。这些行为并非完全无迹可寻&#xff0c;它们往往在网络层面表现出明显的可识别特征。一个显著特点是IP聚集性。工作室为了成本效率&#xff0c;通常会在同一IP或相邻IP段内操作大量账号。尤其是新区开放、活动首…

嵌入模型推理加速:ONNX Runtime在AI原生应用中的使用教程

嵌入模型推理加速&#xff1a;ONNX Runtime在AI原生应用中的使用教程 一、引言&#xff1a;为什么你的嵌入模型跑得比蜗牛还慢&#xff1f; 1.1 一个真实的痛点&#xff1a;RAG应用的"卡脖子"时刻 上周凌晨三点&#xff0c;我收到了创业公司朋友的求助消息&#xff1…

别慌!高AI率论文有救了:我的亲身降重全流程,从90%到10%只需这几招

降AI一年比一年严格了&#xff0c;谁没经历过被知网、维普那些冰冷的红色数字支配的恐惧&#xff1f; 2025年的检测算法早就升级了&#xff0c;它们查的不是简单的关键词重复&#xff0c;而是更深层的逻辑关系。如果你不懂核心逻辑&#xff0c;盲目改词&#xff0c;只会越改越…

AI视觉时代来临:直播美颜SDK与动态贴纸SDK的技术开发新趋势

如果说过去十年是“移动互联网红利期”&#xff0c;那么现在&#xff0c;我们正站在一个全新的拐点上——AI视觉时代。直播、电商、短视频、社交、虚拟人、元宇宙&#xff0c;这些看似不同的赛道&#xff0c;其实都指向同一个核心能力&#xff1a;实时视觉处理技术。而在这个时…

Python毕设选题推荐:基于Python的淘宝玫瑰月季销售预测数据可视化系统 爬虫基于Python的淘宝月季销售预测数据可视化系统【附源码、mysql、文档、调试+代码讲解+全bao等】

java毕业设计-基于springboot的(源码LW部署文档全bao远程调试代码讲解等) 博主介绍&#xff1a;✌️码农一枚 &#xff0c;专注于大学生项目实战开发、讲解和毕业&#x1f6a2;文撰写修改等。全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、…

【计算机毕业设计案例】基于Python爬虫二手房数据可视化系统基于django+网络爬虫的安客居二手房屋信息采集系统的设计与实现(程序+文档+讲解+定制)

博主介绍&#xff1a;✌️码农一枚 &#xff0c;专注于大学生项目实战开发、讲解和毕业&#x1f6a2;文撰写修改等。全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围&#xff1a;&am…

MySQL中如何进行SQL调优?

SQL调优的核心思路是减少磁盘I/O和避免无效计算。 主要就是先通过MySQL的慢查询日志定位慢SQL&#xff0c;再利用EXPLAIN分析执行计划&#xff0c;最后再进行针对性优化。 优化的手段主要有这几大类&#xff0c;分别是索引层面的优化&#xff0c;SQL写法层面的优化以及架构层…

【赶DDL必存】时间紧迫?高效降AI率法:本人亲测从90%到10%的浓缩精华步骤

降AI一年比一年严格了&#xff0c;谁没经历过被知网、维普那些冰冷的红色数字支配的恐惧&#xff1f; 2025年的检测算法早就升级了&#xff0c;它们查的不是简单的关键词重复&#xff0c;而是更深层的逻辑关系。如果你不懂核心逻辑&#xff0c;盲目改词&#xff0c;只会越改越…

毕业生必看!降论文AI率核心技巧公开,照着做你也能从90%降到10%(实测)

降AI一年比一年严格了&#xff0c;谁没经历过被知网、维普那些冰冷的红色数字支配的恐惧&#xff1f; 2025年的检测算法早就升级了&#xff0c;它们查的不是简单的关键词重复&#xff0c;而是更深层的逻辑关系。如果你不懂核心逻辑&#xff0c;盲目改词&#xff0c;只会越改越…

什么是RPC框架?

RPC&#xff08;Remote Procdure Call&#xff09;远程过程调用是一种用于实现在分布式系统中进行跨网络通信的技术&#xff0c;也是一种计算机通信协议。 RPC框架是基于RPC协议实现的。 RPC允许一个程序&#xff08;服务消费者&#xff09;像调用自己程序的方法一样&#xff0…