STM32单片机芯片与内部03 GPIO-LED控制-硬件、库函数配置、HAL库配置

目录

一、硬件连接与基本原理

二、库函数工程模板

[1、GPIO Init structure definition](#1、GPIO Init structure definition)

2、时钟使能

3、配置输出模式、速度

4、初始化

5、设定高低电平

三、库函数API

1、API简介

2、初始化封装

3、输出高低电平封装

五、HAL库工程模板

[1、GPIO Init structure definition](#1、GPIO Init structure definition)

2、时钟使能

3、配置输出模式、速度

4、初始化

5、设定高低电平

六、HAL库API

1、初始化封装

2、输出高低电平封装

七、用户侧

1、库函数

2、HAL库

3、用户


一、硬件连接与基本原理

PB5和PE5,输出高点平则LED关闭,输出低电平LED点亮。

二、库函数工程模板

stm32f10x_gpio.c、stm32f10x_gpio.h为核心。

1、GPIO Init structure definition

cpp 复制代码
typedef struct
{
  uint16_t GPIO_Pin;             /*!< Specifies the GPIO pins to be configured.
                                      This parameter can be any value of @ref GPIO_pins_define */

  GPIOSpeed_TypeDef GPIO_Speed;  /*!< Specifies the speed for the selected pins.
                                      This parameter can be a value of @ref GPIOSpeed_TypeDef */

  GPIOMode_TypeDef GPIO_Mode;    /*!< Specifies the operating mode for the selected pins.
                                      This parameter can be a value of @ref GPIOMode_TypeDef */
}GPIO_InitTypeDef;

首先是配置,刚才前文讲了其实GPIO可以配置的就是模式和速度,除此之外还有引脚,因此可以封装为结构体。

2、时钟使能

前文未进行阐述这个部分,其实很简单的输出有速度,输出有各类时序电路其都需要时钟进行支撑,而对于前面提到的PB5和PE5,其本质也有对应的端口时钟,其挂载在APB2,这个具体在后文进行介绍。

cpp 复制代码
#define RCC_APB2Periph_AFIO              ((uint32_t)0x00000001)
#define RCC_APB2Periph_GPIOA             ((uint32_t)0x00000004)
#define RCC_APB2Periph_GPIOB             ((uint32_t)0x00000008)
#define RCC_APB2Periph_GPIOC             ((uint32_t)0x00000010)
#define RCC_APB2Periph_GPIOD             ((uint32_t)0x00000020)
#define RCC_APB2Periph_GPIOE             ((uint32_t)0x00000040)
#define RCC_APB2Periph_GPIOF             ((uint32_t)0x00000080)
#define RCC_APB2Periph_GPIOG             ((uint32_t)0x00000100)
#define RCC_APB2Periph_ADC1              ((uint32_t)0x00000200)
#define RCC_APB2Periph_ADC2              ((uint32_t)0x00000400)
#define RCC_APB2Periph_TIM1              ((uint32_t)0x00000800)
#define RCC_APB2Periph_SPI1              ((uint32_t)0x00001000)
#define RCC_APB2Periph_TIM8              ((uint32_t)0x00002000)
#define RCC_APB2Periph_USART1            ((uint32_t)0x00004000)
#define RCC_APB2Periph_ADC3              ((uint32_t)0x00008000)
#define RCC_APB2Periph_TIM15             ((uint32_t)0x00010000)
#define RCC_APB2Periph_TIM16             ((uint32_t)0x00020000)
#define RCC_APB2Periph_TIM17             ((uint32_t)0x00040000)
#define RCC_APB2Periph_TIM9              ((uint32_t)0x00080000)
#define RCC_APB2Periph_TIM10             ((uint32_t)0x00100000)
#define RCC_APB2Periph_TIM11             ((uint32_t)0x00200000)

#define RCC_APB1Periph_TIM2              ((uint32_t)0x00000001)
#define RCC_APB1Periph_TIM3              ((uint32_t)0x00000002)
#define RCC_APB1Periph_TIM4              ((uint32_t)0x00000004)
#define RCC_APB1Periph_TIM5              ((uint32_t)0x00000008)
#define RCC_APB1Periph_TIM6              ((uint32_t)0x00000010)
#define RCC_APB1Periph_TIM7              ((uint32_t)0x00000020)
#define RCC_APB1Periph_TIM12             ((uint32_t)0x00000040)
#define RCC_APB1Periph_TIM13             ((uint32_t)0x00000080)
#define RCC_APB1Periph_TIM14             ((uint32_t)0x00000100)
#define RCC_APB1Periph_WWDG              ((uint32_t)0x00000800)
#define RCC_APB1Periph_SPI2              ((uint32_t)0x00004000)
#define RCC_APB1Periph_SPI3              ((uint32_t)0x00008000)
#define RCC_APB1Periph_USART2            ((uint32_t)0x00020000)
#define RCC_APB1Periph_USART3            ((uint32_t)0x00040000)
#define RCC_APB1Periph_UART4             ((uint32_t)0x00080000)
#define RCC_APB1Periph_UART5             ((uint32_t)0x00100000)
#define RCC_APB1Periph_I2C1              ((uint32_t)0x00200000)
#define RCC_APB1Periph_I2C2              ((uint32_t)0x00400000)
#define RCC_APB1Periph_USB               ((uint32_t)0x00800000)
#define RCC_APB1Periph_CAN1              ((uint32_t)0x02000000)
#define RCC_APB1Periph_CAN2              ((uint32_t)0x04000000)
#define RCC_APB1Periph_BKP               ((uint32_t)0x08000000)
#define RCC_APB1Periph_PWR               ((uint32_t)0x10000000)
#define RCC_APB1Periph_DAC               ((uint32_t)0x20000000)
#define RCC_APB1Periph_CEC               ((uint32_t)0x40000000)

通过如下配置即可实现开启APB2上的GPIOB和GPIOE的时钟。

cpp 复制代码
 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOE, ENABLE);	 //使能PB,PE端口时钟

3、配置输出模式、速度

cpp 复制代码
typedef enum
{ GPIO_Mode_AIN = 0x0,
  GPIO_Mode_IN_FLOATING = 0x04,
  GPIO_Mode_IPD = 0x28,
  GPIO_Mode_IPU = 0x48,
  GPIO_Mode_Out_OD = 0x14,
  GPIO_Mode_Out_PP = 0x10,
  GPIO_Mode_AF_OD = 0x1C,
  GPIO_Mode_AF_PP = 0x18
}GPIOMode_TypeDef;
cpp 复制代码
typedef enum
{ 
  GPIO_Speed_10MHz = 1,
  GPIO_Speed_2MHz, 
  GPIO_Speed_50MHz
}GPIOSpeed_TypeDef;

可以看到在库函数已进行了响应的封装,具有了各类的配置。

4、初始化

可以看到在GPIO_Init(GPIOB, &GPIO_InitStructure)前面是对PB进行了配置,后面配置PE5的时候,结构体本质上只有IO、速度、模式的区别,在均相同的时候,直接初始化即可,修改好GPIOB、GPIOE即可。

cpp 复制代码
 GPIO_InitTypeDef  GPIO_InitStructure;
 	
 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOE, ENABLE);	 //使能PB,PE端口时钟
	
 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;				 //LED0-->PB.5 端口配置
 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; 		 //推挽输出
 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;		 //IO口速度为50MHz
 GPIO_Init(GPIOB, &GPIO_InitStructure);					 //根据设定参数初始化GPIOB.5

 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;	    		 //LED1-->PE.5
 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; 		 //推挽输出
 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;		 //IO口速度为50MHz
 GPIO_Init(GPIOE, &GPIO_InitStructure);	  				 //根据设定参数初始化GPIOE.5

