頭文件包含了單片機內部寄存器的地址定義等。引用此頭文件,才能正常對一些寄存器進行讀寫操作,例如PORT口
在頭文件中會出現這樣的語句
#define OUTMOD_0 (0*0x20u),這里的“0*”和“u”分別是什么意思?
u是unsigned的意思,表示無符號整形變量至于前面有了0*,導致結果是0,我估計是因為后面還有其他定義,寫成了1*0x20u,2*0x20u,這樣的形式,為了使代碼整潔好看,所以這里也寫成了0*0x20u
頭文件解析之看門狗
msp430單片機的“msp430f169.h”頭文件中的 #define SHT0_0 (0*0x100u) #define CONSEQ_0 (0*2u) 什么意思?
#define A B
A宏名,B宏內容
編譯預處理中,程序中,所有A的宏,將用B的定義替換。
如
#define PI 3.14
程序中所有出現PI的地方,將用浮點型常量,3.14代替。
0*0x100u結果是0。u表示無符號常量。加括號,可以提高在實際程序中的運算優先級。
如初始化ADC12的程序:
ADC12CTL1 = SHS_1 + SHP + CONSEQ_2; // TA trig., rpt conv.
ADC12MCTL0 = SREF_1 + INCH_10; // Channel A10, Vref+
ADC12IE = 0x01; // Enable ADC12IFG.0
ADC12CTL0 = SHT0_8 + REF2_5V + REFON + ADC12ON + ENC; // Config ADC12
再理解上一段程序,就懂了。后面的內容是配置的參數,程序編譯后,實際上這些內容都是常量。這種做法主要目的是增強程序的可讀性。
MSP430單片機 程序P1DIR |=0x01;這是什么意思啊?還有頭文件里面#define P1DIR是什么意思?
首先P1DIR的功能是選擇P1對應的IO端口的輸入輸出狀態,0為輸出狀態,1為輸入狀態。
P1DIR |=0x01;的意思就是將P1DIR按位和0x01或,即將P1DIR的最低位置1其他位不變,置一的效果就是P1.0設置為輸入狀態。這是P1DIR二進制值是xxxxxxx1,x是前一狀態沒有改變的值。
#define P1DIR 0x0008是定義了P1DIR這個寄存器的地址是根據器件型號不同而變化的,但是對于使用者來說固定的
MSP430設定的方法方便,但前提是看懂頭文件定義,只有這樣才能更好的運用。 #define __MSP430_HAS_WDT__
#define WDTCTL_ (0x0120u) DEFW( WDTCTL , WDTCTL_)
這一段中讓人費解的就是DEFW,百度可以知道它代表新版本對寄存器的定義。 #define DEFCW(name, address) __no_init union { struct {
volatile unsigned char name##_L; volatile unsigned char name##_H; };
volatile unsigned short name; } @ address;
這種定義也變得相對好理解。以上的這種union的定義,將一個16位的地址存儲空間分成2個8bits或者1個16位。可以實現字訪問,也可以實現字節訪問。
WDTCL_代表的就是0X0120u這個地址, 那么DEFW(WDTCTL,WDTCTL_)通過宏擴展就可以理解為 __no_init union { struct {
volatile unsigned char WDTCTL_L; volatile unsigned char WDTCTL_H; };
volatile unsigned short WDTCTL; } @ 0X0120u; 這里指出:
① __no_init是IAR擴展語法里面的一個擴展關鍵字。作用是聲明一個non-volatile類型的內存地址(Support non-valotile memory)。
② @是一種語法。那么它的作用很明顯就是將變量放置到對應的地址中。使用@,一個變量可以明確的制定一個存儲地址。
就是將WDTCTL變量存放在0X0120u地址中,如此一來就可以為每個寄存器進行命名了,也就是說可以實現每個寄存器對應一個或者多個變量。
#define WDTIS0 (0x0001u) 選擇時鐘源周期 #define WDTIS1 (0x0002u)
#define WDTSSEL (0x0004u) 時鐘源選擇(0SM+1A) #define WDTCNTCL (0x0008u) 清除WDTCNT
#define WDTTMSEL (0x0010u) 0看門狗模式,1 定時器模式
#define WDTNMI (0x0020u) 0:RST/NMI引腳復位端,1:RST/NMI引腳為邊沿觸發的非屏蔽中斷輸入
#define WDTNMIES (0x0040u) 0為上升沿觸發NMI中斷,1為下降沿 #define WDTHOLD (0x0080u) 0:WDT功能激活,1為時鐘禁止輸入并停止計數降低功耗
#define WDTPW (0x5A00u) 口令
WDT的定時時間
WDTSSEL WDTISx 定時時間/ms IS1 IS0
0 1 1 0.064 Tsmclk*64 0 1 0 0.51 Tsmclk*512 1 1 1 1.95 Taclk*64 0 0 1 8.19 Tsmclk*8192 1 1 0 15.63 Taclk*512 0 0 0 32.77 Tsmclk*64 1 0 1 250 Taclk*8192 1 0 0 1000 Taclk*32768
口令 :#define WDTPW (0x5A00u)
時鐘源選擇:WDTTMSEL 0為SMCLK 1為ACLK WDTCNTCL 該位為1,清除WDTCNT WDTIS1+WDTIS0
0:計數次數32768 1:8192 2:512 3:64 結合時鐘源的選擇衍生出下面的定時器延時
+++ #define
WDT_MDLY_32 (WDTPW+WDTTMSEL+WDTCNTCL )
MOV #WDTPW+WDTTMSEL+WDTCNTCL ,&WDTCTL 定時32ms #define
WDT_MDLY_8 (WDTPW+WDTTMSEL+WDTCNTCL+WDTIS0 ) #define
WDT_MDLY_0_5 (WDTPW+WDTTMSEL+WDTCNTCL+WDTIS1) #define
WDT_MDLY_0_064 (WDTPW+WDTTMSEL+WDTCNTCL+WDTIS1+WDTIS0)
#define
WDT_ADLY_1000 (WDTPW+WDTTMSEL+WDTCNTCL+WDTSSEL) #define
WDT_ADLY_250 (WDTPW+WDTTMSEL+WDTCNTCL+WDTSSEL+WDTIS0) #define
WDT_ADLY_16 (WDTPW+WDTTMSEL+WDTCNTCL+WDTSSEL+WDTIS1)
#define
WDT_ADLY_1_9 WDTPW+WDTTMSEL+WDTCNTCL+WDTSSEL+WDTIS1+WDTIS0)
#define WDT_MRST_32 (WDTPW+WDTCNTCL) #define WDT_MRST_8 (WDTPW+WDTCNTCL+WDTIS0) #define WDT_MRST_0_5 (WDTPW+WDTCNTCL+WDTIS1) #define
WDT_MRST_0_064 (WDTPW+WDTCNTCL+WDTIS1+WDTIS0)
#define WDT_ARST_1000 (WDTPW+WDTCNTCL+WDTSSEL) #define
WDT_ARST_250 (WDTPW+WDTCNTCL+WDTSSEL+WDTIS0) #define
WDT_ARST_16 (WDTPW+WDTCNTCL+WDTSSEL+WDTIS1) #define
WDT_ARST_1_9 (WDTPW+WDTCNTCL+WDTSSEL+WDTIS1+WDTIS0)
怎么判斷頭文件的數值(如0x0001)是表示十六進制數還是表示地址?
1、#define BIT0 (0×0001) //(0×0001)不是地址,而是一個16進制數值。
例1、P3DIR |= BIT3;實際上也可以寫成P3DIR |= 0×0008;意思是將P3口的默認上電值0×0000和0×0008相與,設置P3口的第三位(即P3.3)管腳作輸出使用。
例2、WDTCTL = WDTPW + WDTHOLD;實際上就是WDTCTL=0×5A80;
你可以在頭文件中查到#define WDTPW (0×5A00)和#define WDTHOLD (0×0080)。WDTCTL是看門狗的控
制寄存器,在msp430的User’Guide中有說明:當它的值為0×5A80時停止看門狗定時。
那為什么我們不直接寫成WDTCTL=0×5A80;呢?這樣的話程序的可讀性會很差。
0×5A80只是一個數值,當你下次再看你寫的程序,或者別人讀你的程序時,就不明白WDTCTL=0×5A80;的
意思了。如果寫成WDTCTL = WDTPW + WDTHOLD;就好理解多了:
WDTPW(Watchdog timer password,看門狗的密碼,WDTCTL的高8位):只有WDTCTL的高8位為
0×5A時才能對WDTCTL寄存器進行寫操作。
WDTHOLD(Watchdog timer hold,WDTCTL的第7位):當WDTCTL的第7位為1時,停止看門狗計時。
這樣我們通過PW,HOLD就可以輕松的知道WDTCTL = WDTPW + WDTHOLD;是做什么的了。可以看出
msp430的頭文件是很人性化的。
2、當然也有表示地址的,例如,頭文件中有以下部分:
#ifdef __IAR_SYSTEMS_ASM__
#define DEFC(name, address) sfrb name = address
#define DEFW(name, address) sfrw name = address;
//運用了可變參數宏的宏定義格式:#define 宏符號名(參數表) 宏體;宏體中就是寫出參數表中各個參數之間的關系。
#endif
……
#define P6OUT_ (0×0035)
DEFC( P6OUT , P6OUT_) //這里就是用了以上的可變參數宏。DEFC( P6OUT , P6OUT_) 就表示:sfrb P6OUT = P6OUT_
//這里的0×0035就是指P6OUT這個寄存器的地址了。
1、#define ME1_ (0x0004)
DEFC( ME1 , ME1_)
me1關聯端口0x0004
DEFC的C是字節的意思,也就是me1 就是地址0004處的一個字節即八位數據
2、#define FCTL1_ (0x0128)
DEFW( FCTL1 , FCTL1_)
其功能是從指定地址開始,定義若干個16位數據
對于對DEFC和DEFW的理解可以到#include《intrinsics.h》這個頭文件里查看
實質上就是要對sfrb和sfrw關鍵字的理解,下面就說說在C430中所擴展的關鍵字吧。
include “io430.h”
#include “msp430x47x3.h”
#include “io430x47x3.h”
這三個有什么區別?如何選擇比較好?
第一個是通用型號的IO頭文件。
第2個是47x所有的寄存器頭文件包含IO。
第3個是47x型號的IO寄存器頭文件。
如果你用的是47X型號的就用第2個比較好,如果你要寫全系列430單片機的就用通用的。第三個不推薦使用。
評論
查看更多