1. 程式人生 > >GPIO輸出-----點燈(輸出)和按鍵(輸入)

GPIO輸出-----點燈(輸出)和按鍵(輸入)

這裡寫圖片描述
LED燈連線到STM32的GPIO引腳,可以通過控制低電平(0)點亮,高電平(1)熄滅。

程式設計要點

1).使能GPIO埠時鐘
2).初始化GPIO目標引腳為推輓輸出模式
3).編寫簡單的測試程式,控制GPIO引腳輸出高、低電平

程式碼分析

1. LED燈引腳巨集定義

將與硬體相關的部分使用巨集來封裝,這些定義儲存在“led.h”檔案中。

//R-紅色
#define LED1_GPIO_PORT GPIOB
#define LED1_GPIO_CLK RCC_APB2Periph_GPIOB
#define LED1_GPIO_PIN GPIO_Pin_5
//G-綠色
#define LED2_GPIO_PORT GPIOB #define LED2_GPIO_CLK RCC_APB2Periph_GPIOB #define LED2_GPIO_PIN GPIO_Pin_0 // B-藍色 #define LED3_GPIO_PORT GPIOB #define LED3_GPIO_CLK RCC_APB2Periph_GPIOB #define LED3_GPIO_PIN GPIO_Pin_1

以上是用程式碼把控制LED的GPIO埠、引腳以及GPIO埠時鐘封裝起來。
注: GPIO 時鐘巨集“RCC_APB2Periph_GPIOB”是 STM32 標準庫定義的 GPIO 埠時鐘相關的巨集,是用於指示暫存器位的。
這裡寫圖片描述


一共32bit,4bit控制一個位,如果使能AFIO時鐘即是((uint32_t)0x00000001),如果使能GPIOB時鐘即是((uint32_t)0x00000008)。這也就是控制暫存器的位,用巨集定義封裝即是

#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)

2.控制 LED燈亮滅狀態的巨集定義

/* 直接操作暫存器的方法控制 IO */
#define digitalHi(p,i) {p->BSRR=i;} //輸出為高電平
#define digitalLo(p,i) {p->BRR=i;} //輸出低電平
#define digitalToggle(p,i) {p->ODR ^=i;} //輸出反轉狀態
 /* 定義控制 IO 的巨集 */
#define LED1_TOGGLE digitalToggle(LED1_GPIO_PORT,LED1_GPIO_PIN)
#define LED1_OFF digitalHi(LED1_GPIO_PORT,LED1_GPIO_PIN)
#define LED1_ON digitalLo(LED1_GPIO_PORT,LED1_GPIO_PIN)
#define LED2_TOGGLE digitalToggle(LED2_GPIO_PORT,LED2_GPIO_PIN)
#define LED2_OFF digitalHi(LED2_GPIO_PORT,LED2_GPIO_PIN)
#define LED2_ON digitalLo(LED2_GPIO_PORT,LED2_GPIO_PIN)
#define LED3_TOGGLE digitalToggle(LED2_GPIO_PORT,LED3_GPIO_PIN)
#define LED3_OFF digitalHi(LED2_GPIO_PORT,LED3_GPIO_PIN)
#define LED3_ON digitalLo(LED2_GPIO_PORT,LED3_GPIO_PIN) 
/* 基本混色,後面高階用法使用 PWM 可混出全綵顏色,且效果更好 */
//紅
#define LED_RED \
LED1_ON;\
LED2_OFF\
LED3_OFF
//綠
#define LED_GREEN \
LED1_OFF;\
LED2_ON\
LED3_OFF
//藍
#define LED_BLUE \
LED1_OFF;\
LED2_OFF\
LED3_ON
//黃(紅+綠)
#define LED_YELLOW \
LED1_ON;\
LED2_ON\
LED3_OFF
//紫(紅+藍)
#define LED_PURPLE \
LED1_ON;\
LED2_OFF\
LED3_ON
//青(綠+藍)
#define LED_CYAN \
LED1_OFF;\
LED2_ON\
LED3_ON
//白(紅+綠+藍)
#define LED_WHITE \
LED1_ON;\
LED2_ON\
LED3_ON
//黑(全部關閉)
#define LED_RGBOFF \
LED1_OFF;\
LED2_OFF\
LED3_OFF

