以下內容基於 STM32F103C8T6 Blue Pill 板子。
使用庫函數進行 STM32 點亮一個 LED 開發時,有以下步驟,這些也是操作某一個 GPIO 的基礎步驟:
-
定義 GPIO_InitTypeDef:
GPIO_InitTypeDef GPIO_InitStructure;
-
配置 RCC 時鐘:以 GPIOB 爲例,Ref.Manual 中可以查到 GPIO 都是掛在 APB2 總線上的,因此 GPIOB 時鐘配置如下:
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE)
-
配置 GPIO_InitStructure:爲了點亮 LED,我們配置 GPIOB_Pin6 爲推輓輸出,並初始化 GPIO 端口:
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;
GPIO_Init(GPIOB,&GPIO_InitStructure);
-
配置 GPIO_Pin6 爲高電平:注意在 GPIO_Pin6 的輸出電流能力,對於常見的發光二極管,一定要串上電阻!
GPIO_SetBits(GPIOB, GPIO_Pin_6);
這樣就完成了最簡單的點亮 LED 設計了。接下來從寄存器來看一下:
-
GPIO_InitTypeDef 相關:
GPIO_InitTypeDef GPIO_InitStructure;
1)後面可以發現,所有的外設都需要定義這樣一個 XXX_InitTypeDef 結構體變量,它在 stm32f10x_gpio.h 中定義。這些結構體就定義了和這個外設相關的各種配置參數
2)GPIO_InitTypeDef 定義了與 GPIO 外設相關的三個配置:GPIO 模式、具體的 GPIO_Pin、GPIO_Speed
3)GPIO_Speed 比較容易忽視,這個速率是 GPIO 輸出的速率,也即 GPIO 驅動電路響應速率。對於像低速的應用,比如 USART 這種,配置成低速即可;但是如果應用要求的 GPIO 輸出速率高於了配置的 GPIO 驅動電路響應速率,則會出現失真(這裏我沒有實際驗證過,也只是理論理解,有興趣的朋友可以試試~) -
RCC 相關:
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE)
1)這個函數在 stm32f10x_rcc.c 中定義,是用來配置 RCC_APB2ENR(APB2 peripheral clock enable register)寄存器(參考 Ref.Manual 7.3.7)。
2)這個寄存器 0,2-15,19-21 bits 是可配的,每個 bit 用來使能/非使能一個外設的時鐘。
3)配置項 RCC_APB2Periph_GPIOB 在 stm32f10x_rcc.h 中定義爲 0x08,即對應 GPIOB 這個外設在 RCC_APB2ENR 中的 bit 位。 -
GPIO_InitStructure 相關:
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;
1)GPIO_Mode 和 GPIO_Speed 配置對應 GPIOx_CRL/GPIOx_CRH (Port configuration register low/high) 寄存器(參考 Ref.Manual 9.2.1, 9.2.2)
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
1)GPIO_Pin 的配置:每個 GPIO_Pin_x 對應一個uint16_t 數,可以在 stm32f10x.h 中查到。這些 uint16_t 的數轉成二進制後,就對應是第 x 位爲 1;比如 GPIO_Pin_6 對應 0x40 = 100 0000。
GPIO_Init(GPIOB,&GPIO_InitStructure);
1)後面可以看到,所有的這些外設的配置都會通過 xxx_Init( ) 這個函數來根據 GPIO_InitStructure 的內容進行初始化。尤其注意的是 GPIO_Init 的參數是兩個指針變量,所以要傳入的是 GPIO_InitStructure 的地址。 -
GPIO_SetBits 相關:
GPIO_SetBits(GPIOB, GPIO_Pin_6);
1)這個函數在 stm32f10x.c 中定義,是用來配置 GPIOx_BSRR(Port bit set/reset register)寄存器的(參考 Ref.Manual 9.2.5)。
2)這個寄存器 0:15 位是用來將 0-15 個 GPIO 端口拉高的(Set bits)的,置 1有效,16:31 位是用來將 0-15 個 GPIO 端口拉低的(Reset bits),置 1有效;但是這個函數只配置 0:15 位寄存器。
3)當需要將某個 GPIO 端口拉低時,通過下面這個函數來進行
GPIO_ResetBits(GPIOB, GPIO_Pin_6);
1)這個函數對應在 stm32f10x.c 中定義,是用來配置 GPIOx_BRR(Port bit reset register)這個寄存器的(參考 Ref.Manual 9.2.6)。
2)這個寄存器只有 0:15 位是有用的,對應將 0-15 個 GPIO 端口拉低(Reset bits),置 1有效。爲什麼不直接配置 GPIOx_BSRR 來拉低呢?因爲 GPIO_Pin_x 這些 uint16_t 的數對應的二進制數正好就是 0-15 第 x 位爲 1。所以如果用 GPIOx_BSRR 寄存器就要去操作 16:31 位,很麻煩,不如直接用 GPIOx_BRR 來操作。
當然除了把 GPIO 做輸出以外,還可以把 GPIO 作爲輸入,進行高低電平的檢測。當把 GPIO 作爲輸入進行高低電平檢測時,絕大部分代碼和上面都是一樣的,只是需要做以下修改:
-
配置 GPIO_InitStructure 時將 GPIO_Mode 上拉/下拉輸入:
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
-
當需要讀取 GPIO 某一個 Pin 狀態時:這個函數會返回 uint8_t 的 0或1,表示輸入電平的低或高
GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_5);
-
如果是需要讀取某一個 GPIO 上多個 Pin 狀態:這個函數會返回 uint16_t 的結果,對應 GPIOx 上 0~15 個 Pin 上輸入電平。
GPIO_ReadInputData(GPIOA);
同樣,從寄存器角度來看一下:
-
GPIO_InitStructure 相關:
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
1) 該寄存器同樣是對應 GPIOx_CRL/GPIOx_CRH (Port configuration register low/high) 寄存器。 -
GPIO_ReadInput 相關:
GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_5);
GPIO_ReadInputData(GPIOA);
1)這兩個函數都是操作 GPIO_IDR(Port input data register)寄存器。該寄存器就是存的 GPIOx 0~15 Port 的輸入電平高低。只是 ReadInputDataBit 函數還會和 GPIO_Pin_x 做與操作,來特定返回這個 GPIO_Pin_x 的輸入電平高低。
以上就是所有關於 GPIO 的基本操作了。