写在前边:
这次我想直接总结一下中断和定时器顺便简单讲述一下时钟,我在中间编程的时候也遇见了许多问题在网上看了好多关于中断和定时器中断的资料,自己也算是明白一点 然后在这里简单总结一下;
一 MSP430G2 时钟系统概述 :
MSP430 单片机的时钟分为 MCLK、SMCLK 和 ACLK 三个。三种时钟的功能区别:
1_MCLK:主时钟(Main System Clock),专为 CPU 运行提供的时钟。MCLK 频率配置的高,CPU 执行的速度越快。
2_ SMCLK:子系统时钟(Sub-main Clock),专为一些需要高速时钟的片内外设提供服务,比如定时器和 ADC 采样等。当 CPU 休眠时,只要 SMCLK 开启,定时器和 ADC 仍可工作。
3_ACLK:辅助时钟(Auxiliary Clock),辅助时钟的频率很低,所以即使一直开启功耗也不大,当然关掉也是可以的。
MSP430G2 系列单片机的时钟来 源有 3 种,分别是内部低频振荡器 VLO、外部晶振 LFXT1 和内部高频数控振荡器 DCO。
1_DCO是 MSP430 内部的数字振荡器,MSP430G2553 的 DCO 最高主频可以达到 16MHz, 主要供 CPU 和其他一些高速外设使用。DCO的频率可以通过 DCOx、MODx 和 RSELx 这几个 寄存器位来调整。
2_LFXT1 是低速外部晶振,频率为 32.768kHz,它的精度相比 DCO高一些。
3_VLO 是 MSP430 内置的低频振荡器,它的频率是 12kHz 左右。VLO 的最主要用途是在 MSP430 进入低功耗模式后作为维持单片机“生命”的时钟源,待 MSP430 被唤醒后再切换到其 他时钟。因此 VLO 的精度不高,但它的功耗是最低的。
在时钟源和系统时钟之间有一系列的选择开关,通过配置这些选择开关(实际操作中是配置寄存器),可以为 3种系统时钟选取不同的时钟源。MCLK 和 SMCLK 可以来自 3 个时钟源中的 任意一个,而 ACLK 只能选择外部晶振或 VLO 作为时钟源。
前边看不懂 没关系,下边直接进入编程环节,直接配置你要配置的四个寄存器,那是个呢? DCOCTL、BCSCTL1、BCSCTL2、 BCSCTL3 这四个寄存器中。
DCOCTL、BCSCTL1这两个寄存器配置的是 数控振荡器 DCO;
配置DCO为1MHZ,可以这么配置:
BCSCTL1 = CALBC1_1MHZ;
DCOCTL = CALDCO_1MHZ;
参数如下:
默认情况下 MSP430G2553 单片机的 MCLK 和 SMCLK 都来源于 DCO,频率约为 1.1MHz;ACLK 来源于外部晶振,频率为 32.768kHz。
举例:
1—如果要将 MSP430G2553 的时钟设置为:MCLK 和 SMCLK 均为 1MHz,ACLK 设为 32.768kHz :
BCSCTL1 = CALBC1_1MHZ; // 要取得精确的1MHz,一定要调取校正参数
DCOCTL = CALDCO_1MHZ; // ACLK保持默认即可
2—将 MSP430G2553 的时钟设置为:MCLK 和 SMCLK 均为 16MHz,ACLK 设为 VLO
BCSCTL1 = CALBC1_16MHZ; // 调取16MHz校正参数
DCOCTL = CALDCO_16MHZ;
BCSCTL3 |= LFXT1S_2; // 设为VLO
低频晶振源是由 BCSCTL3中的 LFXT1Sx 控制的。将 LFXT1Sx 置为 10 即可用 VLO 作为低 频时钟源。
#define LFXT1S_0 (0x00) /* Mode 0 for LFXT1 : Normal operation */
#define LFXT1S_1 (0x10) /* Mode 1 for LFXT1 : Reserved */
#define LFXT1S_2 (0x20) /* Mode 2 for LFXT1 : VLO */
#define LFXT1S_3 (0x30) /* Mode 3 for LFXT1 : Digital input signal */
3—将 MSP430G2553 的时钟设置为:MCLK 为 2MHz,SMCLK 为 4MHz
BCSCTL1 = CALBC1_8MHZ; // 先将DCO设为8MHz
DCOCTL = CALDCO_8MHZ;
BCSCTL2 |= DIVM_1 + DIVS_2; // 再对MCLK 4分频,SMCLK 2分频
分频寄存器 DIVA、DIVS 和 DIVM,其中 DIVM用于MCLK的分频,DIVS用于SMCLK的分频。
实验程序:
靠调时钟 来控制 LED灯 的 闪烁频率:
代码如下:
#include "io430.h"
int main( void )
{
// Stop watchdog timer to prevent time out reset
WDTCTL = WDTPW + WDTHOLD;
P1DIR |= 0x01;
BCSCTL1 = CALBC1_8MHZ;
DCOCTL = CALDCO_8MHZ;
BCSCTL2 |= DIVM_1 + DIVS_2;
BCSCTL3 |= LFXT1S_2;
unsigned int i=50000;
while(1)
{
P1OUT ^= 0x01;
while(i--);
}
}
二 中断总结:
中断的工作过程可以分为 4 个阶段:
1) 中断事件发生
2) 中断标志位被置位
3) CPU 响应中断
4) 执行中断服务函数。
1_中断事件发生
中断事件的发生是一个中断的起点。中断事件指的是可以导致 CPU 进入中断的外部 事件,MSP430 中有许多外设可以产生中断事件,例如串口、GPIO、定时器、ADC 等等。
2_中断标志位被置位
当中断事件被单片机探测到之后,对应的中断标志位(interrupt flag,简称 IFG)就会被自动置位。置位之后除非进行复位,否则中断标志位会一直保持置位状态。MSP430 的外设中有该外设中断所对应的 IFG 寄存器,中断事件发生时这些寄存器就会被改写。 中断事件发生以后,对应的外设 IFG 寄存器被置位,但这 并不意味着 CPU 能够接收到中断信号,要想 CPU 能接收到中断信号,必须将中断使能位 (Interrupt enable bits,简称 IE)打开。
3_CPU 响应中断
CPU 接收到中断信号之后,会暂停正在执行的工作,并执行中断服务函数。
CPU 之所以能识别出中断的源头,是因为每一个中断标志位(IFG)是与一个中断服务函数(ISR)一一对应的,这个对应关系存在一个表里面,这个表叫做中断向量表。例如 P1 口的中断就与中断向量 PORT1_VECTOR对应,因此检测到 P1 口的中断信号后就会自动进入 PORT1_VECTOR 对应的中断服务函数。
格式:
#pragma vector = PORT1_VECTOR
中断向量表:
4_执行中断服务函数
中断服务函数,就是中断产生后要执行的东西写进这个函数;
函数格式如下:
#pragma vector=PORT1_VECTOR
__interrupt void Port_1(void)
{
P2OUT ^= 0x20; // P2.5 toggled
P1IFG &= ~0x08; // P1.3 IFG cleared
}
MSP430 的中断服务函数有固定的格式。 ·
#pragma 关键字后面是中断向量,中断向量决定了这个中断函数响应的是哪一个中断源。 例如这里的 PORT1_VECTOR 就代表来自 P1 I/O 口的中断。 ·
__interrupt(注意有两个下划线)关键字会告诉编译器这个函数是一个中断服务函数,后面的函数名(hs)是编程者可以自行定义的。
要写一个带有中断的程序,需要做下面几件事情:
1) 配置外设中和中断有关的寄存器,例如 I/O 口中断是上升沿触发还是下降沿触发,定时器 中断的计数方式和定时值等等。
2) 依照中断服务函数的模板写中断服务函数,添加中断后要干什么的代码。
3) 使能外设的中断,使能全局中断(GIE)
4) 一旦中断发生,CPU 停下主函数中的任务,并标记位置,进入中断服务函数,执行完中 断服务函数之后回到主函数标记位置处继续运行。
中断服务函数
我们想要单片机在中断里执行什么任务,都是由中断服务函数决定的。中断服务函数的编写和一般的函数略有不同,我们以 P1 端口的中断服务函数来做一个说明:
// Port 1 interrupt service routine
#pragma vector=PORT1_VECTOR
__interrupt void Port_1(void)
{
P1OUT ^= 0x01; // P1.0 = toggle
P1IFG &= ~0x08; // Clear P1.3 IFG
}
1) #pragma vector=PORT1_VECTOR #Pragma 是编译器指令,是告诉编译器将函数与中断向量连接起来。“vector=”后面是中断向量地址的宏定义,例如 P1 口中断就是 PORT1_VECTOR,定时器中断就是 TIMER0_A1_VECTOR。 不同外设的中断向量名可以在。在 msp430g2553.h 中找到“Interrupt Vectors” 这个部分(如下图),其中包含了所有中断向量的名称。
2) __interrupt void Port_1(void) __interrupt 关键字表明这是一个中断服务函数,CPU 见到这个关键字以后就会去做中断之前 的准备工作。Port_1 是用户自己取的函数名称,这个名称可以任意命名。
3) 中断服务函数的具体内容
中断服务函数的内容依据中断的不同种类有所差别。退出中断前一定不要忘记将中断标志位复位。
下面来个实验练习:
实验题目:
利用中断读取 MSP430G2 LaunchPad 上按键 S2 的状态,并根据该状态控制RGB 灯。每按下 S2 一次,LED 改变一次亮灭状态。
代码程序如下:
#include "MSP430G2553.h"
int main( void )
{
// Stop watchdog timer to prevent time out reset
WDTCTL = WDTPW + WDTHOLD;
P2DIR |= 0x20;
P2DIR |= 0x08;
P2DIR |= 0x02;
P1REN |= 0x08; //使能P1.3 的内部上拉电阻;
P1IE |= 0x08; //使能 P1.3 引脚中断
P1IES |= 0x08; //将 P1.3中断设为下降沿中断
P1IFG &= ~0x08; //P1.3 标志位清除
_EINT(); //打开总中断
while (1)
{
__no_operation(); // 空指令
}
}
#pragma vector=PORT1_VECTOR
__interrupt void Port_1(void)
{
P2OUT ^= 0x20;
P2OUT ^= 0x08;
P2OUT ^= 0x02;
P1IFG &= ~0x08; // P1.3 标志位清除
}