当前位置: 首页 > news >正文

网站开发制作培训学校土巴兔装修平台怎么收费

网站开发制作培训学校,土巴兔装修平台怎么收费,php做网站脑图,2017网站开发前景什么是 STM32 函数库 固件库是指“STM32 标准函数库”#xff0c;它是由 ST 公司针对 STM32 提供的函数接口#xff0c;即API (Application Program Interface)#xff0c;开发者可调用这些函数接口来配置 STM32 的寄存器#xff0c;使开发人员得以脱离最底层的寄存器操作…什么是 STM32 函数库 固件库是指“STM32 标准函数库”它是由 ST 公司针对 STM32 提供的函数接口即API (Application Program Interface)开发者可调用这些函数接口来配置 STM32 的寄存器使开发人员得以脱离最底层的寄存器操作有开发快速易于阅读维护成本低等优点。当我们调用库 API 的时候不需要挖空心思去了解库底层的寄存器操作就像当年我们刚开始学习 C 语言的时候用 prinft() 函数时只是学习它的使用格式并没有去研究它的源码实现但需要深入研究的时候经过千锤百炼的库源码就是最佳学习范例。实际上库是架设在寄存器与用户驱动层之间的代码向下处理与寄存器直接相关的配置向上为用户提供配置寄存器的接口。库开发方式与直接配置寄存器方式的区别见图固件库开发与寄存器开发对比图。 为什么采用库来开发及学习 在以前 8 位机时代的程序开发中一般直接配置芯片的寄存器控制芯片的工作方式如中断定时器等。配置的时候常常要查阅寄存器表看用到哪些配置位为了配置某功能该置 1 还是置 0。这些都是很琐碎的、机械的工作因为 8 位机的软件相对来说较简单而且资源很有限所以可以直接配置寄存器的方式来开发。 对于 STM32因为外设资源丰富带来的必然是寄存器的数量和复杂度的增加这时直接配置寄存器方式的缺陷就突显出来了 (1) 开发速度慢 (2) 程序可读性差 (3) 维护复杂 这些缺陷直接影响了开发效率程序维护成本交流成本。库开发方式则正好弥补了这些缺陷。 而坚持采用直接配置寄存器的方式开发的程序员会列举以下原因 (1) 具体参数更直观 (2) 程序运行占用资源少 相对于库开发的方式直接配置寄存器方式生成的代码量的确会少一点但因为 STM32 有充足的资源权衡库的优势与不足绝大部分时候我们愿意牺牲一点 CPU 资源选择库开发。一般只有在对代码运行时间要求极苛刻的地方才用直接配置寄存器的方式代替如频繁调用的中断服务函数。对于库开发与直接配置寄存器的方式就好比编程是用汇编好还是用 C 好一样。 在 STM32F1 系列刚推出函数库时引起程序员的激烈争论但是随着 ST 库的完善与大家对库的了解更多的程序员选择了库开发。现在 STM32F1 系列和 STM32F4 系列各有一套自己的函数库但是它们大部分是兼容的F1 和 F4 之间的程序移植只需要小修改即可。而如果要移植用寄存器写的程序那简直跟脱胎换骨差不多。 构建库函数雏形 虽然库的优点多多但很多人对库还是很忌惮因为一开始用库的时候有很多代码很多文件不知道如何入手。不知道您是否认同这么一句话一切的恐惧都来源于无知。我们对库忌惮那是因为我们不知道什么是库不知道库是怎么实现的。接下来我们在寄存器点亮 LED 的代码上继续完善把代码一层层封装实现库的最初的雏形。 外设寄存器结构体定义写在stm32f10x.h中 我们在操作寄存器的时候操作的是都寄存器的绝对地址如果每个外设寄存器都这样操作那将非常麻烦。我们考虑到外设寄存器的地址都是基于外设基地址的偏移地址都是在外设基地址上逐个连续递增的每个寄存器占 32 个字节这种方式跟结构体里面的成员类似。所以我们可以定义一种外设结构体结构体的地址等于外设的基地址结构体的成员等于寄存器成员的排列顺序跟寄存器的顺序一样。这样我们操作寄存器的时候就不用每次都找到绝对地址只要知道外设的基地址就可以操作外设的全部寄存器即操作结构体的成员即可。在工程中的“stm32f10x.h”文件中我们使用结构体封装 GPIO 及 RCC 外设的的寄存器如下。结构体成员的顺序按照寄存器的偏移地址从低到高排列成员类型跟寄存器类 型一样。 //寄存器的值常常是芯片外设自动更改的即使 CPU 没有执行程序也有可能发生变化 //编译器有可能会对没有执行程序的变量进行优化//volatile 表示易变的变量防止编译器优化 #define __IO volatile typedef unsigned int uint32_t; typedef unsigned short uint16_t;// GPIO 寄存器结构体定义 typedef struct { __IO uint32_t CRL;// 端口配置低寄存器地址偏移 0X00 __IO uint32_t CRH;// 端口配置高寄存器地址偏移 0X04 __IO uint32_t IDR;// 端口数据输入寄存器地址偏移 0X08 __IO uint32_t ODR;// 端口数据输出寄存器地址偏移 0X0C __IO uint32_t BSRR;// 端口位设置/清除寄存器地址偏移 0X10 __IO uint32_t BRR;// 端口位清除寄存器地址偏移 0X14 __IO uint32_t LCKR;// 端口配置锁定寄存器地址偏移 0X18 } GPIO_TypeDef;// RCC 寄存器结构体定义 typedef struct {uint32_t CR;uint32_t CFGR;uint32_t CIR;uint32_t APB2RSTR;uint32_t APB1RSTR;uint32_t AHBENR;uint32_t APB2ENR;uint32_t APB1ENR;uint32_t BDCR;uint32_t CSR; }RCC_TypeDef;这段代码在每个结构体成员前增加了一个“__IO”前缀它的原型在这段代码的第一行代表了C 语言中的关键字“volatile”在 C 语言中该关键字用于表示变量是易变的要求编译器不要优化。这些结构体内的成员都代表着寄存器而寄存器很多时候是由外设或 STM32 芯片状态修改的也就是说即使 CPU 不执行代码修改这些变量变量的值也有可能被外设修改、更新所以每次使用这些变量的时候我们都要求 CPU 去该变量的地址重新访问。若没有这个关键字修饰在某些情况下编译器认为没有代码修改该变量就直接从 CPU 的某个缓存获取该变量值这时可以加快执行速度但该缓存中的是陈旧数据与我们要求的寄存器最新状态可能会有出入。(将白了就是可能有脏数据也就是我们有时候说的玄学问题) 外设存储器映射写在stm32f10x.h中 外设寄存器结构体定义仅仅是一个定义要想实现给这个结构体赋值就达到操作寄存器的效果我们还需要找到该寄存器的地址就把寄存器地址跟结构体的地址对应起来。所以我们要再找到外设的地址根据我们前面的学习我们可以把这些外设的地址定义成一个个宏实现外设存储器的映射。 /* 片上外设基地址*/ #define PERIPH_BASE ((unsigned int)0x40000000) /*APB2 总线基地址 */ #define APB2PERIPH_BASE (PERIPH_BASE 0x10000) /* AHB 总线基地址 */ #define AHBPERIPH_BASE (PERIPH_BASE 0x20000) /*GPIO 外设基地址 */ #define GPIOA_BASE (APB2PERIPH_BASE 0x0800) #define GPIOB_BASE (APB2PERIPH_BASE 0x0C00)#define GPIOC_BASE (APB2PERIPH_BASE 0x1000) #define GPIOD_BASE (APB2PERIPH_BASE 0x1400) #define GPIOE_BASE (APB2PERIPH_BASE 0x1800) #define GPIOF_BASE (APB2PERIPH_BASE 0x1C00) #define GPIOG_BASE (APB2PERIPH_BASE 0x2000) /*RCC 外设基地址 */ #define RCC_BASE (AHBPERIPH_BASE 0x1000)外设声明 写在stm32f10x.h中 定义好外设寄存器结构体实现完外设存储器映射后我们再把外设的基址强制类型转换成相应的外设寄存器结构体指针然后再把该指针声明成外设名这样一来外设名就跟外设的地址对应起来了而且该外设名还是一个该外设类型的寄存器结构体指针通过该指针可以直接操作该外设的全部寄存器如下。 // GPIO 外设声明 #define GPIOA ((GPIO_TypeDef *) GPIOA_BASE) #define GPIOB ((GPIO_TypeDef *) GPIOB_BASE) #define GPIOC ((GPIO_TypeDef *) GPIOC_BASE) #define GPIOD ((GPIO_TypeDef *) GPIOD_BASE) #define GPIOE ((GPIO_TypeDef *) GPIOE_BASE) #define GPIOF ((GPIO_TypeDef *) GPIOF_BASE) #define GPIOG ((GPIO_TypeDef *) GPIOG_BASE)// RCC 外设声明 #define RCC ((RCC_TypeDef *) RCC_BASE)//*RCC 的 AHB1 时钟使能寄存器地址, 强制转换成指针 */ #define RCC_APB2ENR *(unsigned int*)(RCC_BASE0x18)首先通过强制类型转换把外设的基地址转换成 GPIO_TypeDef 类型的结构体指针然后通过宏定义把 GPIOA、GPIOB 等定义成外设的结构体指针通过外设的结构体指针我们就可以达到访问外设的寄存器的目的。 C语言小知识条件编译 /* * C 语言知识条件编译 * #if 为真 * 执行这里的程序 * #else * 否则执行这里的程序 * #endif */通过操作外设结构体指针的方式我们把 main 文件里对应的代码修改掉如下。 // 使用寄存器结构体指针点亮 LED int main(void) { #if 0 // 直接通过操作内存来控制寄存器 // 开启 GPIOB 端口时钟 RCC_APB2ENR | (13);//清空控制 PB0 的端口位 GPIOB_CRL ~( 0x0F (4*0)); // 配置 PB0 为通用推挽输出速度为 10M GPIOB_CRL | (14*0);// PB0 输出 低电平 GPIOB_ODR | (00);while (1);#else // 通过寄存器结构体指针来控制寄存器// 开启 GPIOB 端口时钟 RCC-APB2ENR | (13);//清空控制 PB0 的端口位 GPIOB-CRL ~( 0x0F (4*0)); // 配置 PB0 为通用推挽输出速度为 10M GPIOB-CRL | (14*0);// PB0 输出 低电平 GPIOB-ODR | (00);while (1);#endif }乍一看除了把“_”换成了“-”其他都跟使用寄存器点亮 LED 那部分代码一样。这是因为我们现在只是实现了库函数的基础还没有定义库函数。打好了地基下面我们就来建高楼。接下来使用函数来封装 GPIO 的基本操作方便以后应用的时候不需要再查询寄存器而是直接通过调用这里定义的函数来实现。我们把针对 GPIO 外设操作的函数及其宏定义分别存放在“stm32f10x_gpio.c”和“stm32f10x_gpio.h”文件中这两个文件需要自己新建。 定义位操作函数写在stm32f10x_gpio.c中 在“stm32f10x_gpio.c”文件定义两个位操作函数分别用于控制引脚输出高电平和低电平如下。 /** * 函数功能设置引脚为高电平 * 参数说明GPIOx: 该参数为 GPIO_TypeDef 类型的指针指向 GPIO 端口的地址 *GPIO_Pin: 选择要设置的 GPIO 端口引脚可输入宏 GPIO_Pin_0-15 *表示 GPIOx 端口的 0-15 号引脚。 */ void GPIO_SetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin) { /* 设置 GPIOx 端口 BSRR 寄存器的第 GPIO_Pin 位使其输出高电平 */ /* 因为 BSRR 寄存器写 0 不影响 宏 GPIO_Pin 只是对应位为 1其它位均为 0所以可以直接赋值 */GPIOx-BSRR GPIO_Pin; }/** * 函数功能设置引脚为低电平 * 参数说明GPIOx: 该参数为 GPIO_TypeDef 类型的指针指向 GPIO 端口的地址 *GPIO_Pin: 选择要设置的 GPIO 端口引脚可输入宏 GPIO_Pin_0-15 *表示 GPIOx 端口的 0-15 号引脚。 */ void GPIO_ResetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin) { /* 设置 GPIOx 端口 BRR 寄存器的第 GPIO_Pin 位, 使其输出低电平 */ /* 因为 BRR 寄存器写 0 不影响 宏 GPIO_Pin 只是对应位为 1其它位均为 0所以可以直接赋值 */GPIOx-BRR GPIO_Pin; }这两个函数体内都是只有一个语句对 GPIOx 的 BSRR 或 BRR 寄存器赋值从而设置引脚为高电平或低电平操作 BSRR 或者 BRR 可以实现单独的操作某一位有关这两个的寄存器说明见图 BSRR 寄存器说明 和图 BRR 寄存器说明。其中 GPIOx 是一个指针变量通过函数的输入参数我们可以修改它的值如给它赋予 GPIOA、GPIOB、GPIOH 等结构体指针值这个函数就可以控制相应的 GPIOA、GPIOB、GPIOH 等端口的输出。 验证写在main.c中 利用这两个位操作函数可以方便地操作各种 GPIO 的引脚电平控制各种端口引脚的范例如下 // 开启 GPIOB 端口时钟RCC-APB2ENR | (13);//清空控制 PB0,4,5 的端口位GPIOB-CRL ~( 0x0F (4*0));GPIOB-CRL ~( 0x0F (4*4));GPIOB-CRL ~( 0x0F (4*5));// 配置 PB0,4,5 为通用推挽输出速度为 10MGPIOB-CRL | (14*0);GPIOB-CRL | (14*4);GPIOB-CRL | (14*5); /* 控制 GPIOB 的引脚 0 输出高电平 */GPIO_SetBits(GPIOB,(uint16_t)(10));/* 控制 GPIOB 的引脚 0 输出低电平 */GPIO_ResetBits(GPIOB,(uint16_t)(10)); /* 控制 GPIOB 的引脚 0、引脚 4 输出高电平使用“|”同时控制多个引脚 */GPIO_SetBits(GPIOB,(uint16_t)(10)|(uint16_t)(14));/* 控制 GPIOB 的引脚 0、引脚 4 输出低电平 */GPIO_ResetBits(GPIOB,(uint16_t)(10)|(uint16_t)(14));封装引脚写在stm32f10x.h中 使用以上函数输入参数设置引脚号时还是稍感不便为此我们把表示 16 个引脚的操作数都定义成宏如下。 /*GPIO 引脚号定义 */ #define GPIO_Pin_0 ((uint16_t)0x0001)/*! 选择 Pin0 (10) */ #define GPIO_Pin_1 ((uint16_t)0x0002)/*! 选择 Pin1 (11)*/ #define GPIO_Pin_2 ((uint16_t)0x0004)/*! 选择 Pin2 (12)*/ #define GPIO_Pin_3 ((uint16_t)0x0008)/*! 选择 Pin3 (13)*/ #define GPIO_Pin_4 ((uint16_t)0x0010)/*! 选择 Pin4 */ #define GPIO_Pin_5 ((uint16_t)0x0020)/*! 选择 Pin5 */ #define GPIO_Pin_6 ((uint16_t)0x0040)/*! 选择 Pin6 */ #define GPIO_Pin_7 ((uint16_t)0x0080)/*! 选择 Pin7 */ #define GPIO_Pin_8 ((uint16_t)0x0100)/*! 选择 Pin8 */ #define GPIO_Pin_9 ((uint16_t)0x0200)/*! 选择 Pin9 */ #define GPIO_Pin_10 ((uint16_t)0x0400)/*! 选择 Pin10 */ #define GPIO_Pin_11 ((uint16_t)0x0800)/*! 选择 Pin11 */ #define GPIO_Pin_12 ((uint16_t)0x1000)/*! 选择 Pin12 */ #define GPIO_Pin_13 ((uint16_t)0x2000)/*! 选择 Pin13 */ #define GPIO_Pin_14 ((uint16_t)0x4000)/*! 选择 Pin14 */ #define GPIO_Pin_15 ((uint16_t)0x8000)/*! 选择 Pin15 */ #define GPIO_Pin_All ((uint16_t)0xFFFF)/*! 选择全部引脚 */验证写在main.c中 这些宏代表的参数是某位置“1”其它位置“0”的数值其中最后一个“GPIO_Pin_ALL”是所有数据位都为“1”所以用它可以一次控制设置整个端口的 0-15 所有引脚。利用这些宏GPIO 的控制代码可改为 // 开启 GPIOB 端口时钟RCC-APB2ENR | (13);//清空控制 PB0,4,5 的端口位GPIOB-CRL ~( 0x0F (4*0));GPIOB-CRL ~( 0x0F (4*4));GPIOB-CRL ~( 0x0F (4*5));// 配置 PB0,4,5 为通用推挽输出速度为 10MGPIOB-CRL | (14*0);GPIOB-CRL | (14*4);GPIOB-CRL | (14*5); /* 控制 GPIOB 的引脚 0 输出高电平 */GPIO_SetBits(GPIOB,GPIO_Pin_0);/* 控制 GPIOB 的引脚 0 输出低电平 */GPIO_ResetBits(GPIOB,GPIO_Pin_0); /* 控制 GPIOB 的引脚 0、引脚 4 输出高电平使用“|”同时控制多个引脚 */GPIO_SetBits(GPIOB,GPIO_Pin_0|GPIO_Pin_1);/* 控制 GPIOB 的引脚 0、引脚 4 输出低电平 */GPIO_ResetBits(GPIOB,GPIO_Pin_0|GPIO_Pin_1);使用以上代码控制 GPIO我们就不需要再看寄存器了直接从函数名和输入参数就可以直观看出这个语句要实现什么操作。(英文中“Set”表示“置位”即高电平“Reset”表示“复位”即低电平) 定义初始化结构体 GPIO_InitTypeDef写在stm32f10x.h中 定义位操作函数后控制 GPIO 输出电平的代码得到了简化但在控制 GPIO 输出电平前还需要初始化 GPIO 引脚的各种模式这部分代码涉及的寄存器有很多我们希望初始化 GPIO 也能以如此简单的方法去实现。为此我们先根据 GPIO 初始化时涉及到的初始化参数以结构体的形式封装起来声明一个名为 GPIO_InitTypeDef 的结构体类型如下 typedef struct { uint16_t GPIO_Pin;/*! 选择要配置的 GPIO 引脚 */uint16_t GPIO_Speed;/*! 选择 GPIO 引脚的速率 */uint16_t GPIO_Mode;/*! 选择 GPIO 引脚的工作模式 */ } GPIO_InitTypeDef;这个结构体中包含了初始化 GPIO 所需要的信息包括引脚号、工作模式、输出速率。设计这个结构体的思路是初始化 GPIO 前先定义一个这样的结构体变量根据需要配置 GPIO 的模式对这个结构体的各个成员进行赋值然后把这个变量作为“GPIO 初始化函数”的输入参数该函数能根据这个变量值中的内容去配置寄存器从而实现 GPIO 的初始化。 定义引脚模式的枚举类型写在stm32f10x.h中) 上面定义的结构体很直接美中不足的是在对结构体中各个成员赋值实现某个功能时还需要查询手册的寄存器说明我们不希望每次用到的时候都要去查询手册我们可以使用 C 语言中的枚举定义功能根据手册把每个成员的所有取值都定义好如下。GPIO_Speed 和GPIO_Mode 这两个成员对应的寄存器是 CRL 和 CRH 这两个端口配置寄存器具体见端口配置低寄存器和端口配置高寄存器。 /** * GPIO 输出速率枚举定义 */ typedef enum {GPIO_Speed_10MHz 1,// 10MHZ(01)bGPIO_Speed_2MHz,// 2MHZ(10)bGPIO_Speed_50MHz// 50MHZ(11)b } GPIOSpeed_TypeDef;/** * GPIO 工作模式枚举定义 */ typedef enum {GPIO_Mode_AIN 0x0,// 模拟输入(0000 0000)bGPIO_Mode_IN_FLOATING 0x04,// 浮空输入(0000 0100)bGPIO_Mode_IPD 0x28,// 下拉输入(0010 1000)bGPIO_Mode_IPU 0x48,// 上拉输入(0100 1000)bGPIO_Mode_Out_OD 0x14,// 开漏输出(0001 0100)bGPIO_Mode_Out_PP 0x10,// 推挽输出(0001 0000)bGPIO_Mode_AF_OD 0x1C,// 复用开漏输出(0001 1100)bGPIO_Mode_AF_PP 0x18// 复用推挽输出(0001 1000)b } GPIOMode_TypeDef;关于这两个枚举类型的值如何跟端口控制寄存器里面的说明对应起来我们简单分析下。有关速度的枚举类型有 (01)b 10MHZ、(10)b 2MHZ 和 (11)b 50MHZ这三个值跟寄存器说明对得上很容易理解。至于模式的枚举类型的值理解起来就比较绕这让很多人费了脑筋下面我们通过一个表格来梳理下好帮助我们理解具体如下。 如果但从这些枚举值的十六进制来看很难发现规律转化成二进制之后就比较容易发现规律。bit4 用来区分端口是输入还是输出0 表示输入1 表示输出bit2 和 bit3 对应寄存器的 CNFY[1:0]位是我们真正要写入到 CRL 和 CRH 这两个端口控制寄存器中的值。bit0 和 bit1 对应寄存器的MODEY[1:0] 位这里我们暂不初始化在 GPIO_Init() 初始化函数中用来跟 GPIOSpeed 的值相加即可实现速率的配置。有关具体的代码分析见 GPIO_Init() 库函数。其中在下拉输入和上拉输入中我们设置 bit5 和 bit6 的值为 01 和 10 来以示区别。 有了这些枚举定义我们的 GPIO_InitTypeDef 结构体就可以使用枚举类型来限定输入参数如下。 修改stm32f10x.h中的注释 /** * GPIO 初始化结构体类型定义 */ typedef struct { uint16_t GPIO_Pin;/*! 选择要配置的 GPIO 引脚可输入 GPIO_Pin_ 定义的宏 */GPIOSpeed_TypeDef GPIO_Speed;/*! 选择 GPIO 引脚的速率可输入 GPIOSpeed_TypeDef 定义的枚举值 */GPIOMode_TypeDef GPIO_Mode;/*! 选择 GPIO 引脚的工作模式可输入 GPIOMode_TypeDef 定义的枚举值 */ } GPIO_InitTypeDef;验证写在main.c中写最上面不然可能报错因为是C89标准不是C99标准 如果不使用枚举类型仍使用“uint16_t”类型来定义结构体成员那么成员值的范围就是 0-255而实际上这些成员都只能输入几个数值。所以使用枚举类型可以对结构体成员起到限定输入的作用只能输入相应已定义的枚举值。利用这些枚举定义给 GPIO_InitTypeDef 结构体类型赋值配置就变得非常直观范例如下。 GPIO_InitTypeDef GPIO_InitStructure;/* GPIO 端口初始化 */ /* 选择要控制的 GPIO 引脚 */ GPIO_InitStructure.GPIO_Pin GPIO_Pin_0; /* 设置引脚模式为输出模式 */ GPIO_InitStructure.GPIO_Mode GPIO_Mode_Out_PP; /* 设置引脚的输出类型为推挽输出 */ GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz;定义 GPIO 初始化函数放在stm32f10x_gpio.c中 接着前面的思路对初始化结构体赋值后把它输入到 GPIO 初始化函数由它来实现寄存器配置 。我们的 GPIO 初始化函数实现如下 /** * 函数功能初始化引脚模式 * 参数说明GPIOx该参数为 GPIO_TypeDef 类型的指针指向 GPIO 端口的地址 *GPIO_InitTypeDef:GPIO_InitTypeDef 结构体指针指向初始化变量 */ void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct) { uint32_t currentmode 0x00,currentpin 0x00,pinpos 0x00,pos 0x00; uint32_t tmpreg 0x00, pinmask 0x00;/*---------------- GPIO 模式配置 -------------------*/// 把输入参数 GPIO_Mode 的低四位暂存在 currentmodecurrentmode ((uint32_t)GPIO_InitStruct-GPIO_Mode) ((uint32_t)0x0F);// bit4 是 1 表示输出bit4 是 0 则是输入// 判断 bit4 是 1 还是 0即首选判断是输入还是输出模式if((((uint32_t)GPIO_InitStruct-GPIO_Mode) ((uint32_t)0x10)) ! 0x00){// 输出模式则要设置输出速度currentmode | (uint32_t)GPIO_InitStruct-GPIO_Speed;}/*-----GPIO CRL 寄存器配置 CRL 寄存器控制着低 8 位 IO- ----*/// 配置端口低 8 位即 Pin0~Pin7if (((uint32_t)GPIO_InitStruct-GPIO_Pin ((uint32_t)0x00FF)) ! 0x00){// 先备份 CRL 寄存器的值tmpreg GPIOx-CRL;// 循环从 Pin0 开始配对找出具体的 Pinfor (pinpos 0x00; pinpos 0x08; pinpos){// pos 的值为 1 左移 pinpos 位pos ((uint32_t)0x01) pinpos;// 令 pos 与输入参数 GPIO_PIN 作位与运算currentpin (GPIO_InitStruct-GPIO_Pin) pos;//若 currentpinpos, 则找到使用的引脚if (currentpin pos){//pinpos 的值左移两位 (乘以 4), 因为寄存器中 4 个位配置一个引脚pos pinpos 2;//把控制这个引脚的 4 个寄存器位清零其它寄存器位不变pinmask ((uint32_t)0x0F) pos;tmpreg ~pinmask;// 向寄存器写入将要配置的引脚的模式tmpreg | (currentmode pos);// 判断是否为下拉输入模式if (GPIO_InitStruct-GPIO_Mode GPIO_Mode_IPD){// 下拉输入模式, 引脚默认置 0, 对 BRR 寄存器写 1 对引脚置 0GPIOx-BRR (((uint32_t)0x01) pinpos);}else{// 判断是否为上拉输入模式if (GPIO_InitStruct-GPIO_Mode GPIO_Mode_IPU) {// 上拉输入模式, 引脚默认值为 1, 对 BSRR 寄存器写 1 对引脚置 1GPIOx-BSRR (((uint32_t)0x01) pinpos);}}}}// 把前面处理后的暂存值写入到 CRL 寄存器之中GPIOx-CRL tmpreg;}/*--------GPIO CRH 寄存器配置 CRH 寄存器控制着高 8 位 IO- -----*/// 配置端口高 8 位即 Pin8~Pin15if (GPIO_InitStruct-GPIO_Pin 0x00FF){// // 先备份 CRH 寄存器的值tmpreg GPIOx-CRH;// 循环从 Pin8 开始配对找出具体的 Pinfor (pinpos 0x00; pinpos 0x08; pinpos){pos (((uint32_t)0x01) (pinpos 0x08));// pos 与输入参数 GPIO_PIN 作位与运算currentpin ((GPIO_InitStruct-GPIO_Pin) pos);//若 currentpinpos, 则找到使用的引脚if (currentpin pos){//pinpos 的值左移两位 (乘以 4), 因为寄存器中 4 个位配置一个引脚pos pinpos 2; //把控制这个引脚的 4 个寄存器位清零其它寄存器位不变pinmask ((uint32_t)0x0F) pos;tmpreg ~pinmask;// 向寄存器写入将要配置的引脚的模式tmpreg | (currentmode pos);// 判断是否为下拉输入模式if (GPIO_InitStruct-GPIO_Mode GPIO_Mode_IPD){// 下拉输入模式, 引脚默认置 0, 对 BRR 寄存器写 1 可对引脚置 0GPIOx-BRR (((uint32_t)0x01) (pinpos 0x08));}// 判断是否为上拉输入模式if (GPIO_InitStruct-GPIO_Mode GPIO_Mode_IPU){// 上拉输入模式, 引脚默认值为 1, 对 BSRR 寄存器写 1 可对引脚置GPIOx-BSRR (((uint32_t)0x01) (pinpos 0x08));}}}// 把前面处理后的暂存值写入到 CRH 寄存器之中GPIOx-CRH tmpreg;} }这个函数有 GPIOx 和 GPIO_InitStruct 两个输入参数分别是 GPIO 外设指针和 GPIO 初始化结构体指针。分别用来指定要初始化的 GPIO 端口及引脚的工作模式。 要充分理解这个 GPIO 初始化函数得配合我们刚刚分析的 GPIO 引脚工作模式真值表来看。 先取得 GPIO_Mode 的值判断 bit4 是 1 还是 0 来判断是输出还是输入。如果是输出则设置输出速率即加上 GPIO_Speed 的值输入没有速率之说不用设置。配置 CRL 寄存器。通过 GPIO_Pin 的值计算出具体需要初始化哪个引脚算出后然后把需要配置的值写入到 CRL 寄存器中具体分析见代码注释。这里有一个比较有趣的是上/下拉输入并不是直接通过配置某一个寄存器来实现的而是通过写 BSRR 或者 BRR 寄存器来实现。这让很多只看手册没看固件库底层源码的人摸不着头脑因为手册的寄存器说明中没有明确的指出如何配置上拉/下拉具体见上拉/下拉寄存器说明。配置 CRH 寄存器过程同 CRL。 全新面貌使用函数点亮 LED 灯main.c里面 完成以上的准备后我们就可以用自己定义的函数来点亮 LED 灯如下 GPIO_InitTypeDef GPIO_InitStructure;/* GPIO 端口初始化 *//* 选择要控制的 GPIO 引脚 */GPIO_InitStructure.GPIO_Pin GPIO_Pin_0;/* 设置引脚模式为输出模式 */GPIO_InitStructure.GPIO_Mode GPIO_Mode_Out_PP;/* 设置引脚的输出类型为推挽输出 */GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz;// 开启 GPIO 端口时钟RCC_APB2ENR | (13);// 调用库函数初始化 GPIO 引脚0GPIO_Init(GPIOB, GPIO_InitStructure);// 使引脚输出低电平, 点亮 LED1GPIO_ResetBits(GPIOB,GPIO_Pin_0);while (1){// 使引脚输出低电平, 点亮 LEDGPIO_ResetBits(GPIOB,GPIO_Pin_0);/* 延时一段时间 */Delay(1);/* 使引脚输出高电平关闭 LED1*/GPIO_SetBits(GPIOB,GPIO_Pin_0);/* 延时一段时间 */Delay(1);}总结 什么是 ST 标准固件库不懂的时候总觉得莫测高深懂了之后一切都是纸老虎。我们从寄存器映射开始把内存跟寄存器建立起一一对应的关系然后操作寄存器点亮 LED再把寄存器操作封装成一个个函数。一步一步走来我们实现了库最简单的雏形如果我们不断地增加操作外设的函数并且把所有的外设都写完一个完整的库就实现了。 本章中的 GPIO 相关库函数及结构体定义实际上都是从 ST 标准库搬过来的。这样分析它纯粹是为了满足自己的求知欲学习其编程的方式、思想这对提高我们的编程水平是很有好处的顺便感受一下 ST 库设计的严谨性我认为这样的代码不仅严谨且华丽优美不知您是否也有这 样的感受。 与直接配置寄存器相比从执行效率上看会有额外的消耗初始化变量赋值的过程、库函数在被调用的时候要耗费调用时间在函数内部对输入参数转换所需要的额外运算也消耗一些时间(如 GPIO 中运算求出引脚号时)。而其它的宏、枚举等解释操作是作编译过程完成的这部分并不消耗内核的时间。那么函数库的优点呢是我们可以快速上手 STM32 控制器配置外设状态时不需要再纠结要向寄存器写入什么数值交流方便查错简单。这就是我们选择库的原因。现在的处理器的主频是越来越高我们不需要担心 CPU 耗费那么多时间来干活会不会被累倒库主要应用是在初始化过程而初始化过程一般是芯片刚上电或在核心运算之前的执行的这段时间的等待是 0.02us 还是 0.01us 在很多时候并没有什么区别。相对来说我们还是担心一下如果都用寄存器操作每行代码都要查数据手册的寄存器说明自己会不会被累倒吧。在以后开发的工程中一般不会去分析 ST 的库函数的实现。因为外设的库函数是很类似的库外设都包含初始化结构体以及特定的宏或枚举标识符这些封装被库函数这些转化成相应的值写入到寄存器之中函数内部的具体实现是十分枯燥和机械的工作。如果您有兴趣在您掌握了如何使用外设的库函数之后可以查看一下它的源码实现。通常我们只需要通过了解每种外设的“初始化结构体”就能够通过它去了解 STM32 的外设功能及控制。 如何在Keil5里面新建文件 1、在相关文件夹下新建需要的.c和.h文件 2、双击想加入的文件夹 加入.c文件.h文件在头文件编译后自己就出来了。 全部代码 main.c中的 #include stm32f10x.h #include stm32f10x_gpio.h /* *因为没学32的库函数版本配置72MHZ因此这里不配置PLL采用8MHZ */ #if 0 /*** ****************************************************************************** file main.c* brief 选择RGB灯亮那个颜色* param x0时为绿色* x4时为蓝色* x5时为红色* retval None* author (六千里)* date 2024-03-06* copyright 无* ******************************************************************************/ void RGB_Color(int x){switch(x){case 0: {GPIOB_CRL ~(0x0F(4*0));GPIOB_CRL | (1 (4*0) );GPIOB_ODR |0XFFFF;GPIOB_ODR ~(10);break;}case 4: {GPIOB_CRL ~(0x0F(4*1));GPIOB_CRL | (1 (4*1) );GPIOB_ODR |0XFFFF;GPIOB_ODR ~(11);break;} case 5: {GPIOB_CRL ~(0x0F(4*5));GPIOB_CRL | (1 (4*5) );GPIOB_ODR |0XFFFF;GPIOB_ODR ~(15);break;} default:;} } #endif /*** ****************************************************************************** file main.c* brief 延时函数不准* param time延时多少秒* retval None* author (六千里)* date 2024-03-06* copyright 无* ******************************************************************************/void Delay(int time){int temp_time0x1FFFF;while(time--){temp_time0x1FFFFF;while(temp_time--);} }int main (void) { #if 0RCC_APB2ENR | (13);while(1){RGB_Color(0);Delay(1);RGB_Color(5);Delay(1);RGB_Color(4);Delay(1);}#elif 0// 开启 GPIOB 端口时钟RCC-APB2ENR | (13);//清空控制 PB0 的端口位GPIOB-CRL ~( 0x0F (4*0));// 配置 PB0 为通用推挽输出速度为 10MGPIOB-CRL | (14*0);// PB0 输出 低电平GPIOB-ODR | (00);while (1);#elif 0// 开启 GPIOB 端口时钟RCC-APB2ENR | (13);//清空控制 PB0,4,5 的端口位GPIOB-CRL ~( 0x0F (4*0));GPIOB-CRL ~( 0x0F (4*4));GPIOB-CRL ~( 0x0F (4*5));// 配置 PB0,4,5 为通用推挽输出速度为 10MGPIOB-CRL | (14*0);GPIOB-CRL | (14*4);GPIOB-CRL | (14*5); /* 控制 GPIOB 的引脚 0 输出高电平 */GPIO_SetBits(GPIOB,(uint16_t)(10));/* 控制 GPIOB 的引脚 0 输出低电平 */GPIO_ResetBits(GPIOB,(uint16_t)(10)); /* 控制 GPIOB 的引脚 0、引脚 4 输出高电平使用“|”同时控制多个引脚 */GPIO_SetBits(GPIOB,(uint16_t)(10)|(uint16_t)(14));/* 控制 GPIOB 的引脚 0、引脚 4 输出低电平 */GPIO_ResetBits(GPIOB,(uint16_t)(10)|(uint16_t)(14));#elif 0 // 开启 GPIOB 端口时钟RCC-APB2ENR | (13);//清空控制 PB0,4,5 的端口位GPIOB-CRL ~( 0x0F (4*0));GPIOB-CRL ~( 0x0F (4*4));GPIOB-CRL ~( 0x0F (4*5));// 配置 PB0,4,5 为通用推挽输出速度为 10MGPIOB-CRL | (14*0);GPIOB-CRL | (14*4);GPIOB-CRL | (14*5); /* 控制 GPIOB 的引脚 0 输出高电平 */GPIO_SetBits(GPIOB,GPIO_Pin_0);/* 控制 GPIOB 的引脚 0 输出低电平 */GPIO_ResetBits(GPIOB,GPIO_Pin_0); /* 控制 GPIOB 的引脚 0、引脚 4 输出高电平使用“|”同时控制多个引脚 */GPIO_SetBits(GPIOB,GPIO_Pin_0|GPIO_Pin_1);/* 控制 GPIOB 的引脚 0、引脚 4 输出低电平 */GPIO_ResetBits(GPIOB,GPIO_Pin_0|GPIO_Pin_1);#elseGPIO_InitTypeDef GPIO_InitStructure;/* GPIO 端口初始化 *//* 选择要控制的 GPIO 引脚 */GPIO_InitStructure.GPIO_Pin GPIO_Pin_0;/* 设置引脚模式为输出模式 */GPIO_InitStructure.GPIO_Mode GPIO_Mode_Out_PP;/* 设置引脚的输出类型为推挽输出 */GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz;// 开启 GPIO 端口时钟RCC_APB2ENR | (13);// 调用库函数初始化 GPIO 引脚0GPIO_Init(GPIOB, GPIO_InitStructure);// 使引脚输出低电平, 点亮 LED1GPIO_ResetBits(GPIOB,GPIO_Pin_0);while (1){// 使引脚输出低电平, 点亮 LEDGPIO_ResetBits(GPIOB,GPIO_Pin_0);/* 延时一段时间 */Delay(1);/* 使引脚输出高电平关闭 LED1*/GPIO_SetBits(GPIOB,GPIO_Pin_0);/* 延时一段时间 */Delay(1);}#endif }void SystemInit(void) {// 函数体为空目的是为了骗过编译器不报错 }stm32f10x.h中 #ifndef __STM32F10X_H #define __STM32F10X_H#if 0 /* 片上外设基地址*/ #define PERIPH_BASE ((unsigned int)0x40000000)/* 总线基地址GPIO 都挂载到 APB2 上 */ #define APB2PERIPH_BASE (PERIPH_BASE 0x10000) /* AHB 总线基地址 */ #define AHBPERIPH_BASE (PERIPH_BASE 0x20000)/*GPIOB 外设基地址 */ #define GPIOB_BASE (APB2PERIPH_BASE 0x0C00)/* GPIOB 寄存器地址, 强制转换成指针 */ #define GPIOB_CRL *(unsigned int*)(GPIOB_BASE0x00) #define GPIOB_CRH *(unsigned int*)(GPIOB_BASE0x04) #define GPIOB_IDR *(unsigned int*)(GPIOB_BASE0x08) #define GPIOB_ODR *(unsigned int*)(GPIOB_BASE0x0C) #define GPIOB_BSRR *(unsigned int*)(GPIOB_BASE0x10) #define GPIOB_BRR *(unsigned int*)(GPIOB_BASE0x14) #define GPIOB_LCKR *(unsigned int*)(GPIOB_BASE0x18)/*RCC 外设基地址 */ #define RCC_BASE (AHBPERIPH_BASE 0x1000) /*RCC 的 AHB1 时钟使能寄存器地址, 强制转换成指针 */ #define RCC_APB2ENR *(unsigned int*)(RCC_BASE0x18)#else //寄存器的值常常是芯片外设自动更改的即使 CPU 没有执行程序也有可能发生变化 //编译器有可能会对没有执行程序的变量进行优化//volatile 表示易变的变量防止编译器优化 #define __IO volatile/* 片上外设基地址*/ #define PERIPH_BASE ((unsigned int)0x40000000) /*APB2 总线基地址 */ #define APB2PERIPH_BASE (PERIPH_BASE 0x10000) /* AHB 总线基地址 */ #define AHBPERIPH_BASE (PERIPH_BASE 0x20000) /*GPIO 外设基地址 */ #define GPIOA_BASE (APB2PERIPH_BASE 0x0800) #define GPIOB_BASE (APB2PERIPH_BASE 0x0C00) #define GPIOC_BASE (APB2PERIPH_BASE 0x1000) #define GPIOD_BASE (APB2PERIPH_BASE 0x1400) #define GPIOE_BASE (APB2PERIPH_BASE 0x1800) #define GPIOF_BASE (APB2PERIPH_BASE 0x1C00) #define GPIOG_BASE (APB2PERIPH_BASE 0x2000) /*RCC 外设基地址 */ #define RCC_BASE (AHBPERIPH_BASE 0x1000)/* GPIO 外设声明*/ #define GPIOA ((GPIO_TypeDef *) GPIOA_BASE) #define GPIOB ((GPIO_TypeDef *) GPIOB_BASE) #define GPIOC ((GPIO_TypeDef *) GPIOC_BASE) #define GPIOD ((GPIO_TypeDef *) GPIOD_BASE) #define GPIOE ((GPIO_TypeDef *) GPIOE_BASE) #define GPIOF ((GPIO_TypeDef *) GPIOF_BASE) #define GPIOG ((GPIO_TypeDef *) GPIOG_BASE)// RCC 外设声明 #define RCC ((RCC_TypeDef *) RCC_BASE)//*RCC 的 AHB1 时钟使能寄存器地址, 强制转换成指针 */ #define RCC_APB2ENR *(unsigned int*)(RCC_BASE0x18)/*GPIO 引脚号定义 */ #define GPIO_Pin_0 ((uint16_t)0x0001)/*! 选择 Pin0 (10) */ #define GPIO_Pin_1 ((uint16_t)0x0002)/*! 选择 Pin1 (11)*/ #define GPIO_Pin_2 ((uint16_t)0x0004)/*! 选择 Pin2 (12)*/ #define GPIO_Pin_3 ((uint16_t)0x0008)/*! 选择 Pin3 (13)*/ #define GPIO_Pin_4 ((uint16_t)0x0010)/*! 选择 Pin4 */ #define GPIO_Pin_5 ((uint16_t)0x0020)/*! 选择 Pin5 */ #define GPIO_Pin_6 ((uint16_t)0x0040)/*! 选择 Pin6 */ #define GPIO_Pin_7 ((uint16_t)0x0080)/*! 选择 Pin7 */ #define GPIO_Pin_8 ((uint16_t)0x0100)/*! 选择 Pin8 */ #define GPIO_Pin_9 ((uint16_t)0x0200)/*! 选择 Pin9 */ #define GPIO_Pin_10 ((uint16_t)0x0400)/*! 选择 Pin10 */ #define GPIO_Pin_11 ((uint16_t)0x0800)/*! 选择 Pin11 */ #define GPIO_Pin_12 ((uint16_t)0x1000)/*! 选择 Pin12 */ #define GPIO_Pin_13 ((uint16_t)0x2000)/*! 选择 Pin13 */ #define GPIO_Pin_14 ((uint16_t)0x4000)/*! 选择 Pin14 */ #define GPIO_Pin_15 ((uint16_t)0x8000)/*! 选择 Pin15 */ #define GPIO_Pin_All ((uint16_t)0xFFFF)/*! 选择全部引脚 */typedef unsigned int uint32_t; typedef unsigned short uint16_t;// GPIO 寄存器结构体定义 typedef struct { __IO uint32_t CRL;// 端口配置低寄存器地址偏移 0X00 __IO uint32_t CRH;// 端口配置高寄存器地址偏移 0X04 __IO uint32_t IDR;// 端口数据输入寄存器地址偏移 0X08 __IO uint32_t ODR;// 端口数据输出寄存器地址偏移 0X0C __IO uint32_t BSRR;// 端口位设置/清除寄存器地址偏移 0X10 __IO uint32_t BRR;// 端口位清除寄存器地址偏移 0X14 __IO uint32_t LCKR;// 端口配置锁定寄存器地址偏移 0X18 } GPIO_TypeDef; // RCC 寄存器结构体定义 typedef struct {uint32_t CR;uint32_t CFGR;uint32_t CIR;uint32_t APB2RSTR;uint32_t APB1RSTR;uint32_t AHBENR;uint32_t APB2ENR;uint32_t APB1ENR;uint32_t BDCR;uint32_t CSR; }RCC_TypeDef;/** * GPIO 输出速率枚举定义 */ typedef enum {GPIO_Speed_10MHz 1,// 10MHZ(01)bGPIO_Speed_2MHz,// 2MHZ(10)bGPIO_Speed_50MHz// 50MHZ(11)b } GPIOSpeed_TypeDef;/** * GPIO 工作模式枚举定义 */ typedef enum {GPIO_Mode_AIN 0x0,// 模拟输入(0000 0000)bGPIO_Mode_IN_FLOATING 0x04,// 浮空输入(0000 0100)bGPIO_Mode_IPD 0x28,// 下拉输入(0010 1000)bGPIO_Mode_IPU 0x48,// 上拉输入(0100 1000)bGPIO_Mode_Out_OD 0x14,// 开漏输出(0001 0100)bGPIO_Mode_Out_PP 0x10,// 推挽输出(0001 0000)bGPIO_Mode_AF_OD 0x1C,// 复用开漏输出(0001 1100)bGPIO_Mode_AF_PP 0x18// 复用推挽输出(0001 1000)b } GPIOMode_TypeDef;/** * GPIO 初始化结构体类型定义 */ typedef struct {uint16_t GPIO_Pin;/*! 选择要配置的 GPIO 引脚可输入 GPIO_Pin_ 定义的宏 */GPIOSpeed_TypeDef GPIO_Speed;/*! 选择 GPIO 引脚的速率可输入 GPIOSpeed_TypeDef 定义的枚举值 */GPIOMode_TypeDef GPIO_Mode;/*! 选择 GPIO 引脚的工作模式可输入 GPIOMode_TypeDef 定义的枚举值 */} GPIO_InitTypeDef;#endif #endif /*__STM32F10X_H*/stm32f10x_gpio.c中 #include stm32f10x_gpio.h/** * 函数功能设置引脚为高电平 * 参数说明GPIOx: 该参数为 GPIO_TypeDef 类型的指针指向 GPIO 端口的地址 *GPIO_Pin: 选择要设置的 GPIO 端口引脚可输入宏 GPIO_Pin_0-15 *表示 GPIOx 端口的 0-15 号引脚。 */ void GPIO_SetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin) { /* 设置 GPIOx 端口 BSRR 寄存器的第 GPIO_Pin 位使其输出高电平 */ /* 因为 BSRR 寄存器写 0 不影响 宏 GPIO_Pin 只是对应位为 1其它位均为 0所以可以直接赋值 */GPIOx-BSRR GPIO_Pin; }/** * 函数功能设置引脚为低电平 * 参数说明GPIOx: 该参数为 GPIO_TypeDef 类型的指针指向 GPIO 端口的地址 *GPIO_Pin: 选择要设置的 GPIO 端口引脚可输入宏 GPIO_Pin_0-15 *表示 GPIOx 端口的 0-15 号引脚。 */ void GPIO_ResetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin) { /* 设置 GPIOx 端口 BRR 寄存器的第 GPIO_Pin 位, 使其输出低电平 */ /* 因为 BRR 寄存器写 0 不影响 宏 GPIO_Pin 只是对应位为 1其它位均为 0所以可以直接赋值 */GPIOx-BRR GPIO_Pin; }/** * 函数功能初始化引脚模式 * 参数说明GPIOx该参数为 GPIO_TypeDef 类型的指针指向 GPIO 端口的地址 *GPIO_InitTypeDef:GPIO_InitTypeDef 结构体指针指向初始化变量 */ void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct) { uint32_t currentmode 0x00,currentpin 0x00,pinpos 0x00,pos 0x00; uint32_t tmpreg 0x00, pinmask 0x00;/*---------------- GPIO 模式配置 -------------------*/// 把输入参数 GPIO_Mode 的低四位暂存在 currentmodecurrentmode ((uint32_t)GPIO_InitStruct-GPIO_Mode) ((uint32_t)0x0F);// bit4 是 1 表示输出bit4 是 0 则是输入// 判断 bit4 是 1 还是 0即首选判断是输入还是输出模式if((((uint32_t)GPIO_InitStruct-GPIO_Mode) ((uint32_t)0x10)) ! 0x00){// 输出模式则要设置输出速度currentmode | (uint32_t)GPIO_InitStruct-GPIO_Speed;}/*-----GPIO CRL 寄存器配置 CRL 寄存器控制着低 8 位 IO- ----*/// 配置端口低 8 位即 Pin0~Pin7if (((uint32_t)GPIO_InitStruct-GPIO_Pin ((uint32_t)0x00FF)) ! 0x00){// 先备份 CRL 寄存器的值tmpreg GPIOx-CRL;// 循环从 Pin0 开始配对找出具体的 Pinfor (pinpos 0x00; pinpos 0x08; pinpos){// pos 的值为 1 左移 pinpos 位pos ((uint32_t)0x01) pinpos;// 令 pos 与输入参数 GPIO_PIN 作位与运算currentpin (GPIO_InitStruct-GPIO_Pin) pos;//若 currentpinpos, 则找到使用的引脚if (currentpin pos){//pinpos 的值左移两位 (乘以 4), 因为寄存器中 4 个位配置一个引脚pos pinpos 2;//把控制这个引脚的 4 个寄存器位清零其它寄存器位不变pinmask ((uint32_t)0x0F) pos;tmpreg ~pinmask;// 向寄存器写入将要配置的引脚的模式tmpreg | (currentmode pos);// 判断是否为下拉输入模式if (GPIO_InitStruct-GPIO_Mode GPIO_Mode_IPD){// 下拉输入模式, 引脚默认置 0, 对 BRR 寄存器写 1 对引脚置 0GPIOx-BRR (((uint32_t)0x01) pinpos);}else{// 判断是否为上拉输入模式if (GPIO_InitStruct-GPIO_Mode GPIO_Mode_IPU) {// 上拉输入模式, 引脚默认值为 1, 对 BSRR 寄存器写 1 对引脚置 1GPIOx-BSRR (((uint32_t)0x01) pinpos);}}}}// 把前面处理后的暂存值写入到 CRL 寄存器之中GPIOx-CRL tmpreg;}/*--------GPIO CRH 寄存器配置 CRH 寄存器控制着高 8 位 IO- -----*/// 配置端口高 8 位即 Pin8~Pin15if (GPIO_InitStruct-GPIO_Pin 0x00FF){// // 先备份 CRH 寄存器的值tmpreg GPIOx-CRH;// 循环从 Pin8 开始配对找出具体的 Pinfor (pinpos 0x00; pinpos 0x08; pinpos){pos (((uint32_t)0x01) (pinpos 0x08));// pos 与输入参数 GPIO_PIN 作位与运算currentpin ((GPIO_InitStruct-GPIO_Pin) pos);//若 currentpinpos, 则找到使用的引脚if (currentpin pos){//pinpos 的值左移两位 (乘以 4), 因为寄存器中 4 个位配置一个引脚pos pinpos 2; //把控制这个引脚的 4 个寄存器位清零其它寄存器位不变pinmask ((uint32_t)0x0F) pos;tmpreg ~pinmask;// 向寄存器写入将要配置的引脚的模式tmpreg | (currentmode pos);// 判断是否为下拉输入模式if (GPIO_InitStruct-GPIO_Mode GPIO_Mode_IPD){// 下拉输入模式, 引脚默认置 0, 对 BRR 寄存器写 1 可对引脚置 0GPIOx-BRR (((uint32_t)0x01) (pinpos 0x08));}// 判断是否为上拉输入模式if (GPIO_InitStruct-GPIO_Mode GPIO_Mode_IPU){// 上拉输入模式, 引脚默认值为 1, 对 BSRR 寄存器写 1 可对引脚置GPIOx-BSRR (((uint32_t)0x01) (pinpos 0x08));}}}// 把前面处理后的暂存值写入到 CRH 寄存器之中GPIOx-CRH tmpreg;} }stm32f10x_gpio.h中 #ifndef __STM32F10X_GPIO_H #define __STM32F10X_GPIO_H#include stm32f10x.hvoid GPIO_ResetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin); void GPIO_SetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin); void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct);#endif /*__STM32F10X_GPIO_H*/参考https://doc.embedfire.com/products/link/zh/latest/index.html
http://www.hkea.cn/news/14536623/

