zl程序教程

您现在的位置是:首页 >  工具

当前栏目

CC2530的定时/计数器原理与应用

应用原理 定时 计数器
2023-09-11 14:22:52 时间

1.定时计数器的基本原理

定时/计数器: 一种能够对内部时钟信号外部输入信号进行计数,当计数值达到设定要求时,向CPU提出中断请求,从而实现定时或者计数功能的外设

定时器/计数器最基本的原理: 进行计数,两者本质上都是计数器,可以进行加/减1计数,每出现一个计数信号,计数器就会自动加/减1,当计数值从0变成最大值/从最大值变成0溢出时,定时/计数器就会向CPU提出中断请求

2.CC2530的定时/计数器

CC2530共有6个定时/计数器(4个通用 + 2个特殊功能

  1. 定时器1: 16位定时器,功能最全,应优先选用
    支持输入捕获、输出比较、PWM输出、触发DMA
    5个独立的捕获/比较通道
    具有三种工作模式:自由运行模式、模模式、正计数/倒计数模式

  2. 定时器2: 16位定时器,用于CSMA-CA提供定时

  3. 定时器3、定时器4: 8位定时器
    支持输入捕获、输出比较
    2个独立的捕获/比较通道(对应2个I/O引脚)
    具有四种工作模式:自由运行模式、倒计数、模模式、正计数/倒计数模式

  4. 睡眠定时器: 24位正计数定时器,运行在32KHz的时钟频率,主要用于系统进入或退出低功耗睡眠模式之间的周期定时

  5. 看门狗定时器: 15位计数器,频率由32KHz时钟源规定(不使用看门狗功能时,可作为通用的定时器)

2.1.定时器1的工作原理

16位的定时器,其计数器的工作是在每个活动时钟边沿递增或递减

  1. 支持输入捕获、输出比较、PWM输出、触发DMA
  2. 5个独立的捕获/比较通道,对应5个I/O引脚
  3. 通过2个8位的寄存器读取定时器1的16位计数器值:T1CNTHT1CNTL
    当读取T1CNTL时,计数器的高位字节就会缓冲到T1CNTH,所以必须先读取T1CNTL
  4. 具有三种工作模式:自由运行模式、模模式、正计数/倒计数模式

2.2.定时器1的三种工作模式

  1. 自由运行模式

工作方式: 计数器从0x0000开始,在每个活动时钟边沿增加1,当计数器达到0xFFFF时溢出,计数器重新进入0x0000并开始新一轮的递增计数
计数周期: 固定为0xFFFF,当计数值达到0xFFFF时,标志位T1IFOVFIF被设置,可产生独立的时间间隔,输出信号频率
在这里插入图片描述

  1. 模模式

工作方式: 从0x0000开始,在每个活动时钟边沿增加1,当计数器达到T1CC0寄存器保存的置时溢出,计数器又从0x0000开始新一轮的递增计数
计数周期: 可以由用户自定义设置
在这里插入图片描述

  1. 正计数/倒计数模式

工作方式: 计数器反复从0x0000开始,正计数到TICC0保存的最终计数值,然后再倒计数回0x0000,当达到最终计数值时,标志位T1IFOVFIF被设置,可用于中心对齐的PWM信号输出
在这里插入图片描述

3.CC2530的定时/计数器中断系统

定时器产生中断请求的三种情况:

  1. 计数器达到最终计数值(溢出或回到0)
  2. 输入捕获事件
  3. 输出比较事件(模模式时使用)

注意: 使用模模式,需要开启通道0的输出比较模式,否则计数器的值达到T1CC0后,不会产生溢出中断

CC2530中定时器1~定时器4的中断使能位分别是IEN1寄存器中的T1IE、T2IE、T3IE、T4IE

CC2530中定时器1~定时器4分别有一个计数溢出中断屏蔽位:TxOVFIM(可位寻址)。一般不需要对TxOVFIM进行置1操作,因为上电复位时其初始值就是1

3.1.定时器1的最大计数值和计算与设置

最大计数值 = 定时时长 / 定时器计数周期

当系统时钟为16MHz,分频系数为128,要定时0.1s,最大计数值是多少?在程序设计上如何设置?

  1. 最大计数值 = 定时时长 / 定时器计数周期 = 0.1s / ((1/16M) * 128) = 12500 = 0x30D4
  2. T1CC0L = 0xD4;//先写低8位寄存器 T1CC0H = 0x30; //再写高8位寄存器

T1CTL定时器1控制寄存器
在这里插入图片描述

注意: 一旦设置了定时器1的工作方式,该定时器就立刻开始定时计数工作,一般放在初始化最后一行

T1CCTLx定时器1通道x捕获/比较控制寄存器
在这里插入图片描述

注意: 使用模模式,需要开启通道0的输出比较模式
定时器1通道0的输出比较功能通过T1CCTL0寄存器来设置


T1STAT定时器1状态寄存器
在这里插入图片描述

3.2.定时器1初始化函数设计

  1. 将定时器1的最大计数值写入T1CC0
  2. 通过T1CCTL0寄存器开启定时器1通道0的输出比较模式
  3. 设置定时器1的相关中断控制位
  4. 设置分频系数和工作模式并启动定时器

3.3.定时器中断服务函数设计

  1. 清除T1STAT的中断标志位
  2. 累加全局变量count
  3. count被10整除,即1s的定时到了
  4. 10s定时到,将count清零

4.案例1:应用定时器1的模模式实现1s定时

要求: 将内部系统时钟16MHz的RC振荡器128分频作为定时器1的计数信号,在模模式中实现0. 1秒定时。在定时中断服务函数中对全局变量count进行累加,实现1秒定时,并将LED5作为秒闪灯,而10秒后LED6亮,再10秒后LED6灭… 如此往复
设计思路:

  1. 计算最大计数值,并填入T1CC0H和T1CC0L
  2. 使用模模式,记得开启通道0的输出比较模式
  3. 使能定时器1相关的中断控制位
  4. 在T1CTL寄存器设置分频系数,启动定时器
  5. 编写定时器1中断服务处理函数,在这里记得,手工清除T1STAT寄存器中的中断标志位
  6. 定义一个全局变量count,进行1秒定时

#include "ioCC2530.h"

#define LED5 P1_3
#define LED6 P1_4

/*========================定时器1初始化函数=======================*/

void Init_Timer1(){
  //设置最大计数值
  T1CC0L = 0xd4;//低8位
  T1CC0H = 0x30;//高8位
  
  //开启通道0的比较模式
  T1CCTL0 |= 0x04;
  
  //使能定时器1的中断
  T1IE = 1;
  //T1OVFIM = 1;定时器1溢出中断,因为复位置1,可不设置
  
  //使能总中断
  EA = 1;
  
  //设置定时器1的分配系数和工作模式
  T1CTL = 0x0e;//分频系数是128模模式
}

 unsigned char count = 0;
/*==========================定时器1服务函数=======================*/
#pragma vector = T1_VECTOR
__interrupt void Timer1_Sevice(){
    //清除定时器1通道0中断标志
    T1STAT &= ~0x01;
    count ++;
    //定时1s到
    if (count % 10 == 0){
      LED5 = ~LED5;
    }
    //定时10s到
    if (count == 100){
      LED6 = ~LED6;
      count = 0;
    }
  }
/*==========================端口初始化函数=======================*/
void Init_Port(){
    //将P1_3和P1_4设置为通用I/O口
    P1SEL &= ~0x18;
    //将P1_3和P1_4的端口设置为输出
    P1DIR |= 0x18;
    LED5 = 0;
    LED6 = 0;
  }

/*==========================主函数==============================*/
  void main(){
    Init_Port();
    Init_Timer1();
    while(1);
  }

5.案例2:应用定时器1的模模式实现秒闪灯

要求:

  1. 选择内部16MHz时钟的128分频作为计数器1的计数信号
  2. 在定时器1的模模式中实现0.1s的间隔定时
  3. 在中断服务函数中,实现1s的间隔定时,并反转D4灯的开关状态,以实现灯秒闪的功能,即D4灯亮1s,灭1s…实现3s的间隔定时,并翻转D6的开关状态,即D6灯亮3s,灭3s…
    在这里插入图片描述
#include "ioCC2530.h"

#define D3 P1_0
#define D4 P1_1
#define D5 P1_3
#define D6 P1_4

//长定时累计变量
unsigned char count = 0;

/*=========================端口初始化函数===================================*/
void Init_Port(){
  //初始化LED灯的I/O端口
  P1SEL &= ~0x1b;//将P1_0、P1_1、P1_3和P1_4设为通用I/O
  P1DIR |= 0x1b;//将P1_0、P1_1、P1_3和P1_4设为输出
  
  //关闭所有LED灯
  P1 &= ~0x1b;
}

/*=========================定时器1初始化函数=================================*/
void Init_Timer1(){
//设置最大计数值
T1CC0L = 0xd4; //16MHz时 钟128分频定时100ms
T1CC0H = 0x30; //设 先填低8位,再填高8位

//开启通道0的比较模式
T1CCTL0 |= 0x04;

//使能定时器1中断
T1IE = 1;

//使能总中断打开
EA= 1;

//设置定时器1的分频系数和T作模式
T1CTL = 0x0e;//分频系数是128,模模式
}

/*=========================定时器1中断服务函数===============================*/
#pragma vector = T1_VECTOR
__interrupt void Timer1_Sevice(){
  //清除定时器1通道0中断标志
  T1STAT &= ~0x01;
  //累加定时周期
  count++;
  //定时1s时间到
  if ((count % 10) == 0){
    D4 = ~D4;
  }
  //定时3s到
  if ((count % 10) == 30){
    D6 = ~D6;
    count = 0;
  }
}

/*======================================主函数===============================*/
void main(){
  //端口初始化
  Init_Port();
  //定时器1初始化
  Init_Timer1();
  while(1);
}