久久久久久久999_99精品久久精品一区二区爱城_成人欧美一区二区三区在线播放_国产精品日本一区二区不卡视频_国产午夜视频_欧美精品在线观看免费

 找回密碼
 立即注冊

QQ登錄

只需一步,快速開始

搜索
查看: 8134|回復(fù): 2
收起左側(cè)

單片機C語言的位操作

[復(fù)制鏈接]
ID:104835 發(fā)表于 2016-2-4 01:39 | 顯示全部樓層 |閱讀模式
     由于PIC處理器對位操作是最高效的,所以把一些BOOL變量放在一個內(nèi)存的位中,既可以達到運算速度快,又可以達到最大限度節(jié)省空間的目的。
在C中的位操作有多種選擇。

*********************************************

如:char x;x=x|0B00001000;         /*對X的4位置1。*/

char x;x=x&0B11011111;               /*對X的5位清0。*/
把上面的變成公式則是:
#define bitset(var,bitno)(var |=1<<bitno)
#define bitclr(var,bitno)(var &=~(1<<bitno))
則上面的操作就是:char x;bitset(x,4)
char x;bitclr(x,5)
*************************************************
但上述的方法有缺點,就是對每一位的含義不直觀,最好是能在代碼中能直觀看出每一位代表的意思,這樣就能提高編程效率,避免出錯。
如果我們想用X的0-2位分別表示溫度、電壓、電流的BOOL值可以如下:
unsigned char x @ 0x20;                           /*象匯編那樣把X變量定義到一個固定內(nèi)存中。*/
bit temperature@ (unsigned)&x*8+0;      /*溫度*/
bit voltage@ (unsigned)&x*8+1;            /*電壓*/
bit current@ (unsigned)&x*8+2;             /*電流 */
這樣定義后X的位就有一個形象化的名字,不再是枯燥的1、2、3、4等數(shù)字了。
可以對X全局修改,也可以對每一位進行操作:
char=255;
temperature=0;
if(voltage)......
*****************************************************************
還有一個方法是用C的struct結(jié)構(gòu)來定義:
如:
struct cypok{
                     temperature:1;               /*溫度*/
                     voltage:1;                     /*電壓*/
                     current:1;                      /*電流*/
none:4;
           }x @ 0x20;
這樣就可以用
x.temperature=0;
if(x.current)....
等操作了。
**********************************************************
上面的方法在一些簡單的設(shè)計中很有效,但對于復(fù)雜的設(shè)計中就比較吃力。如象在多路工業(yè)控制上。前端需要分別收集多路的多路信號,然后再設(shè)定控制多路的多路輸出。如:有2路控制,每一路的前端信號有溫度、電壓、電流。后端控制有電機、喇叭、繼電器、LED。如果用匯編來實現(xiàn)的話,是很頭疼的事情,用C來實現(xiàn)是很輕松的事情,這里也涉及到一點C的內(nèi)存管理(其實C的最大優(yōu)點就是內(nèi)存管理)。采用如下結(jié)構(gòu):
union cypok{
                 struct out{
                                 motor:1;               /*電機*/
                                 relay:1;                /*繼電器*/
                                 speaker:1;           /*喇叭*/
                                 led1:1;                /*指示燈*/
                                 led2:1;                /*指示燈*/
                              }out;
struct in{
                                none:5;
                                temperature:1;                  /*溫度*/
                                voltage:1;                         /*電壓*/
                                current:1;                          /*電流*/
                               }in;
                 char x;
};
union cypok an1;
union cypok an2;
上面的結(jié)構(gòu)有什么好處呢?聽小弟道來:
細分了信號的路an1和an2;
細分了每一路的信號的類型(是前端信號in還是后端信號out):
an1.in ;
an1.out;
an2.in;
an2.out;
然后又細分了每一路信號的具體含義,如:
an1.in.temperature;
an1.out.motor;
an2.in.voltage;
an2.out.led2;等
這樣的結(jié)構(gòu)很直觀的在2個內(nèi)存中就表示了2路信號。并且可以極其方便的擴充。
如添加更多路的信號,只需要添加:
union cypok an3;
union cypok an4;
。。。。。。。。。。。。。。。
從上面就可以看出用C的巨大好處。
PICC每日一貼。(初談如何從匯編轉(zhuǎn)向PICC)  
  
  小弟不才,特拋磚引玉,與大家共勉。
