|
用PIC寫高效的位移操作 在許多模擬串行通信中需要用位移操作。 以1-W總線的讀字節(jié)為例,原廠的代碼是: unsigned char read_byte(void) { unsigned char i; unsigned char value = 0; for (i = 0; i < 8; i++) { if(read_bit()) value| = 0 x 01<<i; // reads byte in, one byte at a time and then // shifts it left delay(10); // wait for rest of timeslot } return(value); } 雖然可以用,但編譯后執(zhí)行效率并不高效,這也是很多朋友認(rèn)為C一定不能和匯編相比的認(rèn)識提供了說法。 其實完全可以深入了解C和匯編之間的關(guān)系,寫出非常高效的C代碼,既有C的便利,又有匯編的效率。 首先對 for (i = 0; i < 8; i++)做手術(shù),改成遞減的形式: for(i=8;i!=0;i--),因為CPU判斷一個數(shù)是否是0(只需要一個指令),比判斷一個數(shù)是多大來的快(需要3個指令)。 再對value| = 0 x 01<<i;做手術(shù)。 value| = 0 x 01<<i;其實是一個低水平的代碼,效率低,DALLAS的工程師都是NO1,奇怪為什么會如此疏忽。 仔細(xì)研究C語言的位移操作,可以發(fā)現(xiàn)C總是先把標(biāo)志位清0,然后再把此位移入字節(jié)中,也就是說,當(dāng)前移動進(jìn)字節(jié)的位一定是0。 那么,既然已經(jīng)是0了,我們就只剩下一個步驟:判斷總線狀態(tài)是否是高來決定是否改寫此位,而不需要判斷總線是低的情況。 于是改寫如下代碼: for(i=8;i!=0;i--){ value>>=1; //先右移一位,value最高位一定是0 if(read_bit()) value|=0x80; //判斷總線狀態(tài),如果是高,就把value的最高位置1 } 這樣一來,整個代碼變得極其高效,編譯后根本就是匯編級的代碼。 再舉一個例子: 在采集信號方面,經(jīng)常是連續(xù)采集N次,最后求其平均值。 一般的,無論是用匯編或C,在采集次數(shù)上都推薦用8,16,32、64、128、256等次數(shù),因為這些數(shù)都比較特殊,對于MCU計算有很大好處。 我們以128次采樣為例:注:sampling()為外部采樣函數(shù)。 unsigned int total; unsigned char i,val; for(i=0;i<128;i++){ total+=sampling(); } val=total/128; 以上代碼是很多場合都可以看見的,但是效率并不怎么樣,狂浪費(fèi)資源。 結(jié)合C和匯編的關(guān)系,再加上一些技巧,就可以寫出天壤之別的匯編級的C代碼出來 首先分析128這個數(shù)是0B10000000,發(fā)現(xiàn)其第7位是1,其他低位全是0,那么就可以判斷第7位的狀態(tài)來判斷是否到了128次采樣次數(shù)。 在分析除以128的運(yùn)算,上面的代碼用了除法運(yùn)算,浪費(fèi)了N多資源,完全可以用右移的方法來代替之, val=total/128等同于val=(unsigned char)(total>>7); 再觀察下去:total>>7還可以變通成(total<<1)>>8,先左移動一位,再右移動8位,不就成了右移7位了么? 可知道位移1,4,8的操作只需要一個指令哦。 有上面的概驗了,就可以寫出如下的代碼: unsigned int total; unsigned char i=0 unsigned char val; while(!(i&0x80)){ //判斷i第7位,只需要一個指令。 total+=sampling(); i++; } val=(unsigned char)((total<<1)>>8); //幾個指令就代替了幾十個指令的除法運(yùn)算 哈哈,發(fā)現(xiàn)什么?代碼量竟然可以減少一大半,運(yùn)算速度可以提高幾倍。 再回頭,就可以理解為什么采樣次數(shù)要用推薦的一些特殊值了。
|