创建自己的函数库

news/2024/4/29 6:47:49/文章来源:https://blog.csdn.net/code_lyb/article/details/127910532

创建自己的函数库

  • 前言
  • 一、什么是STM32标准函数库
    • 1.定义:
    • 2.作用:
    • 3.对比:
  • 二、构建库函数
    • 1.修改寄存器地址封装
    • 2.定义访问的结构体指针和引脚
    • 3.创建封装函数
      • 3.1创建拉低引脚函数
      • 3.2创建引脚初始化函数
  • 总结


前言

回顾一下,前面点亮led灯我们都进行了哪些操作。

首先需要看电路图,然后找到led灯的控制引脚,然后了解了控制引脚的方法是通过操作相应的物理地址,接着知道了可以映射物理地址也就是寄存器,通过寄存器来去配置,最后我们通过去查找芯片手册,了解各个寄存器的功能,对需要的寄存器进行配置,实现点亮led灯的功能。

到这里,我们成功将一大串的地址转化成可读性更好的寄存器,但是寄存器的操作相对于大部分人来说仍然是太复杂,大部分人只需要点亮灯,并不想知道它需要用到哪些寄存器,更不想去进行复杂的位操作,大家更希望能将寄存器的这些功能再一次进行封装打包,最好是进行一些简单的传参就可以将这个引脚配置好,所以库函数诞生了。库函数的作用,就是将寄存器根据其功能封装成一个个更加易于调用的函数接口,从而使代码的开发效率更高,可读性更好,更加易于维护。


一、什么是STM32标准函数库

1.定义:

他是ST公司针对stm32设计的一系列函数接口,即API(Application Program Interface)。

2.作用:

让开发者可调用这些函数接口来配置 STM32的寄存器,使开发人员得以脱离最底层的寄存器操作。
在这里插入图片描述

3.对比:

直接代码对比,第一个main函数和第二个main函数所实现的功能是一样的,但是第一个无论是否是开发者本人,都能很清楚明白的看明白代码在干嘛。而第二个main函数,只怕是开发者本人,时间长了也要回头挨条去查一下自己配置这些是在干嘛,一对比,高下立现。

int main(void)
{	led_init();LED_RED=ON;while(1);
}
int main(void)
{	RCC_AHB1ENR |= (1<<7);	GPIOH_MODER  &= ~( 0x03<< (2*10));	GPIOH_MODER |= (1<<2*10);GPIOH_OTYPER &= ~(1<<1*10);GPIOH_OTYPER |= (0<<1*10);GPIOH_OSPEEDR &= ~(0x03<<2*10);GPIOH_OSPEEDR |= (0<<2*10);GPIOH_PUPDR &= ~(0x03<<2*10);GPIOH_PUPDR |= (1<<2*10);GPIOH_BSRR |= (1<<16<<10);while(1);}

到这里,想说的话已经基本说完,后面的构建自己函数库,是否能搞懂其实并不重要,你只需要会用官方固件库即可。对于新手来说,我觉得一定要注意的是:所有的一切是围绕着目标去展开。 无论是地址,还是寄存器,亦或是库函数,都只是我们控制单片机的手段,能把这些全搞懂,很好很牛;只懂库函数操作去完成目标,也很好很牛。

二、构建库函数

1.修改寄存器地址封装

首先我们要知道,寄存器地址是基于物理地址的偏移地址,他们是连续的,和结构体的成员变量关系类似,所以我们可以通过结构体的形势来进行封装,将寄存器映射为结构体变量,再通过结构体变量,宏定义等方式来实现可读性的提升。

代码如下(示例):

