细说ARM MCU中的HAL_GPIO_Init()函数的实现过程

2024-06-02 08:44

本文主要是介绍细说ARM MCU中的HAL_GPIO_Init()函数的实现过程,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

一、实例背景:

二、HAL_GPIO_Init函数的格式如下:

1、HAL_GPIO_Init函数中while语句的条件表达式

2、HAL_GPIO_Init函数中的iocurrent变量赋值语句

3、HAL_GPIO_Init函数中的三条if语句

5、 GPIO作为输入时的电路

6、I/O作为输入时执行的语句

7、I/O作为输出的相关电路说明

8、I/O作为输入时执行的语句

9、读取GPIO状态HAL_GPIO_ReadPin()函数

三、重写读/写GPIO的代码


        继续上一篇文章,分析HAL_GPIO_Init函数是如何实现的,该函数的定义在stm32g4xx_hal _gpio.c文件中。

一、实例背景:

        本文中使用ST的开发板NUCLEO-G474RE,板上MCU型号为STM32G474RET6。配套的扩展板:

        实例中当开发板上的按键B1被按下时,PC13引脚被上拉至高电平VDD,不按下时,PC13下拉至低电平GND。用按键B1控制板上的LD2灯的亮灭,当PA5输出高电平时LD2亮,否则灯灭。

二、HAL_GPIO_Init函数的格式如下:

void HAL_GPIO_Init(GPIO_TypeDef *GPIOx,GPIO_InitTypeDef *GPIO_Init)

        HAL_GPIO_Init函数是void类型,不需要返回值。它有两个参数:一个是端口,为结构体类型,与HAL_GPIO_TogglePin()相同;另一个是端口的配置参数,也是结构体类型。stm32g4xx _hal_gpio.c文件中给出的HAL_GPIO_Init区(部分)如下:

void HAL_GPIO_Init(GPIO_TypeDef  *GPIOx, GPIO_InitTypeDef *GPIO_Init)
{uint32_t position = 0x00U;uint32_t iocurrent;uint32_t temp;/* 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) != 0U){/* Get current io position */iocurrent = (GPIO_Init->Pin) & (1UL << position);if (iocurrent != 0x00u){/*--------------------- GPIO Mode Configuration ------------------------*//* In case of Output or Alternate function mode selection */if(((GPIO_Init->Mode & GPIO_MODE) == MODE_OUTPUT) ||((GPIO_Init->Mode & GPIO_MODE) == MODE_AF)){/* Check the Speed parameter */assert_param(IS_GPIO_SPEED(GPIO_Init->Speed));/* Configure the IO Speed */temp = GPIOx->OSPEEDR;temp &= ~(GPIO_OSPEEDR_OSPEED0 << (position * 2U));temp |= (GPIO_Init->Speed << (position * 2U));GPIOx->OSPEEDR = temp;/* Configure the IO Output Type */temp = GPIOx->OTYPER;temp &= ~(GPIO_OTYPER_OT0 << position) ;temp |= (((GPIO_Init->Mode & OUTPUT_TYPE) >> OUTPUT_TYPE_Pos) << position);GPIOx->OTYPER = temp;}if ((GPIO_Init->Mode & GPIO_MODE) != MODE_ANALOG){/* Check the Pull parameter */assert_param(IS_GPIO_PULL(GPIO_Init->Pull));/* Activate the Pull-up or Pull down resistor for the current IO */temp = GPIOx->PUPDR;temp &= ~(GPIO_PUPDR_PUPD0 << (position * 2U));temp |= ((GPIO_Init->Pull) << (position * 2U));GPIOx->PUPDR = temp;}/* In case of Alternate function mode selection */if ((GPIO_Init->Mode & GPIO_MODE) == MODE_AF){/* Check the Alternate function parameters */assert_param(IS_GPIO_AF_INSTANCE(GPIOx));assert_param(IS_GPIO_AF(GPIO_Init->Alternate));/* Configure Alternate function mapped with the current IO */temp = GPIOx->AFR[position >> 3U];temp &= ~(0xFU << ((position & 0x07U) * 4U));temp |= ((GPIO_Init->Alternate) << ((position & 0x07U) * 4U));GPIOx->AFR[position >> 3U] = temp;}/* Configure IO Direction mode (Input, Output, Alternate or Analog) */temp = GPIOx->MODER;temp &= ~(GPIO_MODER_MODE0 << (position * 2U));temp |= ((GPIO_Init->Mode & GPIO_MODE) << (position * 2U));GPIOx->MODER = temp;/*--------------------- EXTI Mode Configuration ------------------------*//* Configure the External Interrupt or event for the current IO */if ((GPIO_Init->Mode & EXTI_MODE) != 0x00u){/* Enable SYSCFG Clock */__HAL_RCC_SYSCFG_CLK_ENABLE();temp = SYSCFG->EXTICR[position >> 2U];temp &= ~(0x0FUL << (4U * (position & 0x03U)));temp |= (GPIO_GET_INDEX(GPIOx) << (4U * (position & 0x03U)));SYSCFG->EXTICR[position >> 2U] = temp;/* Clear Rising Falling edge configuration */temp = EXTI->RTSR1;temp &= ~(iocurrent);if ((GPIO_Init->Mode & TRIGGER_RISING) != 0x00U){temp |= iocurrent;}EXTI->RTSR1 = temp;temp = EXTI->FTSR1;temp &= ~(iocurrent);if ((GPIO_Init->Mode & TRIGGER_FALLING) != 0x00U){temp |= iocurrent;}EXTI->FTSR1 = temp;temp = EXTI->EMR1;temp &= ~(iocurrent);if ((GPIO_Init->Mode & EXTI_EVT) != 0x00U){temp |= iocurrent;}EXTI->EMR1 = temp;/* Clear EXTI line configuration */temp = EXTI->IMR1;temp &= ~(iocurrent);if ((GPIO_Init->Mode & EXTI_IT) != 0x00U){temp |= iocurrent;}EXTI->IMR1 = temp;}}position++;}
}

