一:端口配置
1:P1DIR 设置为1,相应管脚为输出。设置为0.相应管脚 为输入状态。
2:P1IE 设置为1,相应管脚具有中断功能。设置为0, 相应管脚没有中断功能。
3:P1IES 设置为1,选择下降沿触发方式,设置为0,选 择上升沿触发方式。
4:P1IFG P1端口的中断标志寄存器,如果P1端口当某 个管脚设置成中断管脚,当有中断触发时,想应比特为1 ; 如果没有中断触发,相应比特为0.
5: P1IN P1端口输入寄存器,在输入模式下,读取该寄 存器相应管脚上的数据。
6: P1OUT P1端口的输出去寄存器,在输出模式下,如 果该寄存器相应比特设置为1时,相应管脚输出高电平; 如果该寄存器相应比特为0时,相应管脚输出低电平。
7: P1SEL寄存器 P1端口功能选择寄存器,该寄存器主要 控制P1端口的I/O管脚作为一般I/O还是外围模块的功能 端口,该寄存器的相应比特为1时候,相应管脚为外围功能 模块,当该寄存器为0时,相应管脚为一般I/O管脚。
二 LaunchPad 写程序的必要头文件和格式:
/*===================================================
#include"msp430g2553.h"
Void main()
{
WDTCTL=WDTPW+WDTHOLD;//关闭看门狗。
//WDTPW 是看门狗的密码,写错了会导致系统复位。
**(程序)
}
=====================================================*/
三 点亮LED
仅仅是对IO 口的输入输出操作。与51 很不相同。
P1DIR|=BIT0;//设置P1.0 为输出方向。===P1DIR|=0x01;
//{P1DIR=BIT0;是设置P1.0 为输出,其他全部为输入方向。}
//注意:LaunchPad 中很多操作是与,或,非等操作组成,时刻注意。
P1OUT|=BIT0;//这条指令就是设置P1.0 输出为高电平。
这样,就点亮了LED(接在P1.0 上的LED);
具体程序:
#include"msp430g2553.h"
Void main()
{
WDTCTL=WDTPW+WDTHOLD;
P1DIR|=BIT0;
P1OUT|=BIT0;
While(1);
}
四: 闪烁LED
LaunchPad 上面自带有2 个LED,一个接在P1.0 上,一个接在P1.6 上。
我们用2 个交替闪烁。
#include"msp430g2553.h"
Void main();第一个字母大写
{
WDTCTL=WDTPW+WDTHOLD;
P1DIR|=BIT0+BIT6;//设置P1.0 和P1.6 为输出
P1OUT|=BIT0;//线让LED0 亮。
While(1)
{
Unsigned int i=50000;
While(i--);
P1OUT^=0x41;//对P1.0 和P1.6 取反,所以LED0 和LED1 会交替闪烁。
}
}
五:中断系统
LaunchPad 的中断系统功能相当强大,51 只有5 个中断源,2 个定时,2 个外部,
一个串行口。但是LaunchPad 的中断源几乎是所有的引脚和所用的定时器。
在这里,最重要的就是中断向量的判断了。
定时器一般都是:
vector=TIMER0_A0_VECTOR
vector=TIMER0_A1_VECTOR
vector=TIMER1_A0_VECTOR
vector=TIMER1_A1_VECTOR
引脚中断的向量:
vector=PORT1_VECTOR; P1 口的中断向量。
判断是哪个引脚的话,有2 种办法:
举例子:P1.3 和P1.4 都是中断的输入引脚。现在进了中断,我如何判断是那个引
脚引起的呢?
第一种方法:
vector=PORT1_VECTOR
__interruput void Port1 (void)
{
If(P1IFG&BIT3);判断的是P1.3 产生的中断。
{
;要执行的函数。
P1IFG=0x00;//清0 中断标志位。
}
If(P1IFG&BIT4)
{
;P1.4 产生的中端,执行相应的函数。
P1IFG=0x00;
}
}
第二种方法:
vector=PORT1_VECTOR
__interruput void Port1 (void)
{
P1IFG&=BIT3+BIT4;//因为只用到了P1.0 和P1.4,其他的中断标志全部清零。
(或者:P1IFG=P1IFG&0x18)
Switch(P1IFG)
{
Case 0x08: vector=3;break; //P1.3 产生的中断
Case 0x10:vector=4;break;//P1.4 产生的中断
}
}
切记:进入中断函数后要做的第一件事是,清除中断标志。
例程:
#include
Void main(void)
{
WDTCTL=WDTPW+WDTHOLD;
P1DIR|=BIT0; // 设置P1.0输出
P1IES |=BIT3; // 设置从高到底跳变触发
P1IFG&=~BIT3// 清除中断标志位
P1IE |=BIT3 // 使P1.3能中断
_BIS_SR(LPM_bits+GIE);// 启动LMP4节能模式
}
#proagma vector=PORT1_VECTOR
_interrupt void Port_1(void)
{
If(P1IFG&BIT3)
{
P1OUT^=BIT0;
P1IFG&=~BIT3;
}
}
程序一开始
1 WDTCTL=WDTPW+WDTHOLD; HOLD住看门狗
2 P1DIR|=BIT0;将P1.0 设置为输出口。lunchpad上的P1.O接有一个LED.
3. 接下来到P1IES |= BIT3; 在上一节中已经介绍了,P1IES 寄存器是中断沿选择寄存器。这里是选择位下降沿触发中断。
4. P1IFG &= ~BIT3; 为清除中断标志,保证程序正常运行,当然此句可以不写,这里只是做为例子
5. P1IE |= BIT3; 在上一节中已经介绍了,P1IE寄存器是使能中断事件发生的寄存器。
- _BIS_SR(LPM4_bits + GIE);这里使程序进入最低功耗(LPM4)状态。靠中断来触发唤醒CPU,在文章开始已经介绍有,假如在中断函数中没有写有退出低功耗状态的指令,程序会在进入低功耗的下一句中卡死,不再运行下去。另外_BIS_SR(GIE); 为打开总中断的意思。
- 接下来到中断函数的编写。以此为例,详细介绍中断函数的编写。如上所示,中断函数编写的规则为
#pragma vector= 中断向量源 __interrupt void 函数名(void) |
|
摁住“Ctrl + 左键”点击PORT1_VECTOR即可查看到所有的“中断向量”
在上面的中断向量中,加黑的位中断向量源,写入中断函数编写语法规则里面即可。而函数名则可以任意编写。比如我要编写一个有定时器1,CCR0寄存器溢出产生的中断,则可以这样编写
#pragma vector= TIMER1_A0_VECTOR __interrupt void T1A0Int(void) { //程序代码。。。 } |
假如是多IO输入中断,则如下所写。
vector=PORT1_VECTOR __interrupt void Port1() { |
|
//以下为参考处理程序,不使用的端口应当删除其对于中断源的判断。 if((P1IFG&BIT0) == BIT0) { P2OUT&=~BIT0; //处理P1IN.0中断 P1IFG &= ~BIT0; //清除中断标志 //以下填充用户代码 } else if((P1IFG&BIT1) ==BIT1) { P2OUT&=~BIT1; //处理P1IN.1中断 P1IFG &= ~BIT1; //清除中断标志 //以下填充用户代码 } else if((P1IFG&BIT2) ==BIT2) { P2OUT&=~BIT2; //处理P1IN.2中断 P1IFG &= ~BIT2; //清除中断标志 //以下填充用户代码 } else if((P1IFG&BIT3) ==BIT3) { //处理P1IN.3中断 P1IFG &= ~BIT3; //清除中断标志 //以下填充用户代码 } else if((P1IFG&BIT4) ==BIT4) { P2OUT&=~BIT4; //处理P1IN.4中断 P1IFG &= ~BIT4; //清除中断标志 //以下填充用户代码 } else if((P1IFG&BIT5) ==BIT5) { //处理P1IN.5中断 P1IFG &= ~BIT5; //清除中断标志 //以下填充用户代码 } else if((P1IFG&BIT6) ==BIT6) { //处理P1IN.6中断 P1IFG &= ~BIT6; //清除中断标志 //以下填充用户代码 } else { //处理P1IN.7中断 P1IFG &= ~BIT7; //清除中断标志 //以下填充用户代码 } LPM3_EXIT; //退出中断后退出低功耗模式。若退出中断后要保留低功耗模式,将本句屏蔽 } |
|
六 定时器模块
文先,介绍几个英文缩写的意思以及一些注意的地方。
1. Timer0/1 定时器0/1,在User's Guide中用的是TimerA/B,所指的也是Timer0/1 。G2553Datasheet中用的是Timer0/1 ,本文以G2553Datasheet为准。全文以Timer0为例,Timer1类同。
2. TAxR(x = 0/1)定时器x对应的计数器,这是一个只读寄存器。硬件自动驱动计数。
- EQUy(y = 0/1/2)计数事件发生寄存器,当TAxR = TAxCCRy时EQUy置1
定时器简介
MSPG2553共有两个定时器,Timer0、Timer1,他们都是十六位的定时、计数器,内含三个捕获、比较寄存器。两个定时器均支持多个捕获、PWM输出、间歇性计时,定时器包含多个中断源,可以是计数溢出中断、捕获中断等等。
定时器包含:
同步十六位定时,计数器运行模式。
时钟源从MCLK、SMCLK、ACLK任意选择
三个比较,捕获寄存器。
中断向量寄存器能快速解码的所有定时器中断
Timer0组成框图
下面简要介绍一下该硬件框图的意思,从左上角看,首先是一个时钟源选择寄存器TASSELx,通过该寄存器选择定时器的时钟源,选择了时钟源后有一个分频器Divider,相应的设置寄存器是IDx,再过来就到一个定时器的核心部分,一个16位的定时器TAR。其右侧有一个定时器的计数模块,MCx寄存器用来设置计数模式。接下来,TAR正下方有三个横线,右侧标有CCR0、CCR1、CCR2,意思是CCR1、CCR0的框图和下方CCR2的框图是一样的。此处省略不写。在CCR中,左上角为一个捕获源选择寄存器。可以从CCI2A、CCI2B、GND或者VCC选择捕获源,选择捕获源后有一个选择捕获模式寄存器Capture Mode,然后过来有一个捕获溢出状态寄存器COV,SCS同步/异步捕获模式选择位,然后连接到捕获比较寄存器。下方为模式选择寄存器,具体设置可以查看相应的寄存器设置。
这里仅是大概介绍一下Timer0的寄存器,具体的设置使用还看参考相应的寄存器并结合例程慢慢学习理解。
定时器运行方式
下面简要重点介绍定时器计数模块的四种模式以及7种输出模式。
Timer0有一个在不断计数的只读寄存器TA0R。计数器的计数模式共有四种,
停止模式(Stop mode)、连续增计数模式(Up mode)、递增计数模式(continuous mode)、增减计数模式(Up/down mode)。由上图可知,这四种模式可以通过MCx寄存器进行设置。
以上四种模式可以由下图可以很好理解。
1. Stop模式计数器不工作。
2. 连续计数模式为计数器从零开始连续增计数一直到0xFFFF即65535,然后又重新从零开始计数。
3. 递增计数模式与连续计数模式仅有一点点区别,递增模式为计数器连续增加到TA0CCR0(即图中的CCR0)中的值后又重新从零开始计数。TA0CCR0的值时可以在程序中直接赋值的。
4. 递增递减模式也很好理解,计数器从零开始计数到CCR0后又自动减数,到零后又增计数,就像三角波一样。
每一个捕获比较模块都有一个输出单元,这个输出单元专门用来产生以下如PWM的波形信号,每一个输出单元都可以通过配置OUTMOD寄存器的值来设定八种信号输出模式,
接下来再介绍一下定时器的捕获/比较功能,具体应查看技术手册。
捕获模式
捕获模式可以用来速度计算或时间测量.CCIxA ,CCIxB的捕获源可以连接到外部引脚或者内部信号,可以设定CCIDx,CMx,位让寄存器捕获上升,下降,或者两个信号的边缘.输入信号的电平可以通过CCI位的读取.
当设置寄存器CAP=1时,使能捕获模块.
比较模式
比较模式设置CAP = 0的情况向,比较模式用于产生PWM信号。或者在指定时间里输出终端信号,当TAxR计数到TACCRx时
建立起CCIFG位
中断事件发生标志位EQUx=1
EQUx的隐含改变将影响输出模式
输入信号CCI被锁上SCCI
2. 递增计数模式下的输出
*
**************************************************************** /
/ ******************************************************************
* TACTL寄存器,Timer_A 控制寄存器
* TASSEL_x:TA时钟源选择寄存器
* 00 TACLK
* 01 ACLK
* 10 SMCLK
* 11 INCLK
* IDx: 时钟源分频寄存器。为输入时钟分频选择
* 00 /1
* 01 /2
* 10 /4
* 11 /8
* * ************************************************************** /
* 定时计数模块 =四中模式+7种输出方式
/ *****************************************************************
*
* MCx: 计数模式寄存器 模式控制,当TA不用于节省功耗时,将MCx=00h
* 00 停止模式:定时器停止
* 01 增模式 :定时器计数到TACCR0
* 10 连续模式:定时器计数到0FFFFh
* 11 增减模式:定时器计数到TACCR0 然后减到000h
*
* TACLR: 定时器清零。置位时会复位TAR,时钟分频和计数方向。
* TACLR位会自动复位并读出值为零。
*
* TAIE: TA中断允许。改为允许TAIFG中断请求
* 0 中断禁止
* 1 中断允许
* TAIFG: TA中断标志位
* 0 无中断挂起
* 1 中断挂起
* ************************************************************** /
/ *****************************************************************
*
*
* TACCTLx,捕获比较控制寄存器
* CMx: 捕获模式
* 00 不捕获
* 01 上升沿捕获
* 10 下降沿捕获
* 11 上升和下降同时捕
* CCISx: 捕获比较选择,改为选择TACCRx的输入信号
* 00 CCIxA
* 01 CCIxB
* 10 GND
* 11 VCC
* SCS: 同步捕获源,改为用于将捕获通信和同步时钟
* 0 异步捕获
* 1 同步捕获
* SCCI:同步的捕获/比较输入,所选择的输入信号由EQUx信号所存,
* 并可通过该位读取
* CAP: 捕获模式
* 0 比较模式
* 1 捕获模式
*OUTMODx:输出模式位,对TACCR0无效
* 000 OUT 位的值
* 001 置位
* 010 翻转/复位
* 011 复位/复位
* 100 翻转
* 101 复位
* 110 翻转/置位
* 111 复位/置位
* CCIE: 捕获比较中断允许位
* 0 中断禁止
* 1 中断允许
* CCI: 捕获比较输入
* OUT: 对于输出模式0,该位直接控制输出状态
* COV: 捕获溢出位。该位表示一个捕获溢出发生,由软件复位
* CCIFG:捕获比较中断标志位
* 0 没有中断挂起
* 1 有中断挂起
* ***************************************************************/
#include
unsigned int A=10, B=20 ; void main (void)
{
WDTCTL = WDTPW + WDTHOLD;
TACTL|=TASSEL_2+TACLR+MC_1+ID_3; // SMCLK时钟 ;定时器清零;增模式 8分频
CCTL0=CCIE; //捕获中断允许
CCR0=B; //TACCR0 装载值
CCTL1=OUTMOD_7; //输出模式复位
CCR1=A;
// BCSCTL1
P1DIR=BIT6;
P1SEL=BIT6;
// _EINT(); //使能所有中断
while(1);
}
/*#pragma vector=TIMER0_A0_VECTOR
__interrupt void ta0_isr(void)
{
unsigned int i;
for(i=0;i
pwm=i;
}*/
定时器中断
这里以定时器0为例,定时器1同。
定时器的中断可有定时器TA0CCR0溢出产生,也可由TA0CCRx(x =1/2)溢出产生、捕获/比较事件发生引起的中断,前者有一个专用的中断向量,TIMER0_A0_VECTOR,而后者用的TIMER0_A1_VECTOR,至于是哪一个中断时间发生,还要根据标志位来判断。
下面以官方例程LaunchPad Lab2为例介绍定时器A的操作。
例一
#include void main(void) { WDTCTL = WDTPW + WDTHOLD; // Stop watchdog timer if (CALBC1_1MHZ ==0xFF || CALDCO_1MHZ == 0xFF) { while(1); // If calibration constants erased, trap CPU!! } BCSCTL1 = CALBC1_1MHZ; // Set range DCOCTL = CALDCO_1MHZ; // Set DCO step + modulation BCSCTL3 |= LFXT1S_2; // LFXT1 = VLO P1DIR = 0x40; // P1.6 output (green LED) P1OUT = 0; // LED off IFG1 &= ~OFIFG; // Clear OSCFault flag BCSCTL1 |= DIVA_3; // ACLK = VLO/8 BCSCTL2 |= SELM_3 + DIVM_3 + DIVS_3; // MCLK = DCO/8, SMCLK = DCO/8 // Configure TimerA TACTL = TASSEL_1 + MC_1 + TAIE; // Source: ACLK, UP mode CCR0 = 5100; //Timer count 5100 CCR1 = 2000; //Timer count 100 CCTL0 = CCIE; //CCR0 interrupt enabled CCTL1 = CCIE; //CCR1 interrupt enabled _BIS_SR(GIE); for(;;); } // Timer A0 interrupt service routine #pragma vector=TIMER0_A0_VECTOR __interrupt void Timer_A0 (void) { P1OUT |= BIT6; // P1.6 output High } // Timer A1 Interrupt Vector (TA0IV) handler #pragma vector=TIMER0_A1_VECTOR __interrupt void Timer_A1(void) { switch( TA0IV ) { case 2: P1OUT &= ~BIT6; // P1.6 output Low |
|
程序一开始关闭看门狗,if语句作为时钟校准的范例,可以删去。BCSCTL3 |= LFXT1S_2; 选择超低频时钟源。然后设定输出口,清除中断标志,时钟源分频设定,接下里组建定时器A,即定时器0,在详细介绍代码之前,首先看头文件关于定时器相关寄存器的设定。
例二
下面举一个无需中断服务函数、硬件自动实现产生两路PWM的例子。
代码很简单,初始化一下即可。
#include void Set_TimerB_PWM(void) { //使用系统初始化时的默认时钟1MHz,定时器B专门用于产生PWM 波形。 TA1CTL = TASSEL_2 + MC_1 + TACLR;//使用系统次主机SMCKL、增计数模式、清楚定时器B时钟 TA1CCR0 = 5001 - 1;//在1MHz的主频率下,1*10^6/5000=200Hz的中断频率 TA1CCR1 = 3751 - 2;//当寄存器TACCR1的值小于3750时,输出口保持高电平。5000*3/4=3750,此路产生3:1的PWM波形。 TA1CCR2 = 1251 - 2;//当寄存器TACCR1的值小于1250时,输出口保持高电平。5000*1/4=1250,产生1:3的PWM波 TA1CCTL1 = OUTMOD_7;//输出模式7,计数器计数到5000计数器自动置位,无需中断服务子函数。 TA1CCTL2 = OUTMOD_7;//输出模式7,计数器计数到5000计数器自动置位,无需中断服务子函数。 P2SEL |= BIT1 + BIT5;//只有这两路可选(为什么是这两路?在G2553Datasheet中有特别指明)。做第二功能使用(PWM输出) P1DIR |= BIT6; //电机控制口CTL//这里与本例无关 P1OUT &= ~BIT6; //start with 0 -->IN2,4为1,灭//这里与本例无关 } |
初始化时钟后直接调用该函数即可。
我这里使用的是定时器B(即Timer1)。详细的介绍见以上备注。
七 时钟配置
时钟源:
外部晶体振荡器
超低频率振荡器(VLO)
数字控制振荡器(DCO)
时钟信号:
ACLK :Auxiliary clock.辅助时钟。
MCLK :Master clock主时钟。
SMCLK :Second Master clock次主机时钟。
内部晶体振荡器产生时钟后经过DCOR、SCG0、RSELx、DCO等各个寄存器为MCLK、SMCLK提供时钟源
内部时钟还有一个超低频率内置晶体振荡器(VLO)在上图的最上方。可作为低频时钟源。
另外一个部分是系统的外部时钟,外部晶振经过LFXT等各个寄存器设置后可以为MCLK、ACLK提供时钟源。
上图中SELM、SELS为时钟源选择寄存器。
上图中DIVA、DIVM、DIVS都是分频器,时钟源可以经过1/2/4/8分频后为CPU提供时钟,以降低功耗。
ADC10的时钟部分框图
Timer_A的结构框图
Timer_A不能选择MCLK作为Timer_A的时钟
CPU是处理器的核心部分,它使用的时钟始终是MCLK。
上电后,系统默认使用的主系统时钟MCLK和子系统时钟SMCLK是同为DCOCLK产生的1MHz时钟,而辅助时钟ACLK则为内部VLOCLK产生的12KHz时钟
MSP430低功耗模式
单片机中,功耗最低的单片机要MSP430单片机,这是做手持设备最优选择,MSP430中,用到5种低功耗,LPM0,LPM1,LPM2,LPM3,LPM4,这五种低功耗各种解释如下 :
LPM0:CPU停止工作,MCLK时钟停止,SMCLK、ACLK时钟还在工作。
LPM1:CPU停止工作,MCLK时钟停止,在活动模式如果DCO没有作为MCLK和SMCLK时钟时,则直流发生器被禁止,否则就保持活动状态,SMCLK、ACLK时钟依然还在工作。
LPM2:CPU停止工作,MCLK、SMCLK时钟停止工作,如果DCO没有作为MCLK、SMCLK,自动被禁止直流发生器保持有效,ACLK还处于工作中。
LPM3:CPU停止工作,MCLK、SMCLK时钟停止工作,DCO时钟也停止工作,仅ACLK时钟还处于工作状态。
LPM4:CPU停止工作,MCLK、SMCLK时钟停止工作,DCO时钟也停止工作,ACLK也停止工作。此时功耗最低。
一般情况下,处理器进入低功耗模式后,由中断来唤醒,外部中断或内部中断。
如果想进入低功耗1,则程序可以为:_BIS_SR(LPM1_bits + GIE);退出低功耗1,则程序可以为:LPM1_EXIT; 进入其他低功耗和退出低功耗一样。
低功耗执行的一个过程:程序从main函数入口开始执行程序,当遇到进入低功耗程序时,如:_BIS_SR(LPM1_bits + GIE);此时相当于下面的程序处于停止状态不再执行,当有一个中断来到,则会进入中断处理程序,自动退出低功耗,如果在中断中没有没有退出低功耗,当中断服务程序执行完成后,又会重新进入低功耗。