一、承上启下
上一讲,我们通过一个最简单的LED闪烁小实验,熟悉了IAR开发CC2430程序的基本过程。刀好歹磨过了(虽然我这块磨刀石不咋地),现在就开始屠虫了:)。接下来,我们一起来学习几个CC2430的基础实验。每个小实验,分“实验简介”、“程序流程图”,“实验源码及剖析”三个部分阐述。
本篇讲解外部中断。
二、外部中断(1)实验简介
中断是单片机实时地处理内部或外部事件的一种内部机制。当某种内部或外部事件发生时,单片机的中断系统将迫使CPU暂停正在执行的程序,转而去进行中断事件的处理,中断处理完毕后,又返回被中断的程序处,继续执行下去。
中断分外部中断和内部中断,CC2430共包含18个中断源(具体中断描述及中断向量的定义,可参考《CC2430中文手册》)。
现在我们来看一下本开发板的电路图:
![](http://image53.360doc.com/DownloadImg/2012/07/1514/25525479_1.jpg)
开发板上已把S1按键与P0.1相连,本实验想要达到的效果就是,通过按键S1触发P0.1的中断,进而在中断服务子程序中控制LED1的亮/灭。
(2)实验原理及流程图
实验流程图如下:
![](http://image53.360doc.com/DownloadImg/2012/07/1514/25525479_2.png)
(3)实验源码//头文件
#include <ioCC2430.h>
//延时子函数
#defineled1 P1_0
#defineled2 P1_1
#defineled3 P1_2
#defineled4 P1_3
voidDelay(unsignedn)
{
unsignedtt;
for(tt=0;tt<n;tt++);
for(tt=0;tt<n;tt++);
for(tt=0;tt<n;tt++);
for(tt=0;tt<n;tt++);
for(tt=0;tt<n;tt++);
}
//32M晶振初始化
voidxtal_init(void)
{
SLEEP&=~0x04; //都上电
while(!(SLEEP&0x40)); //晶体振荡器开启且稳定
CLKCON&=~0x47; //选择32MHz 晶体振荡器
SLEEP|=0x04;
}
//LED灯初始化
voidled_init(void)
{
P1SEL =0x00; //P1为普通 I/O 口
P1DIR|=0x0F; //P1.0 P1.1 P1.2 P1.3 输出
led1=0;
led2=0;
led3=0;
led4=0;
}
//io及外部中断初始化
voidio_init(void)
{
P0INP&=~0X02; //P0.1有上拉、下
EA=1; //总中断使能
P0IE = 1; //P0中断使能
PICTL|= 0X09; //P0.1口中断使能,下降沿触发
P0IFG&=~0x02; //P0.1中断标志清0
};
//主函数
voidmain(void)
{
xtal_init();
led_init();
io_init();
while(1); //等待中断
}
//中断服务子程序
#pragma vector = P0INT_VECTOR
__interruptvoidP0_ISR(void)
{
EA=0; //关中断
Delay(10000);
Delay(10000);
Delay(10000);
Delay(10000);
Delay(10000);
if((P0IFG&0x02)>0) //按键中断
{
P0IFG&=~0x02; //P0.1中断标志清0
led1=!led1;
}
P0IF=0; //P0中断标志清
EA=1; //开中断
}
首先初始化统时钟:选用32MHz晶体振荡器。
然后初始化LED:设置P1为通用I/O口,设置 P1.0 ~ P1.3 方向为输出,然后关闭4个LED灯。
再来配置外部中断的相关SFR寄存器,开启各级中断使能,涉及3个SFR:EA、IEN1、PICTL(各SFR详细介绍请查阅《CC2430中文手册》):
EA—— 总中断使能;
IEN1.5—— P0中断使能;
PICTL.3—— P0.1口中断使能;
PICTL.0—— 设置P0.1口输入下降沿引起中断触发。
然后在主函数中使用 while(1) 等待中断即可。
CC2430 小贴士
(1)位赋值语法小结
很多时候,我们需要对单字节的SFR中的某一位赋值(0或1),以精确控制硬件设备。
有的SFR支持位寻址,比如说TCON、P0等,此时,对位的赋值非常简单,只需查询 ioCC2430.h 头文件中 SFR Bit Access 部分的位定义即可:
P0_0 = 0;//对P0第一位赋值0
P0_0 = 1;//对P0第一位赋值1
但有的SFR并不支持位寻址,就如本实验中的PICTL,此时想要对其中的某一位赋值,语法如下:
PICTL &= ~0x01; //对第1位赋值0
PICTL |= 0x01; //对第1位赋值1
大家可以记住&= ~,|=这两个常用的位赋值语法。
(2)中断使能小结
在程序中涉及到某中断时,必须在触发中断前使能此中断。
C51的中断使能系统,其层次结构非常明显:
中断老大:EA是老大,负责总的中断使能:
EA = 1;
各中断分队队长:接下来是针对每一个功能部件(如P0、定时器1等)的使能控制,此类SFR一般可位寻址,命名中一般含有 IE(Interrupt Enable):
P0IE = 1;
各中断队员:分队但由于每个功能部件内部也含有多个中断,所以最后一级就是针对这每一个中断的使能控制,此类SFR一般不可位寻址,命名中一般含有IE(Interrupt Enable)或 IM(Interrupt Mask):
PICTL |= 0x01;
不需死记硬背中断SFR,只要了解其层次结构,然后用时查询手册或头文件即可。
(3)中断程序的编写
在一个程序中使用中断,一般包括、两个部分:中断服务子程序的编写、中断使能的开启。中断使能已在上面介绍过,下面简单介绍一下中断服务子程序的编写:
首先指定中断向量,可以在 ioCC2430.h 头文件中的 Interrupt Vectors 部分查询,语法如下:
#pragma vector = 中断向量
然后紧跟着编写中断处理程序,结构如下:
__interruptvoid函数名(void)
{
//开中断
//中断处理
//中断标志清0
//关中断
}
三、结语
本篇介绍了基于CC2430的简单的外部中断的实现方法,有了中断的基础之后,接下来我们介绍另外一个非常重要的模块——定时器。CC2430共有4个定时器,可分三类:定时器1、定时器2、定时器3/4(3与4的用法基本一样)。
======================================================================= CC2530中断有18个中断源。每个中断源有它自己的,位于一系列特殊功能寄存器中的中断请求标志。每个中断通过相应的标志请求可以单独使能或禁止。中断组合不同,可以选择的优先级别。
为了使能中断功能,应当执行下列步骤:
1.清除中断标志。
2.如果有,设置外部设备特殊功能寄存器中对应的各中断使能位。
3.设置寄存器IEN0 ,IEN1或者IEN2中对应的各中断使能位为1。
4. 设置IEN0中的EA位为1来使能全局中断。
5. 在该中断对应的向量地址上,运行该中断的服务程序。
具体内容参考CC253X用户指南。
电源管理和时钟:
通过不通的运行模式来使能低功耗模式。运行模式有主动模式,空闲模式和功耗模式1,2,3(PM1-PM3)。通过关闭对模块的供电避免静态(泄露)电源消耗来实现超低功耗运行,也可以通过使用门控时钟和关闭振荡器降低动态功耗来实现超低功耗运行。
功耗模式运行方式见CC253X用户指南P50页。
PM1-PM3:复位,一个外部中断或者睡眠定时器到期的情况下,系统都将进入主动模式。PM2/PM3模式下,上电复位有效,掉点检测关闭,也就给定了一个有限的电压管理。在PM2/PM3期间,如果供电电压低于1.4V,温度高于或等于70°,在重新进入主动模式之前,应当提供一个合适的运行电压,在PW2/PW3模式下保存的寄存器和RAM里的内容可能会改变。因此应当注意系统的电源设计,以确保这种情况不会发生。通过进入主动模式可以对电压进行精确地定期监控,因为如果点与低于1.7V左右就会触发一个掉电检测复位。(以下为选择依据:)
因为PW1使用了一个快速断电/上电序列,等待唤醒事件与期望时间相对较短(小于3ms)时使用PM1。
当睡眠时间超过3ms时,相对于PM1,PM2(第二低)可以当作典型选择。和使用PM1相比,使用较短的睡眠时间不会降低系统的功耗。
在等待一个外部事件时PW3(最低)用来实现超低功耗。当期望的睡眠时间超过3ms时使用该模式。电源管理寄存器见CC253X用户指南P52页。
(PCON.IDLE=1 =>SLEEPCMD.MODE[1:0]00:主闲;01:PM1;10:PM2;11:PM3 )SLEEPSTA.RST[1:0]选择复位方式。
振荡器和时钟:
CLKCONCMD寄存器来选择时钟控制(16MHzRC振荡器/32MHz晶体振荡器)。CLKCONSTA寄存器是一个只读寄存器,用来获得当前时钟状态。(运行RF收发器,必须使用32MHz晶体振荡器)振荡器和时钟寄存器见见CC253X用户指南P56页。
I/O:( PxSEL:通用I/O引脚或者外部设备I/O信号;PxDIR: 引脚口得方向;PxINR:上,下,三态模式)。
通用I/O口中断:
P0IEN,P1IEN,P2IEN,每个口的各位实现中断使能。P0IFG,P1IFG,P2IFG,中断状态标志位;
PxIFG必须在清除CPU端口中断标志(PxIF)之前被清除。PICTL:P0(0位),P1(1,2位)和P2(3位)中断触发沿设置。
#include "ioCC2530.h "
#define LED1 P0_6
#define LED2 P1_0
#define LED3 P1_2
void Delay(unsigned n)
{
unsigned tt;
for(tt = 0;tt<n;tt++);
for(tt = 0;tt<n;tt++);
for(tt = 0;tt<n;tt++);
for(tt = 0;tt<n;tt++);
for(tt = 0;tt<n;tt++);
}
//io及外部中断初始化
void io_init(void)
{
EA=0;//禁用中断
P0IFG=0;//P0_1中断标志位清零
P0IF=0;//P0中断标志位清零
IP0=0x20;//设置中断组5为最高优先级
// IP1=0x20;
PICTL=0x01;//设置P0中断为下降沿触发 P0ICTL(0~7)
P0IEN=0x02;//P0_1中断使能
P0IE=1;//P0中断使能
EA=1;//中断使能
/* P0INP &= ~0X02; //P0.1有上拉、下拉
EA = 1; //总中断使能
P0IE = 1; //P0中断使能
PICTL |= 0X09; //P0.1口中断使能,下降沿触发
P0IFG &= ~0x02; //P0.1中断标志清0 */
};
void main()
{
io_init();
P0SEL = 0x00; //设置P0.6为普通 I/O 口
P0DIR |= 0x40; //设置P0.6为输出
P1SEL = 0x00; //设置P1为普通 I/O 口
P1DIR |= 0x05; //设置P1.2 P1.0为输出
LED1 = 0;
LED2 = 0;
LED3 = 0;
while(1);
}
//中断服务子程序
#pragma vector = P0INT_VECTOR
__interrupt void P0_ISR(void)
{
EA = 0; //关中断
Delay(10000);
Delay(10000);
Delay(10000);
if((P0IFG & 0x02 ) >0 ) //按键中断
{
P0IFG &= ~0x02; //P0.1中断标志清0
LED1 = ! LED1;
LED2 = ! LED2;
LED3 = ! LED3;
}
P0IF = 0; //P0中断标志清0
EA = 1; //开中断
}