例如可以修改为:

cpp 复制代码
 GPIO_InitTypeDef  GPIO_InitStructure;
 	
 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOE, ENABLE);	 //使能PB,PE端口时钟
	
 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;				 //LED0-->PB.5 端口配置
 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; 		 //推挽输出
 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;		 //IO口速度为50MHz
 GPIO_Init(GPIOB, &GPIO_InitStructure);					 //根据设定参数初始化GPIOB.5
 GPIO_Init(GPIOE, &GPIO_InitStructure);	  				 //根据设定参数初始化GPIOE.5

5、设定高低电平

前文提到,本质上要设定BSRR寄存器和BRR寄存器,这个部分库函数也进行了相关的封装。

cpp 复制代码
void GPIO_SetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
{
  /* Check the parameters */
  assert_param(IS_GPIO_ALL_PERIPH(GPIOx));
  assert_param(IS_GPIO_PIN(GPIO_Pin));
  
  GPIOx->BSRR = GPIO_Pin;
}
void GPIO_ResetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
{
  /* Check the parameters */
  assert_param(IS_GPIO_ALL_PERIPH(GPIOx));
  assert_param(IS_GPIO_PIN(GPIO_Pin));
  
  GPIOx->BRR = GPIO_Pin;
}

三、库函数API

1、API简介

