CC2530基础实验三 定时器

发布于 / 学习

####一、任务要求
使用CC2530单片机内部定时/计数器来控制LED1进行周期性闪烁,具体闪烁效果要求如下:

①通电后LED1每隔2秒闪烁一次。
②LED1每次闪烁点亮时间为0.5秒

1.定时/计数器介绍
(1)定时/计数器的概念
定时/计数器是一种能够对时钟信号或外部输入信号进行计数,当计数值达到设定要求时便向CPU提出处理请求,从而实现定时或计数功能的外设。在单片机中,一般使用Timer表示定时计数器。
(2)定时/计数器的作用
定时/计数器的基本功能是实现定时和计数,且在整个工作过程中不需要CPU进行过多参与,它的出现将CPU从相关任务中解放出来,提高了CPU的使用效率。例如我们之前实现LED灯闪烁时采用的是软件延时方法,在延时过程中CPU通过执行循环指令来消耗时间,在整个延时过程中会一直占用CPU,降低了CPU的工作效率。若使用定时/计数器来实现延时,则在延时过程中CPU可以去执行其他工作任务。CPU与定时/计数器之间的交互关系可用图4-1来进行表示。
这里写图片描述

单片机中的定时/计数器一般具有以下功能:

1)定时器功能
对规定时间间隔的输入信号的个数进行计数,当计数值达到指定值时,说明定时时间已到。这是定时/计数器的常用功能,可用来实现延时或定时控制,其输入信号一般使用单片机内部的时钟信号。
2)计数器功能
对任意时间间隔的输入信号的个数进行计数。一般用来对外界事件进行计数,其输入信号一般来自单片机外部开关型传感器,可用于生产线产品计数、信号数量统计和转速测量等方面。
3)捕获功能
对规定时间间隔的输入信号的个数进行计数,当外界输入有效信号时,捕获计数器的计数值。通常用来测量外界输入脉冲的脉宽或频率,需要在外界输入信号的上升沿和下降沿进行两次捕获,通过计算两次捕获值的差值可以计算出脉宽或周期等信息。
4)比较功能
当计数值与需要进行比较的值相同时向CPU提出中断请求或改变I/O口输出电平等操作。一般用于控制信号输出。
5)PWM输出功能
对规定时间间隔的输入信号的个数进行计数,根据设定的周期和占空比从I/O口输出控制信号。一般用来控制LED灯亮度或电机转速。

(3)定时/计数器基本工作原理
无论使用定时/计数器的哪种功能,其最基本的工作原理是进行计数。定时/计数器的核心是一个计数器,可以进行加1(或减1)计数,每出现一个计数信号,计数器就自动加1(或自动减1),当计数值从最大值变成0(或从0变成最大值)溢出时定时/计数器便向CPU提出中断请求。计数信号的来源可选择周期性的内部时钟信号(如定时功能)或非周期性的外界输入信号(如计数功能)。
这里写图片描述

####二、CC2530的定时/计数器
CC2530中共包含了5个定时/计数器,分别是定时器1、定时器2、定时器3、定时器4和睡眠定时器。
(1)定时器1

定时器1是一个16位定时器,主要具有以下功能:
支持输入捕获功能,可选择上升沿、下降沿或任何边沿进行输入捕获。
支持输出比较功能,输出可选择设置、清除或切换。
支持PWM功能。
具有5个独立的捕获/比较通道,每个通道使用一个I/O引脚。
具有自由运行、模、正计数/倒计数三种不同工作模式。
具有可被1、8、32或128整除的时钟分频器,为计数器提供计数信号。
能在每个捕获/比较和最终计数上产生中断请求。
能触发DMA功能。
定时器1是CC2530中功能最全的一个定时/计数器,是在应用中被优先选用的对象。

####CC2530定时/计数器的工作模式

CC2530的定时器1、定时器3和定时器4虽然使用的计数器计数位数不同,但它们都具备“自由运行”、“模”和“正计数/倒计数”三种不同的工作模式,定时器3和定时器4还具有单独的倒计数模式。此处以定时器1为例进行介绍。
(1)自由运行模式
在自由运行模式下,计数器从0x0000开始,在每个活动时钟边沿增加1,当计数器达到0xFFFF时溢出,计数器重新载入0x0000并开始新一轮递增计数
这里写图片描述

