找回密码
 立即注册

QQ登录

只需一步,快速开始

点击进入授权页面

只需一步,快速开始

  • QQ空间
  • 回复
  • 收藏

参加活动: 0

组织活动: 0

  • TA的每日心情
    擦汗
    2016-9-23 12:33
  • 签到天数: 170 天

    [LV.7]常住居民III

    tsaiwn 金牌会员 2015-2-24 22:48 楼主
    本帖最后由 tsaiwn 于 2015-3-14 04:27 编辑

    前面已經跟大家分享了使用硬件中斷 MsTimer2庫與 TimerOne 庫定時做多件事:
       http://www.arduino.cn/thread-12435-1-1.html
       http://www.arduino.cn/thread-12441-1-1.html

    現在再來分享自己控制 timer1 定時器, 每 0.1 ms (0.0001 秒)中斷一次(很精準的 0.1 ms),
    阿當然也是可以用來定時做多件事 !

        話不多說(因這對大多數人不容易懂), 請先測試以下範例再說 ..
    以下是對 Pin 13 上的 LED 做亮滅, 每秒閃爍一次: 亮0.5秒滅0.5秒 !

    [C] 纯文本查看 复制代码
    // 用 timer1 的中斷 ISR( TIMER1_COMPA_vect ) 控制 LED 亮滅
    // Prescaler 用 1024
    volatile int ggyy = 1;  // 使用這當 Flag 給  ISR 使用 !
    int ledPin =13;
    // For Prescaler == 1024
    //  1 秒 / (16 000 000 / 1024) = 1/15625 =  0.000064 sec / per cycle
    // 0.5 sec / 0.000064 sec -1 = 7812.5 -1 = 7812
    // const int myTOP = 15624; // 大約 1 秒 when Prescaler == 1024
    // 1.0 sec / 0.000064 sec -1 = 15625 -1 = 15624
    const int myTOP = 7812;  // 大約 0.5 秒 when Prescaler == 1024
    //////////
    /// For Prescaler == 64
    ///  1 秒 / (16 000 000 / 64) = 1/250000 =  0.000004 sec
    /// 0.1 sec / 0.000004 sec -1 = 25000 -1 = 24999
    /// const int myTOP = 24999;  // 0.1 sec when Prescaler == 64
    ///// Interrupt Service Routine for TIMER1 CTC on OCR1A as TOP
    /// 注意以下名稱是有意義的, 不可亂改 !
    ISR(TIMER1_COMPA_vect)
    {
      digitalWrite(ledPin, ggyy);  // ggyy 是 0 或 1
      ggyy = 1 - ggyy; //  給下次進入  ISR 用
    }
    void setup( ) {
      pinMode(ledPin, OUTPUT);
      digitalWrite(ledPin, LOW); // turn Off the LED
      cli();  // 禁止中斷
      TCCR1A = 0;
      TCCR1B = 0; 
      TCCR1B |= (1<<WGM12);  // CTC mode; Clear Timer on Compare
      /// CS12, CS11, and CS10 這三個 bit 都是 1 表示使用 External clock
      // ? TCCR1B |= (1<<CS11);  // External clock ???
      // ? External clock source on T1 pin. Clock on rising edge ?
      // CS12 與 CS10 都是 1 表示 Prescaler 為 1024
      // See  [url=http://www.engblaze.com/microcontroller-tutorial-avr-and-arduino-timer-interrupts/#config]http://www.engblaze.com/microcon ... -interrupts/#config[/url]
      TCCR1B |= (1<<CS10) | (1<<CS12);  // Prescaler == 1024
      /// TCCR1B |= (1<<CS10) | (1<<CS11);  // Prescaler == 64
      /////////
      OCR1A = myTOP;  // TOP count for CTC, 與 prescaler 有關
      TCNT1=0;  // counter 歸零 
      TIMSK1 |= (1 << OCIE1A);  // enable CTC for TIMER1_COMPA_vect
      sei();  // 允許中斷
    }
    void loop() {
      //... 做其他事
      // if( ggyy == 1) ...
    }
    ////////
    ///參考: 
    //   [url=http://blog.oscarliang.net/arduino-timer-and-interrupt-tutorial/]http://blog.oscarliang.net/arduino-timer-and-interrupt-tutorial/[/url]
    
    


    // 關於  prescaler 請看這:
    ///  http://www.engblaze.com/microcon ... -interrupts/#config
    //////
    // 再補充一下..
    //  Prescaler 是用來除以 CPU 所用時脈的頻率以控制 timer 定時器
    // TCCR1B 的 CS12, CS11, and CS10 這三個 bit 控制 timer1 的 Prescaler
    // 三個都是 1 表示使用 External clock
    // CS12 與 CS10 都是 1 表示 Prescaler 為 1024
    // 這樣每 count 一次用掉  1/(16*10^6 / 1024) ==  6.4e-5 seconds.
    // 所以 OCR1A= 7812; 則 7812 * 6.4e-5 seconds = 0.5 秒
    // 用 OCR1A=15624 則是 15624 * 6.4e-5 seconds = 1 秒
    // For more detail, see:
    //  http://www.engblaze.com/microcon ... -interrupts/#config
    /////////////////         //////
    ///  http://sphinx.mythic-beasts.com/~markt/ATmega-timers.html
    ///  http://maxembedded.com/2011/07/avr-timers-ctc-mode/
    ///  http://roboexperiments.com/begin ... s-interrupts-part2/
    ///

    程序看不太懂 ?
    正常啦, 很多人都看不懂 沒興趣看懂照樣可以用啦 !
    接下來看新版本,
    這次 Prescaler 改設 64,
    每 0.1 秒做一次中斷處理 ISR(TIMER1_COMPA_vect)


    [C] 纯文本查看 复制代码
    // 控制 LED 亮滅, 每秒閃爍 5 次: 亮 0.1 秒滅 0.1 秒 ...
    // Prescaler 用 64
    volatile int ggyy = 1;  // 使用這當 Flag 給  ISR 使用 !
    int ledPin =13;
    /// For Prescaler == 64
    ///  1 秒 / (16 000 000 / 64) = 1/250000 =  0.000004 sec / per cycle
    /// 0.1 sec / 0.000004 sec -1 = 25000 -1 = 24999
    const int myTOP = 24999;  // 0.1 sec when Prescaler == 64
    ///// Interrupt Service Routine for TIMER1 CTC on OCR1A as TOP
    /// 注意以下名稱是有意義的, 不可亂改 !
    ISR(TIMER1_COMPA_vect)
    {
      digitalWrite(ledPin, ggyy);  // ggyy 是 0 或 1
      ggyy = 1 - ggyy; //  給下次進入  ISR 用
    }
    void setup( ) {
      pinMode(ledPin, OUTPUT);
      digitalWrite(ledPin, LOW); // turn Off the LED
      cli();  // 禁止中斷
      TCCR1A = 0;
      TCCR1B = 0; 
      TCCR1B |= (1<<WGM12);  // CTC mode; Clear Timer on Compare
      /// CS12, CS11, and CS10 這三個 bit 都是 1 表示使用 External clock
      // ? TCCR1B |= (1<<CS11);  // External clock ???
      // ? External clock source on T1 pin. Clock on rising edge ?
      // CS12 與 CS10 都是 1 表示 Prescaler 為 1024
      // See  [url=http://www.engblaze.com/microcontroller-tutorial-avr-and-arduino-timer-interrupts/#config]http://www.engblaze.com/microcon ... -interrupts/#config[/url]
      // TCCR1B |= (1<<CS10) | (1<<CS12);  // Prescaler == 1024
      TCCR1B |= (1<<CS10) | (1<<CS11);  // Prescaler == 64
      /////////
      OCR1A = myTOP;  // TOP count for CTC, 與 prescaler 有關
      TCNT1=0;  // counter 歸零 
      TIMSK1 |= (1 << OCIE1A);  // enable CTC for TIMER1_COMPA_vect
      sei();  // 允許中斷
    }
    void loop() {
      //... 做其他事
      // if( ggyy == 1) ...
    }
    ////////
    



    看完這範例應該有點概念了吧 ?!
    再看第三個版本, 這次只改 OCR1A 用的 myTOP, 但Prescaler仍用64,
    因為 myTOP 改為 24, 每隔 0.0001 秒會做一次 ISR( )


    [C] 纯文本查看 复制代码
    // 控制 LED 亮滅, 每秒閃爍 5000 次: 亮 0.0001 秒滅 0.0001 秒 ...
    // Prescaler 用 64
    volatile int ggyy = 1;  // 使用這當 Flag 給  ISR 使用 !
    int ledPin =13;
    /// For Prescaler == 64
    ///  1 秒 / (16 000 000 / 64) = 1/250000 =  0.000004 sec / per cycle
    /// 0.1 sec / 0.000004 sec -1 = 25000 -1 = 24999
    /// 0.0001 sec / 0.000004 sec -1 = 25 -1 = 24
    const int myTOP = 24;  // 0.0001 sec when Prescaler == 64
    ///// Interrupt Service Routine for TIMER1 CTC on OCR1A as TOP
    /// 注意以下名稱是有意義的, 不可亂改 !
    ISR(TIMER1_COMPA_vect)
    {
      digitalWrite(ledPin, ggyy);  // ggyy 是 0 或 1
      ggyy = 1 - ggyy; //  給下次進入  ISR 用
    }
    void setup( ) {
      pinMode(ledPin, OUTPUT);
      digitalWrite(ledPin, LOW); // turn Off the LED
      cli();  // 禁止中斷
      TCCR1A = 0;
      TCCR1B = (1<<WGM12);  // CTC mode; Clear Timer on Compare
      TCCR1B |= (1<<CS10) | (1<<CS11);  // Prescaler == 64
      /////////
      OCR1A = myTOP;  // TOP count for CTC, 與 prescaler 有關
      TCNT1=0;  // counter 歸零 
      TIMSK1 |= (1 << OCIE1A);  // enable CTC for TIMER1_COMPA_vect
      sei();  // 允許中斷
    }
    void loop() {
      //... 做其他事
      // if( ggyy == 1) ...
    }
    ////////
    




    啥? 根本不會閃爍 !?
    對啦, 每秒閃爍 5000 次,
    Super Man 超人的眼睛也看不見啊 !
    現在每 0.1 ms (0.0001 秒) 會做 ISR( ) 一次,
    我們可以使用一個 int 或 long 變量計數, 達到某數值就做某事情 !!
    以下這第四個範例每 0.25秒閃爍 pin 13 的 LED,               
    且每 0.5 秒閃爍 pin 8 的 LED (即每秒閃爍亮燈滅燈一次) !


    [C] 纯文本查看 复制代码
    // 控制 LED on pin 13亮滅, 每秒閃爍 2 次: 亮 0.25 秒滅 0.25 秒 ...
    // LED on pin 8 每秒閃爍 1 次: 亮 0.5 秒滅 0.5 秒 ...
    const int intA = 2500;   // 2500 * 0.1 ms = 250ms
    const int intB = 5000;   // 5000 * 0.1 ms = 500ms = 0.5秒
    // Prescaler 用 64
    volatile int ggyy = 1;  // 使用這當 Flag 給  ISR 使用 !
    int ledPin =13;
    int led8 = 8;  // pin 8
    /// For Prescaler == 64
    ///  1 秒 / (16 000 000 / 64) = 1/250000 =  0.000004 sec / per cycle
    /// 0.1 sec / 0.000004 sec -1 = 25000 -1 = 24999
    /// 0.0001 sec / 0.000004 sec -1 = 25 -1 = 24
    const int myTOP = 24;  // 0.0001 sec when Prescaler == 64
    ///// Interrupt Service Routine for TIMER1 CTC on OCR1A as TOP
    /// 注意以下名稱是有意義的, 不可亂改 !
    ISR(TIMER1_COMPA_vect)
    {
       static unsigned int aaa = 0;
       static unsigned int bbb = 0;
       ++aaa; bbb++;
       if(aaa == intA){
          aaa=0; myJobOne( );
       }
       if(bbb == intB){
          bbb=0; myJobTwo( );
       }
    }
    void setup( ) {
      pinMode(ledPin, OUTPUT);
      pinMode(led8, OUTPUT); digitalWrite(led8, 1); // 故意
      digitalWrite(ledPin, LOW); // turn Off the LED
      setMyTimerOne( );
    }
    void loop() {
      //... 做其他事
      // if( ggyy == 1) ...
    }
    void myJobOne( ) {
      digitalWrite(ledPin, ggyy);  // ggyy 是 0 或 1
      ggyy = 1 - ggyy; //  給下次進入 用
    }
    void myJobTwo( ) {
      digitalWrite(led8, ! digitalRead(led8));  // Toggle led8
    }
    ////////
    void setMyTimerOne( ){
      cli();  // 禁止中斷
      TCCR1A = 0;
      TCCR1B = (1<<WGM12);  // CTC mode; Clear Timer on Compare
      TCCR1B |= (1<<CS10) | (1<<CS11);  // Prescaler == 64
      /////////
      OCR1A = myTOP;  // TOP count for CTC, 與 prescaler 有關
      TCNT1=0;  // counter 歸零 
      TIMSK1 |= (1 << OCIE1A);  // enable CTC for TIMER1_COMPA_vect
      sei();  // 允許中斷
    }
    //////////////////////
    




    好啦, 有了上面這精準度 0.1 ms 做中斷並定時做兩件事myJobOne( ) 和 myJobTwo( )的範例,
    應該很容易修改為定時做三件或更多事!
    這次我故意用不一樣的寫法做計數與檢查是否時間到要做myJob???()的時機 !
    你可以對照之前使用 MsTimer2 庫的寫法:
       http://www.arduino.cn/thread-12435-1-1.html

    以及用 TimerOne 庫的寫法:
       http://www.arduino.cn/thread-12441-1-1.html

    請注意, 在前兩篇中我們說過因為 Servo.h  庫以及 pin 9 和 pin 10  的 PWM 都是使用 timer1 定時器,
    所以, 自己控制 timer1 計時器之後, Servo.h 庫就不能用了,  還有 pin 9 和 pin 10  的 PWM 也沒有用了 !!

    更多關於中斷(interrupt)的詳細說明可以參考:
         http://gammon.com.au/interrupts
         http://maxembedded.com/2011/07/avr-timers-ctc-mode/
         http://www.uchobby.com/index.php/2007/11/24/arduino-interrupts/
         http://www.engblaze.com/microcon ... no-timer-interrupts
         http://www.avrbeginners.net/architecture/timers/timers.html
         http://playground.arduino.cc/Code/Timer1
      還有以下這三篇也很有用:
        http://sphinx.mythic-beasts.com/~markt/ATmega-timers.html
        http://maxembedded.com/2011/07/avr-timers-ctc-mode/
        http://arduino.cc/en/Tutorial/SecretsOfArduinoPWM
    ======================================


















    评分

    参与人数 1贡献 +1 收起 理由
    coloz + 1 感谢分享

    查看全部评分

    本帖最后由 tsaiwn 于 2015-3-16 15:21 编辑

    補充如何暫時停止這 timer1 的CTC中斷 ?
    很簡單:
       TIMSK1 &= ( ~ (1 << OCIE1A) );  // 禁止 CTC for TIMER1_COMPA_vect

    那要如何又重新啟動  timer1 的CTC中斷 ?
    阿也是很簡單:
       TIMSK1 |= (1 << OCIE1A); // enable CTC for TIMER1_COMPA_vect

    或是寫成 function 方便使用:

    void stopT1( ) {
        TIMSK1 &= ( ~ (1 << OCIE1A) );  // 禁止 CTC for TIMER1_COMPA_vect
    }
    void resumeT1( ) {
        TIMSK1 |= (1 << OCIE1A);  // enable CTC for TIMER1_COMPA_vect
    }


    More control about the timer1
    Also trigger ISR(TIMER1_COMPB_vect) :
    [Bash shell] 纯文本查看 复制代码
    // myTimer1AB, 同時測試 ISR(TIMER1_COMPA_vect) 與 ISR(TIMER1_COMPB_vect)
    // by tsaiwn  @  cs.nctu.edu.tw
    // 控制 LED on pin 13亮滅, 每20秒閃爍 1 次: 亮 10 秒滅 10 秒 ...
    // LED on pin 8 每2秒閃爍 1 次: 亮 1 秒滅 1 秒 ...
    const int intA = 100;   // 100 * 0.1 s = 10s
    const int intB = 10;   // 10 * 0.1 s = 1s = 1秒
    // Prescaler 用 64
    volatile int ggyy = 1;  // 使用這當 Flag 給  ISR 使用 !
    int ledPin =8;
    int led8 = 13;  // pin 8
    /// For Prescaler == 64
    ///  1 秒 / (16 000 000 / 64) = 1/250000 =  0.000004 sec / per cycle
    /// 0.1 sec / 0.000004 sec -1 = 25000 -1 = 24999
    /// 0.0001 sec / 0.000004 sec -1 = 25 -1 = 24
    //const int myTOP = 24;  // 0.0001 sec when Prescaler == 64
    const int myTOP = 24999; // 0.1sec
    // const int someBB = myTOP+3;  // someBB > myTOP 則沒用 !
    const int someBB = myTOP/3;  // 大約三分之一的時間
    ///// Interrupt Service Routine for TIMER1 CTC on OCR1A as TOP
    /// 注意以下名稱是有意義的, 不可亂改 !
    volatile unsigned long ms;
    ISR(TIMER1_COMPA_vect)
    {
       static unsigned int aaa = 0;
       static unsigned int bbb = 0;
       ms = millis( );
       ++aaa; bbb++;
       if(aaa == intA){
          aaa=0; myJobOne( );
       }
       if(bbb == intB){
          bbb=0; myJobTwo( );
       }
    }
    void setup( ) {
      Serial.begin(9600);
      pinMode(ledPin, OUTPUT);
      pinMode(led8, OUTPUT); digitalWrite(led8, 1); // 故意
      digitalWrite(ledPin, LOW); // turn Off the LED
      delay(1);
      setMyTimerOne( ); 
    }
    volatile unsigned long doP = 0;  // do print ?
    volatile unsigned long msB = 0; // channel B
    volatile int p2Val, pTwo = 0;
    const char msg[][5] = { "off", "ON" };
    void loop() {
      //... 做其他事
      // if( ggyy == 1) ...
      if(doP) {
         doP = 0;
         Serial.print("\n == OCR1A: ");
         Serial.println(ms);
      }
      if(pTwo) {
        pTwo = 0;
        Serial.print(" Job2 LED ");
        Serial.print(msg[p2Val]); // off? ON
      }
      if(msB) {  // not 0
         Serial.print("\nhit OCR1B: ");
         Serial.println(msB);
         msB = 0;
      }
    } // loop(
    void myJobOne( ) {
      digitalWrite(ledPin, ggyy);  // ggyy 是 0 或 1
      ggyy = 1 - ggyy; //  給下次進入 用
      doP = 1; // tell him to print
      //Serial.println(ms);
    }
    void myJobTwo( ) {
      pTwo = 1; // yes to print
      int kk = digitalRead(led8);
      p2Val = !kk;
      digitalWrite(led8, !kk );  // Toggle led8
    }
    ////////
    void setMyTimerOne( ){
      cli();  // 禁止中斷
      TCCR1A = 0;
      TCCR1B = (1<<WGM12);  // CTC mode; Clear Timer on Compare
      TCCR1B |= (1<<CS10) | (1<<CS11);  // Prescaler == 64
      /////////
      OCR1A = myTOP;  // TOP count for CTC, 與 prescaler 有關
      TCNT1=0;  // counter 歸零 
      TIMSK1 |= (1 << OCIE1A);  // enable CTC for TIMER1_COMPA_vect
      OCR1B = someBB;
      TIMSK1 |= (1 << OCIE1B); // enable B channel too
      sei();  // 允許中斷
    }// setMyTimerOne( 
    ISR(TIMER1_COMPB_vect) {  // channel B
       static unsigned int aaa = 0;
       if(++aaa == intA){  // 用 myJobOne() 用的 intA
          aaa=0;
          msB = millis( );
       }
    } // ISR for timer1 CTC B channel
    //////////////////////


    這麼好的文章一定要好好的推

    一般這方面的資訊大都是原文的

    不僅看不懂也沒版大如此詳細的教程及範例

    再次感謝版大的用心與付出

    謝謝 !!
    谢谢,写了这么多版本
    /// 0.0001 sec / 0.000004 sec -1 = 25 -1 = 24
    为什么减一?
    谢谢!
    楼主您好,如果我设定中断为0.1ms,在每次中断时都用analogRead()从传感器端采样了,会不会影响中断时间,analogRead()采样花多少时间呢
    发新帖
    发表评论
    高级模式  
    您需要登录后才可以回帖 登录 | 立即注册  
    关闭

    推荐主题 上一条 /4 下一条