API(应用程序编程接口)是软件系统中不同部分之间通信的一套规则。它定义了请求的格式、传输方式、数据结构和操作规则,使得不同的软件应用能够相互交互和数据交换。

因此如何设计前文的初始化使得用户层可以不用管底层配置,从而直接调用很重要。

2、初始化封装

cpp 复制代码
void LED_Init(void)
{
 
 GPIO_InitTypeDef  GPIO_InitStructure;
 	
 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOE, ENABLE);	 //使能PB,PE端口时钟
	
 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;				 //LED0-->PB.5 端口配置
 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; 		 //推挽输出
 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;		 //IO口速度为50MHz
 GPIO_Init(GPIOB, &GPIO_InitStructure);					 //根据设定参数初始化GPIOB.5
 GPIO_SetBits(GPIOB,GPIO_Pin_5);						 //PB.5 输出高
 GPIO_Init(GPIOE, &GPIO_InitStructure);	  				 //推挽输出 ,IO口速度为50MHz
 GPIO_SetBits(GPIOE,GPIO_Pin_5); 						 //PE.5 输出高 
}

可以看到封装为LED_Init,用户侧基本上直接调用即可。

cpp 复制代码
LED_Init();		  	//初始化与LED连接的硬件接口

3、输出高低电平封装

除了初始化一次性配置外,对于GPIO输出高低电平肯定是要进行重复的操作的。直接看GPIO_SetBits、GPIO_ResetBits本质上还是官方提供的函数名,不适合用户侧去直观使用,可以进行额外封装。封装后几乎隔离了PB5的概念。

cpp 复制代码
void LED0_HIGH()
{
  GPIO_SetBits(GPIOB,GPIO_Pin_5);
}
void LED1_HIGH()
{
  GPIO_SetBits(GPIOE,GPIO_Pin_5);
}
void LED0_LOW()
{
  GPIO_ResetBits(GPIOB,GPIO_Pin_5);
}
void LED1_LOW()
{
  GPIO_ResetBits(GPIOE,GPIO_Pin_5);
}

后续如果要添加额外内容,即可在LED_HIGH、LED_LOW内进行编写即可。

五、HAL库工程模板

stm32f1xx_hal_gpio.c、 stm32f1xx_hal_gpio.h为核心。其实HAL就是更深层次的封装,进一步隔离了BSP和用户层。

1、GPIO Init structure definition

cpp 复制代码
typedef struct
{
  uint32_t Pin;       /*!< Specifies the GPIO pins to be configured.
                           This parameter can be any value of @ref GPIO_pins_define */

  uint32_t Mode;      /*!< Specifies the operating mode for the selected pins.
                           This parameter can be a value of @ref GPIO_mode_define */

  uint32_t Pull;      /*!< Specifies the Pull-up or Pull-Down activation for the selected pins.
                           This parameter can be a value of @ref GPIO_pull_define */

  uint32_t Speed;     /*!< Specifies the speed for the selected pins.
                           This parameter can be a value of @ref GPIO_speed_define */
} GPIO_InitTypeDef;

首先是配置,刚才前文讲了其实GPIO可以配置的就是模式和速度,除此之外还有引脚,因此可以封装为结构体。相比较于库函数,其增加了Pull作为上下拉的配置。

2、时钟使能

前面提到本质是挂载在APB2总线,HAL库则不再让用户考虑这个。

cpp 复制代码
    __HAL_RCC_GPIOB_CLK_ENABLE();           	//开启GPIOB时钟
	__HAL_RCC_GPIOE_CLK_ENABLE();           	//开启GPIOE时钟

3、配置输出模式、速度

可以看到不再封装在结构体了,而是直接配置为define。

cpp 复制代码
#define  GPIO_MODE_INPUT                        0x00000000u   /*!< Input Floating Mode                   */
#define  GPIO_MODE_OUTPUT_PP                    0x00000001u   /*!< Output Push Pull Mode                 */
#define  GPIO_MODE_OUTPUT_OD                    0x00000011u   /*!< Output Open Drain Mode                */
#define  GPIO_MODE_AF_PP                        0x00000002u   /*!< Alternate Function Push Pull Mode     */
#define  GPIO_MODE_AF_OD                        0x00000012u   /*!< Alternate Function Open Drain Mode    */
#define  GPIO_MODE_AF_INPUT                     GPIO_MODE_INPUT          /*!< Alternate Function Input Mode         */
cpp 复制代码
#define  GPIO_NOPULL        0x00000000u   /*!< No Pull-up or Pull-down activation  */
#define  GPIO_PULLUP        0x00000001u   /*!< Pull-up activation                  */
#define  GPIO_PULLDOWN      0x00000002u   /*!< Pull-down activation                */
cpp 复制代码
#define  GPIO_SPEED_FREQ_LOW              (GPIO_CRL_MODE0_1) /*!< Low speed */
#define  GPIO_SPEED_FREQ_MEDIUM           (GPIO_CRL_MODE0_0) /*!< Medium speed */
#define  GPIO_SPEED_FREQ_HIGH             (GPIO_CRL_MODE0)   /*!< High speed */

