细说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

相关文章

Redis分片集群的实现

《Redis分片集群的实现》Redis分片集群是一种将Redis数据库分散到多个节点上的方式,以提供更高的性能和可伸缩性,本文主要介绍了Redis分片集群的实现,具有一定的参考价值,感兴趣的可以了解一... 目录1. Redis Cluster的核心概念哈希槽(Hash Slots)主从复制与故障转移2.

springboot+dubbo实现时间轮算法

《springboot+dubbo实现时间轮算法》时间轮是一种高效利用线程资源进行批量化调度的算法,本文主要介绍了springboot+dubbo实现时间轮算法,文中通过示例代码介绍的非常详细,对大家... 目录前言一、参数说明二、具体实现1、HashedwheelTimer2、createWheel3、n

使用Python实现一键隐藏屏幕并锁定输入

《使用Python实现一键隐藏屏幕并锁定输入》本文主要介绍了使用Python编写一个一键隐藏屏幕并锁定输入的黑科技程序,能够在指定热键触发后立即遮挡屏幕,并禁止一切键盘鼠标输入,这样就再也不用担心自己... 目录1. 概述2. 功能亮点3.代码实现4.使用方法5. 展示效果6. 代码优化与拓展7. 总结1.

Mybatis 传参与排序模糊查询功能实现

《Mybatis传参与排序模糊查询功能实现》:本文主要介绍Mybatis传参与排序模糊查询功能实现,本文通过实例代码给大家介绍的非常详细,感兴趣的朋友跟随小编一起看看吧... 目录一、#{ }和${ }传参的区别二、排序三、like查询四、数据库连接池五、mysql 开发企业规范一、#{ }和${ }传参的

Docker镜像修改hosts及dockerfile修改hosts文件的实现方式

《Docker镜像修改hosts及dockerfile修改hosts文件的实现方式》:本文主要介绍Docker镜像修改hosts及dockerfile修改hosts文件的实现方式,具有很好的参考价... 目录docker镜像修改hosts及dockerfile修改hosts文件准备 dockerfile 文

C/C++错误信息处理的常见方法及函数

《C/C++错误信息处理的常见方法及函数》C/C++是两种广泛使用的编程语言,特别是在系统编程、嵌入式开发以及高性能计算领域,:本文主要介绍C/C++错误信息处理的常见方法及函数,文中通过代码介绍... 目录前言1. errno 和 perror()示例:2. strerror()示例:3. perror(

基于SpringBoot+Mybatis实现Mysql分表

《基于SpringBoot+Mybatis实现Mysql分表》这篇文章主要为大家详细介绍了基于SpringBoot+Mybatis实现Mysql分表的相关知识,文中的示例代码讲解详细,感兴趣的小伙伴可... 目录基本思路定义注解创建ThreadLocal创建拦截器业务处理基本思路1.根据创建时间字段按年进

SpringBoot3实现Gzip压缩优化的技术指南

《SpringBoot3实现Gzip压缩优化的技术指南》随着Web应用的用户量和数据量增加,网络带宽和页面加载速度逐渐成为瓶颈,为了减少数据传输量,提高用户体验,我们可以使用Gzip压缩HTTP响应,... 目录1、简述2、配置2.1 添加依赖2.2 配置 Gzip 压缩3、服务端应用4、前端应用4.1 N

将Mybatis升级为Mybatis-Plus的详细过程

《将Mybatis升级为Mybatis-Plus的详细过程》本文详细介绍了在若依管理系统(v3.8.8)中将MyBatis升级为MyBatis-Plus的过程,旨在提升开发效率,通过本文,开发者可实现... 目录说明流程增加依赖修改配置文件注释掉MyBATisConfig里面的Bean代码生成使用IDEA生

SpringBoot实现数据库读写分离的3种方法小结

《SpringBoot实现数据库读写分离的3种方法小结》为了提高系统的读写性能和可用性,读写分离是一种经典的数据库架构模式,在SpringBoot应用中,有多种方式可以实现数据库读写分离,本文将介绍三... 目录一、数据库读写分离概述二、方案一:基于AbstractRoutingDataSource实现动态