MSP430 工作筆記一(轉(zhuǎn))
(一),IO口模塊,
本文引用地址:http://2s4d.com/article/201611/316298.htm1,我們所用的MSP430G2553有兩組IO口,P1和P2。
2,IO口的寄存器有:方向選擇寄存器PxDIR,輸出寄存器PxOUT,輸入寄存器PxIN,IO口內(nèi)部上拉或下拉電阻使能寄存器PxREN,IO口功能選擇寄存器PxSEL和PxSEL2,IO口中斷使能寄存器PxIE,中斷沿選擇寄存器PxIES,IO口中斷標志寄存器PxIFG。
3,所有的IO都帶有中斷,其中所有的P1口公用一個中斷向量,所有的P2口公用一個中斷向量。所以在使用中斷時,當進入中斷后,還要判斷到底是哪一個IO口產(chǎn)生的中斷,判斷方法可以是判斷各個IO口的電平。
4,中斷標志PxIFG需要軟件清除,也可以用軟件置位,從而用軟件觸發(fā)一個中斷。
注意:在設(shè)置PxIESx時根據(jù)PxINx有可能會引起相應(yīng)的PxIFGx置位(具體的情況見用戶指南),所以在初始化完IO口中斷以后,正式使用IO中斷前要先將對應(yīng)的PxIFGx清零。程序如下:
voidIO_interrupt_init()//IO中斷初始化函數(shù)
{
P1REN|=BIT4+BIT5+BIT6+BIT7;//pullup內(nèi)部上拉電阻使能
//使用中斷時,使能內(nèi)部的上拉電阻這樣當該腳懸空是,電平不會跳變,防止懸空時電平跳變不停的觸發(fā)中斷
P1OUT=BIT4+BIT5+BIT6+BIT7;//當引腳上的上拉或下拉電阻使能時,PxOUT選擇是上拉還是下來
//0:下拉,1:上拉
P1IE|=BIT4+BIT5+BIT6+BIT7;//interruptenabledP13中斷使能
P1IES|=BIT4+BIT5+BIT6+BIT7;//Hi/loedge下降沿中斷
//P1IES&=~BIT3;//上升沿觸發(fā)中斷
P1IFG&=~(BIT4+BIT5+BIT6+BIT7);//中斷標志位清零
}
5,PxOUT:如果引腳選擇了內(nèi)部的上拉或下拉電阻使能,則PxOUT設(shè)定電阻是上拉還是下拉,0:下拉,1:上拉
6,當IO口不用時,最好不要設(shè)為輸入,且為浮動狀態(tài)(這是IO口的默認狀態(tài)),因為當輸入為浮動時,輸入電壓有可能會在VIL和VIH之間,這樣會產(chǎn)生擊穿電流。所以不用的IO口可以設(shè)為輸出狀態(tài),或設(shè)為輸入狀態(tài)但通過外圍電路接至VCC或GND,或接一個上拉/下拉電阻。
7,當使用msp430g2553的IO口時要注意,因為g2553的IO口寄存器的操作,不像51,它不能單獨針對某一位進行操作,必須對整個寄存器進行操作。所以就不像51,g2553不可以定義bit型的數(shù)據(jù)。所以在使用msp的IO口時要注意對需要位的操作,而不要影響其他無關(guān)的位,可以用|&^等按位操作的符號。在使用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,默認是自動設(shè)為了晶振管腳功能,但是當想把它們用為普通的IO時,也可以,設(shè)置對應(yīng)的SEL設(shè)為普通的IO即可,如下:
P2DIR|=BIT6+BIT7;//把P26和P27配置為普通IO并為輸出腳默認為晶振的輸入和輸出引腳作為dac0832的
P2SEL&=~(BIT6+BIT7);//cs和wr控制端
P2SEL2&=~(BIT6+BIT7);
(二),時鐘系統(tǒng)
1,msp430能做到超低功耗,合理的時鐘模塊是功不可沒的。但是功能強大的時鐘模塊設(shè)置起來也相對復雜一些。
2,msp430的時鐘源有:
(1),外接低頻晶振LFXT1CLK:低頻模式接手表晶體32768Hz,高頻模式450KHz~8MHz;
(2),外接高速晶振XT2CLK:8MHz;
(3),內(nèi)部數(shù)字控制振蕩器DCO:是一個可控的RC振蕩器,頻率在0~16MHz;
(4),超低功耗低頻振蕩器VLO:不可控,4~20KHz典型值為12KHz;
3,時鐘模塊:430的時鐘模塊有MCLKSMCLKACLK:
(1),主系統(tǒng)時鐘MCLK:提供給MSP430的CPU時鐘??梢詠碜訪FXT1CLKXT2CLKDCOVLO可選,默認為DCO。
(2),子系統(tǒng)時鐘SMCLK:提供給高速外設(shè)??梢詠碜訪FXT1CLKXT2CLKDCOVLO可選,默認為DCO。
(3),輔助系統(tǒng)時鐘ACLK:提供給低速外設(shè)??蓙碜訪FXT1CLKVLO。
4,內(nèi)部的振蕩器DCO和VLO提供的時鐘頻率不是很精確,隨外部環(huán)境變化較大。
DCO默認的頻率大概為800KHz,但我用示波器觀察的為1.086MHz左右,當DCO設(shè)置的過高時,用示波器可以看到波形不再是方波,而是類似于正弦波。DCO可以用CCS提供的宏定義進行相對比較精確的設(shè)置,如下:
DCOCTL=CALDCO_12MHZ;//DCO設(shè)為12MHz這種方法設(shè)DCO頻率比較精確,實際測得為12.08MHz左右正弦波
BCSCTL1=CALBC1_12MHZ;
用這種方法可以設(shè)置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可以很大程度地降低系統(tǒng)功耗,下面的例子是設(shè)置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
//因為前面已經(jīng)選擇了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,如上面的程序所示,其中的延遲函數(shù)用那種方法,使用系統(tǒng)的延遲周期函數(shù)__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空函數(shù)
#definenop()_NOP();
7,系統(tǒng)上電后默認使用的是DCO時鐘,DCO默認的頻率大概為800KHz,但我用示波器觀察的為1.086MHz左右,當DCO設(shè)置的過高時,用示波器可以看到波形不再是方波,而是類似于正弦波。
(三),定時器Timer_A
1,MSP430g2553具有兩個16位的定時器:Timer0_ATimer1_A。分別具有三個捕捉/比較寄存器,具有輸入捕捉,輸出比較功能??梢援a(chǎn)生定時中斷,也可以產(chǎn)生PWM。
2,產(chǎn)生PWM,例子如下:
#include
voidTimer_A0_1_init()//TA0.1輸出PWM
{
TACTL|=TASSEL_1+MC_1;//ACLK,增計數(shù)
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,增計數(shù)
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位的計數(shù)寄存器。TACCRx是Timer_A的捕獲/比較寄存器,當為捕獲模式時:當捕獲發(fā)生時,把TAR的值裝載到TACCRx中。當為比較模式時:TACCRx中裝的是要與TAR寄存器相比較的值。
4,捕獲模式
捕獲外部輸入的信號的上升沿或下降沿或上升沿下降沿都捕捉,當捕捉發(fā)生時,把TAR的值裝載到TACCRx中,同時也可以進入中斷,執(zhí)行相應(yīng)的操作。這樣利用捕捉上升沿或下降沿就可以計算外部輸入信號的周期,得出頻率。利用捕捉上升沿和下降沿可以得出輸入信號的高電平或低電平的持續(xù)時間。也可以算出占空比。下面是一個例子,是Timer_A捕獲初始化的程序:
voidtimer_init()//使用Timer1_A時要特別注意各個寄存器的寫法,因為Timer0_A的寄存器都簡寫了,所以在寫
//Timer1_A的寄存器時,要特別注意與Timer0_A的不同
{
P1SEL|=BIT2;//選擇P12作為捕捉的輸入端子Timer0_A
//TACCTL1|=CM_3+SCS+CAP+CCIE;//上下沿都觸發(fā)捕捉,用于測脈寬,同步模式、時能中斷CCI1A
TACCTL1|=CM_1+SCS+CAP+CCIE;//上升沿觸發(fā)捕捉,同步模式、時能中斷CCI1A
TACTL|=TASSEL1+MC_2;//選擇SMCLK時鐘作為計數(shù)時鐘源,不分頻增計數(shù)模式不行,必須連續(xù)計數(shù)模式
P2SEL|=BIT1;//選擇P21作為捕捉的輸入端子Timer1_A
//TA1CCTL1|=CM_3+SCS+CAP+CCIE;//上下沿都觸發(fā)捕捉,用于測脈寬,同步模式、時能中斷CCI1A
TA1CCTL1|=CM_1+SCS+CAP+CCIE;//上升沿觸發(fā)捕捉,同步模式、時能中斷CCI1A
TA1CTL|=TASSEL1+MC_2;//選擇SMCLK時鐘作為計數(shù)時鐘源,不分頻增計數(shù)模式不行,必須連續(xù)計數(shù)模式
}
相對應(yīng)的中斷函數(shù)如下:
#pragmavector=TIMER0_A1_VECTOR//Timer0_ACC1的中斷向量
__interruptvoidTimer_A(void)
{
//CCI0A使用的捕捉比較寄存器是TA0CCR0,TA0CCR0單獨分配給一個
//中斷向量TIMER1_A0_VECTOR,所以進入中斷后直接就是Timer0_ACC0產(chǎn)生的中斷,不用經(jīng)過類似
//下面的方法判斷中斷源了。
//Timer0_ACC1-4,TA0公用一個中斷向量TIMER0_A1_VECTOR,所以進入了中斷后還要用下面
//的方法進行判斷是哪一個中斷源產(chǎn)生的中斷
switch(TAIV)//如果是Timer0_ACC1產(chǎn)生的中斷
{
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調(diào)試用的
//LPM1_EXIT;//退出低功耗模式因為使用的是CCI0A使用的捕捉比較寄存器是TA1CCR0,TA1CCR0單獨分配給一個
//中斷向量TIMER1_A0_VECTOR,所以進入中斷后直接就是Timer1_ACC0產(chǎn)生的中斷,不用經(jīng)過類似
//下面注釋掉的方法判斷。
//而Timer1_ACC1-4,TA1則公用一個中斷向量TIMER1_A1_VECTOR,所以進入了中斷后還要用下面
//的方法進行判斷是哪一個中斷源產(chǎn)生的中斷
switch(TA1IV)//如果是Timer1_ACC1產(chǎn)生的中斷
{
case2:
{
flag=2;
LPM1_EXIT;//退出低功耗模式
//_BIC_SR_IRQ(LPM1_bits);
//_bic_SR_register_on_exit(LPM1_bits);
break;
}
case4:break;
case10:break;
}
}
//如果要測量更低頻率的信號的話,可以在中斷中判斷溢出中斷發(fā)生的次數(shù),這樣就可以得到溢出的次數(shù),從而可以測量更
//低頻率的信號
5,Timer_A的計數(shù)模式
計數(shù)模式有:增計數(shù)模式,連續(xù)計數(shù)模式和增減計數(shù)模式。具體的各個模式的詳解,參見用戶指南。
6,定時器的定時中斷
在使用定時器的定時中斷時,要注意定時器計數(shù)模式的選擇。在使用中斷時,要注意中斷向量的使用和中斷源的判斷,下面就舉一個例子,注釋的也較詳細:
#include
unsignedintt=0;
voidmain(void)
{
WDTCTL=WDTPW+WDTHOLD;//StopWDT
P1DIR|=0x01;//P1.0output
CCTL0=CCIE;//CCTLx是捕獲/比較控制寄存器interruptenabledCCIE=0x0010時能定時器A中斷
CCR0=50000;//捕獲/比較寄存器設(shè)置計數(shù)器CCR0的初值16位寄存器,最大值為65535
//默認SMCLK使用的是DCO,默認的DCO大約為800KHz,而CCR0=50000,所以中斷產(chǎn)生的頻率大約為16Hz
TACTL=TASSEL_2+MC_2;//SMCLK,contmode連續(xù)計數(shù)模式從0計到0FFFFh
//TACTL=TASSEL_2+MC_1;//SMCLK,upmode增計數(shù)模式從0計到CCR0
_BIS_SR(LPM0_bits+GIE);//EnterLPM0w/interrupt進入低功耗模式0,允許中斷
}
//TimerA0interruptserviceroutine
#pragmavector=TIMER0_A0_VECTOR
__interruptvoidTimer_A(void)//CCIFG中斷被響應(yīng)后,該標志位自動清零
{
//P1OUT^=0x01;//ToggleP1.0
t++;
if(t==5)
{
P1OUT^=BIT0;//ToggleP1.0
t=0;
}
CCR0+=50000;//AddOffsettoCCR0增加CCR0偏移
//定時器總是從0開始往上計數(shù),一直到計滿再從0開始,在連續(xù)計數(shù)模式下,當定時器的值等于CCR0時,產(chǎn)生中斷
//在中斷中對CCR0增加50000,這樣的話定時器從當前值到下一時刻再次等于CCR0時的間隔為50000,恒定
//這樣產(chǎn)生中斷的時間間隔就相等了
//所以在連續(xù)計數(shù)模式下,要想使中斷的時間間隔一定,就要有CCR0+=n;這句話
//在中斷中CCR0不需要從新賦值,區(qū)別于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就相當于一個計數(shù)器使用。這樣就可以用Timer0_A接外接信號,Timer1_A接標準的時鐘如32768Hz的晶振,就可以實現(xiàn)等精度測頻了。其實Timer1_A的時鐘也可以外接的,但是在g2553中沒有這個外接管腳(P37),所以就只能選擇正常的時鐘了。
Timer0_A的外接時鐘輸入TACLK(P10)的設(shè)置如下:下面是我實現(xiàn)等精度測頻時,兩個定時器的初始化程序:
voidtimer0_init()
{
TACTL|=TASSEL_0+MC_2+TACLR;//選擇TACLK時鐘作為計數(shù)時鐘源,不分頻必須連續(xù)計數(shù)模式
P1SEL|=BIT0;//P10為Timer0_A的時鐘TACLK輸入,接外部待測信號,這樣Timer0_A就當作計數(shù)器用
}
//Timer1_A采用ACLK作為時鐘源計數(shù),這樣ACLK就相當于是標準信號,這樣兩個定時器相當于都工作在計數(shù)器方式,
//ACLK32768Hz作為標準信號,這樣可以實現(xiàn)等精度測頻
voidtimer1_init()
{
TA1CCTL0=CCIE;
TA1CCR0=32768;//1s定時
TA1CTL|=TASSEL_1+MC_2+TACLR;//選擇ACLK時鐘作為計數(shù)時鐘源,不分頻必須連續(xù)計數(shù)模式
}
8,用定時器和比較器可以實現(xiàn)DAC
使用定時器也可以實現(xiàn)串口通信
評論