聊聊如何從匯編轉(zhuǎn)向PICC。
因為HIDE-TECH PICC破解版很多,所以HIDE PICC有比其它PICC有更多的用戶,雖然它的編譯效率不是最好。最好的是CCS,但沒破戒版。。。,不過用HIDE PICC精心安排函數(shù)一樣可以獲得很高的編譯效率,還是人腦是第一的。
當(dāng)然要求你要有C語言的基礎(chǔ)。PICC不支持C++,這對于習(xí)慣了C++的朋友還得翻翻C語言的書。
C代碼的頭文件一定要有
#include<pic.h>
它是很多頭文件的集合,C編譯器在pic.h中根據(jù)你的芯片自動栽入相應(yīng)的其它頭文件。
這點比匯編好用。
載入的頭文件中其實是聲明芯片的寄存器和一些函數(shù)。
順便摘抄一個片段:
static volatile unsigned char TMR0 @ 0x01;
static volatile unsigned char PCL @ 0x02;
static volatile unsigned char STATUS @ 0x03;
可以看出和匯編的頭文件中定義寄存器是差不多的。如下:
TMR0 EQU 0X01;
PCL  EQU 0X02;
STATUS EQU 0X03;
都是把無聊的地址定義為大家公認的名字。
一:怎么附值?
如對TMR0附值:
匯編中:MOVLW 200;
MOVWF TMR0;當(dāng)然得保證當(dāng)前頁面在0,不然會出錯。
C語言:TMR0=200;//無論在任何頁面都不會出錯。
可以看出來C是很直接了當(dāng)?shù)摹2⑶易畲蠛锰幨遣僮饕粋寄存器時候,不用考慮頁面的問題。一切由C自動完成。
二:怎么位操作?
匯編中的位操作是很容易的。在C中更簡單。
C的頭文件中已經(jīng)對所有可能需要位操作的寄存器的每一位都有定義名稱:
如:PORTA的每一個I/O口定義為:RA0、RA1、RA2。。。RA7。
OPTION的每一位定義為:PS0、PS1、PS2 、PSA 、T0SE、T0CS、INTEDG 、RBPU。
可以對其直接進行運算和附值。
如:
RA0=0;
RA2=1;
在匯編中是:
BCF PORTA,0;
BSF PORTA,2;
可以看出2者是大同小異的,只是C中不需要考慮頁面的問題。
三:內(nèi)存分配問題:
在匯編中定義一個內(nèi)存是一件很小心的問題,要考慮太多的問題,稍微不注意就會出錯。比如16位的運算等。用C就不需要考慮太多。
下面給個例子:
16位的除法(C代碼):
INT X=5000;
INT Y=1000;
INT Z=X/Y;
而在匯編中則需要花太多精力。
給一個小的C代碼,用RA0控制一個LED閃爍:
#include<pic.h>
void main(){
int x;  
CMCON=0B111;   file://關(guān)掉A口比較器,要是有比較器功能的話。
ADCON1=0B110;          file://關(guān)掉A/D功能,要是有A/D功能的話。
TRISA=0;              file://A口全為輸出。
loop:RA0=!RA0;           
for(x=60000;--x;){;}           file://延時
goto loop;
}
說說RA0=!RA0的意思:PIC對PORT寄存器操作都是先讀取----修改----寫入。
上句的含義是程序先讀RA0,然后取反,最后把運算后的值重新寫入RA0,這就實現(xiàn)了閃爍的功能。

  (一點經(jīng)驗)如何有效的實時控制LED閃爍。  
  
   在很多設(shè)計中需要有精彩而實用的LED閃爍來表示設(shè)備工作正常與否和工作狀態(tài)。