1、HAL_GPIO_Init函数中while语句的条件表达式

        HAL_GPIO_Init函数中主要就是一个while的条件表达式:

((GPIO_Init->Pin)>>position) != 0U

        其中,最后的“0U”,是无符号数0是无符(Unsigned int)。后面的1UL,“1”后面的“UL”表示数1为Unsigned long int。

        此处的GPIO_Init -> Pin中GPIO_Init是通过结构体变量传递过来的参数,实际就是引脚号。因此,对PC13来说,这个GPIO_Init->Pin就是GPIO_PIN_13对应的数(0010 0000 0000 0000),即0x2000,“>>”表示右移。右移的位数在position变量中。由于position初始为0,0x2000右移0位,值不会改变,还是0x2000,条件不等于0,所以,while的条件是满足的,程序继续执行。

2、HAL_GPIO_Init函数中的iocurrent变量赋值语句

        继续,给变量iocurrent赋值:

iocurrent = (GPIO_Init->Pin) & (1UL <<position);

        当前是在配置PC13,此时GPIO_Init->Pin为0x2000,按位逻辑“与”后面的1UL<<position,是将“1”左移position位,而position此时为0,所以还是1。用0x2000与1按位相“与”,结果为0。所以,接下来的if语句的条件(iocurrent!=0x00u)不满足的,此时程序就会跳到if之外,执行最后的position++,让position自加1;执行后,position为1。随后,会继续判断while条件是否成立。当然,虽然此时position为1,当0x2000右移1位后,依然不等于0,条件是满足的。接着执行iocurrent赋值语句,此时将0x2000与0x0002按位相与,结果iocurrent依然为0。所以,会继续执行position++语句,一直到position为13,此时在while的条件中0x2000右移13位,结果为1,还是不等于0,所以while条件还是满足的。接下来将会继续执行iocurrent赋值语句,此时1左移13位的结果刚好为0x2000,而GPIO_Init->Pin也为0x2000,这两个数按位逻辑“与”的结果就不再为0了;接下来的if语句,条件是满足的,所以会执行if中的语句。

