创建自己的函数库

news2025/8/3 4:35:30

创建自己的函数库

  • 前言
  • 一、什么是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.coloradmin.cn/o/15955.html

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

相关文章

世界儿童日,周大福真诚关爱儿童成长

守护童心 呵护成长 周大福秉持着“用真诚让幸福永恒”的企业理念&#xff0c;百周年承诺“以人为本”。以爱和关怀凝聚社会力量&#xff0c;提倡社会共融&#xff0c;缔造可持续未来。 梦想家祝福 2021年周大福珠宝集团与美丽中国&#xff0c;携手打造“家源于此”项目。于2…

podman-compose 有前途吗?

文章目录1. 前言2. Docker Compose 和 Podman Compose 的历史3. 未来4. 观点5. 安装5.1 pip3 安装5.2 python 安装5.3 dnf 安装6. 示例1. 前言 虽然 Kubernetes 已经发展成为容器编排的主导者&#xff0c;但人们仍然对管理较小规模的容器&#xff08;通常是单个系统&#xff0…

centos7—安装mysql

文章目录1.1 卸载MariaDB1.2 官网下载包文件1.3 解压安装包1.4 安装相关的Mysql服务1.5 开启Mysql服务1.1 卸载MariaDB 由于centOS7中默认安装了MariaDB,需要先进行卸载 rpm -qa | grep -i mariadb #查找本地mariadb rpm -e --nodeps mariadb-libs-5.5.68-1.el7.aarch64 #这个…

nuxt 如何生成sitemap.xml 动静态站点地图

前言 sitemap.xml的作用是将我们网站的所有页面都被SEO&#xff08;浏览器搜索引擎&#xff09;收录&#xff0c;我们网站的内容更容易被用户搜到&#xff0c;同时增加我们的网站的知名度&#xff0c;排名更靠前。简言之就是用技术做网站推广&#xff0c;所以对于网站推广sitem…

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 方法区(线程共享…

Matlab图像处理基础(part 1)

目录 0. 概要 1. 图像表示 Image Representation 1.1 图像格式 Image format 1.2 图像分辨率 resolution of image 1.3 图像的编码 1.4 Matlab图像加载、显示和保存 1.5 Image Information 1.6 图像格式转换 1.7 其它类型的像素 1.8 像素数值格式 1.9 图像数据的访问…

vue实现防抖函数、节流函数,全局使用【输入框、按钮】

博主介绍 &#x1f4e2;点击下列内容可跳转对应的界面&#xff0c;查看更多精彩内容&#xff01; &#x1f34e;主页&#xff1a;水香木鱼 &#x1f34d;专栏&#xff1a;后台管理系统 文章目录 简介&#xff1a;这是一篇有关【vue实现防抖函数、节流函数&#xff0c;全局使用…

第一章《初学者问题大集合》第4节:Java程序是如何执行的

想要学好Java编程,就必须先弄清楚Java程序是如何执行的。首先来解释一个最基本的概念:什么是程序?把你想让计算机做的事情用编程语言一条条列出,这个由多条编程语言所组成的“代码序列”就是程序。 那么Java程序又是如何运行的呢?这个过程如图1-1所示。 图1-1 Java程序运…

C语言小游戏之三子棋(井字棋)(1.5w字超详细讲解)

hello&#xff0c;csdn的伙伴们&#xff0c;大家好&#xff0c;我们已经学习到了分支与循环&#xff0c;函数&#xff0c;数组这三大块知识&#xff0c;那么我们现在就可以尝试综合运用前面所学的知识&#xff0c;来完成一个简单的小游戏-----三子棋&#xff08;井字棋&#xf…

Allegro DFM Ravel Rule工具使用指导书

Allegro DFM Ravel Rule工具使用指导书 Allegro任何一个版本都支持DFM Ravel Rule检查,即便是166的版本 打开后的界面如下所示 可以检查项目 测试点,阻焊,走线,丝印,过孔,milling,装配,outline相关的DFM检查 可以让违反规则的设计处以DRC的形式报出来 避免加工问题 …

基于数字孪生打造智慧园区运营平台,助力园区数字化转型

在各行各业数字化转型的浪潮中&#xff0c;园区也在经历数字化转型发展&#xff0c;从传统园区向智慧园区不断演进。传统园区缺乏系统性规划&#xff0c;基于单点功能建设&#xff0c;存在系统孤立、管理粗放且服务不足等问题&#xff0c;难以满足人们日益增长的多样化需求。在…

第四章. Pandas进阶—数据合并

第四章. Pandas进阶 4.6 数据合并 数据合并主要使用的是Merge方法和Concat方法 1.数据合并(merge函数) 1).语法&#xff1a; pandas.merge(right,how‘inner’, on“None”, left_on“None”, right_on“None”, left_indexFalse, right_indexFalse... )参数说明: right&…

