說(shuō)完位運(yùn)算,再說(shuō)說(shuō)其他的運(yùn)算符。+ - * / 不用多說(shuō),應(yīng)該都比較清楚,但是還是要注意的就是使用 / 進(jìn)行整型變量的計(jì)算時(shí),它不像平常一樣可以得到小數(shù)的,而只有整數(shù)部分,并沒(méi)有小數(shù)。還有就是各個(gè)運(yùn)算符的順序,如果不確定哪個(gè)先運(yùn)算,不如加上括號(hào)()吧,不用擔(dān)心效率的問(wèn)題,因?yàn)榧恿死ㄌ?hào)只是告訴編譯器該如何處理這條語(yǔ)句而已。另外使用#define定義一些表達(dá)式的時(shí)候也最好加上括號(hào),因?yàn)槟悴荒艽_定你這個(gè)宏定義會(huì)在什么地方使用,為了安全起見還是加上比較好,這些內(nèi)容在宏定義小節(jié)將進(jìn)行更詳細(xì)的說(shuō)明。
現(xiàn)在來(lái)重點(diǎn)說(shuō)說(shuō) % 取余運(yùn)算。這個(gè)運(yùn)算就很有意思了。如果從51單片機(jī)過(guò)來(lái)的,看到這個(gè)運(yùn)算符最多的地方就是在為定時(shí)器賦值時(shí)將高低字節(jié)進(jìn)行分離了,還有在數(shù)碼管實(shí)驗(yàn)中將一個(gè)數(shù)分離成十進(jìn)制的。這些都是很常見的應(yīng)用。但是其實(shí)更廣泛的應(yīng)用不在此。
編程的時(shí)候,很多時(shí)候都會(huì)要求一個(gè)數(shù)在某一個(gè)范圍內(nèi)進(jìn)行反復(fù)循環(huán),0100循環(huán),05循環(huán)等等。一般的方法是使用if語(yǔ)句,當(dāng)判斷達(dá)到最大值的時(shí)候回到開始處。實(shí)際上使用這種方法也是可以的,但是如果有更簡(jiǎn)單更高效的方法你是否還會(huì)使用if語(yǔ)句呢。
先說(shuō)說(shuō)使用 & 的方法吧。比如說(shuō)我想讓一個(gè)數(shù)在0~7內(nèi)循環(huán),該如何做呢?temp = (temp++)&0x07,如此就簡(jiǎn)單的實(shí)現(xiàn)了07循環(huán)。因?yàn)橐獙?shí)現(xiàn)07的循環(huán),其實(shí)只要提取一個(gè)變量遞增的低三位即可。不管這個(gè)變量如何變化,它的低三位始終都是在07循環(huán)變化的。同理,它也可以實(shí)現(xiàn)015、0~31變化。但是這個(gè)方法有局限,它只能按照連續(xù)bit位的最大值進(jìn)行循環(huán)。
現(xiàn)在再說(shuō) %,這個(gè)就厲害了,它不存在這個(gè)限制??梢栽?任意數(shù)循環(huán)。比如05循環(huán),只要temp = (temp++)%6(注意是6而不是5),那么temp就會(huì)在0~5之間循環(huán)了(這是我在看循環(huán)隊(duì)列的時(shí)候看到的方法,當(dāng)時(shí)很是震驚,關(guān)于循環(huán)隊(duì)列更多的東西將在循環(huán)隊(duì)列中講解)。
很神奇吧,更神奇的是使用它還可以計(jì)算兩個(gè)變量之間的距離(因?yàn)榫嚯x時(shí)沒(méi)有負(fù)數(shù)的,這里我稱之為距離可能不是很好理解,慢慢來(lái))我們知道不管是8bit數(shù)據(jù),16bit、32bit、64bit,它始終有一個(gè)位數(shù)的限制,如何在有限的位數(shù)里面獲得兩個(gè)數(shù)據(jù)之間準(zhǔn)確距離信息呢。以8bit為例,最大數(shù)為255,第一次讀取為0x4,第二次讀取是0x9,那么從0x04變化到0x09,變化了幾次(遞增數(shù)為1)? 9 – 4 = 5,如果變化后的數(shù)小于255當(dāng)然好辦,但是超過(guò)了255,又從0開始遞增呢?這個(gè)時(shí)候又該如何。比如說(shuō)一開始讀取的是251,之后再讀一次,變成了1,怎么算,251-1=250?肯定不對(duì),1-250=-250,更不是?那到底變化了幾次?252、253、254、255、0、1,這里可以看出是6,但是該怎么計(jì)算,又是否有一個(gè)公式可以在不改變?cè)瓉?lái)數(shù)據(jù)變化的情況下將超過(guò)限制和沒(méi)超過(guò)限制這兩種情況的計(jì)算包含呢。有的。就是length = (num2 – num1 + max)%max。關(guān)于這個(gè)公式更詳細(xì)內(nèi)容請(qǐng)看循環(huán)隊(duì)列小節(jié)。
確實(shí),使用%可以在任意數(shù)之間循環(huán),但是她也有限制,就是目前來(lái)看只能實(shí)現(xiàn)遞增1的情況,我想遞減呢?就是說(shuō)我想從9減為0,然后從9開始繼續(xù),又該如何。我的一個(gè)項(xiàng)目就需要這樣的變化,怎么辦,如果用if確實(shí)能夠解決問(wèn)題,但是直覺告訴我,肯定有簡(jiǎn)單方法實(shí)現(xiàn),所以我就上網(wǎng)搜,但是可能我搜索的方法不對(duì),始終沒(méi)有搜到,所以我暫時(shí)擱置了。直到一天夜里,回想遞增循環(huán)的情況,慢慢的思考其中的本質(zhì),再結(jié)合距離計(jì)算的公式,突然明悟了。就是num--; num = (num+max)%max;這樣兩條語(yǔ)句去實(shí)現(xiàn)。比如9~0,就是num--; num = (num+10)%10;
那么怎么理解呢,按理說(shuō)0再自減就是0xff,即255,再加10就是265,265%10 =5,怎么就變成了9呢?這和存儲(chǔ)有關(guān)。255從有符號(hào)的角度來(lái)看,就是-1,-1+10等于9,9%10=9,沒(méi)錯(cuò),就是如此。但是我的變量聲明不是有符號(hào)的,而是無(wú)符號(hào)的,怎么也沒(méi)有出問(wèn)題呢?這是因?yàn)橐绯隽耍驗(yàn)?65在8bit情況下溢出就變成了9,所以計(jì)算也不會(huì)出現(xiàn)問(wèn)題。所以這些計(jì)算機(jī)基礎(chǔ)方面的東西一定要理解透徹清晰,才能更好的駕馭一門語(yǔ)言,你也會(huì)發(fā)現(xiàn)其中的東西真的很神奇。
--------------------------------------------------------------2018/10/14 Osprey
在運(yùn)用的時(shí)候發(fā)現(xiàn),其實(shí)當(dāng)最大值就是溢出值時(shí),可以簡(jiǎn)化這個(gè)公式。很多時(shí)候單片機(jī)都需要一個(gè)運(yùn)行時(shí)間信息,一般的處理方法就是使用if語(yǔ)句進(jìn)行延時(shí),但是實(shí)際上不需要如此麻煩。
比如說(shuō)STM32單片機(jī),16位定時(shí)器,分頻時(shí)間根據(jù)你需要延時(shí)的最大時(shí)間進(jìn)行設(shè)置。比如最大所需延時(shí)6s,16位定時(shí)器最大值65535,那么在定時(shí)器時(shí)鐘頻率72M情況下,分頻系數(shù)設(shè)置為7200-1(分頻系數(shù)越大,定時(shí)精度越小,所以要根據(jù)最大延時(shí)間確定分頻系數(shù)),那么最大溢出時(shí)間為6,553,600 us,即6.5536 s,這樣就可以讓定時(shí)器一直處于運(yùn)行狀態(tài),即使到達(dá)最大值也會(huì)重新從0開始計(jì)數(shù),這樣就可以不需要變量也可以準(zhǔn)確獲取時(shí)間(如果定時(shí)時(shí)間很長(zhǎng),即使使用最大分頻65535也不能得到最大延時(shí),又不想增加變量在溢出中斷中進(jìn)行計(jì)時(shí)怎么辦,可以采用定時(shí)器主從模式,用一個(gè)定時(shí)器給另一個(gè)分頻)
既然定時(shí)器的寄存器CNT一直在0~65535循環(huán)變化,那么就可以通過(guò)獲得該時(shí)間來(lái)實(shí)現(xiàn)延時(shí)的效果。根據(jù)前面的內(nèi)容,使用 (time – CNT + 65535)%65535就可以獲得從定時(shí)開始到現(xiàn)在的時(shí)間了(time為16位變量,該值為開始定時(shí)那一刻CNT的值)。
比如現(xiàn)在要定時(shí)1s,那么首先獲取當(dāng)前的CNT值,然后等待,如
time = CNT; // 更新當(dāng)前時(shí)間
while((CNT- time + 65536) % 65536 < 10,000);
這樣,當(dāng)1s時(shí)間到的時(shí)候就可以跳出循環(huán)了,當(dāng)然了可以使用if語(yǔ)句,這樣就不會(huì)卡死在這里,只有時(shí)間到的情況下才會(huì)進(jìn)入。如:
if(( CNT- time + 65536) % 65536 > (10,000 – 1))
{
Time = CNT; // 更新當(dāng)前時(shí)間,用于下一次定時(shí)
// 1 s 時(shí)間到,處理事件
}
但是你是否發(fā)現(xiàn)65536是一個(gè)很特殊的數(shù)字,定時(shí)器因?yàn)槲粩?shù)的限制,始終都在0~65535內(nèi)循環(huán),不需要使用 % 來(lái)讓它循環(huán),那么以上的判斷語(yǔ)句就可以簡(jiǎn)化成這樣
time = CNT; // 更新當(dāng)前時(shí)間
while((uint16_t) (CNT- time + 65536) < 10,000);
// 和
if((uint16_t) (CNT- time + 65536) > (10,000 – 1))
{
Time = CNT; // 更新當(dāng)前時(shí)間,用于下一次定時(shí)
// 1 s 時(shí)間到,處理事件
}
從效率上來(lái)說(shuō)明顯后者更高,不信的話可以自己測(cè)試。
但是需要注意的是 (time –CNT + 65536) & 65535 和 (time –CNT + 65536) % 65536之間的區(qū)別,雖然這兩種寫法都可以使計(jì)算的值在0~65535之間循環(huán),也等效,但是明顯后者效率稍低一些,因?yàn)榍懊婺耸俏贿\(yùn)算。但是呢,后者的好處就像前面說(shuō)的,沒(méi)有限制。
另外再說(shuō)一點(diǎn),STM32F4系列單片機(jī)有一個(gè)DWT模塊,在當(dāng)systick時(shí)鐘被操作系統(tǒng)(如ucos)使用的時(shí)候,可以使用它作為延時(shí)時(shí)鐘,延時(shí)精度為系統(tǒng)時(shí)鐘頻率(納秒級(jí)),而且是32位的,延時(shí)時(shí)間也夠長(zhǎng),不用白不用??!
------------------------------------------------------更新于2018/11/19 Osprey