在Lesson 1有提到GPIO操作的相關位元運算,在此可以更詳細的解說在ODR及IDR的操作為什麼需要Shift、AND、OR、NOT或是XOR。
下文將使用的IDE為Keil uVision5(MDK-ARM),而STM32CubeMX的版本為6.2.1,因是使用底層暫存器控制,不限開發板及MCU型號。
首先Output High,以PA5為例。
GPIOA->ODR|=(1<<5);
因為ODR是直接控制整個Port A的16Bits輸出狀態,因此位元操作要正確,否則會影響到其他的位元。下方以表格的方式呈現,會較容易了解整個操作的流程與邏輯變化。
PIN | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
GPIOA->ODR | 1 | 0 | 0 | 1 | 0 | 1 | 0 | 1 |
我們要將PA5設置為1,而保留其於PIN7~PIN6、PIN4~PIN0的狀態。可以了解到數位邏輯的OR運算,與0運算則是保留自己,與1運算則是固定為1。因此需與00100000做OR運算,達到PA5設定為1的目的。
PIN | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
GPIOA->ODR | 1 | 0 | 0 | 1 | 0 | 1 | 0 | 1 |
OR | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 |
GPIOA->ODR | 1 | 0 | 1 | 1 | 0 | 1 | 0 | 1 |
因此上述可以寫成 GPIOA->ODR=GPIOA->ODR | 32;
GPIOA->ODR=GPIOA->ODR | 32;
而我們可以瞭解到32是2的5次方,1每向左位移一個位元,就是乘2的運算,因此1位移5次則會是32,故可以將32替換為1<<5,又可以稱做PA0設定1,向左移5次,移到PA5,每左移一次,就推進一個0。
PIN | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
1<<0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 |
1<<1 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 |
1<<2 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 |
1<<3 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 |
1<<4 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 |
1<<5 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 |
所以Output High則可以寫為GPIOA->ODR|=(1<<5);
接著以PA5,Output Low為例。
GPIOA->ODR&=~(1<<5);
我們要將PA5設置為0,而保留其於PIN7~PIN6、PIN4~PIN0的狀態。可以了解到數位邏輯的AND運算,與1運算則是保留自己,與0運算則是固定為0。因此需與11011111做AND運算,達到PA5清除為0的目的。
PIN | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
GPIOA->ODR | 1 | 0 | 1 | 1 | 0 | 1 | 0 | 1 |
AND | 1 | 1 | 0 | 1 | 1 | 1 | 1 | 1 |
GPIOA->ODR | 1 | 0 | 0 | 1 | 0 | 1 | 0 | 1 |
而在Output High的地方提到位移,在這邊AND的運算看起來就像是0位移5次,但補上的位元皆是補1,從中觀察可以發現其實就是1<<5的反向,因此可以寫為
GPIOA->ODR = GPIOA->ODR & ~(1<<5);
簡化後為GPIOA->ODR&=~(1<<5);
GPIOA->ODR&=~(1<<5);
最後是Toggle,反轉PA5的訊號。
GPIOA->ODR^=(1<<5);
許多人可能會直接的聯想到反向,但我們更仔細地來觀察,若PA5為0,則要輸出1,若PA5為1則要輸出0,但因為要使用ODR暫存器,因此不能直接賦值,不能直接反向。而可以做到反向的就是互斥或XOR,與1做XOR,皆會反向,與0做XOR,皆會與原狀態一樣。
PIN | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
GPIOA->ODR | 1 | 0 | 1 | 1 | 0 | 1 | 0 | 1 |
XOR | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 |
GPIOA->ODR | 1 | 0 | 0 | 1 | 0 | 1 | 0 | 1 |
PIN | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
GPIOA->ODR | 1 | 0 | 0 | 1 | 0 | 1 | 0 | 1 |
XOR | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 |
GPIOA->ODR | 1 | 0 | 1 | 1 | 0 | 1 | 0 | 1 |
因此可以寫成
GPIOA->ODR = GPIOA->ODR ^ (1<<5);
而「^」符號並不是次方的意思哦,在C語言當中是XOR的運算。簡化後可以寫成
GPIOA->ODR^=(1<<5);
以上是位元運算的詳細教學,希望可以幫助操作時更直覺的運用與有更好的理解。