1 //volatile 表示易变的变量,防止编译器优化
2 #define __IO volatile
3 typedef unsigned int uint32_t;
4 typedef unsigned short uint16_t;
5 
6 /* GPIO 寄存器列表 */
7 typedef struct {
8 __IO uint32_t MODER; /*GPIO 模式寄存器 地址偏移: 0x00 */
9 __IO uint32_t OTYPER; /*GPIO 输出类型寄存器 地址偏移: 0x04 */
10 __IO uint32_t OSPEEDR; /*GPIO 输出速度寄存器 地址偏移: 0x08 */
11 __IO uint32_t PUPDR; /*GPIO 上拉/下拉寄存器 地址偏移: 0x0C */
12 __IO uint32_t IDR; /*GPIO 输入数据寄存器 地址偏移: 0x10 */
13 __IO uint32_t ODR; /*GPIO 输出数据寄存器 地址偏移: 0x14 */
14 __IO uint16_t BSRRL; /*GPIO 置位/复位寄存器低 16 位部分 地址偏移: 0x18 */
15 __IO uint16_t BSRRH; /*GPIO 置位/复位寄存器 高 16 位部分地址偏移: 0x1A */
16 __IO uint32_t LCKR; /*GPIO 配置锁定寄存器 地址偏移: 0x1C */
17 __IO uint32_t AFR[2]; /*GPIO 复用功能配置寄存器 地址偏移: 0x20-0x24 */
18 } GPIO_TypeDef;
19 
20 /*RCC 寄存器列表*/
21 typedef struct {
22 __IO uint32_t CR; /*!< RCC 时钟控制寄存器,地址偏移: 0x00 */
23 __IO uint32_t PLLCFGR; /*!< RCC PLL 配置寄存器,地址偏移: 0x04 */
24 __IO uint32_t CFGR; /*!< RCC 时钟配置寄存器,地址偏移: 0x08 */
25 __IO uint32_t CIR; /*!< RCC 时钟中断寄存器,地址偏移: 0x0C */
26 __IO uint32_t AHB1RSTR; /*!< RCC AHB1 外设复位寄存器,地址偏移: 0x10 */
27 __IO uint32_t AHB2RSTR; /*!< RCC AHB2 外设复位寄存器,地址偏移: 0x14 */
28 __IO uint32_t AHB3RSTR; /*!< RCC AHB3 外设复位寄存器,地址偏移: 0x18 */
29 __IO uint32_t RESERVED0; /*!< 保留, 地址偏移:0x1C */
30 __IO uint32_t APB1RSTR; /*!< RCC APB1 外设复位寄存器,地址偏移: 0x20 */
31 __IO uint32_t APB2RSTR; /*!< RCC APB2 外设复位寄存器,地址偏移: 0x24*/
32 __IO uint32_t RESERVED1[2]; /*!< 保留,地址偏移:0x28-0x2C*/
33 __IO uint32_t AHB1ENR; /*!< RCC AHB1 外设时钟寄存器,地址偏移: 0x30 */
34 __IO uint32_t AHB2ENR; /*!< RCC AHB2 外设时钟寄存器,地址偏移: 0x34 */
35 __IO uint32_t AHB3ENR; /*!< RCC AHB3 外设时钟寄存器,地址偏移: 0x38 */
36 /*RCC 后面还有很多寄存器,此处省略*/
37 } RCC_TypeDef;

简单分析一下代码,前面几行将volatile,unsigned int,unsigned short这几种关键字进行了宏定义,接着用这些宏定义后的关键字创建了一个名字为GPIO_TypeDef结构体和一个名字为RCC_TypeDef的结构体。

看到这,先提出几个问题,为什么不直接用C语言所支持的关键字而将其进行宏定义后,再用宏定义配置?后面创建的结构体所依据的是什么,成员变量定义的依据是什么?

先说第一个,我认为是方便移植更新,c语言中的关键字在其它语言中可能并不能生效,这样做的好处是,如果volatile,int这些类型在别的平台是叫别的名字,那么只需要将这个地方一替换那么整个代码都将会替换掉,这样可以很好地移植或者更新。相反如果你直接用的是关键字,那么则要将所有用到这个关键字的地方全部替换掉。从这里我们应该学到一个很重要的经验,对于一些高频用到的又可能有改动的变量,关键字等等,用宏定义去定义一下再去使用,可以在你修改代码时十分方便。

第二个结构体的创建所依据的是芯片手册,而变量的名字,排序及其大小都是按照芯片手册中寄存器的名字,排序,及其大小去设计的。

在这里插入图片描述

2.定义访问的结构体指针和引脚

代码如下(示例):

