头文件包含了单片机内部寄存器的地址定义等。引用此头文件,才能正常对一些寄存器进行读写操作,例如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单片机的就用通用的。第三个不推荐使用。
评论
查看更多