在一些實時性要求不高的設(shè)計中可以用插入延時來控制LED閃爍。
它的缺點現(xiàn)而易見:1:LED閃爍方式反映慢。2:在延時過程不能干其它工作(中斷除外),浪費了資源。3:代碼雍長,真正控制LED就幾個個指令,其它的延時代碼占了99%的空間。
如果用TMR1或TMR2來做一個時鐘,上面的種種缺點就可以避免,使得你可以騰出大量的時間做更有效的工作。
下面是用TMR1作時鐘的C代碼(RB1、RB2、RB3控制LED)示例:
void set_tmr1(){
TMR1L=0xdc;
TMR1H=0xb;       /*設(shè)定初值3036*/
T1CON=0B10001;             /*設(shè)定TMR1   0.125s溢出一次*/
}
void interrupt time(){
if(TMR1IF){
T1CON=0B10000;            /*關(guān)閉TMR1*/
TMR1L=0xdc;                  
TMR1H=0xb;                   /*TMR1設(shè)初值
T1CON=0B10001;            /*從新設(shè)分頻比,打開TMR1*/
if(s++>8){        /*每S清0*/
s=0;
if(ss++>60)/*每分鐘清0*/
ss=0;
}
TMR1IF=0;
return;
}
}unsigned char s;                    /*每0.125S累加1*/
unsigned char ss;                  /*每1秒累加1*/
void main(){
set_tmr1();
........;                             /*設(shè)定I/O口,開TMR1中斷*/
while(1){
if(...)                              /*判斷閃爍方式語句,下同*/
RB1=(bit)(s>4);            /*每1s閃爍一次,占空比50%(調(diào)節(jié)>后面值可以改變)*/
if(...)
RB2=(bit)(!ss);             /*每1分鐘閃爍一次,亮1秒,熄59秒*/
if(...)
RB3=(bit)(s==0 || s==2 || s== 4 || s== 6);      /*每0.25S閃爍一次*/
.........;                            /*其它工作*/
}
}
這樣的框架對于基于要求實時性高的軟件查詢的程序是很有效的。
在PICC中使用常數(shù)指針。  
常數(shù)指針使用非常靈活,可以給編程帶來很多便利。
我測試過,PICC也支持常數(shù)指針,并且也會自動分頁,實在是一大喜事。
定義一個指向8位RAM數(shù)據(jù)的常數(shù)指針(起始為0x00):
#define DBYTE ((unsigned char volatile *) 0)
定義一個指向16位RAM數(shù)據(jù)的常數(shù)指針(起始為0x00):
#define CWORD ((unsigned int volatile *) 0)
((unsigned char volatile *) 0)中的0表示指向RAM區(qū)域的起始地址,可以靈活修改它。

DBYTE[x]中的x表示偏移量。

下面是一段代碼1:

char a1,a2,a3,a4;

#define DBYTE ((unsigned char volatile *) 0)

void main(void){
long cc=0x89abcdef;
a1=DBYTE[0x24];
a2=DBYTE[0x25];
a3=DBYTE[0x26];
a4=DBYTE[0x27];
while(1);
}

2:

char a1,a2,a3,a4;

#define DBYTE ((unsigned char volatile *) 0)

void pp(char y){
a1=DBYTE[y++];
a2=DBYTE[y++];
a3=DBYTE[y++];
a4=DBYTE[y];
}
void main(void){
long cc=0x89abcdef;
char x;
x=&cc;
pp(x);
while(1);
}
3:
char a1,a2,a3,a4;
#define DBYTE ((unsigned char volatile *) 0)
void pp(char y){
a1=DBYTE[y++];
a2=DBYTE[y++];
a3=DBYTE[y++];
a4=DBYTE[y];
}
void main(void){
bank1 static long cc=0x89abcdef;
char x;
x=&cc;
pp(x);
while(1);
}
關(guān)于BOOL量的一點應(yīng)用。  
  
  /*bit型變量只能是全局的或靜態(tài)的,
而有時我門在實際應(yīng)用中既要改變某“位"變量的值;
又要保證這個函數(shù)的獨立性;那不可避免的要把
這個函數(shù)做成有參函數(shù),可是bit型變量是不能用做參數(shù)的;
那該咋辦泥?還好!有位段。
看看:*/
/********************************************/
union FLAG
{
unsigned char BYTE;
struct
{
  unsigned char b0:1;
  unsigned char b1:1;
  unsigned char b2:1;
  unsigned char b3:1;
  unsigned char b4:1;
  unsigned char b5:1;
  unsigned char b6:1;
  unsigned char b7:1;  
}bool;
};
/********************************************/
union      FLAG               mode;
#define    auto_bit         mode.bool.b0
#define    cool_bit         mode.bool.b1
#define    dar_bit         mode.bool.b2
#define    fan_bit         mode.bool.b3
#define    heat_bit         mode.bool.b4
#define    swing_bit     mode.bool.b5
#define    bed_bit        mode.bool.b6
#define    time_bit         mode.bool.b7
/********************************************/
void mode_task(in_mode)
union FLAG *in_mode;
{
in_mode -> bool.b0=1;
in_mode -> bool.b5=1;
/*也可這樣寫
in_mode -> BYTE|=0x21;*/
}
/********************************************/
void main(void)
{
  mode.BYTE=0X00;
  while(1)
  {
   mode_task(&mode);
  }
}
/********************************************/
這樣寫多爽!
這里涉及了結(jié)構(gòu),聯(lián)合,位段,及指針;可得先把基礎(chǔ)概念搞清楚!

   用PICC寫高效的位移操作。  
  
  在許多模擬串行通信中需要用位移操作。