1 /*定义 GPIOA-H 寄存器结构体指针*/
2 #define GPIOA ((GPIO_TypeDef *) GPIOA_BASE)
3 #define GPIOB ((GPIO_TypeDef *) GPIOB_BASE)
4 #define GPIOC ((GPIO_TypeDef *) GPIOC_BASE)
5 #define GPIOD ((GPIO_TypeDef *) GPIOD_BASE)
6 #define GPIOE ((GPIO_TypeDef *) GPIOE_BASE)
7 #define GPIOF ((GPIO_TypeDef *) GPIOF_BASE)
8 #define GPIOG ((GPIO_TypeDef *) GPIOG_BASE)
9 #define GPIOH ((GPIO_TypeDef *) GPIOH_BASE)
10 
11 /*定义 RCC 外设 寄存器结构体指针*/
12 #define RCC ((RCC_TypeDef *) RCC_BASE)
1 /*GPIO 引脚号定义*/
2 #define GPIO_Pin_0 (uint16_t)0x0001) /*!< 选择 Pin0 (1<<0) */
3 #define GPIO_Pin_1 ((uint16_t)0x0002) /*!< 选择 Pin1 (1<<1)*/
4 #define GPIO_Pin_2 ((uint16_t)0x0004) /*!< 选择 Pin2 (1<<2)*/
5 #define GPIO_Pin_3 ((uint16_t)0x0008) /*!< 选择 Pin3 (1<<3)*/
6 #define GPIO_Pin_4 ((uint16_t)0x0010) /*!< 选择 Pin4 */
7 #define GPIO_Pin_5 ((uint16_t)0x0020) /*!< 选择 Pin5 */
8 #define GPIO_Pin_6 ((uint16_t)0x0040) /*!< 选择 Pin6 */
9 #define GPIO_Pin_7 ((uint16_t)0x0080) /*!< 选择 Pin7 */
10 #define GPIO_Pin_8 ((uint16_t)0x0100) /*!< 选择 Pin8 */
11 #define GPIO_Pin_9 ((uint16_t)0x0200) /*!< 选择 Pin9 */
12 #define GPIO_Pin_10 ((uint16_t)0x0400) /*!< 选择 Pin10 */
13 #define GPIO_Pin_11 ((uint16_t)0x0800) /*!< 选择 Pin11 */
14 #define GPIO_Pin_12 ((uint16_t)0x1000) /*!< 选择 Pin12 */
15 #define GPIO_Pin_13 ((uint16_t)0x2000) /*!< 选择 Pin13 */
16 #define GPIO_Pin_14 ((uint16_t)0x4000) /*!< 选择 Pin14 */
17 #define GPIO_Pin_15 ((uint16_t)0x8000) /*!< 选择 Pin15 */
18 #define GPIO_Pin_All ((uint16_t)0xFFFF) /*!< 选择全部引脚 */

有了这两组定义,接下来就可以写封装函数了。

3.创建封装函数

3.1创建拉低引脚函数

代码如下(示例):

void GPIO_ResetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
{GPIOx->BSRRH = GPIO_Pin;
}

创建的函数有两个传参,一个是GPIO类型,一个是引脚号。也就是说我们想将某一个引脚拉低,只需要调用这个函数,将对应引脚的类型和引脚号填写上即可。比如我们之前控制PH10引脚。

代码如下(示例):

GPIO_ResetBits(GPIOH,GPIO_Pin_10);

3.2创建引脚初始化函数

接下来创建一个复杂一点的端口初始化函数,首先根据上一篇我们知道了要配置一个端口,需要对引脚号、工作模式、输出速率、输出类型以及上/下拉模式这些进行配置。那么我们就以此创建结构体。
代码如下:

1 typedef uint8_t unsigned char;
2 /**
3 * GPIO 初始化结构体类型定义
4 */
5 typedef struct {
6 uint32_t GPIO_Pin; /*!< 选择要配置的 GPIO 引脚
7 可输入 GPIO_Pin_ 定义的宏 */
8 
9 uint8_t GPIO_Mode; /*!< 选择 GPIO 引脚的工作模式
10 可输入二进制值: 00 、01、 10、 11
11 表示输入/输出/复用/模拟 */
12 
13 uint8_t GPIO_Speed; /*!< 选择 GPIO 引脚的速率
14 可输入二进制值: 00 、01、 10、 11
15 表示 2/25/50/100MHz */
16 
17 uint8_t GPIO_OType; /*!< 选择 GPIO 引脚输出类型
18 可输入二进制值: 0 、1
19 表示推挽/开漏 */
20 
21 uint8_t GPIO_PuPd; /*!<选择 GPIO 引脚的上/下拉模式
22 可输入二进制值: 00 、01、 10
23 表示浮空/上拉/下拉*/
24 } GPIO_InitTypeDef;

如果这样配置的话,那么每个变量赋值仍然是要进行位操作赋值,依旧很不好识别,所以我们可以通过创建枚举来解决这个问题。