3、HAL_GPIO_Init函数中的三条if语句

        在if(iocurrent!=0x00u){...}中还有三条if语句:

        第一条if语句用于复用功能(alternate function),本例中是用作GPIO,所以不会执行。

        第二条if语句输出或复用功能,由于当前是配置PC13作为输入,所以此时也不会执行。

        第三条if语句用于外部中断、触发等模式,本例中也不会执行。

      此外,在第一条if语句与第二条if语句之间有段配置I/O模式的语句(配置GPIO的GPIOx->MODER寄存器),在第二条if语句和第三条if语句之间有段配置I/O上拉/下拉功能的(配置GPIO的GPIOx_PUPDR寄存器),这两段代码在配置GPIO的输入与输出功能时都会执行。

4、I/O作为输入时执行的语句

        配置PC13作为输入引脚时会执行下面4条语句:

/* Activate the Pull-up or Pull down resistor for the current IO */
temp = GPIOx -> PUPDR;
temp &= ~(GPIO_PUPDR_PUPD0 << (position *2U));
temp |= ((GPIO_Init->Pull) << (position *2U));
GPIOx -> PUPDR = temp;

        第一条语句是将GPIOx -> PUPDR赋值给变量temp。PUPDR是GPIO的寄存器。查STM32G4系列MCU的参考手册,可以看到PUPDR的寄存结构:

31

30

29

28

27

26

25

24

23

22

21

20

19

18

17

16

PUPD15[1:0]

PUPD14[1:0]

PUPD13[1:0]

PUPD12[1:0]

PUPD11[1:0]

PUPD10[1:0]

PUPD9[1:0]

PUPD8[1:0]

rw

rw

rw

rw

rw

rw

rw

rw

rw

rw

rw

rw

rw

rw

rw

rw

15

14

13

12

11

10

9

8

7

6

5

4

3

2

1

0

PUPD7[1:0]

PUPD6[1:0]

PUPD5[1:0]

PUPD4[1:0]

PUPD3[1:0]

PUPD2[1:0]

PUPD1[1:0]

PUPD0[1:0]

rw

rw

rw

rw

rw

rw

rw

rw

rw

rw

rw

rw

rw

rw

rw

rw

 GPIOx_PUPDR寄存器(x为A、B、C、D、E、F、G)结构

        PUPDR有32位,每2位构成一组,共有16组,即PUPD0~PUPD15(PUPD是Pull-up,Pull-down的缩写)。实际上每一组PUPDx[1:0]对应一个GPIO端口引脚的上拉/下拉配置(二进制):

00:没有上拉,下拉;
01:上拉;
10:下拉;
11:保留。

        由于配置的是PC13,所以对应的就是PUPD13[1:0],它们在PUPD寄存器的第26和27位(最低位从0开始)。

        此外,还需要提一下PUPDR寄存器的默认值。在STM32G4系列MCU的参考手册中,提到了GPIOx_PUPDR寄存器的默认值(初始值,即复位后的值)。对于端口A(GPIOA),默认值是0x6400 0000。也就是说,PUPD15[1:0]=01(二进制),PA15默认为上拉;PUPD14[1:0]=10(二进制),PA14默认为下拉。

        对于端口B(GPIOB),默认值为0x0000 0100。也就是说,除了PUPD4[1:0]=01以外,其它均为00。意思是PB4默认为上拉,其他不开启上拉、下拉功能。

        除了GPIOA和GPIOB以外,对于其他GPIO,PUPDR寄存器的值均为0x0000 0000,即为不开启上拉、下拉功能。由于PC13属于GPIOC,所以默认情况下该端口的PUPDR的值为0。因此,执行temp = GPIOx->PUPDR语句后,temp的值为0。

5、 GPIO作为输入时的电路

        GPIO的引脚既可以用作输入,也可以用作输出,但是在同一时刻,只能配置为其中一种(即要么为输入,要么为输出)。用作输入时,输出通道是要关闭的。

6、I/O作为输入时执行的语句

        继续,分析配置PUPDR的代码。接下来的一行语句是:

temp &= ~(GPIO_PUPDR_PUPD0 << (position *2U));

        其中,“&=”是将变量temp与“=”后的表达式的值相“与”,并把结果赋给temp。不过由于执行完前面的赋值语句后,temp已经为0了,所以执行这一条语句后,temp会依然为0。

        在这条语句中,GPIO_PUPDR_PUPD0是个常量,实际就是在PUPDR寄存器中PUP0[1:0]所在的位的掩码,也就是表中的最低2位的掩码,用二进制表示掩码就是11,即十进制数值3。

        由于此时position为13(十进制数),与2相乘就是26,所以上边语句中等号右侧的表达式意思就是将二进制11左移26位,此时得到的值是一个第26和27位为1、其他位均为0的32位数:0x0C00 0000。然后取反,得到的数值为0xf3ff ffff。不过,与temp相“与”后,结果还是为0。所以,执行完这一条语句后,temp的值还是0。

        接下来的语句是:

temp |= ((GPIO_Init->Pull) << (position *2U));

        其中,GPIO_Init是HAL_GPIO_Init()函数的第二个参数,是由MX_GPIO_Init(void)函数传递过来的。在MX_GPIO_Init(void)函数中,就是结构体变量GPIO_InitStruct。在该函数中,该变量做过如下赋值:

GPIO_InitStruct.Pull =GPIO_PULLDOWN;

        所以,上述语句中的GPIO_Init->Pull就是GPIO_PULLDOWN,也就是下拉。在STM32g4xx _hal_gpio.h中,关于GPIO_PULLDOWN有一个宏定义:

# define GPIO_PULLDOWN (0x0000 0002U)

        此时positon为13,GPIO_PULLDOWN的值是2,用二进制表示就是10。重面己知,此时position为13,position*2就是26;二进制数10左移26位,结果是0x0800 0000;,

        “|=”是将“=”后的表达式的值与temp相“或”,然后再赋值给temp。所以,此句执行完的值为0x0800 0000。

        接下来的语句是:

GPIOx -> PUPDR = temp;

        该句表示将temp的值赋值给PUPDR寄存器,也就是说,把0x0800 0000赋值给PUPDR寄存器。对照表中PUPDR寄存器的结构,刚好是PUPD13[1:0]的值为二进制数10,也就是将PC13配置为下拉模式。

        至此,PUPDR就配置完毕。

7、I/O作为输出的相关电路说明

要配置PA5作为输出引脚,先来看一下将GPIO配置为输出I/O功能需要做哪些事情。

        在STM32 MCU中,输出有两种模式:一种是开漏(open drain),另一种是推挽(push-pull)。这是两种常见的电路输出方式:

        在push-pull模式时,会用到P-MOS和N-MOS两个MOS管。这两个管子是互补输出的,也就是说,上面的P-MOS导通,下面的N-MOS就会截止,此时输出高电平;P-MOS截止,N-MOS导通,则输出低电平。

        开漏(open drain)的“漏”,就是MOS管的漏极。开漏模式就是由MOS管的漏为输出。此时,只需要将下面那个N-MOS管接在输出和地之间。开漏模式比推挽模式少用一个P-MOS。此时,要输出高电平就需要上拉电阻配合。因此,如果设置了为开漏输出模式,通常要配置为上拉。

8、I/O作为输入时执行的语句

        接着分析HAL_GPIO_Init函数。在配置PA5时,程序会执行到中间这条if语句。再来分析一下配置GPIOx_OSPEED寄存器的过程。这个寄存器是配置I/O引脚速度参数的,关键的几句代码如下:

/*In case of Output or Alternate function mode selection */
if(…)
{……/*Configure the IO Speed */temp = GPIOx -> OSPEEDR;temp &= ~(GPIO_OSPEEDR_OSPEED0 << (position *2U));temp |= (GPIO_Init->Speed << (position *2U));GPIOx -> OSPEEDR = temp;……
}

        配置这个寄存器也是执行4条语句,与前面介绍的配置PUPDR寄存器的过程基本类似。在配置PA5时,当程序执行到这几句代码时,变量position的值应该是5。

        第一条语句是将GPIOx->OSPEEDR赋值给变量temp。OSPEED是GPIO的寄存器。查看STM32G4系列MCU的参考手册,可以看到OSPEED的寄存器结构。OSPEED有32位,每2位构成一组,共有16组,即OSPEED0~OSPEED15。实际上,每一组OSPEEDx[1:0]对应一个GPIO端口引脚的速度配置(二进制):