可以看到在库函数已进行了相应的封装,具有了各类的配置。

4、初始化

可以看到在GPIO_Init(GPIOB, &GPIO_InitStructure)前面是对PB进行了配置,后面配置PE5的时候,结构体本质上只有IO、速度、模式的区别,在均相同的时候,直接初始化即可,修改好GPIOB、GPIOE即可。

cpp 复制代码
    GPIO_InitTypeDef GPIO_Initure;

    __HAL_RCC_GPIOB_CLK_ENABLE();           	//开启GPIOB时钟
	__HAL_RCC_GPIOE_CLK_ENABLE();           	//开启GPIOE时钟
	
    GPIO_Initure.Pin=GPIO_PIN_5; 				//PB5
    GPIO_Initure.Mode=GPIO_MODE_OUTPUT_PP;  	//推挽输出
    GPIO_Initure.Pull=GPIO_PULLUP;          	//上拉
    GPIO_Initure.Speed=GPIO_SPEED_FREQ_HIGH;    //高速
    HAL_GPIO_Init(GPIOB,&GPIO_Initure);

	GPIO_Initure.Pin=GPIO_PIN_5; 				//PE5
	HAL_GPIO_Init(GPIOE,&GPIO_Initure);

5、设定高低电平

前文提到,本质上要设定BSRR寄存器和BRR寄存器,这个部分HAL库函数也进行了相关的封装。

cpp 复制代码
void HAL_GPIO_WritePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin, GPIO_PinState PinState)
{
  /* Check the parameters */
  assert_param(IS_GPIO_PIN(GPIO_Pin));
  assert_param(IS_GPIO_PIN_ACTION(PinState));

  if (PinState != GPIO_PIN_RESET)
  {
    GPIOx->BSRR = GPIO_Pin;
  }
  else
  {
    GPIOx->BSRR = (uint32_t)GPIO_Pin << 16u;
  }
}

不同于HAL库,这次直接使用一个函数进行了封装,且只使用BSRR寄存器,前文提到BSRR可以直接设定0和1。

除此之外还封装了一个Toggle,即如果目前GPIO为高电平,则切换为低电平;如果目前GPIO为低电平,则切换为高电平,这个也是很好用的。

cpp 复制代码
void HAL_GPIO_TogglePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin)
{
  /* Check the parameters */
  assert_param(IS_GPIO_PIN(GPIO_Pin));

  if ((GPIOx->ODR & GPIO_Pin) != 0x00u)
  {
    GPIOx->BRR = (uint32_t)GPIO_Pin;
  }
  else
  {
    GPIOx->BSRR = (uint32_t)GPIO_Pin;
  }
}

可以看到,之前提到过ODR寄存器不仅可以输出而且可以输入,相当于切换之前读取一下当前的状态。

六、HAL库API

1、初始化封装

cpp 复制代码
void LED_Init(void)
{
    GPIO_InitTypeDef GPIO_Initure;

    __HAL_RCC_GPIOB_CLK_ENABLE();           	//开启GPIOB时钟
	__HAL_RCC_GPIOE_CLK_ENABLE();           	//开启GPIOE时钟
	
    GPIO_Initure.Pin=GPIO_PIN_5; 				//PB5
    GPIO_Initure.Mode=GPIO_MODE_OUTPUT_PP;  	//推挽输出
    GPIO_Initure.Pull=GPIO_PULLUP;          	//上拉
    GPIO_Initure.Speed=GPIO_SPEED_FREQ_HIGH;    //高速
    HAL_GPIO_Init(GPIOB,&GPIO_Initure);

	GPIO_Initure.Pin=GPIO_PIN_5; 				//PE5
	HAL_GPIO_Init(GPIOE,&GPIO_Initure);
	
    HAL_GPIO_WritePin(GPIOB,GPIO_PIN_5,GPIO_PIN_SET);	//PB5置1,默认初始化后灯灭
    HAL_GPIO_WritePin(GPIOE,GPIO_PIN_5,GPIO_PIN_SET);	//PE5置1,默认初始化后灯灭
}

