STM32筆記(2)GPIO介紹及IO口操作

GPIO簡介

GPIO:每個連接到I/O總線上的設備都有自己的I/O地址集,即所謂的I/O端口。類似51單片機的P0~P3,但與51單片機不同的是,對stm32的GPIO來說,使用前需要設置其工作方式。。STM32 的每個 IO 端口都有 7 個寄存器來控制其工作方式,而每一個寄存器都需要用32bit來控制。在STM32中,一組GPIO有16個IO口。

端口位基本結構:

在這裏插入圖片描述

工作方式

stm32的IO口一共有8種工作方式:
1、 輸入浮空:讀取相應外部的電平,但當引腳不輸入時,相當於懸空,輸入電平未知,範圍爲0~VCC。
2、 輸入上拉:保證在無信號輸入時輸入端的電平爲高電平。而在信號輸入爲低電平時輸入端的電平也爲低電平。
3、 輸入下拉:保證在無信號輸入時輸入端的電平爲低電平。而在信號輸入爲高電平時輸入端的電平也爲高電平。
4、 模擬輸入:傳統方式的輸入,將0,1的二進制數字信號,通過數模轉換,變成模擬信號,應用ADC模擬輸入,或者低功耗下省電。簡單來說,就是將原本的高低電平轉換爲範圍爲0~VCC信號。
5、 開漏輸出:IO口輸出0接地,輸出1由外接電阻控制爲0/1.
6、 推輓輸出:低電平輸出爲0,高電平輸出爲VCC
鏈接:用三極管很好地說明上面兩者輸出
7、 複用推輓輸出:用作串口的輸出。
8、 複用開漏輸出 :用在IIC。
7和8兩種輸出共同點:可以理解爲GPIO口被用作第二功能時的配置情況(即並非作爲通用IO口使用),給內部外設使用的推輓,開漏輸出,此時端口必須配置爲複用功能輸出模式。

STM32 的 IO 口位配置表 :
在這裏插入圖片描述
STM32 輸出模式配置 :

在這裏插入圖片描述

相關寄存器介紹

配置模式的 2 個 32 位的端口配置寄存器 CRL 和 CRH; 2 個 32 位的數據寄存器 IDR 和 ODR; 1 個 32 位的置位/復位寄存器BSRR;一個 16 位的復位寄存器 BRR; 1 個 32 位的鎖存寄存器 LCKR。

1.CRL 和 CRH(控制着每個 IO 口的模式及輸出速率)
在這裏插入圖片描述
CRH 的作用和 CRL 完全一樣,只是 CRL 控制的是低 8 位輸出口,而 CRH 控制的是高 8位輸出口。

2.IDR 和 ODR
IDR:IDR 是一個端口輸入數據寄存器,只用了低 16 位。該寄存器爲只讀寄存器,並且只能以16 位的形式讀出。 在這裏插入圖片描述
在固件庫中操作 IDR 寄存器讀取 IO 端口數據是通過GPIO_ReadInputDataBit 函數實現的:

uint8_tGPIO_ReadInputDataBit(GPIO_TypeDef*GPIOx,uint16_tGPIO_Pin)

比如讀取 GPIOA.5 的電平狀態,那麼方法是:

GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_5);

返回值是 1(Bit_SET)或者 0(Bit_RESET);

ODR:ODR 是一個端口輸出數據寄存器,也只用了低 16 位。該寄存器爲可讀寫,從該寄存器讀出來的數據可以用於判斷當前 IO 口的輸出狀態。而向該寄存器寫數據,則可以控制某個 IO 口的輸出電平。
在這裏插入圖片描述
在固件庫中設置 ODR 寄存器的值來控制 IO 口的輸出狀態是通過函數GPIO_Write 來實現的:

void GPIO_Write(GPIO_TypeDef* GPIOx, uint16_t PortVal);

該函數一般用來對一個 GPIO 的多個端口設值。