31

30

29

28

27

26

25

24

23

22

21

20

19

18

17

16

OSPEED15[1:0]

OSPEED14[1:0]

OSPEED13[1:0]

OSPEED12[1:0]

OSPEED11[1:0]

OSPEED10[1:0]

OSPEED9[1:0]

OSPEED8[1:0]

rw

rw

rw

rw

rw

rw

rw

rw

rw

rw

rw

rw

rw

rw

rw

rw

15

14

13

12

11

10

9

8

7

6

5

4

3

2

1

0

OSPEED7[1:0]

OSPEED6[1:0]

OSPEED5[1:0]

OSPEED4[1:0]

OSPEED3[1:0]

OSPEED2[1:0]

OSPEED1[1:0]

OSPEED0[1:0]

rw

rw

rw

rw

rw

rw

rw

rw

rw

rw

rw

rw

rw

rw

rw

rw

 GPIOx_OSPEED寄存器(x为A、B、C、D、E、F、G)结构

00:低速;
01:中速;
10:高速;
11:很高速。

       GPIOx_OSPEED寄存器的默认值有两种情况:对于端口A(GPIOA),默认值为0x0C00 0000;对于其他端口,默认值一律为0x0000 0000。

        此时配置的是PA5,对应的就是OSPEED5[1:0],它们在OSPEED寄存器的第10和11位(最低位从0开始)。不过,由于GPIOA的OSPEED寄存器,默认值为0x0C00 0000,所以执行完这条赋值语句后,temp的值为0x0C00 0000。然后,

temp &= ~(GPIO_OSPEEDR_OSPEED0 << (position *2U));

        “&=”是将变量temp与“=”后的表达式的值相“与”,并把结果赋给temp。GPIO_OSPEEDR _OSPEED0 是个常量,实际就是在OSPEED寄存器中,OSPEEDO[1:0]在位的掩码,也就是表中的最低2位的掩码,用二进制表示掩码就是11,即数值3(十进制)。

        由于此时position为5,与2相乘就是10(十进制)。所以上边的语句中等号右侧的表达式意思就是将二进制11左移10位,结果为0x0000 0C00;然后,将该值取反,可以得到0xFFFF F3FF。

        将temp当前的值0x0C00 0000与0xFFFF F3FF相“与”,结果为0x0C00 0000,该值会赋给temp。所以该语句执行完毕后,temp的值为0x0C00 0000。这个结果与OSPEED寄存器的默认值相比较,在数值上没有什么变化。做这些操作有何意义呢?

        实际上,这两个temp赋值语句的目的是,将OSPEED寄存器中要配置的相应位(对PA5来说就是第10和11位)变为00,以便接下来做修改。这里与默认值相同的原因是,在此次读取OSPEED寄存器的时刻,这两位本来就为00。继续:

temp |= (GPIO_Init->Speed << (position *2U));

        该语句中,GPIO_Init -> Speed是取出在MX_GPIO_Init函数中配置的Speed值:

GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
GPIO_SPEED_FREQ_HIGH在stm32g4**_hal_gpio.h中被定义如下:
# define GPIO_SPEED_FREQ_HIGH (0x00000002U)

        也就是二进制数10,是高速时钟。将二进制数10左移10位后,结果为0x0000 0800,与temp的当前值0x0C00 0000相“或”后,得到0x0C00 0800。这个结果与OSPEED寄存器的默认值相比较,OSPEED5[1:0]的值被修改为二进制数10,即配置PA5的速度为高速。

9、读取GPIO状态HAL_GPIO_ReadPin()函数