將用直接操作暫存器的方法控制IO口,控制 LED 亮滅的操作是直接向 BSRR、BRR 和 ODR 這三個暫存器寫入控制指令來實現的,對 BSRR 寫 1 輸出高電平,對 BRR 寫 1 輸出低電平,對 ODR 暫存器某位進行異或操作可反轉位的狀態。再將其定義成IO口的巨集(開、關、反轉),基本混色程式碼中的“\”是 C 語言中的續行符語法,表示續行符的下一行與續行符所在的程式碼是同一行。程式碼中因為巨集定義關鍵字“#define”只是對當前行有效,所以我們使用續行符來連線起來,以下的程式碼是等效的:

#define    LED_YELLOW    LED1_ON; LED2_ON; LED3_OFF

應用續行符的時候要注意,在“\”後面不能有任何字元(包括註釋、空格),只能直接回車。

3. LED GPIO初始化函式

在“led.c”中編寫LED燈的初始化函式

void LED_GPIO_Config(void)
{
/*定義一個 GPIO_InitTypeDef 型別的結構體*/
GPIO_InitTypeDef GPIO_InitStructure;
/*開啟 LED 相關的 GPIO 外設時鐘*/
RCC_APB2PeriphClockCmd( LED1_GPIO_CLK|
LED2_GPIO_CLK|
LED3_GPIO_CLK, ENABLE);
/*選擇要控制的 GPIO 引腳*/
GPIO_InitStructure.GPIO_Pin = LED1_GPIO_PIN;
/*設定引腳模式為通用推輓輸出*/
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
/*設定引腳速率為 50MHz */
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
/*呼叫庫函式,初始化 GPIO*/
GPIO_Init(LED1_GPIO_PORT, &GPIO_InitStructure);
/*選擇要控制的 GPIO 引腳*/
GPIO_InitStructure.GPIO_Pin = LED2_GPIO_PIN;
/*呼叫庫函式,初始化 GPIO*/
GPIO_Init(LED2_GPIO_PORT, &GPIO_InitStructure);
/*選擇要控制的 GPIO 引腳*/
GPIO_InitStructure.GPIO_Pin = LED3_GPIO_PIN;
/*呼叫庫函式,初始化 GPIOF*/
GPIO_Init(LED3_GPIO_PORT, &GPIO_InitStructure);
/* 關閉所有 led 燈 */
GPIO_SetBits(LED1_GPIO_PORT, LED1_GPIO_PIN);
/* 關閉所有 led 燈 */
GPIO_SetBits(LED2_GPIO_PORT, LED2_GPIO_PIN);
/* 關閉所有 led 燈 */
GPIO_SetBits(LED3_GPIO_PORT, LED3_GPIO_PIN);
}

函式執行流程如下:
(1) 使用GPIO_InitTypeDef定義 GPIO初始化結構體變數,以便下面用於儲存GPIO配置
(2) 呼叫庫函式 RCC_APB2PeriphClockCmd 來使能 LED 燈的 GPIO埠時鐘,在前面的章節中我們是直接向 RCC 暫存器賦值來使能時鐘的,不如這樣直觀。該函式有兩個輸入引數,第一個引數用於指示要配置的時鐘,如本例中的“RCC_ APB2Periph_GPIOB”,應用時我們使用“|”操作同時配置 3個 LED 燈的時鐘;函式的第二個引數用於設定狀態,可輸入“Disable”關閉或“Enable”使能時鐘。
(3) 向 GPIO 初始化結構體賦值,把引腳初始化成推輓輸出模式,其中的 GPIO_Pin 使用巨集“LEDx_GPIO_PIN”來賦值,使函式的實現方便移植。
(4) 使用以上初始化結構體的配置,呼叫 GPIO_Init 函式向暫存器寫入引數,完成 GPIO 的初始化,這裡的 GPIO 埠使用“LEDx_GPIO_PORT”巨集來賦值,也是為了程式移植方便。
(5) 使用同樣的初始化結構體,只修改控制的引腳和埠,初始化其它 LED 燈使用的GPIO引腳。
(6) 使用巨集控制 RGB燈預設關閉。

