MSP430 工作笔记一(转)
(一),IO口模块,
本文引用地址://m.amcfsurvey.com/article/201611/316298.htm1,我们所用的MSP430G2553有两组IO口,P1和P2。
2,IO口的寄存器有:方向选择寄存器PxDIR,输出寄存器PxOUT,输入寄存器PxIN,IO口内部上拉或下拉电阻使能寄存器PxREN,IO口功能选择寄存器PxSEL和PxSEL2,IO口中断使能寄存器PxIE,中断沿选择寄存器PxIES,IO口中断标志寄存器PxIFG。
3,所有的IO都带有中断,其中所有的P1口公用一个中断向量,所有的P2口公用一个中断向量。所以在使用中断时,当进入中断后,还要判断到底是哪一个IO口产生的中断,判断方法可以是判断各个IO口的电平。
4,中断标志PxIFG需要软件清除,也可以用软件置位,从而用软件触发一个中断。
注意:在设置PxIESx时根据PxINx有可能会引起相应的PxIFGx置位(具体的情况见用户指南),所以在初始化完IO口中断以后,正式使用IO中断前要先将对应的PxIFGx清零。程序如下:
voidIO_interrupt_init()//IO中断初始化函数
{
P1REN|=BIT4+BIT5+BIT6+BIT7;//pullup内部上拉电阻使能
//使用中断时,使能内部的上拉电阻这样当该脚悬空是,电平不会跳变,防止悬空时电平跳变不停的触发中断
P1OUT=BIT4+BIT5+BIT6+BIT7;//当引脚上的上拉或下拉电阻使能时,PxOUT选择是上拉还是下来
//0:下拉,1:上拉
P1IE|=BIT4+BIT5+BIT6+BIT7;//interruptenabledP13中断使能
P1IES|=BIT4+BIT5+BIT6+BIT7;//Hi/loedge下降沿中断
//P1IES&=~BIT3;//上升沿触发中断
P1IFG&=~(BIT4+BIT5+BIT6+BIT7);//中断标志位清零
}
5,PxOUT:如果引脚选择了内部的上拉或下拉电阻使能,则PxOUT设定电阻是上拉还是下拉,0:下拉,1:上拉
6,当IO口不用时,最好不要设为输入,且为浮动状态(这是IO口的默认状态),因为当输入为浮动时,输入电压有可能会在VIL和VIH之间,这样会产生击穿电流。所以不用的IO口可以设为输出状态,或设为输入状态但通过外围电路接至VCC或GND,或接一个上拉/下拉电阻。
7,当使用msp430g2553的IO口时要注意,因为g2553的IO口寄存器的操作,不像51,它不能单独针对某一位进行操作,必须对整个寄存器进行操作。所以就不像51,g2553不可以定义bit型的数据。所以在使用msp的IO口时要注意对需要位的操作,而不要影响其他无关的位,可以用|&^等按位操作的符号。在使用IO都控制其他外围模块时也要注意要使用的IO口的定义,可以用如下的定义方法:
#defineCLR_RSP2OUT&=~BIT0;//RS=P2.0
#defineSET_RSP2OUT|=BIT0;
#defineCLR_RWP2OUT&=~BIT1;//RW=P2.1
#defineSET_RWP2OUT|=BIT1;
#defineCLR_ENP2OUT&=~BIT2;//EN=P2.2
#defineSET_ENP2OUT|=BIT2;
#defineDataPortP1OUT
8,g2553的P27和P26脚分别接外部晶体的输出和输入脚XOUT和XIN,默认是自动设为了晶振管脚功能,但是当想把它们用为普通的IO时,也可以,设置对应的SEL设为普通的IO即可,如下:
P2DIR|=BIT6+BIT7;//把P26和P27配置为普通IO并为输出脚默认为晶振的输入和输出引脚作为dac0832的
P2SEL&=~(BIT6+BIT7);//cs和wr控制端
P2SEL2&=~(BIT6+BIT7);
(二),时钟系统
1,msp430能做到超低功耗,合理的时钟模块是功不可没的。但是功能强大的时钟模块设置起来也相对复杂一些。
2,msp430的时钟源有:
(1),外接低频晶振LFXT1CLK:低频模式接手表晶体32768Hz,高频模式450KHz~8MHz;
(2),外接高速晶振XT2CLK:8MHz;
(3),内部数字控制振荡器DCO:是一个可控的RC振荡器,频率在0~16MHz;
(4),超低功耗低频振荡器VLO:不可控,4~20KHz典型值为12KHz;
3,时钟模块:430的时钟模块有MCLKSMCLKACLK:
(1),主系统时钟MCLK:提供给MSP430的CPU时钟。可以来自LFXT1CLKXT2CLKDCOVLO可选,默认为DCO。
(2),子系统时钟SMCLK:提供给高速外设。可以来自LFXT1CLKXT2CLKDCOVLO可选,默认为DCO。
(3),辅助系统时钟ACLK:提供给低速外设。可来自LFXT1CLKVLO。
4,内部的振荡器DCO和VLO提供的时钟频率不是很精确,随外部环境变化较大。
DCO默认的频率大概为800KHz,但我用示波器观察的为1.086MHz左右,当DCO设置的过高时,用示波器可以看到波形不再是方波,而是类似于正弦波。DCO可以用CCS提供的宏定义进行相对比较精确的设置,如下:
DCOCTL=CALDCO_12MHZ;//DCO设为12MHz这种方法设DCO频率比较精确,实际测得为12.08MHz左右正弦波
BCSCTL1=CALBC1_12MHZ;
用这种方法可以设置1,8,12,16MHz
宏定义如下:
#ifndef__DisableCalData
SFR_8BIT(CALDCO_16MHZ);
SFR_8BIT(CALBC1_16MHZ);
SFR_8BIT(CALDCO_12MHZ);
SFR_8BIT(CALBC1_12MHZ);
SFR_8BIT(CALDCO_8MHZ);
SFR_8BIT(CALBC1_8MHZ);
SFR_8BIT(CALDCO_1MHZ);
SFR_8BIT(CALBC1_1MHZ);
#endif
5,使用超低功耗低频振荡器VLO可以很大程度地降低系统功耗,下面的例子是设置ACLK为VLO,MCLK为VLO的8分频:
#include
//1延时
//#defineCPU_F((double)16000000)//cpufrequency16000000
#defineCPU_F((double)1630)//cpufrequency1630//CPU的实际MCLK大约为13.05/8=1.63KHz
#definedelay_us(x)__delay_cycles((long)(CPU_F*(double)x/1000000.0))
#definedelay_ms(x)__delay_cycles((long)(CPU_F*(double)x/1000.0))
voidmain(void)
{
volatileunsignedinti;//Volatiletopreventremoval
WDTCTL=WDTPW+WDTHOLD;//Stopwatchdogtimer
BCSCTL3|=LFXT1S_2;//LFXT1=VLO低频时钟选择为VLOACLK选为VLO
IFG1&=~OFIFG;//ClearOSCFaultflag清除振荡器错误中断标志
__bis_SR_register(SCG1+SCG0);//StopDCOSCG1禁止SMCLKSCG0禁止DCO
BCSCTL2|=SELM_3+DIVM_3;//MCLK=LFXT1/8
//因为前面已经选择了LFXT1=VLO所以MCLK选为VLO8分频所以CPU的MCLK大约为1.5KHz
P1DIR=0xFF;//AllP1.xoutputs
P1OUT=0;//AllP1.xreset
P2DIR=0xFF;//AllP2.xoutputs
P2OUT=0;//AllP2.xreset
P1SEL|=BIT0+BIT4;//P10P14options功能选择为外围模块
//p10输出ACLK,来自VLO,p14输出SMCLK,因为禁止了SMCLK,所以P14脚无波形输出
//VLO典型值为12KHz实际用示波器测得为:13.05KHz左右波动
//所以CPU的实际MCLK大约为13.05/8=1.63KHz
for(;;)
{
P1OUT^=BIT6;//P1.6闪烁
delay_ms(1000);
}
}
6,如上面的程序所示,其中的延迟函数用那种方法,使用系统的延迟周期函数__delay_cycles(intn);可以达到比较精确的延迟,如下:
//more_
//1延时
//#defineCPU_F((double)16000000)//cpufrequency16000000
#defineCPU_F((double)12000000)//cpufrequency12000000
#definedelay_us(x)__delay_cycles((long)(CPU_F*(double)x/1000000.0))
#definedelay_ms(x)__delay_cycles((long)(CPU_F*(double)x/1000.0))
//2空函数
#definenop()_NOP();
7,系统上电后默认使用的是DCO时钟,DCO默认的频率大概为800KHz,但我用示波器观察的为1.086MHz左右,当DCO设置的过高时,用示波器可以看到波形不再是方波,而是类似于正弦波。
(三),定时器Timer_A
1,MSP430g2553具有两个16位的定时器:Timer0_ATimer1_A。分别具有三个捕捉/比较寄存器,具有输入捕捉,输出比较功能。可以产生定时中断,也可以产生PWM。
2,产生PWM,例子如下:
#include
voidTimer_A0_1_init()//TA0.1输出PWM
{
TACTL|=TASSEL_1+MC_1;//ACLK,增计数
CCTL1=OUTMOD_7;//输出模式为复位/置位
CCR0=328;//时钟频率为32768HZ,100HZ
//CCR1=164;//时钟频率为32768HZ,占空比CCR1/CCR0=50%
CCR1=109;//占空比CCR1/CCR0=1/3TA0.1由P1.2P1.6输出
}
voidTimer_A1_2_init()//TA1.2输出PWM
{
TA1CTL|=TASSEL_1+MC_1;//ACLK,增计数
TA1CCTL2=OUTMOD_7;//输出模式为复位/置位,注意CCTL2要写为TA1CCTL2
TA1CCR0=164;//时钟频率为32768HZ,波形32768/CCR0=199HZ
TA1CCR2=41;//占空比CCR2/CCR0=1/4,注意CCR2要写成TA1CCR2TA1.2由P2.4P2.5输出
}
voidTimer_A1_1_init()//TA1.1输出PWM
{
TA1CCTL1=OUTMOD_7;
TA1CCR1=123;//占空比CCR1/CCR0=3/4,注意CCR1要写成TA1CCR1TA1.1由P2.1P2.2输出
}
voidIO_init()
{
P1SEL|=BIT2+BIT6;
P1DIR|=BIT2+BIT6;//P1.2P1.6输出TA0.1OUT1
P2SEL|=BIT4+BIT5;
P2DIR|=BIT4+BIT5;//P2.4P2.5输出TA1.2OUT2
P2SEL|=BIT1+BIT2;
P2DIR|=BIT1+BIT2;//P2.1P2.2输出TA1.1OUT1
}
voidmain(void){
WDTCTL=WDTPW+WDTHOLD;
IO_init();
Timer_A0_1_init();
Timer_A1_2_init();
Timer_A1_1_init();
_BIS_SR(CPUOFF);//EnterLPM0进入低功耗模式0SMCLKON,ACLKON
}
3,Timer_A的捕获/比较寄存器
TAR寄存器是Timer_A的16位的计数寄存器。TACCRx是Timer_A的捕获/比较寄存器,当为捕获模式时:当捕获发生时,把TAR的值装载到TACCRx中。当为比较模式时:TACCRx中装的是要与TAR寄存器相比较的值。
4,捕获模式
捕获外部输入的信号的上升沿或下降沿或上升沿下降沿都捕捉,当捕捉发生时,把TAR的值装载到TACCRx中,同时也可以进入中断,执行相应的操作。这样利用捕捉上升沿或下降沿就可以计算外部输入信号的周期,得出频率。利用捕捉上升沿和下降沿可以得出输入信号的高电平或低电平的持续时间。也可以算出占空比。下面是一个例子,是Timer_A捕获初始化的程序:
voidtimer_init()//使用Timer1_A时要特别注意各个寄存器的写法,因为Timer0_A的寄存器都简写了,所以在写
//Timer1_A的寄存器时,要特别注意与Timer0_A的不同
{
P1SEL|=BIT2;//选择P12作为捕捉的输入端子Timer0_A
//TACCTL1|=CM_3+SCS+CAP+CCIE;//上下沿都触发捕捉,用于测脉宽,同步模式、时能中断CCI1A
TACCTL1|=CM_1+SCS+CAP+CCIE;//上升沿触发捕捉,同步模式、时能中断CCI1A
TACTL|=TASSEL1+MC_2;//选择SMCLK时钟作为计数时钟源,不分频增计数模式不行,必须连续计数模式
P2SEL|=BIT1;//选择P21作为捕捉的输入端子Timer1_A
//TA1CCTL1|=CM_3+SCS+CAP+CCIE;//上下沿都触发捕捉,用于测脉宽,同步模式、时能中断CCI1A
TA1CCTL1|=CM_1+SCS+CAP+CCIE;//上升沿触发捕捉,同步模式、时能中断CCI1A
TA1CTL|=TASSEL1+MC_2;//选择SMCLK时钟作为计数时钟源,不分频增计数模式不行,必须连续计数模式
}
相对应的中断函数如下:
#pragmavector=TIMER0_A1_VECTOR//Timer0_ACC1的中断向量
__interruptvoidTimer_A(void)
{
//CCI0A使用的捕捉比较寄存器是TA0CCR0,TA0CCR0单独分配给一个
//中断向量TIMER1_A0_VECTOR,所以进入中断后直接就是Timer0_ACC0产生的中断,不用经过类似
//下面的方法判断中断源了。
//Timer0_ACC1-4,TA0公用一个中断向量TIMER0_A1_VECTOR,所以进入了中断后还要用下面
//的方法进行判断是哪一个中断源产生的中断
switch(TAIV)//如果是Timer0_ACC1产生的中断
{
case2:
{
flag=1;
LPM1_EXIT;//退出低功耗模式
//_BIC_SR_IRQ(LPM1_bits);
//_bic_SR_register_on_exit(LPM1_bits);
break;
}
case4:break;
case10:break;
}
}
#pragmavector=TIMER1_A1_VECTOR//Timer1_ACC1的中断向量
__interruptvoidTimer_A1(void)
{
//P1OUT|=BIT0;//led调试用的
//LPM1_EXIT;//退出低功耗模式因为使用的是CCI0A使用的捕捉比较寄存器是TA1CCR0,TA1CCR0单独分配给一个
//中断向量TIMER1_A0_VECTOR,所以进入中断后直接就是Timer1_ACC0产生的中断,不用经过类似
//下面注释掉的方法判断。
//而Timer1_ACC1-4,TA1则公用一个中断向量TIMER1_A1_VECTOR,所以进入了中断后还要用下面
//的方法进行判断是哪一个中断源产生的中断
switch(TA1IV)//如果是Timer1_ACC1产生的中断
{
case2:
{
flag=2;
LPM1_EXIT;//退出低功耗模式
//_BIC_SR_IRQ(LPM1_bits);
//_bic_SR_register_on_exit(LPM1_bits);
break;
}
case4:break;
case10:break;
}
}
//如果要测量更低频率的信号的话,可以在中断中判断溢出中断发生的次数,这样就可以得到溢出的次数,从而可以测量更
//低频率的信号
5,Timer_A的计数模式
计数模式有:增计数模式,连续计数模式和增减计数模式。具体的各个模式的详解,参见用户指南。
6,定时器的定时中断
在使用定时器的定时中断时,要注意定时器计数模式的选择。在使用中断时,要注意中断向量的使用和中断源的判断,下面就举一个例子,注释的也较详细:
#include
unsignedintt=0;
voidmain(void)
{
WDTCTL=WDTPW+WDTHOLD;//StopWDT
P1DIR|=0x01;//P1.0output
CCTL0=CCIE;//CCTLx是捕获/比较控制寄存器interruptenabledCCIE=0x0010时能定时器A中断
CCR0=50000;//捕获/比较寄存器设置计数器CCR0的初值16位寄存器,最大值为65535
//默认SMCLK使用的是DCO,默认的DCO大约为800KHz,而CCR0=50000,所以中断产生的频率大约为16Hz
TACTL=TASSEL_2+MC_2;//SMCLK,contmode连续计数模式从0计到0FFFFh
//TACTL=TASSEL_2+MC_1;//SMCLK,upmode增计数模式从0计到CCR0
_BIS_SR(LPM0_bits+GIE);//EnterLPM0w/interrupt进入低功耗模式0,允许中断
}
//TimerA0interruptserviceroutine
#pragmavector=TIMER0_A0_VECTOR
__interruptvoidTimer_A(void)//CCIFG中断被响应后,该标志位自动清零
{
//P1OUT^=0x01;//ToggleP1.0
t++;
if(t==5)
{
P1OUT^=BIT0;//ToggleP1.0
t=0;
}
CCR0+=50000;//AddOffsettoCCR0增加CCR0偏移
//定时器总是从0开始往上计数,一直到计满再从0开始,在连续计数模式下,当定时器的值等于CCR0时,产生中断
//在中断中对CCR0增加50000,这样的话定时器从当前值到下一时刻再次等于CCR0时的间隔为50000,恒定
//这样产生中断的时间间隔就相等了
//所以在连续计数模式下,要想使中断的时间间隔一定,就要有CCR0+=n;这句话
//在中断中CCR0不需要从新赋值,区别于51
}
中断的使用注意情况:还是把举个例子吧:
#include
voidmain(void)
{
WDTCTL=WDTPW+WDTHOLD;//StopWDT
P1DIR|=0x01;//P1.0output
TACTL=TASSEL_2+MC_2+TAIE;//SMCLK,contmode,interruptTAIE允许定时器溢出中断
_BIS_SR(LPM0_bits+GIE);//EnterLPM0w/interruptGIE允许中断
}
//Timer_A3InterruptVector(TA0IV)handler
#pragmavector=TIMER0_A1_VECTOR
__interruptvoidTimer_A(void)
{
switch(TA0IV)//TAIV中断向量寄存器用于
{
case2:break;//CCR1notused捕获/比较器1
case4:break;//CCR2notused捕获/比较器2
case10:P1OUT^=0x01;//overflow定时器溢出
break;
}
}
7,注意:定时器Timer0_A的时钟可以选择为外接时钟输入TACLK(P10),这样当外接一个信号时,定时器Timer0_A就相当于一个计数器使用。这样就可以用Timer0_A接外接信号,Timer1_A接标准的时钟如32768Hz的晶振,就可以实现等精度测频了。其实Timer1_A的时钟也可以外接的,但是在g2553中没有这个外接管脚(P37),所以就只能选择正常的时钟了。
Timer0_A的外接时钟输入TACLK(P10)的设置如下:下面是我实现等精度测频时,两个定时器的初始化程序:
voidtimer0_init()
{
TACTL|=TASSEL_0+MC_2+TACLR;//选择TACLK时钟作为计数时钟源,不分频必须连续计数模式
P1SEL|=BIT0;//P10为Timer0_A的时钟TACLK输入,接外部待测信号,这样Timer0_A就当作计数器用
}
//Timer1_A采用ACLK作为时钟源计数,这样ACLK就相当于是标准信号,这样两个定时器相当于都工作在计数器方式,
//ACLK32768Hz作为标准信号,这样可以实现等精度测频
voidtimer1_init()
{
TA1CCTL0=CCIE;
TA1CCR0=32768;//1s定时
TA1CTL|=TASSEL_1+MC_2+TACLR;//选择ACLK时钟作为计数时钟源,不分频必须连续计数模式
}
8,用定时器和比较器可以实现DAC
使用定时器也可以实现串口通信
评论