3.BSRR:BSRR 寄存器是端口位設置/清除寄存器。 該寄存器和 ODR 寄存器具有類似的作用,都可以用來設置 GPIO 端口的輸出位是 1 還是 0。
在這裏插入圖片描述
4. BRR: BRR 寄存器是端口位清除寄存器。該寄存器的作用跟 BSRR 的高 16 位相同。

5.LCKR(不常用)

在這裏插入圖片描述

IO 操作步驟

1) 使能 IO 口時鐘。調用函數爲 RCC_APB2PeriphClockCmd()。

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOX,ENABLE);//GPIOX  使能時鐘,X=A~E

2) 初始化 IO 參數。調用函數 GPIO_Init();(確定操作的IO口)

void GPIO_Init(GPIO_TypeDef*GPIOx,GPIO_InitTypeDef*GPIO_InitStruct);

其中,兩個參數分別爲:配置引腳組(GPIO_TypeDef* GPIOx),配置的參數( GPIO_InitTypeDef* GPIO_InitStruct)。

3) 操作 IO。(固件庫操作,寄存器操作,位操作)

IO口三種操作細解

1.固件庫操作

 GPIO_InitStructure.GPIO_Mode= GPIO_Mode_Out_PP; //模式選擇爲推輓輸出
 GPIO_InitStructure.GPIO_Pin=GPIO_Pin_5;//確定選位爲第五個IO口
 GPIO_InitStructure.GPIO_Speed=  GPIO_Speed_50MHz;//速率選擇爲50M
 GPIO_Init(GPIOX,&GPIO_InitStructure);//初始化GPIOX
 
 GPIO_SetBits(GPIOX,GPIO_Pin_5);//設置該位爲高電平
  GPIO_ResetBits(GPIOX,GPIO_Pin_5);//設置該位爲低電平

八種模式在 MDK 中是通過一個枚舉類型定義的:

typedef enum
{ GPIO_Mode_AIN = 0x0, //模擬輸入
GPIO_Mode_IN_FLOATING = 0x04, //浮空輸入
GPIO_Mode_IPD = 0x28, //下拉輸入
GPIO_Mode_IPU = 0x48, //上拉輸入
GPIO_Mode_Out_OD = 0x14, //開漏輸出
GPIO_Mode_Out_PP = 0x10, //通用推輓輸出
GPIO_Mode_AF_OD = 0x1C, //複用開漏輸出
GPIO_Mode_AF_PP = 0x18 //複用推輓
}GPIOMode_TypeDef; 

IO 口速度設置, 有三個可選值,在 MDK 中同樣是通過枚舉類型定義 :

typedef enum
{
GPIO_Speed_10MHz = 1,
GPIO_Speed_2MHz,
GPIO_Speed_50MHz
}GPIOSpeed_TypeDef; 

在模式設置完成後,可通過其它庫函數進行IO口設置:
控制IDR:

uint_8t GPIO_ReadIputDataBit(GPIO_TypeDef*GPIOx,uint16_t GPIO_Pin)

控制ODR:

void GPIO_Write(GPIO_TypeDef*GPIOx,uint16_t PortVal)

控制BSRR與BRR:

void GPIO_SetBits(GPIO_TypeDef*GPIOx,uint16_t GPIO_Pin)

void GPIO_ResetBits(GPIO_TypeDef*GPIOx,uint16_t GPIO_Pin)

2.寄存器操作
完整IO口初始化示例:

void LED_Init(void)
 {     
  RCC->APB2ENR |= 1<<2;    //使能PTA時鐘     
  RCC->APB2ENR |= 1<<5;    //使能PTD時鐘     
  GPIOA->CRH&=0XFFFF FFF0; //清空PA8設置     
  GPIOA->CRH|=0X0000 0003; //設置PA8推輓輸出     
  GPIOA->ODR|=1<<8;  //PA8輸出高     
  GPIOD->CRL&=0XFFFF F0FF;//清空PD2設置     
  G)PIOD->CRL|=0X0000 0300;//設置PD2推輓輸出     
  GPIOD->ODR|=1<<2;//PD2輸出高 
}

