wordpress旅游公司主題百度seo營銷推廣
前面,我們對點燈代碼進行了第一次優(yōu)化,效果如下
盡管第一次優(yōu)化以后代碼可讀性確實高了不少,也看起來更加簡潔,但是,這里仍舊存在一個很嚴重的問題:就在每一個表達式右邊,我們給寄存器的數(shù)據(jù)賦值的操作。
我們每一個操作都是直接整體全部賦值,與自己不相關(guān)的位直接就用默認的0或1處理了,這樣就會造成一個問題:原來其他位可能被賦予特定值的現(xiàn)在因為整體賦值而被修改,這樣是很不合適的。?我們更希望的是,在修改我們期望的特定位的數(shù)據(jù)以后仍不改變其他位的值。那么怎么辦呢?這時候我們就要利用C語言中的位運算操作了。
一、位運算在代碼中的用法
考慮到我們可能長時間沒有使用位運算,所以這里放置一段代碼,供我們?nèi)セ貞浺幌?/p>
#include?<stdio.h>
#include?<stdlib.h>
void printfBinary(char * str, uint32_t num)
{
????char buffer[33];
? ? itoa(num, buffer, 2); // 把result轉(zhuǎn)成2進制字符串
? ? printf("%s = (%s)2 \n", str, buffer);
}
int main()
{
? ? /* 左移 8<<1 = 1000<<1 = 10000*/
? ? printfBinary("8 << 1", 8 << 1);
? ? /* 右移 8>>1 = 1000>>1 = 100*/
? ? printfBinary("8 >> 1", 8 >> 1);
? ? /* 按位或 8|7 = 1000|0111 = 1111 */
? ? printfBinary("8 | 7", 8 | 7);
? ? /* 按位或 8&7 = 1000&0111 = 0000 */
? ? printfBinary("8 & 7", 8 & 7);
? ? /* 按位取反 ~8 = ~1000 = 0111 */
? ? printfBinary("~8", ~8);
? ? /*
? ? ? ? 把某位置 1 ?(0 位 1位 ...)
? ? ? ? ? ? 比如把 num 的第 2 位置 1
? ? ? ? ? ? ? ? 1. 得到一個數(shù)第 2 位是 1 其他都為 0
? ? ? ? ? ? ? ? ? ?a = ?0000 0100 ?是由 1<<2 得到
? ? ? ? ? ? ? ? 2. 讓 num | a
? ? ?*/
? ? printfBinary("8置第 2 位為 1 ", 8 | (1 << 2));
? ? /*
? ? ? ? 把連續(xù)的多位同時置 1 ?(0 位 1位 ...)
? ? ? ? ? ? 比如把 num 的第 1和2 位置 1
? ? ? ? ? ? ? ? 1 ?a = ?3 << 1
? ? ? ? ? ? ? ? 2. num | a
? ? ?*/
? ? printfBinary("8置第 1和2 位為 1 ", 8 | (3 << 1));
? ? /*
? ? ? ? 把某位置 0 ?(0位 1位 ...)
? ? ? ? ? ? 比如把 num 的第 2 位置 0
? ? ? ? ? ? ? ? 1. 得到一個數(shù)第 2 位是 0 其他都為 1
? ? ? ? ? ? ? ? ? ?a = ?1111 1011 ?是由 ~(1<<2) 得到
? ? ? ? ? ? ? ? 2. 讓 num & a
? ? ?*/
? ? printfBinary("7置第 2 位為 0 ", 7 & ~(1 << 2));
? ? /*
? ? ? ? 把連續(xù)多位同時置 0 ?(0位 1位 ...)
? ? ? ? ? ? 比如把 num 的第 1和2 位置 0
? ? ? ? ? ? ? ? 1. a = ~(3<<1)
? ? ? ? ? ? ? ? 2. 讓 num & a
? ? ?*/
? ? printfBinary("7置第 1和2 位為 0 ", 7 & ~(3 << 1));
? ? /*
? ? ? ? 把連續(xù)的多位同時置位 ?101 (二進制)
? ? ? ? ? ? 比如把 num 的第 1,2,3 位置為 101
? ? ? ? ? ? 1. num的 1,2,3位置為0
? ? ? ? ? ? ? ? num &= ~(7<<1)
? ? ? ? ? ? 2. num |= (5 << 1); ? ?(5 = 101)
? ? ?*/
? ? unsigned char num = 13;
? ? num &= ~(7 << 1);
? ? num |= 5 << 1;
? ? printfBinary("13", 13);
? ? printfBinary("13的123位置為101 ", num);
}
通過這段代碼可以發(fā)現(xiàn),如果我們想要把特定位置為1,就要利用或運算。比如1000,我們要讓他第1位變?yōu)?(這里說的第幾位是以0開頭),則要用或運算,即1000 | 0010,然后每一位對應(yīng)做或運算,1|0->1,0|0->0,0|1->1,0|0->0,即變成1010?,很明顯這樣就可以實現(xiàn)了。同時這里用的0010可以借助移位得到,即1<<1。整理一下就是1000 |= (1<<1),這里總結(jié)一個方法:置1位或,位1余0(如果要把特定位置1,就要讓寄存器數(shù)據(jù)與一個數(shù)n做或運算,然后這個數(shù)n的取法就是讓對應(yīng)特定位的地方為1,其余給0即可)
同理,如果我們想要讓特定位變成0,則就要做與運算。比如0011,要讓第三位變成1,就要對其進行與操作,與誰做呢,就是與1011或,即0011&1011,0&1->0,0&0->1,1&1->1,1&1->1,這樣結(jié)果就是0111,很明顯實現(xiàn)了我們想要的操作。當然這個1011我們不好去找,所以這里還要用到取反的操作~,我們將100取反,就得到了1011,同時這個100也可以用移位得到,即1<<2。整理一下就是0011 &=? ~(1<<2)。實際上就是為了讓除了特定位以外的位全部置1,這樣才好實現(xiàn),當然了,這是一個技巧,記得了就好。所以總結(jié)一個方法:置0位與,位0余1(如果要把特定位置0,就要讓寄存器數(shù)據(jù)與一個數(shù)n做與運算,然后這個數(shù)n的取法就是讓對應(yīng)特定位的地方為0,其余給1即可(這個過程我們也會用取反實現(xiàn)))
由此可以看出,利用位運算即可實現(xiàn)在不改變其他位的情況下修改特定位的值了,這樣做能夠更加精確化的修改寄存器中的數(shù)據(jù),而不影響其他位的值的情況,更加合理了。
二、利用位運算優(yōu)化代碼
好,前面我們對位運算進行了大致的回憶和使用方法的總結(jié),現(xiàn)在我們就來對代碼進行優(yōu)化。主要就是對表達式右邊進行修改。
以點亮第一個LED燈為例
?第一,開啟時鐘的時候,我們是要將寄存器數(shù)據(jù)二進制位的第二位置為1,其他不變,就是100。那么,我們想想,前面我們說置1位或,位1余0,好,那就是對原數(shù)據(jù)或一個...0100就行,同時100也可以寫成1<<2,故代碼修改后
// 開啟時鐘 第二位置為1,其他不變
RCC->APB2ENR |= (1<<2);
第二,配置GPIOA的工作模式部分?
我們是讓PA0端口變成最大速度的推挽輸出模式,所以要讓前四位變成0011。這時候就遇到一個問題,我們之前講的都是修改一位,那么現(xiàn)在要修改四位,要怎么辦呢?誒其實一樣的,我們一位一位的修改不就好了。
從高向低位修改,首先看第3位,我們要置0,因為置0位與 位0余1,所以我們要讓原數(shù)據(jù)與一個...110111,同時這個110111是由1000取反得到的,且1000是1<<3得到,因此代碼可以寫成這樣:&= ~(1<<3)
同理再看第二位,也是要置0,那么由于置0位與 位0余1,所以我們要讓原數(shù)據(jù)與一個...111011,同時111011是由100取反得到,且100可以表示成1<<2,因此代碼可以寫成 &= ~(1<<2)
同理再看第1位,這時候要置為1,那么由于置1位或 位1余0,所以我們要讓原數(shù)據(jù)或一個10,且10可以表示成1<<1,所以代碼可以寫成 |= (1<<1)
同理再看第0位,也是要置1,那么由于置1位或 位1余0,因此我們讓原數(shù)據(jù)或一個1即可(1也能寫成1<<0),所以保證與前三段代碼格式一致,這里就改成 |= (1<<0)
// 設(shè)置GPIOA的工作模式
GPIOA->CRL &= ~(1<<3) // 第三位置0
GPIOA->CRL &= ~(1<<2) // 第二位置0
GPIOA->CRL |= (1<<1) // 第一位置1
GPIOA->CRL |= (1<<0) // 第0位置1
?第三,設(shè)置端口高低電平
?因為我們要讓LED-1燈亮,又連接LED-1燈的端口是PA0,所以我們只需修改端口輸出寄存器中的數(shù)據(jù)的二進制第0位的值為0就好了。要讓第0位置0,由于置0位與 位0余1,所以我們讓原數(shù)據(jù)與一個...1110就好,由于...1110可以由1取反得到,所以代碼可以寫成 &= ~(1<<0)
// 設(shè)置PA0為低電平
GPIOA->ODR &= ~(1<<0); // 第0位置0
?總的二次優(yōu)化代碼如下
#include<stm32f10x.h>int main(void)
{// 開啟時鐘?RCC->APB2ENR |= (1<<2);// 設(shè)置GPIOA的工作模式GPIOA->CRL &= ~(1<<3);GPIOA->CRL &= ~(1<<2);GPIOA->CRL |= (1<<1);GPIOA->CRL |= (1<<0);// 設(shè)置PA0為低電平GPIOA->ODR &= ~(1<<0);// 死循環(huán)保持狀態(tài)while(1){}
}
三、測試優(yōu)化代碼
接下來就時在keil中測試了
點擊編譯,運行,下載
?無錯誤
觀察現(xiàn)象,很明顯,黃燈確實亮了,說明本次優(yōu)化代碼沒有問題!
?四、結(jié)語
通過本次優(yōu)化代碼,我們再次加深了對位運算的理解,也了解到了其一個應(yīng)用場景
這里實際上還有一個小地方可以簡化代碼,就是設(shè)置GPIOA工作模式地方,我們發(fā)現(xiàn)四段代碼兩兩類似,其實咱可以兩兩合并,只需要用一個或就可以,即
// 設(shè)置GPIOA的工作模式
GPIOA->CRL &= ~(1<<3|1<<2)
GPIOA->CRL |= (1<<1|1<<0)
這樣其實也是同樣的意思,實際上就是利用或運算同時對兩位進行修改,我們可自行嘗試運行一下,效果是一樣的
OK,這就是第二次的代碼優(yōu)化了,繼續(xù)加油,拜拜