可以看到封装为LED_Init,用户侧基本上直接调用即可。

cpp 复制代码
LED_Init();		  	//初始化与LED连接的硬件接口

2、输出高低电平封装

除了初始化一次性配置外,对于GPIO输出高低电平肯定是要进行重复的操作的。直接看GPIO_SetBits、GPIO_ResetBits本质上还是官方提供的函数名,不适合用户侧去直观使用,可以进行额外封装。封装后几乎隔离了PB5的概念。

cpp 复制代码
void LED0_HIGH()
{
  HAL_GPIO_WritePin(GPIOB,GPIO_PIN_5,GPIO_PIN_SET);
}
void LED1_HIGH()
{
  HAL_GPIO_WritePin(GPIOE,GPIO_PIN_5,GPIO_PIN_SET);
}
void LED0_LOW()
{
  HAL_GPIO_WritePin(GPIOB,GPIO_PIN_5,GPIO_PIN_RESET);
}
void LED1_LOW()
{
  HAL_GPIO_WritePin(GPIOE,GPIO_PIN_5,GPIO_PIN_RESET);
}

后续如果要添加额外内容,即可在LED_HIGH、LED_LOW内进行编写即可。

七、用户侧

1、库函数

cpp 复制代码
int main(void)
{	
	delay_init();	    //延时函数初始化	  
	LED_Init();		  	//初始化与LED连接的硬件接口
	while(1)
	{
		LED0_HIGH();
		LED1_LOW();
		delay_ms(300);	 //延时300ms
		LED0_LOW();
		LED1_HIGH();
		delay_ms(300);	//延时300ms
	}
}

delay_init()和delay_ms()先不进行介绍,可以看到经过封装后,用户侧非常好进行操作修改。

2、HAL库

cpp 复制代码
int main(void)
{
    HAL_Init();                    	 	//初始化HAL库    
    Stm32_Clock_Init(RCC_PLL_MUL9);   	//设置时钟,72M
	delay_init(72);               		//初始化延时函数
	LED_Init();							//初始化LED	
	
	while(1)
	{
        LED0_HIGH(); 	//LED0对应引脚PB5拉低,亮,等同于LED0(0)
        LED1_HIGH();   	//LED1对应引脚PE5拉高,灭,等同于LED1(1)
        delay_ms(500);											//延时500ms
        LED0_LOW();   	//LED0对应引脚PB5拉高,灭,等同于LED0(1)
        LED1_LOW(); 	//LED1对应引脚PE5拉低,亮,等同于LED1(0)
        delay_ms(500);                                      	//延时500ms 
	}
}

HAL_Init、Stm32_Clock_Init、delay_init以后进行介绍。

3、用户

对于用户而言,不管使用标准库还是HAL库,都是LED_Init()初始化LED,LED_HIGH、LED_LOW设定LED的高低电平状态,非常非常方便。这就是API封装的魅力所在。

相关推荐
wenchm1 小时前
细说STM32F407单片机SPI基础知识
stm32·单片机·嵌入式硬件
jikuaidi6yuan2 小时前
STM32 ADC 配置
stm32·单片机·嵌入式硬件
Be Legendary-CGK4 小时前
有源模拟滤波器的快速设计
嵌入式硬件·硬件工程
Anin蓝天(北京太速科技-陈)4 小时前
204-基于Xilinx Virtex-6 XC6VLX240T 和TI DSP TMS320C6678的信号处理板
嵌入式硬件·fpga开发·信号处理
Jack电子实验室4 小时前
STM32 出租车计价器系统设计(一) 江科大源码改写
c语言·stm32·单片机·嵌入式硬件·嵌入式
腾飞的信仰5 小时前
51单片机 串口UART
单片机·嵌入式硬件·51单片机
电子科技圈5 小时前
XMOS将在CES 2025上展出多款由边缘AI驱动的创新音效、音频、识别和处理解决方案
人工智能·科技·嵌入式硬件·mcu·物联网·音视频·iot
日晨难再6 小时前
Formality:set_svf命令
硬件工程·数字ic
lantiandianzi6 小时前
基于单片机的输液速度监控系统设计
单片机·嵌入式硬件
紫阡星影6 小时前
【模块系列】STM32&PCF8563
c语言·stm32·单片机·嵌入式硬件