一般用寄存器控制IO口的形式如上,可以直接賦值,也可以通過與(&),或(|),非(~)進行控制 。

3.位操作:通過直接對IO口的地址進行操作來改變IO口的值,達到操作IO的目的。
原理:將每個位膨脹爲一個32位的字(即其地址),稱爲這個位的“位帶別名區 ”,如下圖:
在這裏插入圖片描述

STM32中有兩個位帶區:

在這裏插入圖片描述
對於片上外設位帶區的某個比特,記它所在字節的地址爲 A,位序號爲 n(0<=n<=7),則該比特在別名區的地址爲:

AliasAddr= 0x42000000+((A‐0x40000000)*8+n)*4 =0x42000000+ (A‐0x40000000)*32 + n*4 

對 SRAM 位帶區的某個比特,記它所在字節地址爲 A,位序號爲 n(0<=n<=7),則該比特 在別名區的地址爲:

AliasAddr= 0x42000000+((A‐0x40000000)*8+n)*4 =0x42000000+ (A‐0x40000000)*32 + n*4

“*4”表示一個字爲 4 個字節,“*8”表示一個字節中有 8 個比特。

使用(以控制GPIOA的16個IO口中的第一個爲例):

//IO口操作宏定義,獲取相應地址
#define BITBAND(addr, bitnum) ((addr & 0xF0000000)+0x2000000+((addr &0xFFFFF)<<5)+(bitnum<<2)) 
#define MEM_ADDR(addr)  *((volatile unsigned long  *)(addr)) 
#define BIT_ADDR(addr, bitnum)   MEM_ADDR(BITBAND(addr, bitnum)) 

#define GPIOA_ODR_Addr    (GPIOA_BASE+12) //0x4001080C 
//IO口地址映射,即獲取GPIOA_ODR的地址

#define PAout(n)   BIT_ADDR(GPIOA_ODR_Addr,n)  //輸出 
#define PAin(n)    BIT_ADDR(GPIOA_IDR_Addr,n)  //輸入

PAin(1=1//使得GPIOA的第一個IO口爲高電平

附錄:C語言相關

1.C語言位操作
在這裏插入圖片描述

2.define 是 C 語言中的預處理命令,它用於宏定義,可以提高源代碼的可讀性,爲編程提供方便。常見的格式:#define 標識符 字符串(無分號)“標識符”爲所定義的宏名。“字符串”可以是常數、表達式、格式串等。 (類似typedef )

3.單片機程序開發過程中,經常會遇到一種情況, 當滿足某條件時對一組語句進行編譯,而當條件不滿足時則編譯另一組語句。 條件編譯命令最常見的形式爲:

#ifdef 標識符
程序段 1
#else
程序段 2
#endif 

當標識符已經被定義過(一般是用#define 命令定義),則對程序段 1 進行編譯,否則編譯程序段 2。 其中#else 部分也可以沒有,。

4.C 語言中 extern 可以置於變量或者函數前,以表示變量或者函數的定義在別的文件中,提示編譯器遇到此變量和函數時在其他模塊中尋找其定義。 如:

extern u16 USART_RX_STA; 

此時,在其它文件會有:

u16 USART_RX_STA;

但是如果有多個文件同時要對應用的變量進行操作,而且可能會修改該變量,那就會影響其他模塊的使用。

5.結構體
聲明結構體類型:

Struct 結構體名{
成員列表;
}變量名列表; 

例:
struct U_TYPE {
int BaudRate
int WordLength;
}usart1,usart2; 

結構體成員變量的引用方法是:結構體變量名字.成員名

usart1.BaudRate//引用usart1中的成員BaudRate

當變量定義過多,或者某幾個變量是用來描述某一個對象,結構體可以使函數更加易讀。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章