在STM32CubbeIDE中要設定GPIO相對來的容易~一樣點開.ioc檔可以去對各個腳位去做設定。
在這邊我將PB4-PB8設定為GPIO_Output,首先點選PB4將它設為GPIO_Output,其餘的腳位同樣設定。
接下來點擊左側的SystemCore當中的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位元
IDR Register:主要控制IO的輸入電位情形,一共有32bit但這邊只使用了低16位,高16位保留。要注意的是這個暫存器只能讀而已~
ODR Register: 主要為控制IO輸出電位情形,同樣一共有32bit只使用低16位,對這個暫存器寫可以直接控制GPIO輸出狀況。(在這裡使用左移的方式會改變暫存器當中所有的值,與BSRR不同的是假設 1<<1 會變成00000011,而ODR則會變成00000010)
BSRR Register:用來做完IO位設置或是清除的暫存器,簡單來說就是用來設置GPIO輸出位是1還是0。如果想如果設置GPIOA_1的值為1,則要對BSRR低16位寫入即可。
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位元類似。
上方所說的是直接透過暫存器對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去對他讀取值。
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也是同理!
剛剛看到你的推挽輸出那張圖有點問題喔,網路上很多人解釋推挽輸出都是用那張圖,但STM的手冊的GPIO章節的架構圖是用CMOS方式做推挽輸出,剛確認過了VDD接PMOS、GND接NMOS這種架構為CMOS,而那張圖是相反的VDD接NMOS、GND接PMOS,這種是B類輸出級。
兩個都可以做推拉,至於好像設計這種微控制器的輸出都偏向CMOS的推拉,我剛剛去看盛群的MCU也是這樣設計的,詳細的原因這篇好像有解釋到:
https://ppfocus.com/0/fo288613b.html
我覺得有點怪,很多人文章都在講B類但手冊明明是CMOS的架構!
附上STM手冊裡的圖:
黃色的地方是CMOS架構喔,好奇為什麼網路的解釋STM32輸出模式的文章都是B類輸出級,個人突然有這疑問有請各為大大解惑。
這部分我也不是太了解 我看了很多網路上的資訊整理出來的~~我的專長不是電子電路這部分,我得再研究一下!
查了一些資料我想的應該沒錯,也去翻了史密斯的微電子學大概理解為什麼 MCU 設計推拉輸出要用 CMOS 了,而不是用 BJT 的 B 類輸出級。
根據史密斯的第11.10.3微電子學的部分可以整理出以下重點
好像跟 STM32 扯遠了XD,抱歉打擾了
感謝你的解說~太專業了