自由运行模式最大为FFFF也就是65535

自由运行模式的计数周期是固定值0xFFFF,当计数器达到最终计数值0xFFFF时,系统自动设置标志位IRCON.T1IF和T1STAT.OVFIF,如果用户设置了相应的中断屏蔽位TIMIF.T1OVFIM和IEN1.T1EN,将产生一个中断请求。
(2)模模式
在模模式下,计数器从0x0000开始,在每个活动时钟边沿增加1,当计数器达到T1CC0寄存器保存的值时溢出,计数器将复位到0x0000并开始新一轮递增计数
这里写图片描述

计数溢出后,将置位相应标志位,同时如果设置了相应的中断使能则会产生一个中断请求。T1CC0由2个8位寄存器T1CC0H和T1CC0L构成,分别用来保存最终计数值的高8位和低8位。模模式的计数周期不是固定值,可由用户自行设定,以便获取不同时长的定时时间。
定时器3和定时器4的倒计数模式类似与模模式,只不过计数值是从最大计数值向0x00倒序计数。
(3)正计数/倒计数模式
在正计数/倒计数模式下,计数器反复从0x0000开始,正计数到T1CC0保存的最终计数值,然后再倒计时回0x0000

这里写图片描述
正计数/倒计数模式下,计数器在到达最终计数值时溢出,并置位相关标志位,若用户已使能相关中断,则会产生中断请求。这种工作模式在用来进行PWM控制时可以实现中心对齐的PWM输出。

####定时器设置流程
这里写图片描述

执行流程

这里写图片描述

####三、CC2530时钟源
时钟源
1、内部RC震荡器(32KHz、16MHz)
2、外部石英晶振(32.768KHz、32MHz)
注意:外部石英晶振比较稳定,在无线收发中采用外部石英晶振

时钟源的切换
CLKCONSTA
用于判断时钟源是否切换成功

这里写图片描述

CLKCONCMD
用于设置时钟源

这里写图片描述

用命令寄存器(CLKCONCMD)来改变系统时钟源,用状态寄存器 (CLKCONSTA) 来判断改变后的寄存器是否稳定了。

这里写图片描述

####四、定时器
这里写图片描述

定时器设置

定时器时间:1/CLKCONCMD*T1CTL分频*工作模式*溢出次数

CLKCONCMD -->TI计数器(T1CTL模式选择)-->TISTAT.OVFIF(标志位)-->TIMIF.OVFIM-->T1IF-->EA-->T1IE

定时器初始化

T1CTL 设置分频 与模式
T1CCOH T1CCOL 高低八位
T1CCTL0|=0x04 通道0 比较模式

T1IF=0 清除TIMER1中断标志
T1STAT &=~0x01; 清除通道0中断标志
IEN1|=0X02 使能定时器1的中断
EA=1

T1定时器中断条件

1、设置时钟源
CLKCONCMD &= ~0X40;
  while(CLKCONSTA & 0x40);
  CLKCONCMD &= ~0X47;
2、设置T1定时器的工作模式和分频
T1CTL = 0X05;
3、打开T1中断和中断总开关
  IEN1 |= 0X02;
EA = 1;
4、写中断函数
  #pragma vector = T1_VECTOR
__interrupt void ti()
{
    counter++;
    if(counter == 50)
    {
      T1IF = 0;   //清空T1溢出中断标志位
      counter = 0;
/***************
代码
**************/
    }
}

按键控制定时器T1

1、初始化按键中断寄存器,打开中断总开关
  IEN2 |= 0X10;
  P1IEN |= 0X04;
  PICTL &= ~0X02;
EA = 1;
2、修改32MHz时钟源
  CLKCONCMD &= ~0X40;
  while(CLKCONSTA & 0x40);
  CLKCONCMD &= ~0X07;
3、按键中断函数,并设置定时器的工作模式和分频,打开T1中断
  #pragma vector = P1INT_VECTOR
