zoukankan      html  css  js  c++  java
  • AVR开发 Arduino方法(三) 定时/计数器子系统

      Arduino UNO R3的主处理器ATMega328P拥有3个定时/计数器,它们分别是Timer0Timer1Timer2;它们都通过对来自内部或外部的脉冲信号进行计数的方式完成基本的定时/计数功能以及一些其他的功能。

      Timer0Timer28位定时/计时器,Timer116位定时/计数器;下面以Timer2为例讨论定时/计数器子系统的典型应用,这些内容同样适用于Timer0Timer1

    1. 精准延时

      在前面的例子中,已经使用了一些与精准延时相关的Arduino库函数:

     

      delay(ms):延迟一段时间

      ms:延迟的时长,单位是毫秒

     

      请注意,上面的Arduino库函数使用了Timer0的中断,因此不要在任何中断服务程序中调用它,否则程序可能无法正常工作。

      如果需要在延迟的同时,Arduino可以执行其他程序,这需要借助第三方库MsTimer2;你可以在http://playground.arduino.cc/Main/MsTimer2下载到它,把下载到的MsTimer2.zip解压到Arduino IDE安装目录下的libraries文件夹里就可以使用了,它包含了一个示例:

     1 // FlashLed.ino
     2 #include <MsTimer2.h>
     3 
     4 void flash() {
     5   static boolean output = HIGH;
     6   
     7   digitalWrite(13, output);
     8   output = !output;
     9 }
    10 
    11 void setup() {
    12   pinMode(13, OUTPUT);
    13 
    14   MsTimer2::set(500, flash);
    15   MsTimer2::start();
    16 }
    17 
    18 void loop() {
    19 }

       这个示例的效果与Blink示例基本相同,MsTimer2库提供了3个库函数:

     

      MsTimer2::set(interval, function):每隔一段时间执行指定函数

      interval:间隔时长,单位是毫秒

      function:指定执行函数的名称

      MsTimer2::start():开始每隔一段时间执行指定函数

      MsTimer2::stop():结束每隔一段时间执行指定函数

     

      Timer22个控制寄存器:TCCR2ATCCR2B,它们的结构如下图所示:

    COM2A1

    COM2A0

    COM2B1

    COM2B0

     

     

    WGM21

    WGM20

     

    FOC2A

    FOC2B

     

     

    WGM22

    CS22

    CS21

    CS20

    其中WGM2[2:0]位用于设置模式,CS2[2:0]位用于设置时钟源,如下表所示:

    WGM2[2:0]

    模式

    计数上限

     

    CS2[2:0]

    时钟源

    000

    正常

    0xFF

     

    000

    001

    相位校正脉宽调制

    0xFF

     

    001

    系统时钟

    010

    比较匹配时清零

    OCR2A

     

    010

    系统时钟8分频

    011

    快速脉宽调制

    0xFF

     

    011

    系统时钟32分频

    100

    (保留)

     

     

    100

    系统时钟64分频

    101

    相位校正脉宽调制

    OCR2A

     

    101

    系统时钟128分频

    110

    (保留)

     

     

    110

    系统时钟256分频

    111

    快速脉宽调制

    OCR2A

     

    111

    系统时钟1024分频

     其余位暂且设置为0

      精准延时可以采用正常模式。正常模式下,Timer2不断从0x00计数到0xFF;当定时计数器寄存器TCNT2每一次返回0x00时,若Timer2中断屏蔽寄存器TIMSK2的溢出中断使能位TOIE21,则产生中断,它的结构如下图所示:

     

     

     

     

     

    OCIE0B

    OCIE0A

    TOIE0

       通过直接访问寄存器改写以上程序为:

     1 // FlashLed_reg.ino
     2 void flash() {
     3   PORTB ^= (1 << PB5);
     4 }
     5 void setup() {
     6   DDRB |= (1 << PB5);
     7 
     8   // 正常模式,系统时钟256分频,计数初值为6
     9   TCCR2A = 0x00;
    10   TCCR2B = 0x06;
    11   TCNT2 = 0x06;
    12   
    13   TIMSK2 |= (1 << TOIE2);
    14   sei();
    15 }
    16 
    17 void loop() {
    18 }
    19 
    20 ISR(TIMER2_OVF_vect) {
    21   static volatile int iTimes = 0;
    22 
    23   // 中断125次为500毫秒
    24   if (++iTimes == 125) {
    25     flash();
    26     iTimes = 0;
    27   }
    28   // 重新赋初值6
    29   TCNT2 = 0x06;
    30 }

      程序中的中断125次为500毫秒是这样计算出来的:Arduino UNO R3开发板使用16MHz的系统时钟,Timer2使用系统时钟256分频,每记256-6=250个脉冲溢出一次,则每秒溢出16000000÷256÷250=250次,因此每溢出125次为500毫秒。

    2. 脉宽调制

      脉宽调制的一个典型应用是控制直流电机速度。将直流电机两极分别连接到直流电源的正负两极上,电机会以最快速度运行;要调整电机速度,一个很容易想到的方法是调整直流电源的功率,但在数字系统中还有一个更简单的方式:使用高低电平宽度不一样的脉冲信号快速开关直流电机;因为惯性的作用,电机不会以最快的速度运行。一般来说,高电平在一个脉冲周期中所占宽度更宽时,直流电机速度越快;我们把高电平所占一个脉冲周期的宽度称为占空比。

      如图所示连接电路,11PB3/OC2A)引脚通过三极管间接控制直流电机:

      用Arduino库函数输出一个脉宽调制信号十分简单,下面的示例使得直流电机由慢到快,又由快到慢反复运行:

     1 // FadingMotor.ino
     2 int motor = 11;
     3 int speed = 0;
     4 int fadeAmount = 5;
     5 
     6 void setup() {
     7   pinMode(motor, OUTPUT);
     8 }
     9 
    10 void loop() {
    11   analogWrite(motor, speed);
    12 
    13   speed = speed + fadeAmount;
    14   if (speed <= 0 || speed >= 255) {
    15     fadeAmount = -fadeAmount;
    16   }
    17 
    18   delay(30);
    19 }

      与脉宽调制相关的Arduino库函数有:

     

      analogWrite(pin, value):在指定引脚上输出一个指定占空比脉宽调制信号

      pin:指定引脚

      value:脉宽调制信号的占空比;00%255100%

     

      Timer2拥有2输出比较寄存器OCR2AOCR2B,它们通过与TCNT2寄存器发生比较匹配时对引脚置位、清零或取反来完成脉宽调制信号的输出。TCCR2A寄存器中的COM2A[1:0]位用于设置OCR2A寄存器发生比较匹配时的行为,如下表所示:

    COM2A[1:0]

    行为

    00

    正常的端口操作,OC2A未连接

    01

    WGM22=0,正常的端口操作,OC2A未连接

    WGM22=1,发生比较匹配时OC2A取反

    10

    发生比较匹配时OC2A清零,计数到下限时OC2A置位

    11

    发生比较匹配时OC2A置位,计数到下限时OC2A清零

      通过直接访问寄存器改写以上程序为:

     1 // FadingMotor_reg.ino
     2 int speed = 0;
     3 int fadeAmount = 5;
     4 
     5 void setup() {
     6   DDRB |= (1 << PB3);
     7 
     8   // 快速脉宽调制,OC2A比较匹配置位,下限清零,系统时钟
     9   TCCR2A = 0x83;
    10   TCCR2B = 0x01;
    11 }
    12 
    13 void loop() {
    14   OCR2A = speed;
    15 
    16   speed = speed + fadeAmount;
    17   if (speed <= 0 || speed >= 255) {
    18     fadeAmount = -fadeAmount;
    19   }
    20 
    21   delay(30);
    22 }

    3. 输入捕获*

      输入捕获用来计算外部输入信号的周期,它是Timer1的特有功能,Arduino官网提供了一个输入捕获的示例:

     1 // PulseIn.ino
     2 const int pin = 8;
     3 unsigned long duration;
     4 
     5 void setup() {
     6   pinMode(pin, INPUT);
     7 }
     8 
     9 void loop() {
    10   duration = pulseIn(pin, HIGH);
    11 }

      与输入捕获相关的Arduino库函数有:

     

      pulseIn(pin, value):计算指定引脚输入外部信号的周期

      pin:指定引脚

      value:捕获脉冲的类型,LOW(低电平,0V)或HIGH(高电平,5V

      函数返回外部输入信号的周期,是一个unsigned long类型的整数,单位为微秒

     

      Timer1的控制寄存器与Timer2的不尽相同,请参阅ATMega328P芯片手册16-bit Timer/Counter1 with PWM章的Register Description小节进行设置。通过直接访问寄存器改写以上程序为:

     1 // PulseIn_reg.ino
     2 unsigned long duration;
     3 unsigned long timer1_pulse_in();
     4 
     5 void setup() {
     6   DDRB &= ~(1 << PB0);
     7   PORTB &= ~(1 << PB0);
     8 
     9   // 正常模式,系统时钟8分频
    10   // 噪声抑制关闭,下降沿触发输入捕获
    11   TCCR1A = 0x00;
    12   TCCR1B = 0x42;
    13   TCCR1C = 0x00;
    14   TIMSK1 |= (1 << ICIE1) | (1 << TOIE1);
    15   sei();
    16 }
    17 
    18 void loop() {
    19   duration = timer1_pulse_in();
    20 }
    21 
    22 volatile int iOvf = 0;
    23 volatile bool isCap = false;
    24 volatile uint16_t iCap = 0;
    25 volatile uint16_t iLastCap = 0;
    26 
    27 unsigned long timer1_pulse_in() {
    28   unsigned long duration;
    29 
    30   while (!isCap);
    31   duration = 0xffff - iCap 
    32       + 0xffff * (iOvf - 1) 
    33       + iLastCap;
    34 
    35   isCap = false;
    36   return duration;
    37 }
    38 
    39 ISR(TIMER1_CAP_vect) {
    40   iCap = iLastCap;
    41   iLastCap = (ICR1H << 8) | ICR1L;
    42   
    43   isCap = true;
    44 }
    45 
    46 ISR(TIMER1_OVF_vect) {
    47   iOvf += 1;
    48 }

      事实上,本章仅仅讨论了定时/计数器子系统的典型应用,这仅仅是定时/计数器子系统强大功能的冰山一角,由于篇幅关系没有提及的部分,另请参阅ATMega328P芯片手册。 

  • 相关阅读:
    angular6 input节流
    遇到跨域别害怕,我来帮你搞定它
    angular4,6 引入第三方插件的方法
    带有动画的字体
    angular 多端打包
    css 常用布局
    chrome 49 版本 跨越 --args --disable-web-security --user-data-dir
    JavaScript中的document.cookie的使用
    ios/iphone手机请求微信用户头像错位BUG及解决方法
    Safari 前端开发调试 iOS 完美解决方案
  • 原文地址:https://www.cnblogs.com/lets-blu/p/7519297.html
Copyright © 2011-2022 走看看