4. 主函式

#include "stm32f10x.h"
#include "./led/bsp_led.h"

#define SOFT_DELAY Delay(0x0FFFFF);

void Delay(__IO u32 nCount);

int main(void)
{
/* LED 埠初始化 */
LED_GPIO_Config(); 
while (1)
{
LED1_ON; // 亮
SOFT_DELAY;
LED1_OFF; // 滅
LED2_ON; // 亮
SOFT_DELAY;
LED2_OFF; // 滅
LED3_ON; // 亮
SOFT_DELAY;
LED3_OFF; // 滅
/*輪流顯示 紅綠藍黃紫青白 顏色*/
LED_RED;
SOFT_DELAY;
LED_GREEN;
SOFT_DELAY;
LED_BLUE;
SOFT_DELAY;
LED_YELLOW;
SOFT_DELAY;
LED_PURPLE;
SOFT_DELAY;
LED_CYAN;
SOFT_DELAY;
LED_WHITE;
SOFT_DELAY;
LED_RGBOFF;
SOFT_DELAY;
}
}
void Delay(__IO uint32_t nCount) //簡單的延時函式
{
for (; nCount != 0; nCount--);
}

點燈完畢

這裡寫圖片描述
從按鍵原理圖中可知,按鍵沒被按下的時候GPIO輸入的是低電平,當有按鍵按下,GPIO輸入的是高電平。只要檢測引腳的電平就能判斷按鍵是否被按下。按鍵通過在點燈的基礎上新建“bsp_key.c”及“bsp_key.h”檔案。
注:此按鍵電路利用電容充放電的延時,消除了波紋,從而簡化了軟體的處理,軟體只需要直接檢測引腳的電平即可。

程式設計要點

  1. 使能 GPIO埠時鐘;
  2. 初始化 GPIO 目標引腳為輸入模式(浮空輸入);
  3. 編寫簡單測試程式,檢測按鍵的狀態,實現按鍵控制 LED 燈。

程式碼分析

1. 按鍵引腳巨集定義

// 引腳定義
#define KEY1_GPIO_CLK RCC_APB2Periph_GPIOA
#define KEY1_GPIO_PORT GPIOA
#define KEY1_GPIO_PIN GPIO_Pin_0

#define KEY2_GPIO_CLK RCC_APB2Periph_GPIOC
#define KEY2_GPIO_PORT GPIOC
#define KEY2_GPIO_PIN GPIO_Pin_13

2. 按鍵 GPIO初始化函式

void Key_GPIO_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;

 /*開啟按鍵埠的時鐘*/
 RCC_APB2PeriphClockCmd(KEY1_GPIO_CLK|KEY2_GPIO_CLK,ENABLE);

//選擇按鍵的引腳
GPIO_InitStructure.GPIO_Pin = KEY1_GPIO_PIN;
 // 設定按鍵的引腳為浮空輸入
 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
//使用結構體初始化按鍵
GPIO_Init(KEY1_GPIO_PORT, &GPIO_InitStructure);

//選擇按鍵的引腳
GPIO_InitStructure.GPIO_Pin = KEY2_GPIO_PIN;
//設定按鍵的引腳為浮空輸入
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
//使用結構體初始化按鍵
GPIO_Init(KEY2_GPIO_PORT, &GPIO_InitStructure);
}

