9.1观察DCO(MCLK)频率变化的程序
下述的程序可以用来观察DCO(MCLK)频率的变化。在while(1)循环中,让分别外接于P1.0和P1.6的红、绿LED亮灭,并用延时函数__delay_cycles(1000000)来设定LED亮灭的间隔。当外接于P1.3的按键S2按下时产生I/O口中断,在中断服务程序中进行按键判断,并切换DCO(MCLK)的频率,DCO的频率越高,延时函数__delay_cycles(1000000)产生的延时间隔越短,红、绿LED的闪烁越快。通过多次按下S2,可以观察红、绿LED的闪烁间隔的变化。
例9.1观察DCO(MCLK)频率的变化
#include<msp430.h>
void main(void)
{
WDTCTL=WDTPW+WDTHOLD;//关闭看门狗
BCSCTL2=SELM_0|DIVM_0; //为MCLK选择DCOCLK,不分频
P1DIR|=BIT6+BIT0; //P1.0、P1.6被设置为输出方向
P1OUT|=BIT6+BIT0; //点亮两个LED
P1REN|=BIT3; //P1.3的上/下拉电阻使能
P1OUT|=BIT3; //P1.3被上拉,连接于P1.3的按键未按下时,P1.3的输入为高电平
P1DIR&=~BIT3; //P1.3被设置为输入方向
P1IES|=BIT3; //设置由下降沿引起P1.3中断
P1IFG=0; //上述写P1DIR、P1OUT和P1IES,会导致相应的P1IFG被置位,该语句清除这些
//额外产生的中断标志
P1IE|=BIT3; //允许P1.3中断
_EINT(); //总中断允许
while(1)
{
P1OUT^=BIT6+BIT0; //亮灭红色和绿色LED
__delay_cycles(1000000); //延时函数,延时的长短取决于MCLK的大小,MCLK越高,延时越短,
//红、绿LED的闪烁越快
}
}
#pragma vector=PORT1_VECTOR //P1口中断向量,注意:必须严格按此格式书写
__interrupt voidP1_ISR(void) //声明一个中断服务程序,名为P1_ISR()。
{
static unsigned int Freq=0; //静态变量的性质:初始化只有一次,但是可以多次赋值
unsigned int i=0;
for(i=0;i<1000;i++); //延时消抖
if(P1IN&BIT3) //若为按键抖动产生的中断,则只进行一个空操作_NOP()
_NOP();
else//若为真正的按键按下所产生的中断,则执行下条语句的操作
Freq++;
if(Freq>3) Freq=0;
switch(Freq)
{
case 0: DCOCTL = 0x00; BCSCTL1=CALBC1_1MHZ; DCOCTL=CALDCO_1MHZ; break; //MCLK 1MHz
case 1: DCOCTL = 0x00; BCSCTL1=CALBC1_8MHZ; DCOCTL=CALDCO_8MHZ; break; //MCLK 8MHz
case 2: DCOCTL = 0x00; BCSCTL1=CALBC1_12MHZ; DCOCTL=CALDCO_12MHZ; break; //MCLK 12MHz
case 3: DCOCTL = 0x00; BCSCTL1=CALBC1_16MHZ; DCOCTL=CALDCO_16MHZ; break; //MCLK 16MHz
default: break;
}
P1IFG=0;//中断标志清零。注意:P1、P2口的中断标志需要人工清除,否则该中断会不停地被执行
}//中断函数(中断服务程序)到此结束
在CCS中建立工程,将上述的C文件加入工程中。调试无误后,将程序下载到开发板,即可正常运行。重复按下S2,可以观察红、绿LED的闪烁间隔的变化。
在上述程序中,对P1.0、P1.6、P1.3三个端口的设置可以打包为一个函数,然后在主函数main( )中调用该函数即可。如下所示:
例9.2 在例9.1的基础上,将P1.0、P1.6、P1.3三个端口的设置打包为一个函数,通过函数调用的方法可以实现同样的功能。
#include<msp430.h>
void P1_IO_Init(); //在主函数main()前声明函数,该函数实现对P1.0、P1.6、P1.3的初始化
void main(void)
{
WDTCTL=WDTPW+WDTHOLD;//关闭看门狗
BCSCTL2=SELM_0|DIVM_0; //为MCLK选择DCOCLK,不分频
P1_IO_Init(); //调用P1_IO_Init()函数,初始化相关的I/O口
_EINT(); //总中断允许
while(1)
{
P1OUT^=BIT6+BIT0; //使LED的亮灭状态取反
__delay_cycles(1000000); //延时函数,延时的长短取决于MCLK的大小,MCLK越高,延时越短,红绿LED的闪烁越快
}
}
#pragma vector=PORT1_VECTOR //P1口中断向量,注意:必须严格按此格式书写
__interrupt void P1_ISR(void) //声明一个中断服务程序,名为P1_ISR()
{
static unsigned int Freq=0; //静态变量的性质:初始化只有一次,但是可以多次赋值
unsigned int i=0;
for(i=0;i<1000;i++); //延时消抖
if(P1IN&BIT3) //若为按键抖动产生的中断,则只进行一个空操作_NOP()
_NOP();
else//若为真正的按键按下所产生的中断,则执行下条语句的操作
Freq++;
if(Freq>3) Freq=0;
switch(Freq)
{
case 0: DCOCTL = 0x00; BCSCTL1=CALBC1_1MHZ; DCOCTL=CALDCO_1MHZ; break; //MCLK 1MHz
case 1: DCOCTL = 0x00; BCSCTL1=CALBC1_8MHZ; DCOCTL=CALDCO_8MHZ; break; //MCLK 8MHz
case 2: DCOCTL = 0x00; BCSCTL1=CALBC1_12MHZ; DCOCTL=CALDCO_12MHZ; break; //MCLK 12MHz
case 3: DCOCTL = 0x00; BCSCTL1=CALBC1_16MHZ; DCOCTL=CALDCO_16MHZ; break; //MCLK 16MHz
default: break;
}
P1IFG=0; //中断标志清零。注意:P1、P2口的中断标志需要人工清除,否则该中断会不停地执行
}
/****************************************************************************
* 名称:P1_IO_Init()
* 功能:设定P1.0和P1.6为输出方向。设定P1.3为输入方向,其内部上拉电阻使能,允许P1.3 *的中断。
* 入口参数:无
* 出口参数:无
* 说明:无
* 范例:无
****************************************************************************/
void P1_IO_Init()
{
P1DIR|=BIT6+BIT0; //P1.0、P1.6被设置为输出方向
P1OUT|=BIT6+BIT0; //点亮两个LED
P1REN|=BIT3; //P1.3的上/下拉电阻使能
P1OUT|=BIT3; //P1.3被上拉,连接于P1.3的按键未按下时,P1.3的输入为高电平
P1DIR&=~BIT3; //P1.3被设置为输入方向
P1IES|=BIT3; //设置由下降沿引起P1.3中断
P1IFG=0; //上述写P1DIR、P1OUT和P1IES,会导致相应的P1IFG被置位,该语句清除这些额外产生的中断标志
P1IE|=BIT3; //允许P1.3中断
}
再进一步,对上述中断函数中的按键消抖、频率切换功能也可以打包为函数,在中断中直接调用即可,如下所示。
例9.3 在例9.2的基础上,将按键消抖、频率切换功能也打包为函数。
#include<msp430.h>
//在主函数main()前声明函数
void P1_IO_Init(); //对P1口的初始化函数
void P1_3_Push_Detect(); //对P1.3的按键检测函数
void MCLK_DCO_Change(); //切换MCLK(DCO)频率的函数
void main(void)
{
WDTCTL=WDTPW+WDTHOLD;//关闭看门狗
BCSCTL2 = SELM_0 | DIVM_0; //为MCLK选择DCOCLK,不分频
P1_IO_Init(); //调用P1_IO_Init()函数,初始化相关的I/O口
_EINT(); //总中断允许
while(1)
{
P1OUT^=BIT6+BIT0; //使LED的亮灭状态取反
__delay_cycles(1000000); //延时函数,延时的长短取决于MCLK的大小,MCLK越高,延时越短,红、绿LED的闪烁越快
}
}
#pragma vector=PORT1_VECTOR //P1口中断向量,注意:必须严格按此格式书写
__interrupt void P1_ISR(void) //声明一个中断服务程序,名为P1_ISR()
{
P1_3_Push_Detect(); //调用对P1.3的按键检测函数,并在该函数中调用MCLK频率切换函数
P1IFG=0; //中断标志清零。注意:P1、P2口的中断标志需要人工清除,否则该中断会不停地被执行
}
/****************************************************************************
* 名称:P1_IO_Init()
* 功能:设定P1.0和P1.6为输出方向。设定P1.3为输入方向,其内部上拉电阻使能,允许P1.3 的中断。
* 入口参数:无
* 出口参数:无
* 说明:无
* 范例:无
****************************************************************************/
void P1_IO_Init()
{
P1DIR|=BIT6+BIT0; //P1.0、P1.6被设置为输出方向
P1OUT|=BIT6+BIT0; //点亮两个LED
P1REN|=BIT3; //P1.3的上/下拉电阻使能
P1OUT|=BIT3; //P1.3被上拉,连接于P1.3的按键未按下时,P1.3的输入为高电平
P1DIR&=~BIT3; //P1.3被设置为输入方向
P1IES|=BIT3; //设置由下降沿引起P1.3中断
P1IFG=0; //上述写P1DIR、P1OUT和P1IES,会导致相应的P1IFG被置位,该语句清除这些额外产生的中断标志
P1IE|=BIT3; //允许P1.3中断
}
/****************************************************************************
* 名称:P1_3_Push_Detect()
* 功能:对外接于P1.3的按键S2进行按键按下的消抖及识别
* 入口参数:无
* 出口参数:无
* 说明:无
* 范例:无
****************************************************************************/
void P1_3_Push_Detect()
{
unsigned int i=0;
unsigned char PushKey;
PushKey=P1IFG&BIT3;
for(i=0;i<1000;i++); //延时消抖
if(P1IN&PushKey==PushKey) //若为按键抖动产生的中断,则清除中断标志并返回
{
P1IFG=0;
return;
}
if(PushKey&BIT3) //若为真正的按键按下所产生的中断,则执行下述语句的操作
{
MCLK_DCO_Change(); //调用切换MCLK频率的函数
}
return;
}
/****************************************************************************
* 名称:MCLK_DCO_Change()
* 功能:根据P1.3中断产生的次数,切换MCLK(DCO)的频率为1MHz、8MHz、12MHz或16MHz
* 入口参数:无
* 出口参数:无
* 说明:无
* 范例:无 ****************************************************************************/
void MCLK_DCO_Change()
{
static unsigned int Freq=0; //静态变量的性质:初始化只有一次,但是可以多次赋值
Freq++;
if(Freq>3) Freq=0;
switch(Freq)
{
case 0: DCOCTL = 0x00; BCSCTL1=CALBC1_1MHZ; DCOCTL=CALDCO_1MHZ; break; //MCLK 1MHz
case 1: DCOCTL = 0x00; BCSCTL1=CALBC1_8MHZ; DCOCTL=CALDCO_8MHZ; break; //MCLK 8MHz
case 2: DCOCTL = 0x00; BCSCTL1=CALBC1_12MHZ; DCOCTL=CALDCO_12MHZ; break; //MCLK 12MHz
case 3: DCOCTL = 0x00; BCSCTL1=CALBC1_16MHZ; DCOCTL=CALDCO_16MHZ; break; //MCLK 16MHz
default: break;
}
}
上述例子都可以在CCS中建立工程,在C文件中添加、编辑程序,然后建立、编译、下载到LaunchPad实验板中运行,观察实验结果。
采用定义函数和调用函数的方法可以增强程序的可移植性和可读性,便于实现结构化和模块化编程的思想。而且许多函数可以直接用于或稍加修改用于其它程序中,可大大提高编程效率。
单片机的学习要注重实践环节,无论是软件还是硬件,都需要亲自动手调试,在实践中发现问题、排查问题、解决问题。比如:在CCS中调试软件,会碰到各种各样的出错信息,需要返回到程序中去排查错误。当搭建外围电路时,也会出现各种各样的错误。软、硬件方面的这些bug(错误)都是难免的,消灭这些bug也是学习的一部分,我们的能力也在消灭这些bug的过程中一天天的提升。