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進行輸出高低電平。