2013年1月29日 星期二

單晶片的一些小事(some tips of MCU)

這幾個月來,翻了不少人的code,由於沒有人教我或是入門,所以摸索之間碰到許多困難,不過這些問題也慢慢找到解答。

這裡將它寫出來給大家分享。



1.關於sbit用法

  sbit可以將SFR裡面的某個bit做變數宣告,用以查看IO位置是高電位還是低電位,標準的8052或是8051裡面你會看到,90H和A0H恰好就是P1和P2存放位置,書上都然很快樂的就這樣設定

sbit P1_0     = P1^0;
sbit P1_1     = P1^1;
sbit P1_2     = P1^2;
sbit P1_3     = P1^3;
sbit P1_4     = P1^4;
sbit P1_5     = P1^5;
sbit P1_6     = P1^6;
sbit P1_7     = P1^7;

但是問題來了,當你的MCU不只有P1是IO,P4 P5怎麼辦,你依樣畫葫蘆,去用sbit設定,然後卻不能用,為什麼??

後來發現可以這樣用的SFR是90H 98H A0H A8H...原來末碼是0或是8才可以這樣用,而P4 P5他們SFR的位置並不是如此,所以不能用。

如果你要看單個bit的狀態,就只能用P5&(0x04)這樣之類的方法了。

2.暫存器賦值的慣用手法
 
#define P21_PS3V_PWR_ON()       (  P2 |=  (0x01<<1) ) //P21 HIGH
#define P21_PS3V_PWR_OFF()      (  P2 &= ~(0x01<<1) ) //P21 LOW

當我們要對特定暫存器賦值的時候,常看到以上的寫法,搞了一堆or還有and這樣有比較厲害嗎?看起來是比較專業的樣子,只是有必要嗎?

根據我問了一些工程師,他們寫了兩三年他們說他們也不知道,剛開始看前輩這樣寫就這樣寫了,也有的說法說這樣子做比直接賦值效率快,減少程式運算時間,可是這樣子玩多了,可讀性就不高了。

那時我很單純的想
#define P21_PS3V_PWR_ON()       (  P2 = 0x02 ) //P21 HIGH
#define P21_PS3V_PWR_OFF()      (  P2 = 0xFD ) //P21 LOW

這樣寫不就好了嗎?不是意思都一樣,後來我才知道,其實兩種寫法的效率真的感覺沒差多少,但是重點是牽一髮動全身,用下面的方法賦值,就不管P20 P22 P23 P24 P25 P26 P27他們的死活了,所以 |= 和 &= ~ 這兩個是成雙成對的,唯有這樣才可以對單一SFR的其中一個bit做賦值,這也呼應了前面講到sbit不能用時,對SFR的賦值方法。

2 則留言:

  1. 0.0b 恩你碰到了不可 bit addressable 的位址
    當碰到類似文中所述類似 P4 P5
    不能 bit addressable 的記憶體空間
    夜風也可以考慮系統化技巧,
    使用一種稱為 "代理操作" 的技術
    更複雜的晶片基本上都會用上這種技巧像
    32-bit ColdFire C32MX, 16-bit dsPIC
    不過因為這些晶片有 DMA 控制器等於它們有
    硬體版的代理操作,DMA 控制器會有自己
    專屬 DMA RAM 跟 CPU 式完全分離的喔

    你可以想像成自己宣告的 BYTE Port4 就類似
    那些擁有 DMA RAM 的 CPU。

    會使用這種技巧以後當你碰到有DMA控制器的CPU
    你就可以馬上上手,因為那個只是把下面那種技巧
    做成硬體而已,指定好暫存器跟哪個DMA RAM關聯
    就可以直接使用。

    Ex:
    typedef unsigned char BYTE;

    typedef union tagMYPORT
    {
    struct{
    unsigned char bit0:1;
    unsigned char bit1:1;
    unsigned char bit2:1;
    unsigned char bit3:1;
    unsigned char bit4:1;
    unsigned char bit5:1;
    unsigned char bit6:1;
    unsigned char bit7:1;
    }bitVal;

    unsigned char u8Val;
    }MYPORT;

    #define DelegatePort(X) (*((MYPORT*)( &(X) )))

    void main(void)
    {
    BYTE Port4; // C51 編譯器會讓初始值為零
    // 使用代理操作
    DelegatePort(Port4).u8Val = 0x80; // 此時 Port4 = 0x80
    DelegatePort(Port4).bitVal.bit6 = 1; // 此時 Port4 = 0xC0
    P4 = Port4; // 移動值到 P4
    P4 = 0x80; // 假設把 0x80 移給 P4
    Port4 = P4; // Port4 接收 P4 (Port4=0x80)
    DelegatePort(Port4).bitVal.bit6 = 1; // Port4 利用代理操作更動 bit6 (Port4 變成 0xC0)
    }

    這個程式碼會把值交給 Port4 去代理
    這樣處理 bit 就會變成輕而易舉
    不管是你要比較某個 bit 或是修改某個 bit

    夜風可以用 Simulator 跑跑看 但是 Keil 的
    Simulator只能模擬標準 51 ; 也就是 P4
    可要改成 P0~P3 其中之一 當然你的8051有專屬
    ICD/ICE 的話就沒這種問題~~~

    話說夜風那顆 8051 GPIO還真多 :)
    話說我那篇好像也快回來囉 (希望會上XD)
    夜風學很快阿 有這種觀念 其實學 CAN bus
    應該會很快 CAN 的 Mask Filter 會用到很多這種
    位元移動的技巧

    回覆刪除
  2. 參考資料 Keil Technical Support
    http://www.keil.com/support/docs/2799.htm

    多多看官方的 Technical Support 會進步很快

    還有 Keil 安裝完那本
    Cx51 Compiler User’s Guide
    專心看 compiler 手冊其實可以發現很多技巧
    都隱藏在字裡行間,一個很好的例子就是

    data char *x; is equivalent to char *data x;

    原來在 Keil C 編譯器裡面
    這樣
    data char *x;

    char *data x;
    都是可以的語法 :)

    回覆刪除