同為 GPIO的初始化函式,初始化的流程與“LED GPIO初始化函式”章節中的類似,主要區別是引腳的模式。函式執行流程如下:
(1) 使用GPIO_InitTypeDef定義 GPIO初始化結構體變數,以便下面用於儲存GPIO配置。
(2) 呼叫庫函式 RCC_APB2PeriphClockCmd 來使能按鍵的 GPIO 埠時鐘,呼叫時我們使用“|”操作同時配置兩個按鍵的時鐘。
(3) 向 GPIO 初始化結構體賦值,把引腳初始化成浮空輸入模式,其中的 GPIO_Pin 使用巨集“KEYx_GPIO_PIN”來賦值,使函式的實現方便移植。由於引腳的預設電平受按鍵電路影響,所以設定成浮空輸入。
(4) 使用以上初始化結構體的配置,呼叫 GPIO_Init 函式向暫存器寫入引數,完成 GPIO 的初始化,這裡的 GPIO 埠使用“KEYx_GPIO_PORT”巨集來賦值,也是為了程式移植方便。
(5) 使用同樣的初始化結構體,只修改控制的引腳和埠,初始化其它按鍵檢測時使用的GPIO引腳。

3. 檢測按鍵的狀態

 #define KEY_ON 1
 #define KEY_OFF 0

 uint8_t Key_Scan(GPIO_TypeDef* GPIOx,uint16_t GPIO_Pin)
 {
 /*檢測是否有按鍵按下 */
 if (GPIO_ReadInputDataBit(GPIOx,GPIO_Pin) == KEY_ON ) {
 /*等待按鍵釋放 */
 while (GPIO_ReadInputDataBit(GPIOx,GPIO_Pin) == KEY_ON);
 return KEY_ON;
 } else
 return KEY_OFF;
 }

在這裡我們定義了一個 Key_Scan 函式用於掃描按鍵狀態。GPIO 引腳的輸入電平可通過 讀 取 IDR 寄 存 器 對 應 的 數 據 位 來 感 知 , 而 STM32 標 準 庫 提 供 了 庫 函 數GPIO_ReadInputDataBit 來獲取位狀態,該函式輸入 GPIO 埠及引腳號,函式返回該引腳的電平狀態,高電平返回 1,低電平返回 0。Key_Scan 函式中以 GPIO_ReadInputDataBit 的返回值與自定義的巨集“KEY_ON”對比,若檢測到按鍵按下,則使用while迴圈持續檢測按鍵狀態,直到按鍵釋放,按鍵釋放後 Key_Scan函式返回一個“KEY_ON”值;若沒有檢測到按鍵按下,則函式直接返回“KEY_OFF”。若按鍵的硬體沒有做消抖處理,需要在這個Key_Scan 函式中做軟體濾波,防止波紋抖動引起誤觸發。
注:埠輸入資料暫存器(GPIOx_IDR)(x=A…E)
這裡寫圖片描述

4. 主函式

int main(void)
 {
 /* LED 埠初始化 */
 LED_GPIO_Config();

 /*初始化按鍵*/
 Key_GPIO_Config();

 /* 輪詢按鍵狀態,若按鍵按下則反轉 LED */
 while (1) {
 if ( Key_Scan(KEY1_GPIO_PORT,KEY1_PIN) == KEY_ON ) {
 /*LED1 反轉*/
 LED1_TOGGLE;
 }

 if ( Key_Scan(KEY2_GPIO_PORT,KEY2_PIN) == KEY_ON ) {
 /*LED2 反轉*/
 LED2_TOGGLE;
 }
 }
 }

程式碼中初始化 LED 燈及按鍵後,在 while函式裡不斷呼叫 Key_Scan函式,並判斷其返回值,若返回值表示按鍵按下,則反轉 LED 燈的狀態。
按鍵結束。

LED初始化先設定一個結構體,開啟時鐘所有所需埠的時鐘用“|”來連線,再給埠,引腳,輸出模式賦值,將這些設定儲存著結構體中,最後用賦值的內容初始化所需埠。設定完一個引腳的輸出模式時,其他的如相同輸出模式則不需重複設定,可以直接用結構體初始化所需埠即可。

按鍵初始化也是先設定一個結構體,開啟時鐘所有所需埠的時鐘用“|”來連結,設定引腳和輸入模式為浮空輸入,使用結構體初始化按鍵。

好記性不如爛鍵盤。學路漫漫。學習野火STM32開發板中…………