代码如下:

 typedef enum {
5 GPIO_Mode_IN = 0x00, /*!< 输入模式 */
6 GPIO_Mode_OUT = 0x01, /*!< 输出模式 */
7 GPIO_Mode_AF = 0x02, /*!< 复用模式 */
8 GPIO_Mode_AN = 0x03 /*!< 模拟模式 */
9 } GPIOMode_TypeDef;
10 
11 /**
12 * GPIO 输出类型枚举定义
13 */
14 typedef enum {
15 GPIO_OType_PP = 0x00, /*!< 推挽模式 */
16 GPIO_OType_OD = 0x01 /*!< 开漏模式 */
17 } GPIOOType_TypeDef;
18 
19 /**
20 * GPIO 输出速率枚举定义
21 */
22 typedef enum {
23 GPIO_Speed_2MHz = 0x00, /*!< 2MHz */
24 GPIO_Speed_25MHz = 0x01, /*!< 25MHz */
25 GPIO_Speed_50MHz = 0x02, /*!< 50MHz */
26 GPIO_Speed_100MHz = 0x03 /*!<100MHz */
27 } GPIOSpeed_TypeDef;
28 
29 /**
30 *GPIO 上/下拉配置枚举定义
31 */
32 typedef enum {
33 GPIO_PuPd_NOPULL = 0x00,/*浮空*/
34 GPIO_PuPd_UP = 0x01, /*上拉*/
35 GPIO_PuPd_DOWN = 0x02 /*下拉*/
36 } GPIOPuPd_TypeDef;

然后通过这些枚举去定义开始的结构体成员。

代码如下:

 typedef struct {
5 uint32_t GPIO_Pin; /*!< 选择要配置的 GPIO 引脚
6 可输入 GPIO_Pin_ 定义的宏 */
7 
8 GPIOMode_TypeDef GPIO_Mode; /*!< 选择 GPIO 引脚的工作模式
9 可输入 GPIOMode_TypeDef 定义的枚举值*/
10 
11 GPIOSpeed_TypeDef GPIO_Speed; /*!< 选择 GPIO 引脚的速率
12 可输入 GPIOSpeed_TypeDef 定义的枚举值 */
13 
14 GPIOOType_TypeDef GPIO_OType; /*!< 选择 GPIO 引脚输出类型
15 可输入 GPIOOType_TypeDef 定义的枚举值*/
16 
17 GPIOPuPd_TypeDef GPIO_PuPd; /*!<选择 GPIO 引脚的上/下拉模式
18 可输入 GPIOPuPd_TypeDef 定义的枚举值*/
19 } GPIO_InitTypeDef;

这样,在我们配置时,只需要给变量附上对应的枚举值就好了。

代码如下:

1 GPIO_InitTypeDef InitStruct;
2 
3 /* LED 端口初始化 */
4 /*选择要控制的 GPIO 引脚*/
5 InitStruct.GPIO_Pin = GPIO_Pin_10;
6 /*设置引脚模式为输出模式*/
7 InitStruct.GPIO_Mode = GPIO_Mode_OUT;
8 /*设置引脚的输出类型为推挽输出*/
9 InitStruct.GPIO_OType = GPIO_OType_PP;
10 /*设置引脚为上拉模式*/
11 InitStruct.GPIO_PuPd = GPIO_PuPd_UP;
12 /*设置引脚速率为 2MHz */
13 InitStruct.GPIO_Speed = GPIO_Speed_2MHz;

这样,我们将InitStruct这个结构体的各个成员都赋上值了,接着就是创建一个函数,来处理这个结构体的值。

代码如下:

1 
2 /**
3 *函数功能:初始化引脚模式
4 *参数说明:GPIOx,该参数为 GPIO_TypeDef 类型的指针,指向 GPIO 端口的地址
5 * GPIO_InitTypeDef:GPIO_InitTypeDef 结构体指针,指向初始化变量
6 */
7 void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct)
8 {
9 uint32_t pinpos = 0x00, pos = 0x00 , currentpin = 0x00;
10 
11 /*-- GPIO Mode Configuration --*/
12 for (pinpos = 0x00; pinpos < 16; pinpos++) {
13 /*以下运算是为了通过 GPIO_InitStruct->GPIO_Pin 算出引脚号 0-15*/
14 
15 /*经过运算后 pos 的 pinpos 位为 1,其余为 0,与 GPIO_Pin_x 宏对应。
16 pinpos 变量每次循环加 1,*/
17 pos = ((uint32_t)0x01) << pinpos;
18 
19 /* pos 与 GPIO_InitStruct->GPIO_Pin 做 & 运算,
20 若运算结果 currentpin == pos,
21 则表示 GPIO_InitStruct->GPIO_Pin 的 pinpos 位也为 1,
22 从而可知 pinpos 就是 GPIO_InitStruct->GPIO_Pin 对应的引脚号:0-15*/
23 currentpin = (GPIO_InitStruct->GPIO_Pin) & pos;
24 
25 /*currentpin == pos 时执行初始化*/
26 if (currentpin == pos) {
27 /*GPIOx 端口,MODER 寄存器的 GPIO_InitStruct->GPIO_Pin 对应的引脚,
28 MODER 位清空*/
29 GPIOx->MODER &= ~(3 << (2 *pinpos));
30 
31 /*GPIOx 端口,MODER 寄存器的 GPIO_Pin 引脚,
32 MODER 位设置"输入/输出/复用输出/模拟"模式*/
33 GPIOx->MODER |= (((uint32_t)GPIO_InitStruct->GPIO_Mode) << (2 *pinpos));
34 
35 /*GPIOx 端口,PUPDR 寄存器的 GPIO_Pin 引脚,
36 PUPDR 位清空*/
37 GPIOx->PUPDR &= ~(3 << ((2 *pinpos)));
38 
39 /*GPIOx 端口,PUPDR 寄存器的 GPIO_Pin 引脚,
40 PUPDR 位设置"上/下拉"模式*/
41 GPIOx->PUPDR |= (((uint32_t)GPIO_InitStruct->GPIO_PuPd) << (2 *pinpos));
42 
43 /*若模式为"输出/复用输出"模式,则设置速度与输出类型*/
44 if ((GPIO_InitStruct->GPIO_Mode == GPIO_Mode_OUT) ||
45 (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_AF)) {
46 /*GPIOx 端口,OSPEEDR 寄存器的 GPIO_Pin 引脚,
47 OSPEEDR 位清空*/
48 GPIOx->OSPEEDR &= ~(3 << (2 *pinpos));
49 /*GPIOx 端口,OSPEEDR 寄存器的 GPIO_Pin 引脚,
50 OSPEEDR 位设置输出速度*/
51 GPIOx->OSPEEDR |= ((uint32_t)(GPIO_InitStruct->GPIO_Speed)<<(2 *pinpos));
52 
53 /*GPIOx 端口,OTYPER 寄存器的 GPIO_Pin 引脚,
54 OTYPER 位清空*/
55 GPIOx->OTYPER &= ~(1 << (pinpos)) ;
56 /*GPIOx 端口,OTYPER 位寄存器的 GPIO_Pin 引脚,
57 OTYPER 位设置"推挽/开漏"输出类型*/
58 GPIOx->OTYPER |= (uint16_t)(( GPIO_InitStruct->GPIO_OType)<< (pinpos));
59 }
60 }
61 }

读一下这个函数,有两个传参,第一个是端口类型,也就是之前我们创建的那些GPIOx指针(x=A…H),第二个就是我们刚刚赋值的结构体,然后函数内部将结构体变量的值传给对应寄存器,最终控制电路板实现端口初始化。

我们要想实现拉低PH10引脚,只需要调用这两个函数便能实现。

代码如下:

12 int main(void)
13 {
14 GPIO_InitTypeDef InitStruct;
15 
16 /*开启 GPIOH 时钟,使用外设时都要先开启它的时钟*/
17 RCC->AHB1ENR |= (1<<7);
18 
19 /* LED 端口初始化 */
20 
21 /*初始化 PH10 引脚*/
22 /*选择要控制的 GPIO 引脚*/
23 InitStruct.GPIO_Pin = GPIO_Pin_10;
24 /*设置引脚模式为输出模式*/
25 InitStruct.GPIO_Mode = GPIO_Mode_OUT;
26 /*设置引脚的输出类型为推挽输出*/
27 InitStruct.GPIO_OType = GPIO_OType_PP;
28 /*设置引脚为上拉模式*/
29 InitStruct.GPIO_PuPd = GPIO_PuPd_UP;
30 /*设置引脚速率为 2MHz */
31 InitStruct.GPIO_Speed = GPIO_Speed_2MHz;
32 /*调用库函数,使用上面配置的 GPIO_InitStructure 初始化 GPIO*/
33 GPIO_Init(GPIOH, &InitStruct);
34 
35 /*使引脚输出低电平,点亮 LED1*/
36 GPIO_ResetBits(GPIOH,GPIO_Pin_10);
51 while (1);
52 
53 }

总结

这一篇主要篇幅比较长,主要想分享为什么要有库函数,以及库函数为什么要这么去写,这么写的好处是什么,在今后的应用中,我们很少需要自己去写库函数,标准库函数已经满足我们绝大部分的需求了,我们只需要去调用。不过相对于学习本身,我更希望分享如何去学习,这样才能举一反三,在这个科技与狠活快速更新的年代,始终跟的上脚步。

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

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

相关文章

堆 (带图详解)

文章目录1.堆的基本概念1. 概念2.性质1.必须为完全二叉树2.满足大堆/小堆成立的条件3.存储方式1.逻辑结构2.物理结构4. 孩子与父亲之间下标的关系2.堆的基本实现1.push——插入1.代码2. 情况分析情况1情况23. 向上调整算法1.过程分析2. 临界条件的判断2. pop—— 删除1.代码2. …

redis哨兵系列1

需要配合源码一起康~ 9.1 哨兵基本概念 官网手册yyds&#xff1a;https://redis.io/docs/manual/sentinel/ redis主从模式&#xff0c;如果主挂了&#xff0c;需要人工将从节点提升为主节点&#xff0c;通知应用修改主节点的地址。不是很友好&#xff0c;so Redis 2.8之后开…

C# async / await 用法

目录 一、简介 二、异步等待返回结果 三、异步方法返回类型 四、await foreach 五、Task.Delay 结束 一、简介 await 运算符暂停对其所属的 async 方法的求值&#xff0c;直到其操作数表示的异步操作完成。 异步操作完成后&#xff0c;await 运算符将返回操作的结果&…

使用STM32CubeMX实现按下按键,电平反转

需提前学习&#xff1a;使用STM32CubeMX实现LED闪烁 目录 原理图分析 按键部分原理图分析 LED部分原理图分析 STM32CubeMX配置 关于STM32CubeMXSYS的Debug忘记配置Serial Wire处理办法 GPIO配置 LED的GPIO配置 KEY1配置 关于PA0后面这个WKUP是什么&#xff1f; 那么啥…

利用ogg微服务版将oracle同步到kafka

ogg微服务版可以再界面上配置抽取、复制进程&#xff0c;不必进入到shell中进行配置&#xff0c;并且图形化界面可以看到更多信息。 系统架构 源端安装ogg for oracle 19C , 目标端安装ogg for bigdata 21C kafka 2.2 数据库&#xff1a;19C 所有软件安装在同台服务器上&#…

理解Linux32位机器下虚拟地址到物理地址的转化

文章目录前言一、基本概念介绍二、虚拟地址到物理地址的转化过程总结前言 简要介绍LINUX32位系统下虚拟地址到物理地址的转化过程。 一、基本概念介绍 在32位机器下&#xff0c;IO的基本单位是块&#xff08;块&#xff1a;4kb),在程序编译成可执行程序时也划分好了以4kb为单…

JVM【类加载与GC垃圾回收机制】

JVM【类加载与GC垃圾回收机制】&#x1f34e;一.JVM&#x1f352;1.1JVM简介&#x1f352;1.2JVM执行流程&#x1f34e;二.JVM运行时数据区&#x1f352;2.1 程序计数器(线程私有)&#x1f352;2.2 栈(线程私有)&#x1f352;2.3 堆(线程共享)&#x1f352;2.4 方法区(线程共享…

OWASP API SECURITY TOP 10

目录 1. API 安全风险 2. 细说TOP10 1. Broken Object Level Authorization 2. Broken User Authentication 3 Excessive Data Exposure 4 Lack of Resources & Rate Limiting 5 Broken Function Level Authorization 6 Mass Assignment 7 security misconfigura…

【原创】使用Golang的电商搜索技术架构实现

作者&#xff1a;黑夜路人 时间&#xff1a;2022年11月 一、背景&#xff1a; 现在搜索技术已经是非常主流的应用技术&#xff0c;各种优秀的索引开源软件已经很普遍了&#xff0c;比如 Lucene/Solr/Elasticsearch 等等主流搜索索引开源软件&#xff0c;让我们搭建一个优秀的…

【FLASH存储器系列十】Nand Flash芯片使用指导之一

目录 1.1 芯片简介 1.2 功能框图 1.3 存储结构 1.4 信号定义 1.5 双平面&#xff08;plane&#xff09;操作 1.6 Die间交错操作 1.7 错误管理 今天以MT29F8G08AJADAWP芯片为例&#xff0c;说明nand flash的操作方法。 1.1 芯片简介 这是一款镁光的容量8Gb&#xff0c;总…

liunx集成jmeter进行压测实践

首先liunx环境需要部署jdk 1,获取jmeter免安装包&#xff1a;点击我获取免安装包 2,获取jmeter-manger工具&#xff0c;用于生成报告&#xff0c;日志等 点击我获取工具 3,在服务器上新建一个文件夹存放jmeter&#xff0c;推荐在/usr/local/下面&#xff0c;我这里由于权限问…

E 排队(排列组合)[牛客小*白月赛61]

题面如下&#xff1a; 思路 or 题解&#xff1a; 对于一个长度为 nnn 的 排列组合 如果存在一对 逆序对 (x,y)(x, y)(x,y) xxx 在 yyy 的前面有 n∗(n−1)2\frac{n * (n - 1)}{2}2n∗(n−1)​ 种情况 剩下 n−2n - 2n−2 个位置可以随意填数进去&#xff0c;不会影响到逆序对 …

狗屎一样的面试官,你遇到过几个?

做了几年软件开发&#xff0c;我们都或多或少面试过别人&#xff0c;或者被别人面试过。大家最常吐槽的就是面试造火箭&#xff0c;进厂拧螺丝。今天就来吐槽一下那些奇葩&#xff08;gou&#xff09;一样的面试官 A 那是在我刚工作1年的时候&#xff0c;出去面试前端开发。 那…

Python编程 元组的创建

作者简介&#xff1a;一名在校计算机学生、每天分享Python的学习经验、和学习笔记。 座右铭&#xff1a;低头赶路&#xff0c;敬事如仪 个人主页&#xff1a;网络豆的主页​​​​​​ 目录 前言 一.元组知识点 二.元组(tuple) 1.元组介绍(掌握) 2.元组创建(掌握) 3.…

鉴源论坛 · 观模丨浅谈随机测试

作者 | 黄杉 华东师范大学软件工程学院博士 苏亭 华东师范大学软件工程学院教授 首发 | 鉴源论坛 观模 01 什么是随机测试 &#xff08;Random Testing&#xff09; 随机测试是一种使用随机、相互独立的程序输入来对计算机程序进行测试的黑盒软件测试&#xff08;在完全忽…

Springboot常用参数注解

访问路径为http://localhost:8080/ PathVariable GetMapping("/get/{id}/blank/{name}")public Map getValue(PathVariable("id") Integer id,PathVariable("name") String name,PathVariable Map<String,String> kv){Map map new Hash…

大一新生HTML期末作业 学生个人网页设计作业 HTML5响应式个人简历网站模板 web前端网页制作课作业

&#x1f389;精彩专栏推荐&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb; ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 &#x1f482; 作者主页: 【主页——&#x1f680;获取更多优质源码】 &#x1f393; web前端期末大作业…

CNN (吴恩达 2021

week1-2 02_边缘检测例子_哔哩哔哩_bilibili ​ ​ 我们之前在说面部识别介绍过&#xff0c;要识别面部&#xff0c;都是从细微的边缘入手&#xff0c;一层一层聚类&#xff0c;最终实现人脸的识别。神经网络由浅层到深层&#xff0c;分别可以检测出图片的边缘特征 、局部特…

web前端大一实训 HTML+CSS+JavaScript王者荣耀(60页) web课程设计网页规划与设计 HTML期末大作业 HTML网页设计结课作业

&#x1f389;精彩专栏推荐&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb; ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 &#x1f482; 作者主页: 【主页——&#x1f680;获取更多优质源码】 &#x1f393; web前端期末大作业…

【MySQL进阶】深入理解B+树索引底层原理

【MySQL进阶】深入理解B树索引底层原理 文章目录【MySQL进阶】深入理解B树索引底层原理一、前言——没有索引的查找1、在一个页中的查找2、在很多页中查找3、总结二、索引1、一个简单的索引方案2、InnoDB中的索引方案3、B 树4、聚簇索引5、二级索引6、回表7、联合索引三、InnoD…