stm32 HAL库笔记(一)——普通IO口

时间:2022-08-20 06:19:14

  今天介HAL库操作普通IO口,就是输入/输出。

  如果用CubeMX配置io工程,打开以后可以看到如下代码:

    GPIO_InitTypeDef GPIO_Initure;
__HAL_RCC_GPIOB_CLK_ENABLE(); // 根据名字,这是使能B端口 GPIO_Initure.Pin=GPIO_PIN_0;       // 0口
GPIO_Initure.Mode=GPIO_MODE_OUTPUT_PP; // 推挽输出
GPIO_Initure.Pull=GPIO_PULLUP; // 上拉
GPIO_Initure.Speed=GPIO_SPEED_HIGH; // 高速
HAL_GPIO_Init(GPIOB,&GPIO_Initure); // 调用初始化函数 HAL_GPIO_WritePin(GPIOB,GPIO_PIN_0,GPIO_PIN_SET); // 初始化电平

看到代码的时候,如果想深入了解一定要深入的仔细分析,看看到底是怎么调用的,操作了那些寄存器(不需要自己操作,但是要了解),用一个词来概括,抽丝剥茧。首先定义了一个 GPIO_InitTypeDef 类型的结构体,从名字可以看出是IO口初始化的类型配置,然后使能B端口,再然后配置刚才定义的结构体,把各种功能赋给各个成员变量。我们可以看一下这个结构体怎么定义的,,代码如下:

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 */ uint32_t Alternate; /*!< Peripheral to be connected to the selected pins.
This parameter can be a value of @ref GPIO_Alternate_function_selection */
}GPIO_InitTypeDef;

这个结构体里面每一个元素都要仔细的看一下,Pin:指定配置那个脚,那参数怎么看呢?看英文注释,他说:这个参数是GPIO_pins_define的任何一个,这个又是什么呢?进去看看?会发现这不是变量,找不到他,那没办法,既注释中有他,那么我们肯定是可以找到的,那就整个工程搜索一下,还真的搜到了,如下:

/** @defgroup GPIO_pins_define GPIO pins define
* @{
*/
#define GPIO_PIN_0 ((uint16_t)0x0001) /* Pin 0 selected */
#define GPIO_PIN_1 ((uint16_t)0x0002) /* Pin 1 selected */
#define GPIO_PIN_2 ((uint16_t)0x0004) /* Pin 2 selected */
#define GPIO_PIN_3 ((uint16_t)0x0008) /* Pin 3 selected */
#define GPIO_PIN_4 ((uint16_t)0x0010) /* Pin 4 selected */
#define GPIO_PIN_5 ((uint16_t)0x0020) /* Pin 5 selected */
#define GPIO_PIN_6 ((uint16_t)0x0040) /* Pin 6 selected */
#define GPIO_PIN_7 ((uint16_t)0x0080) /* Pin 7 selected */
#define GPIO_PIN_8 ((uint16_t)0x0100) /* Pin 8 selected */
#define GPIO_PIN_9 ((uint16_t)0x0200) /* Pin 9 selected */
#define GPIO_PIN_10 ((uint16_t)0x0400) /* Pin 10 selected */
#define GPIO_PIN_11 ((uint16_t)0x0800) /* Pin 11 selected */
#define GPIO_PIN_12 ((uint16_t)0x1000) /* Pin 12 selected */
#define GPIO_PIN_13 ((uint16_t)0x2000) /* Pin 13 selected */
#define GPIO_PIN_14 ((uint16_t)0x4000) /* Pin 14 selected */
#define GPIO_PIN_15 ((uint16_t)0x8000) /* Pin 15 selected */
#define GPIO_PIN_All ((uint16_t)0xFFFF) /* All pins selected */ #define GPIO_PIN_MASK ((uint32_t)0x0000FFFF) /* PIN mask for assert test */

其实意思就是,结构体中的Pin变量可以是这些宏定义中的任何一个,从命名来说也就是0-15哪些脚。继续看,Mode:指定那个脚的模式,这个参数可以是 GPIO_mode_define 中的任何一个,找到他看看,如下:

/** @defgroup GPIO_mode_define GPIO mode define
* @brief GPIO Configuration Mode
* Elements values convention: 0xX0yz00YZ
* - X : GPIO mode or EXTI Mode
* - y : External IT or Event trigger detection
* - z : IO configuration on External IT or Event
* - Y : Output type (Push Pull or Open Drain)
* - Z : IO Direction mode (Input, Output, Alternate or Analog)
* @{
*/
#define GPIO_MODE_INPUT ((uint32_t)0x00000000) /*!< Input Floating Mode */
#define GPIO_MODE_OUTPUT_PP ((uint32_t)0x00000001) /*!< Output Push Pull Mode */
#define GPIO_MODE_OUTPUT_OD ((uint32_t)0x00000011) /*!< Output Open Drain Mode */
#define GPIO_MODE_AF_PP ((uint32_t)0x00000002) /*!< Alternate Function Push Pull Mode */
#define GPIO_MODE_AF_OD ((uint32_t)0x00000012) /*!< Alternate Function Open Drain Mode */ #define GPIO_MODE_ANALOG ((uint32_t)0x00000003) /*!< Analog Mode */ #define GPIO_MODE_IT_RISING ((uint32_t)0x10110000) /*!< External Interrupt Mode with Rising edge trigger detection */
#define GPIO_MODE_IT_FALLING ((uint32_t)0x10210000) /*!< External Interrupt Mode with Falling edge trigger detection */
#define GPIO_MODE_IT_RISING_FALLING ((uint32_t)0x10310000) /*!< External Interrupt Mode with Rising/Falling edge trigger detection */ #define GPIO_MODE_EVT_RISING ((uint32_t)0x10120000) /*!< External Event Mode with Rising edge trigger detection */
#define GPIO_MODE_EVT_FALLING ((uint32_t)0x10220000) /*!< External Event Mode with Falling edge trigger detection */
#define GPIO_MODE_EVT_RISING_FALLING ((uint32_t)0x10320000) /*!< External Event Mode with Rising/Falling edge trigger detection */

上面就是IO口的各种功能配置,依次是:输入、推挽输出、开漏输出等等。同理,可以可以找到Pull、Speed等可以赋什么值。到此这个结构体就“充满”了,配置好了,我们的告诉系统啊,好接下来调用 HAL_GPIO_Init(GPIOB,&GPIO_Initure); 就是把刚才配制好的结构体扔到这个叫做IO口初始化的函数中。这个函数有两个参数,第一个是B就是那个端口,第二个是我们配置的结构体,其实就是:B端口的0号脚,这样的功能。这个函数太长了,说关键的部分,该函数会把我们的引脚的功能逐个抽离出来,然后配置相应的寄存器,就实现了这个脚的配置,代码如下:

void HAL_GPIO_Init(GPIO_TypeDef  *GPIOx, GPIO_InitTypeDef *GPIO_Init)
{
uint32_t position;
uint32_t ioposition = 0x00;
uint32_t iocurrent = 0x00;
uint32_t temp = 0x00; /* Check the parameters 这里是检查我们的配置的参数是否正确 */
assert_param(IS_GPIO_ALL_INSTANCE(GPIOx));
assert_param(IS_GPIO_PIN(GPIO_Init->Pin));
assert_param(IS_GPIO_MODE(GPIO_Init->Mode));
assert_param(IS_GPIO_PULL(GPIO_Init->Pull)); /* Configure the port pins */
for(position = ; position < GPIO_NUMBER; position++)
{
/* Get the IO position */
ioposition = ((uint32_t)0x01) << position;
/* Get the current IO position */
iocurrent = (uint32_t)(GPIO_Init->Pin) & ioposition; if(iocurrent == ioposition)
{
/*--------------------- GPIO Mode Configuration ------------------------*/
/* In case of Alternate function mode selection */
if((GPIO_Init->Mode == GPIO_MODE_AF_PP) || (GPIO_Init->Mode == GPIO_MODE_AF_OD))
{
/* Check the Alternate function parameter */
assert_param(IS_GPIO_AF(GPIO_Init->Alternate));
/* Configure Alternate function mapped with the current IO */
temp = GPIOx->AFR[position >> ];
temp &= ~((uint32_t)0xF << ((uint32_t)(position & (uint32_t)0x07) * )) ;
temp |= ((uint32_t)(GPIO_Init->Alternate) << (((uint32_t)position & (uint32_t)0x07) * ));
GPIOx->AFR[position >> ] = temp;
} /* Configure IO Direction mode (Input, Output, Alternate or Analog) */
temp = GPIOx->MODER;
temp &= ~(GPIO_MODER_MODER0 << (position * ));
temp |= ((GPIO_Init->Mode & GPIO_MODE) << (position * ));
GPIOx->MODER = temp; /* In case of Output or Alternate function mode selection */
if((GPIO_Init->Mode == GPIO_MODE_OUTPUT_PP) || (GPIO_Init->Mode == GPIO_MODE_AF_PP) ||
(GPIO_Init->Mode == GPIO_MODE_OUTPUT_OD) || (GPIO_Init->Mode == GPIO_MODE_AF_OD))
{
/* Check the Speed parameter */
assert_param(IS_GPIO_SPEED(GPIO_Init->Speed));
/* Configure the IO Speed */
temp = GPIOx->OSPEEDR;
temp &= ~(GPIO_OSPEEDER_OSPEEDR0 << (position * ));
temp |= (GPIO_Init->Speed << (position * ));
GPIOx->OSPEEDR = temp; /* Configure the IO Output Type */
temp = GPIOx->OTYPER;
temp &= ~(GPIO_OTYPER_OT_0 << position) ;
temp |= (((GPIO_Init->Mode & GPIO_OUTPUT_TYPE) >> ) << position);
GPIOx->OTYPER = temp;
} /* Activate the Pull-up or Pull down resistor for the current IO */
temp = GPIOx->PUPDR;
temp &= ~(GPIO_PUPDR_PUPDR0 << (position * ));
temp |= ((GPIO_Init->Pull) << (position * ));
GPIOx->PUPDR = temp; /*--------------------- EXTI Mode Configuration ------------------------*/
/* Configure the External Interrupt or event for the current IO */
if((GPIO_Init->Mode & EXTI_MODE) == EXTI_MODE)
{
/* Enable SYSCFG Clock */
__HAL_RCC_SYSCFG_CLK_ENABLE(); temp = SYSCFG->EXTICR[position >> ];
temp &= ~(((uint32_t)0x0F) << ( * (position & 0x03)));
temp |= ((uint32_t)(GPIO_GET_INDEX(GPIOx)) << ( * (position & 0x03)));
SYSCFG->EXTICR[position >> ] = temp; /* Clear EXTI line configuration */
temp = EXTI->IMR;
temp &= ~((uint32_t)iocurrent);
if((GPIO_Init->Mode & GPIO_MODE_IT) == GPIO_MODE_IT)
{
temp |= iocurrent;
}
EXTI->IMR = temp; temp = EXTI->EMR;
temp &= ~((uint32_t)iocurrent);
if((GPIO_Init->Mode & GPIO_MODE_EVT) == GPIO_MODE_EVT)
{
temp |= iocurrent;
}
EXTI->EMR = temp; /* Clear Rising Falling edge configuration */
temp = EXTI->RTSR;
temp &= ~((uint32_t)iocurrent);
if((GPIO_Init->Mode & RISING_EDGE) == RISING_EDGE)
{
temp |= iocurrent;
}
EXTI->RTSR = temp; temp = EXTI->FTSR;
temp &= ~((uint32_t)iocurrent);
if((GPIO_Init->Mode & FALLING_EDGE) == FALLING_EDGE)
{
temp |= iocurrent;
}
EXTI->FTSR = temp;
}
}
}
}

回过头来,再看一下开启GPIOB时钟,__HAL_RCC_GPIOB_CLK_ENABLE(); 他是一个很大的宏,把和这个宏有关的都贴上一起分析:

#define __HAL_RCC_GPIOB_CLK_ENABLE()    do { \
__IO uint32_t tmpreg = 0x00; \
SET_BIT(RCC->AHB1ENR, RCC_AHB1ENR_GPIOBEN);\
/* Delay after an RCC peripheral clock enabling */ \
tmpreg = READ_BIT(RCC->AHB1ENR, RCC_AHB1ENR_GPIOBEN);\
UNUSED(tmpreg); \
} while() #define SET_BIT(REG, BIT) ((REG) |= (BIT)) #define RCC_AHB1ENR_GPIOBEN ((uint32_t)0x00000002) #define READ_BIT(REG, BIT) ((REG) & (BIT)) #define UNUSED(x) ((void)(x)) // 避免警告——百度

这个宏,简单一点看,把当作一个函数看一下,执行一次do里面的,把寄存器 AHB1ENR 1位置1,就是使能GPIOB。

  总结一下IO是使用,想配置一个脚作为普通口用,就要定义一个配置结构体,初始化这个脚所在的口的时钟,然后“填满”它,在把他扔到初始化函数中,到此,就配置好了一个普通的IO口作为输入输出。