STM32学习笔记(5)-点亮LED(使用标准库函数)
仅供个人学习,记录学习STM32中的要点,加深对知识的巩固
前言
上一小节笔者讲述了如何利用STM32所提供的的库资源,也就是头文件来控制寄存器。所使用的的方法就是库资源里面的头文件对寄存器的封装来实现的。所以既然官方提供 了这样的库资源,那么我们可以使用库函数来控制外设。这就是本小节所要讲述的内容。本小节依旧在前几节的基础上进行修改。
添加库函数
可以看到当前工程所存在的库函数只有这一个,使我们之前用到的。继续添加库函数,如下所示:
这样它的每一个外设都对应一个C文件。
添加完后,要做的第一步就是将下面这行代码替换为库函数代码的形式。
使用库函数
RCC_APB2PeriphClockCmd介绍
在我们的库函数中找到使能ABP2外设时钟的函数,详细介绍如下:
RCC_APB2PeriphClockCmd 是一个函数宏,用于启用或禁用 STM32 微控制器的某个外设的时钟。该函数宏包含在 STM32 标准外设库或 HAL 库中,用于简化外设时钟的配置和控制。
具体来说,RCC_APB2PeriphClockCmd 的语法为:
void RCC_APB2PeriphClockCmd(uint32_t RCC_APB2Periph, FunctionalState NewState);
其中,RCC_APB2Periph 参数表示需要启用或禁用的外设时钟,可以通过或运算符 | 将多个外设时钟合并到一起;NewState 参数表示需要设置的状态,可以为 ENABLE 或 DISABLE。
例如,要启用 GPIOA端口的时钟,可以使用以下代码:
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
这样就可以将 GPIOA 和 GPIOB 端口的时钟都启用。代码变化如下:
GPIO_InitTypeDef 介绍
开启时钟之后, 将PA1-PA4设置为通用推挽输出。工作频率为50MHz。这里先介绍一下GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitTypeDef
是一个结构体类型,用于描述 STM32 微控制器的 GPIO 端口的初始化参数。该结构体类型包含在 STM32 标准外设库或 HAL 库中,用于简化 GPIO 端口的配置和初始化。
具体来说,GPIO_InitTypeDef
结构体类型包含以下成员变量:
GPIO_Pin
: 表示要配置的 GPIO 引脚的编号,可以使用或运算符 | 将多个引脚合并到一起。例如,可以使用 GPIO_Pin_0 | GPIO_Pin_1 表示配置 GPIO 的第 0 和第 1 个引脚。
GPIO_Speed
: 表示 GPIO 端口的输出速率,可以为 GPIO_Speed_10MHz、GPIO_Speed_2MHz 或 GPIO_Speed_50MHz,分别表示输出速率为 10MHz、2MHz 或 50MHz。
GPIO_Mode
: 表示 GPIO 引脚的工作模式,可以为输入模式、推挽输出模式、开漏输出模式、复用模式或模拟模式。
GPIO_InitTypeDef的示例
使用 GPIO_InitTypeDef
结构体类型进行 GPIO 端口的初始化,一般可以按照以下步骤进行:
- 定义一个 GPIO_InitTypeDef 结构体变量,例如:
GPIO_InitTypeDef GPIO_InitStructure;
- 初始化 GPIO_InitStructure 结构体变量的各个成员变量,例如
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_4;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
3.调用 GPIO_Init 函数,将 GPIO_InitStructure 结构体变量作为参数传入,实现 GPIO 端口的初始化,例如:
GPIO_Init(GPIOA, &GPIO_InitStructure);
现在的代码如下图
这样就通过库函数完成了对PA1-PA4的配置。
剩下的就是对PA1-PA4输出高低电平。用到这样的函数。
GPIO_WriteBit
是一个函数宏,用于设置 STM32 微控制器的 GPIO 引脚的输出电平。该函数宏包含在 STM32 标准外设库或 HAL 库中,用于简化 GPIO 端口的输出控制。
GPIO_WriteBit
的语法为:
GPIO_WriteBit(GPIOx, GPIO_Pin, BitVal)
其中,GPIOx
参数表示要控制的 GPIO 端口,可以为 GPIOA、GPIOB、GPIOC 等;GPIO_Pin
参数表示要设置的 GPIO 引脚编号;BitVal
参数表示要设置的输出电平,可以为 Bit_SET 或 Bit_RESET,分别表示设置为高电平或低电平。
例如,要将 GPIOA 端口的第 1 个到第 4个引脚设置为高电平,可以使用以下代码:
GPIO_WriteBit(GPIOA, GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_4, Bit_SET);
低电平则将Bit_SET改为Bit_RESET。
代码
完整代码如下:
#include <stm32f10x.h>
void Delay(unsigned long nCount);
int main(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
//RCC->APB2ENR |= (1<<2);//RCCAPB2ENR |= (1<<2);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
//GPIOA->CRL &= 0xFFF0000F;//GPIOACRL &= 0xFFF0000F;
//GPIOA->CRL |= 0x00033330;//GPIOACRL |= 0x00033330;
//GPIOA->ODR &= ~((1<<1)|(1<<2)|(1<<3)|(1<<4)); //GPIOAODR &= ~((1<<1)|(1<<2)|(1<<3)|(1<<4));
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_4;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(GPIOA, &GPIO_InitStructure);
while(1)
{
//点亮LED1~LED4 PA1~PA4输出高电平
//GPIOA->ODR |= (1<<1)|(1<<2)|(1<<3)|(1<<4);//GPIOAODR |= (1<<1)|(1<<2)|(1<<3)|(1<<4);
GPIO_WriteBit(GPIOA, GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_4, Bit_SET);
Delay(0xfffff);
//熄灭LED1~LED4 PA1~PA4输出低电平
//GPIOA->ODR &= ~((1<<1)|(1<<2)|(1<<3)|(1<<4));//GPIOAODR &= ~((1<<1)|(1<<2)|(1<<3)|(1<<4));
GPIO_WriteBit(GPIOA, GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_4, Bit_RESET);
Delay(0xfffff);
}
}
void Delay(unsigned long nCount)
{
while(nCount--)
{
}
}
总结
那么之后的代码,笔者都会使用库函数的方式来编写。 这里笔者总结一下使用寄存器和库函数的区别:
-
复杂性:库函数的调用比寄存器访问方式要更简单和方便,不需要了解寄存器的具体操作方式和特性,也不需要手动计算各个位的偏移量和掩码。库函数通过封装底层的寄存器操作,提供了一组高层次的函数接口,方便用户直接调用和使用。
-
可读性:库函数比寄存器访问方式更易读和易懂,通过函数名、参数和返回值等语义化的信息,可以清晰地表达函数的功能和用途。而寄存器访问方式需要通过寄存器名称、偏移量和掩码等底层细节信息,对每个操作进行手动计算和控制,容易出现代码难以理解和维护的情况。
-
可移植性:库函数比寄存器访问方式更具有可移植性,不依赖于硬件平台和具体实现方式。库函数通过抽象出通用的接口和函数调用规范,使得代码可以在不同的芯片和开发环境中使用和移植,提高了代码的可重用性和可维护性。而寄存器访问方式需要对硬件平台和具体寄存器映射方式有深入的了解,才能进行正确的寄存器操作,容易受到平台差异的影响,不够具有可移植性。
需要注意的是,虽然库函数的调用比寄存器访问方式要更简单和方便,但是库函数的性能和效率通常不如寄存器访问方式,特别是在需要频繁读写寄存器的情况下。在对性能和功耗有严格要求的应用场景中,推荐使用寄存器访问方式进行开发和优化。