以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í)行效率并不高效,這也是很多朋友認為C一定不能和匯編相比的認識提供了說法。
其實完全可以深入了解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,奇怪為什么會如此疏忽。
仔細研究C語言的位移操作,可以發(fā)現(xiàn)C總是先把標(biāo)志位清0,然后再把此位移入字節(jié)中,也就是說,當(dāng)前移動進字節(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;
以上代碼是很多場合都可以看見的,但是效率并不怎么樣,狂浪費資源。
結(jié)合C和匯編的關(guān)系,再加上一些技巧,就可以寫出天壤之別的匯編級的C代碼出來
首先分析128這個數(shù)是0B10000000,發(fā)現(xiàn)其第7位是1,其他低位全是0,那么就可以判斷第7位的狀態(tài)來判斷是否到了128次采樣次數(shù)
在分析除以128的運算,上面的代碼用了除法運算,浪費了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);                    //幾個指令就代替了幾十個指令的除法運算
哈哈,發(fā)現(xiàn)什么?代碼量竟然可以減少一大半,運算速度可以提高幾倍。
再回頭,就可以理解為什么采樣次數(shù)要用推薦的一些特殊值了。













回復(fù)

使用道具 舉報

ID:160143 發(fā)表于 2017-1-5 15:38 來自觸屏版 | 顯示全部樓層
新手,暫時看不懂…
回復(fù)

使用道具 舉報

ID:215453 發(fā)表于 2017-9-13 09:37 | 顯示全部樓層
樓主寫得好,向您學(xué)習(xí),謝謝!
回復(fù)

使用道具 舉報

您需要登錄后才可以回帖 登錄 | 立即注冊

本版積分規(guī)則

小黑屋|51黑電子論壇 |51黑電子論壇6群 QQ 管理員QQ:125739409;技術(shù)交流QQ群281945664

Powered by 單片機教程網(wǎng)

快速回復(fù) 返回頂部 返回列表
主站蜘蛛池模板: 国产高清久久久 | 日韩一区二区三区在线 | 国产视频1区 | 美日韩免费视频 | 国产在线97| 欧美视频第三页 | 天天综合天天 | 国产精品综合视频 | 日韩毛片网 | 日韩www视频 | 在线免费观看黄a | 国产高清视频在线观看 | av片免费 | 精品久久久久久国产 | 亚洲一区二区视频 | 国产一区二区三区在线免费观看 | 免费视频久久 | 特级特黄特色的免费大片 | 91视频一区 | 91精品久久久久久久久 | 丝袜美腿一区 | 亚洲一区二区三区观看 | 欧美亚洲视频在线观看 | 日韩在线观看中文字幕 | 日韩在线视频精品 | 日韩一区二区三区av | 国产精品日韩 | 国产高清一区二区 | 久久福利电影 | 成人欧美一区二区三区黑人孕妇 | 激情久久网 | 毛片电影 | 日韩成人免费视频 | 久久久久国产一区二区三区 | 国产一区二区自拍 | 香蕉视频久久久 | 国产永久免费 | 国产性色视频 | av毛片在线免费观看 | 国产成人99久久亚洲综合精品 | 韩日在线视频 |