1、GPIO工作模式
1.1 端口输入数据寄存器(IDR)
1.2 端口输出数据寄存器(ODR)
1.3 端口置位/复位寄存器(BSRR)
为什么有了 ODR 寄存器,还要这个 BSRR 寄存器呢?我们先看看 BSRR 的寄存器描述,首先 BSRR 是只写权限,而 ODR 是可读可写权限。 BSRR 寄存器 32 位有效,对于低 16 位(0- 15),我们往相应的位写 1(BSy=1),那么对应的 IO 口会输出高电平,往相应的位写 0(BSy=0),对 IO 口没有任何影响,高 16 位(16-31)作用刚好相反,对相应的位写 1(BRy=1)会输出低电平,写 0(BRy=0)没有任何影响, y=0~15。
也就是说,对于 BSRR 寄存器,你写 0 的话,对 IO 口电平是没有任何影响的。我们要设置某个 IO 口电平,只需要相关位设置为 1 即可。而 ODR 寄存器,我们要设置某个 IO 口电平,我们首先需要读出来 ODR 寄存器的值,然后对整个 ODR 寄存器重新赋值来达到设置某个或者某些 IO 口的目的,而 BSRR 寄存器,我们就不需要先读,而是直接设置即可,这在多任务实时操作系统中作用很大。 BSRR 寄存器还有一个好处,就是 BSRR 寄存器改变引脚状态的时候,不会被中断打断,而 ODR 寄存器有被中断打断的风险。
2、硬件设计
要使用一个外设,首先要进行初始化,hal库初始化函数为:
void HAL_GPIO_Init(GPIO_TypeDef *GPIOx, GPIO_InitTypeDef *GPIO_Init);
形参1为端口号,形参2为结构体变量,结构体如下:
typedef struct
{
uint32_t Pin; /* 引脚号 */
uint32_t Mode; /* 模式设置 */
uint32_t Pull; /* 上拉下拉设置 */
uint32_t Speed; /* 速度设置 */
uint32_t Alternate; /* 复用功能 */
} GPIO_InitTypeDef;
GPIO写函数:
void HAL_GPIO_WritePin(GPIO_TypeDef *GPIOx,
uint16_t GPIO_Pin,
GPIO_PinState PinState);
形参1为端口号,形参2为引脚号,形参3为输出状态。
GPIO电平翻转函数:
void HAL_GPIO_TogglePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin);
形参1为端口号,形参2为引脚号。
要进行输出配置,首先使能对应的GPIO时钟再进行初始化配置。再通过写函数控制GPIO输出高低电平。
3、软件设计
流程图
led.h
#ifndef __LED_H
#define __LED_H#include "./SYSTEM/sys/sys.h"/******************************************************************************************/
/* 引脚 定义 */#define LED0_GPIO_PORT GPIOF
#define LED0_GPIO_PIN GPIO_PIN_9
#define LED0_GPIO_CLK_ENABLE() do{ __HAL_RCC_GPIOF_CLK_ENABLE(); }while(0) /* PF口时钟使能 */#define LED1_GPIO_PORT GPIOF
#define LED1_GPIO_PIN GPIO_PIN_10
#define LED1_GPIO_CLK_ENABLE() do{ __HAL_RCC_GPIOF_CLK_ENABLE(); }while(0) /* PF口时钟使能 *//******************************************************************************************//* LED端口定义 */
#define LED0(x) do{ x ? \HAL_GPIO_WritePin(LED0_GPIO_PORT, LED0_GPIO_PIN, GPIO_PIN_SET) : \HAL_GPIO_WritePin(LED0_GPIO_PORT, LED0_GPIO_PIN, GPIO_PIN_RESET); \}while(0) /* LED0 = RED */#define LED1(x) do{ x ? \HAL_GPIO_WritePin(LED1_GPIO_PORT, LED1_GPIO_PIN, GPIO_PIN_SET) : \HAL_GPIO_WritePin(LED1_GPIO_PORT, LED1_GPIO_PIN, GPIO_PIN_RESET); \}while(0) /* LED1 = GREEN *//* LED取反定义 */
#define LED0_TOGGLE() do{ HAL_GPIO_TogglePin(LED0_GPIO_PORT, LED0_GPIO_PIN); }while(0) /* LED0 = !LED0 */
#define LED1_TOGGLE() do{ HAL_GPIO_TogglePin(LED1_GPIO_PORT, LED1_GPIO_PIN); }while(0) /* LED1 = !LED1 *//******************************************************************************************/
/* 外部接口函数*/
void led_init(void); /* 初始化 */#endif
led.c
#include "./BSP/LED/led.h"/*** @brief 初始化LED相关IO口, 并使能时钟* @param 无* @retval 无*/
void led_init(void)
{GPIO_InitTypeDef gpio_init_struct;LED0_GPIO_CLK_ENABLE(); /* LED0时钟使能 */LED1_GPIO_CLK_ENABLE(); /* LED1时钟使能 */gpio_init_struct.Pin = LED0_GPIO_PIN; /* LED0引脚 */gpio_init_struct.Mode = GPIO_MODE_OUTPUT_PP; /* 推挽输出 */gpio_init_struct.Pull = GPIO_PULLUP; /* 上拉 */gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH; /* 高速 */HAL_GPIO_Init(LED0_GPIO_PORT, &gpio_init_struct); /* 初始化LED0引脚 */gpio_init_struct.Pin = LED1_GPIO_PIN; /* LED1引脚 */HAL_GPIO_Init(LED1_GPIO_PORT, &gpio_init_struct); /* 初始化LED1引脚 */LED0(1); /* 关闭 LED0 */LED1(1); /* 关闭 LED1 */
}
main.c
#include "./SYSTEM/sys/sys.h"
#include "./SYSTEM/usart/usart.h"
#include "./SYSTEM/delay/delay.h"
#include "./BSP/LED/led.h"int main(void)
{HAL_Init(); /* 初始化HAL库 */sys_stm32_clock_init(336, 8, 2, 7); /* 设置时钟,168Mhz */delay_init(168); /* 延时初始化 */led_init(); /* 初始化LED */while(1){LED0(0); /* LED0 亮 */LED1(1); /* LED1 灭 */delay_ms(500);LED0(1); /* LED0 灭 */LED1(0); /* LED1 亮 */delay_ms(500);}
}
本实验源码来自正点原子探索者开发板HAL库跑马灯例程。重点不是点灯,点灯很简单,主要是理解STM32如何控制GPIO进行输出高低电平。