甘露糖-聚乙二醇-马来酰亚胺 mannose-PEG-MAL 马来酰亚胺-PEG-甘露糖

甘露糖-聚乙二醇-马来酰亚胺 mannose-PEG-MAL 马来酰亚胺-PEG-甘露糖&#xff0c;溶于大部分有机溶剂&#xff0c;如&#xff1a;DCM、DMF、DMSO、THF等等。在水中有很好的溶解性 中文名称&#xff1a;甘露糖-马来酰亚胺 英文名称&#xff1a;mannose-MAL 别称&#xff1a;…

【LeetCode 每日一题】53. 最大子数组和

01 题目描述 给你一个整数数组 nums &#xff0c;请你找出一个具有最大和的连续子数组&#xff08;子数组最少包含一个元素&#xff09;&#xff0c;返回其最大和。 子数组 是数组中的一个连续部分。 02 示例 示例1&#xff1a; 输入&#xff1a;nums [-2,1,-3,4,-1,2,1,-5…

从 React 源码彻底搞懂 Ref 的全部 api

ref 是 React 里常用的特性&#xff0c;我们会用它来拿到 dom 的引用。 它一般是这么用的&#xff1a; 函数组件里用 useRef&#xff1a; import React, { useRef, useEffect } from "react";export default function App() {const inputRef useRef();useEffect(…

几乎涵盖了近半年90%的Java面试题,可以肝起来了

前言 很多人在问&#xff1a;八股文还有必要背吗&#xff1f; 近半年来大家听到的、用到的不少&#xff0c;带来的争议也不断。 有人奉为面试神器&#xff0c;全文背诵。有人觉得八股文铺天盖地实际作用不大&#xff0c;还害人不浅… 我觉得不是背不背八股文的问题&#xff0c…

【机器学习并行计算】2 parameter server参数服务器

使用ps实现异步梯度下降。 14年提出的。 异步 vs 同步 可以看出异步运行效率非常高。 异步梯度下降的流程 ps架构流程 worker&#xff1a; 首先从参数服务器拉取最新的参数&#xff1b;然后用自己节点上的数据计算梯度&#xff1b;最后把梯度推给参数服务器参数服务器&#xf…

最快速的文件传输软件,解析镭速文件传输软件

想到每天都需要进行文件传输&#xff0c;就会烦躁&#xff0c;要是有一夸最快速的文件传输软件的话&#xff0c;这样就可以节省大量的时间了&#xff0c;那么针对于用户的这一个需求&#xff0c;我们来介绍一下镭速的文件传输软件&#xff0c;看是否是那么快&#xff0c;快到你…

无监督端到端框架:IVIF

VIF-Net: An Unsupervised Framework for Infrared and Visible Image Fusion &#xff08; VIF-Net: 红外和可见光图像融合的无监督框架&#xff09; &#xff08;本文理解上的难易程度&#xff1a;易&#xff09; 在本文中&#xff0c;我们提出了一种用于红外和可见图像融合…

css 动画实现节流效果

今天在做节流操作时&#xff0c;无意间看到可以用css动画去实现节流效果&#xff0c;然后一顿操作发现果然可以&#xff0c;记录一下 CSS pointer-events 属性 一、 用css中的pointer-events&#xff08;指针事件&#xff09;、animation&#xff08;动画&#xff09;以及:act…