__interrupt void f1()
{
  EA = 0;
  if(P1IFG & 0X04)
  {
    while(P1_2 == 0);
    delay(100);
    while(P1_2 == 0);

    if(k == 1)
    {
      k = 0;
      IEN1 |= 0X02;
      T1CTL = 0X05;
    }
    else if(k == 0)
    {
      k = 1;
      IEN1 &= ~0X02;
      T1CTL = 0;
    }
    P1IFG = 0;
    P1IF = 0;
  }
  EA = 1;
}

4、定时器中断函数

  #pragma vector = T1_VECTOR
__interrupt void t1()
{
  counter++;
  if(counter == 30)
  {
    T1IF = 0;
    counter = 0;
    /***************
    代码
    ******************/
      }
}


寄存器
T1CTL
设置T1定时器操作模式和分频

这里写图片描述

T1STAT

这里写图片描述

IRCON
溢出中断标志定时器计数满后需要清楚标志位
当定时器T1产生中断时也可以写作  
IRCON &= 0X02;    T1IF = 0;

这里写图片描述

T1CCTL0
设置TIMER1通道0比较模式      T1CCTL0  |= 0X04;

这里写图片描述

T1CC0L、T1CC0H

这里写图片描述

正计数/倒计数器工作模式从0x0000~T1CC0,再从T1CC0~0x0000需设置

这里写图片描述

TIMIF
不产生定时器T1的溢出中断
常用设置
TIMIF &= ~0X40;

这里写图片描述

IEN1
用于设置中断使能
常用设置
    IEN1 |= 0X02;

这里写图片描述

####五、具体代码

#include "ioCC2530.h" //引用CC2530头文件

#define LED1 (P1_0)     //LED1端口宏定义
#define LED2 (P1_1)     //LED1端口宏定义

unsigned char flag_Pause=0;  //流水灯运行标志位,为1暂停,为0运行。

/**************************************************************
函数名称:delay
功    能:软件延时
入口参数:time--延时循环执行次数
出口参数:无
返 回 值:无
**************************************************************/
void delay(unsigned int time)
{
    unsigned int i;
    unsigned char j;
    for(i = 0;i < time;i++)
        for(j = 0;j < 240;j++)
        {
            asm("NOP");//asm用来在C代码中嵌入汇编语言操作,汇
            asm("NOP");//编命令nop是空操作,消耗1个指令周期。
            asm("NOP");
            while(flag_Pause);//根据flag_Pause的值确定是否在此循环
        }
}

/**************************************************************
函数名称:main
功    能:程序主函数
入口参数:无
出口参数:无
返 回 值:无
**************************************************************/
void main(void)
{
    P1SEL &= ~0x03;         //设置P1_0口和P1_1口为普通I/O口
    P1DIR |= 0x03;          //设置P1_0口和P1_1口为输出口

    LED1 = 0;               //熄灭LED1
    LED2 = 0;               //熄灭LED2

    /*************新增外部中断初始化部分****************/
    IEN2 |= 0x10;           //使能P1口中断
    P1IEN |= 0x04;          //使能P1_2口中断
    PICTL |= 0x02;          //P1_3到P1_0口下降沿触发中断
    EA = 1;                 //使能总中断
    /***************************************************/

    while(1)//程序主循环
    {
        delay(1000);        //延时
        LED1 = 1;           //点亮LED1
        delay(1000);        //延时
        LED2 = 1;           //点亮LED2
        delay(1000);        //延时
        LED1 = 0;           //熄灭LED1
        delay(1000);        //延时
        LED2 = 0;           //熄灭LED2
    }
}

/**************************************************************
函数名称:P1_INT
功    能:P1口外部中断服务函数
入口参数:无
出口参数:无
返 回 值:无
**************************************************************/
#pragma  vector = P1INT_VECTOR
__interrupt void P1_INT(void)
{
    if(P1IFG & 0x04)       //如果P1_2口中断标志位置位
    {
        if(flag_Pause == 0)
        {
            flag_Pause = 1;
        }
        else
        {
            flag_Pause = 0;
        }
        P1IFG &= ~0x04;    //清除P1_2口中断标志位
    }
    P1IF = 0;              //清除P1口中断标志位
}
本文采用 CC BY-NC-SA 3.0 Unported 协议进行许可
本文链接: https://www.ahwgs.cn/cc2530jichushiyansan-dingshiqi.html