iT邦幫忙

0

STM32-3 GPIO初探

  • 分享至 

  • xImage
  •  

Open Drain (漏極開路)與 push-pull(推挽) 介紹

  1. Open Drain 輸出為開路,使用時須加上上拉電阻,輸出電壓由外部決定。
    https://ithelp.ithome.com.tw/upload/images/20220308/20146325DVTQamUtzN.png
  2. push-pull可以理解為既可以推也可以拉,所以就不需要再加上外部元件了~圖中可以看到當輸出為高電位時,上方 的電晶體導通而下方的關閉,所以輸出為高電位,低電位則兩者相反故無需加上上拉電阻。
    https://ithelp.ithome.com.tw/upload/images/20220308/20146325rWUGbQxqs5.png

IDE中的GPIO設定介紹

在STM32CubbeIDE中要設定GPIO相對來的容易~一樣點開.ioc檔可以去對各個腳位去做設定。
https://ithelp.ithome.com.tw/upload/images/20220308/201463258sKIR3R9Mz.png
在這邊我將PB4-PB8設定為GPIO_Output,首先點選PB4將它設為GPIO_Output,其餘的腳位同樣設定。
https://ithelp.ithome.com.tw/upload/images/20220308/20146325zXRyITlXZt.png
接下來點擊左側的SystemCore當中的GPIO再點選腳位,可以設定GPIO的相關功能。這邊介紹一下下方選項的代表意思。

  1. GPIO output level: 用來設定腳位的初始輸出為高電位還是低電位
  2. GPIO Mode: 這就是前面所提到的Open Drain 與 Push-pull的設定
  3. GPIO Pull-up / Pull-down:這選項主要是針對輸入模式,一般如果是輸出選擇no-pull,如果是輸入的話就要看實際應用的默認輸出值是0還是1,假如是輸入0就配置為pull-down,輸入1為pull-up。
  4. Maximum output speed:有分為Low、High,主要是控制電壓轉換速率,簡單來說就是方波電壓由波谷上升到波峰的主要時間。
  5. User Label :可以自行設定腳位的名稱方便區分。
    https://ithelp.ithome.com.tw/upload/images/20220308/20146325LvjCAqxng3.png
    當設定完成後記得按下Ctrl+S來儲存剛剛所做的設定,這時IDE會自動生成相關的GPIO函數。

GPIO相關函數介紹與暫存器

在透過上方.ioc檔中設定完成後除了相關GPIO函數IDE會自動設定完成外,GPIO的暫存器也會配置好。在STM32當中GPIO有6個暫存器CRL、CRH、IDR、ODR、BRSS、BRR,CRL與CRH主要用來表示GPIO的狀態一共有32bit,這部分會由IDE設置好。
CRL/CRH Register :CRL控制低8位元 CRH則負責控制高8位元
https://ithelp.ithome.com.tw/upload/images/20220308/20146325FvXUNb004d.png
IDR Register:主要控制IO的輸入電位情形,一共有32bit但這邊只使用了低16位,高16位保留。要注意的是這個暫存器只能讀而已~
https://ithelp.ithome.com.tw/upload/images/20220308/20146325XsLRPZOtpr.png
ODR Register: 主要為控制IO輸出電位情形,同樣一共有32bit只使用低16位,對這個暫存器寫可以直接控制GPIO輸出狀況。(在這裡使用左移的方式會改變暫存器當中所有的值,與BSRR不同的是假設 1<<1 會變成00000011,而ODR則會變成00000010)
https://ithelp.ithome.com.tw/upload/images/20220308/201463252Mn86iY8ii.png
BSRR Register:用來做完IO位設置或是清除的暫存器,簡單來說就是用來設置GPIO輸出位是1還是0。如果想如果設置GPIOA_1的值為1,則要對BSRR低16位寫入即可。
https://ithelp.ithome.com.tw/upload/images/20220308/20146325xtLO1IUhJl.png

GPIOA->BSRR = 1<<1;  //00000001 << 1 = 00000010

同樣的如果想要設置GPIOA_1為0,則往高16位寫入1即可。

GPIOA->BSRR =  1 << (16+1) //00000001 經過左移16+1位後會在bit17

BRR Register:為GPIO清除Register,作用跟上方BSRR的高16位元類似。
https://ithelp.ithome.com.tw/upload/images/20220308/20146325Abn9Lk5jM6.png

上方所說的是直接透過暫存器對GPIO去做操作,那有沒有更好用的方法?當然有!接下來要介紹的是HAL庫,可以在左側專案面板當中的Drivers當中STM32xxxxx_HAL_Driver中找到,裡面有各式HAL庫所提供的函數!那要怎麼使用HAL庫所提供的函數去控制GPIO腳位呢?這邊先介紹幾個函數~
1. HAL_GPIO_WritePin這函數用來設置腳位是1 or 0

HAL_GPIO_WritePin(GPIOx, GPIO_Pin, PinState);

函數應用

HAL_GPIO_WritePin(GPIOA,GPIO_PIN_5,GPIO_PIN_SET); //將腳位設定為高電位
HAL_GPIO_WritePin(GPIOA,GPIO_PIN_5,GPIO_PIN_RESET); //將腳位設定為低電位

2. HAL_GPIO_ReadPin這函數用來讀取腳位是1 or 0

HAL_GPIO_ReadPin(GPIOx, GPIO_Pin);

函數應用

HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_5); //讀取GPIOA_5

3. HAL_GPIO_TogglePin這函數可以理解為相反輸出 也就是說假設原先設定為1則下次會輸出0,每一次的輸出都與現在輸出相反。

HAL_GPIO_TogglePin(GPIOx, GPIO_Pin);

函數應用

HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5);//與當前輸出相反

接下來另外介紹一個Delay函數,單位為毫秒。
4. HAL_Delay

HAL_Delay(1000);//1000ms = 1s

上面所介紹的函數可以相互組合來控制LED燈的明亮變化~

首先可以先查詢一下Datasheet,我這塊板子為L746RG在當中有寫到板載LED燈LD2腳位為PA5,我們可以利用WritePin與ReadPin去對他讀取值。
https://ithelp.ithome.com.tw/upload/images/20220308/20146325B9LlchYobN.png

while(1)
{
	HAL_GPIO_WritePin(GPIOA,GPIO_PIN_5,GPIO_PIN_SET);
	State = HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_5);
	HAL_Delay(500); //0.5s
	HAL_GPIO_WritePin(GPIOA,GPIO_PIN_5,GPIO_PIN_RESET);
	State = HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_5);
	HAL_Delay(500); //0.5s
}

記得要在全域變數的位置宣告State,接著在編譯後可以在現場表達式當中輸入State就可以看到PA5的變化~

接下來介紹另外一種控制方式,透過上面所說的暫存器去控制GPIO的變化!
GPIO的設定照上方的PB4-PB8去做設定,皆為Output!
透過BSRR去將腳位輸出設為1,接著透過BRR去將腳位清0,一樣可以實現跑馬燈的效果!
如果手邊沒有多餘的LED的話可以透過像下方設定全域變數PB_1-PB_5的方式,接著透過現場表達式來觀看輸出的變化!

while(1)
{
		int i;
	  for(i=0;i<5;i++)
	  {
		  GPIOB->BSRR = (16<<i);

		  HAL_Delay(1000);
		  PB_1 = HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_4);
		  PB_2 = HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_5);
		  PB_3 = HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_6);
		  PB_4 = HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_7);
		  PB_5 = HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_8);
	  }
	  for(i=0;i<5;i++)
	  {
	  	GPIOB->BRR = (16<<i);

	  	HAL_Delay(1000);
	  	PB_1 = HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_4);
	  	PB_2 = HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_5);
	  	PB_3 = HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_6);
	  	PB_4 = HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_7);
	  	PB_5 = HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_8);
	  }
}


上方程式碼當中可以看到GPIOB→BSRR = (16<<i),16的計算方式可以對應上方BSRR暫存器來看,16轉二進位表示方式為00010000,對應到暫存器可以看到1的位置剛好是PB4,透過左移的方式可以陸續將PB5-8也設為1,BRR也是同理!


圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

1 則留言

2
Shortbread
iT邦研究生 5 級 ‧ 2022-03-08 22:38:37

剛剛看到你的推挽輸出那張圖有點問題喔,網路上很多人解釋推挽輸出都是用那張圖,但STM的手冊的GPIO章節的架構圖是用CMOS方式做推挽輸出,剛確認過了VDD接PMOS、GND接NMOS這種架構為CMOS,而那張圖是相反的VDD接NMOS、GND接PMOS,這種是B類輸出級。

兩個都可以做推拉,至於好像設計這種微控制器的輸出都偏向CMOS的推拉,我剛剛去看盛群的MCU也是這樣設計的,詳細的原因這篇好像有解釋到:
https://ppfocus.com/0/fo288613b.html

我覺得有點怪,很多人文章都在講B類但手冊明明是CMOS的架構!
附上STM手冊裡的圖:
https://ithelp.ithome.com.tw/upload/images/20220308/20141979gH2mFjP45C.png
黃色的地方是CMOS架構喔,好奇為什麼網路的解釋STM32輸出模式的文章都是B類輸出級,個人突然有這疑問有請各為大大解惑。

TsaiiiY iT邦新手 4 級 ‧ 2022-03-08 23:03:49 檢舉

這部分我也不是太了解 我看了很多網路上的資訊整理出來的~~我的專長不是電子電路這部分,我得再研究一下!

Shortbread iT邦研究生 5 級 ‧ 2022-03-19 23:30:44 檢舉

查了一些資料我想的應該沒錯,也去翻了史密斯的微電子學大概理解為什麼 MCU 設計推拉輸出要用 CMOS 了,而不是用 BJT 的 B 類輸出級。
根據史密斯的第11.10.3微電子學的部分可以整理出以下重點

  1. MOSFET 不需要直流閘級來推動電流,大大簡化推拉電路上的設計。
  2. MOSFET 操作的推拉速度遠高於 BJT 的 B 類的推拉速度(這大概是最重要的一點)。
    但這種 MOSFET 是無法進行高功率的應用,而 BJT 的 B 類適合較高輸出功率的應用,這樣的話我們一般 MCU 一般使用都不會直接用 GPIO 來推動高功率的應用,例如較高瓦數的喇叭是推不動的,通常都會再接個功率放大器。

好像跟 STM32 扯遠了XD,抱歉打擾了
/images/emoticon/emoticon01.gif

TsaiiiY iT邦新手 4 級 ‧ 2022-03-20 19:27:49 檢舉

感謝你的解說~太專業了

我要留言

立即登入留言