相关文章:

  • 天津高端网站设计公司成都有没有做网站建设的
  • 数据分析师岗位要求seo岗位培训
  • 做电视网站需要多大的服务器优化型网站是模板
  • 高端网站制作网站建设建站平台软件
  • 做的网站百度推广平台登录网址
  • 招聘网站官网马鞍山网站建设 明达
  • 视频分享网站建设难吗管理软件是什么
  • 做30个精品网站自适应手机模板
  • 南京营销型网站建设婚姻网站建设注意事项
  • 怎么在百度上面做网站网站建设工程设计图
  • 网站建设前景东莞关键词搜索排名
  • 南昌旅游网站建设方案wordpress优惠券模板
  • 万户做网站好不好企业网企业网站制作
  • 河南省通信管理局网站备案电话木马设计公司官网
  • 做数据统计的网站机票售票网站开发
  • 深圳企业专业网站建设广州做网站哪家好
  • 做网站的专业公司易企秀h5
  • 吉林市网站推广erp系统页面
  • 公司的网站开发部门叫什么核工业华南建设工程集团公司网站
  • 阜城网站建设价格用户体验网站
  • 企业服务公司经营范围免费优化网站建设
  • 做pc端网站信息网站和微信公众号建设
  • 网站建设和开发做网站运营需要学什么软件
  • 东莞网站优化快速排名网站怎么查哪家公司做的
  • 保定网站设计多少钱好看的企业网站模板
  • 东莞网站搜索排名南阳市网站建设
  • 微盟如何做网站泰安八戒电子商务有限公司
  • wordpress 导航站模板深圳建站服务公司
  • 南昌企业建站系统怎样做网站优化 关键词
  • 自助建站网站模板免费图纸网