GPIO_PinState HAL_GPIO_ReadPin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin)
{GPIO_PinState bitstatus;/* Check the parameters */assert_param(IS_GPIO_PIN(GPIO_Pin));if ((GPIOx->IDR & GPIO_Pin) != 0x00U){bitstatus = GPIO_PIN_SET;}else{bitstatus = GPIO_PIN_RESET;}return bitstatus;
}

        HAL_GPIO_ReadPin函数的类型是GPIO_PinState,而GPIO_PinState是枚举类型。它有两个成员GPIO_PIN_RESET和GPIO_PIN_SET,取值为0和1。调用HAL_GPIO_ReadPin函数,需要返回引脚的状态,该状态要么为0(低电平),要么为1(高电平)。

        在HAL_GPIO_ReadPin函数中,首先声明了类型同样为GPIO_PinState的变量bitstatus,紧接着是一个assert_param()语句,用于判断传递过来的引脚号是否在有效范围内;随后,在if语句的条件表达式中,读取了GPIO的输入数据寄存器IDR。GPIOx ->IDR & GPIO_PIN的作用是取出IDR中与引脚号相对应的位,如果该位不为0,则将1(GPIO_PIN_SET)赋给变量bitstatus;如果该位为0,则将0赋给bitstatus。if语句之后,用return返回bitstatus。

        在STM32G4系列MCU的参考手册中,可以查到IDR寄存器的结构:

31

30

29

28

27

26

25

24

23

22

21

20

19

18

17

16

Res

Res

Res

Res

Res

Res

Res

Res

Res

Res

Res

Res

Res

Res

Res

Res

15

14

13

12

11

10

9

8

7

6

5

4

3

2

1

0

ID15

ID14

ID13

ID12

ID11

ID10

ID9

ID8

ID7

ID6

ID5

ID4

ID3

ID2

ID1

ID0

r

r

r

r

r

r

r

r

r

r

r

r

r

r

r

r

 GPIOx_IDR寄存器(x为A、B、C、D、E、F、G)结构

        IDR只是用了低16位,分别对应GPIO端口的16个引脚。就是相应引脚的输入状态数据,不过,ID[15:0]下面标有“r”,表示该位只可读。

三、重写读/写GPIO的代码

        用PC13作为按键状态的输入引脚,该引脚所属端口为GPIOC;用PB5作为控制发光二极管的输出引脚,所属端口为GPIOB。不过,在前面配置端口的模式时,给PC13起一个用户标识KEY,给PB5起的是LED,所以在main.h文件中,可以看到如下的宏定义:

# define KEY_Pin GPIO_PIN_13
# define KEY_GPIO_Port GPIOC
# define LED_Pin GPIO_PIN_5
# define LED_GPIO_Port GPIOA

        这样,就可以写出这两个函数的完整语句:

HAL_GPIO_ReadPin(KEY_GPIO_Port,KEY_Pin);
HAL_GPIO_WritePin(LED_GPIO_Port,LED_Pin,GPIO_PIN_SET);

        由于HAL_GPIO_ReadPin函数需要返回引脚的输入状态,类型是GPIO_PinState,所以,可以先在main.c中定义一个变量KEY,类型为GPIO_PinState。因为GPIO_PinState的成员是GPIO_ PIN_RESET和GPIO_PIN_SET,实际就是0和1,所以,也可以将变量KEY的类型定义为uint8_t。

/*USER CODE BEGIN 1 */
GPIO_PinState KEY;
/*USER CODE END 1 */
/*Infinite loop */
while(1)
{/*USER CODE BEGIN 3*/KEY = HAL_GPIO_ReadPin(KEY_GPIO_Port,KEY_Pin);if(KEY == GPIO_PIN_SET){HAL_GPIO_WritePin(LED_GPIO_Port,LED_Pin,GPIO_PIN_RESET);}else{HAL_GPIO_WritePin(LED_GPIO_Port,LED_Pin,GPIO_PIN_SET);}
}
/*USER CODE END 3*/

        代码编写完毕后,编译工程。如果没有出现错误,就可以下载到硬件中。运行后,开发板上的LD2默认状态下是点亮着的,按下B1键后LD2灯熄灭。如此便实现了通过按键控制LED灯亮灭的效果。

这篇关于细说ARM MCU中的HAL_GPIO_Init()函数的实现过程的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1023486

相关文章

HTML5 getUserMedia API网页录音实现指南示例小结

《HTML5getUserMediaAPI网页录音实现指南示例小结》本教程将指导你如何利用这一API,结合WebAudioAPI,实现网页录音功能,从获取音频流到处理和保存录音,整个过程将逐步... 目录1. html5 getUserMedia API简介1.1 API概念与历史1.2 功能与优势1.3

Java实现删除文件中的指定内容

《Java实现删除文件中的指定内容》在日常开发中,经常需要对文本文件进行批量处理,其中,删除文件中指定内容是最常见的需求之一,下面我们就来看看如何使用java实现删除文件中的指定内容吧... 目录1. 项目背景详细介绍2. 项目需求详细介绍2.1 功能需求2.2 非功能需求3. 相关技术详细介绍3.1 Ja

使用Python和OpenCV库实现实时颜色识别系统

《使用Python和OpenCV库实现实时颜色识别系统》:本文主要介绍使用Python和OpenCV库实现的实时颜色识别系统,这个系统能够通过摄像头捕捉视频流,并在视频中指定区域内识别主要颜色(红... 目录一、引言二、系统概述三、代码解析1. 导入库2. 颜色识别函数3. 主程序循环四、HSV色彩空间详解

PostgreSQL中MVCC 机制的实现

《PostgreSQL中MVCC机制的实现》本文主要介绍了PostgreSQL中MVCC机制的实现,通过多版本数据存储、快照隔离和事务ID管理实现高并发读写,具有一定的参考价值,感兴趣的可以了解一下... 目录一 MVCC 基本原理python1.1 MVCC 核心概念1.2 与传统锁机制对比二 Postg

SpringBoot整合Flowable实现工作流的详细流程

《SpringBoot整合Flowable实现工作流的详细流程》Flowable是一个使用Java编写的轻量级业务流程引擎,Flowable流程引擎可用于部署BPMN2.0流程定义,创建这些流程定义的... 目录1、流程引擎介绍2、创建项目3、画流程图4、开发接口4.1 Java 类梳理4.2 查看流程图4

C++中零拷贝的多种实现方式

《C++中零拷贝的多种实现方式》本文主要介绍了C++中零拷贝的实现示例,旨在在减少数据在内存中的不必要复制,从而提高程序性能、降低内存使用并减少CPU消耗,零拷贝技术通过多种方式实现,下面就来了解一下... 目录一、C++中零拷贝技术的核心概念二、std::string_view 简介三、std::stri

C++高效内存池实现减少动态分配开销的解决方案

《C++高效内存池实现减少动态分配开销的解决方案》C++动态内存分配存在系统调用开销、碎片化和锁竞争等性能问题,内存池通过预分配、分块管理和缓存复用解决这些问题,下面就来了解一下... 目录一、C++内存分配的性能挑战二、内存池技术的核心原理三、主流内存池实现:TCMalloc与Jemalloc1. TCM

OpenCV实现实时颜色检测的示例

《OpenCV实现实时颜色检测的示例》本文主要介绍了OpenCV实现实时颜色检测的示例,通过HSV色彩空间转换和色调范围判断实现红黄绿蓝颜色检测,包含视频捕捉、区域标记、颜色分析等功能,具有一定的参考... 目录一、引言二、系统概述三、代码解析1. 导入库2. 颜色识别函数3. 主程序循环四、HSV色彩空间

Python中help()和dir()函数的使用

《Python中help()和dir()函数的使用》我们经常需要查看某个对象(如模块、类、函数等)的属性和方法,Python提供了两个内置函数help()和dir(),它们可以帮助我们快速了解代... 目录1. 引言2. help() 函数2.1 作用2.2 使用方法2.3 示例(1) 查看内置函数的帮助(

Python实现精准提取 PDF中的文本,表格与图片

《Python实现精准提取PDF中的文本,表格与图片》在实际的系统开发中,处理PDF文件不仅限于读取整页文本,还有提取文档中的表格数据,图片或特定区域的内容,下面我们来看看如何使用Python实... 目录安装 python 库提取 PDF 文本内容:获取整页文本与指定区域内容获取页面上的所有文本内容获取