查看: 38268|回复: 9

補充設定 timer1 計時器和 timer2 定时器定時做多件事(教程)

[复制链接]
  • TA的每日心情
    郁闷
    2019-2-20 14:52
  • 签到天数: 172 天

    [LV.7]常住居民III

    发表于 2015-2-25 14:31 | 显示全部楼层 |阅读模式
    本帖最后由 tsaiwn 于 2015-3-14 04:17 编辑

    前面跟大家分享了如何自己設定Arduino內部定时器定時做事
    包括"自己控制 timer1 計時器定時做多件事(教程)":
        http://www.arduino.cn/thread-12445-1-1.html
    以及
      "自己控制 timer2 定时器定時做多件事(教程)":
        http://www.arduino.cn/thread-12448-1-1.html

    雖然很多人對以上兩篇教程內的程序寫法看不太懂也不想搞懂,
    但是總會有人很想看懂,很想知道那些有如天書的語法到底在寫啥碗糕(saying what) ?
    所以現在我就來寫一些補充說明,以下就以對 timer1 定时器的設定原理做補充。

    要"定時"做事, 最佳方式當然要採用中斷請求(Interrupt ReQuest, IRQ),
    其實概念上很簡單, 我們日常生活中隨時都在處理"中斷請求" ! 例如:
      吃飯時突然電話響了, 接不接(要不要處理該中斷請求)?
    接著電話講話中, 突然門鈴響了, 要不要先暫停講電話先去開門看看?
    開完門繼續講電話, 講完電話阿就繼續吃飯

    Arduino 的 CPU 只能依照程序內容一次處理一個指令,
    但也被設計成每個指令做完都會"看看"有沒有中斷事件要處理!
    關於中斷的概念可以看看奈何大神寫的這篇有趣文章:
       http://www.arduino.cn/thread-2421-1-1.html

    該篇主要是介紹Arduino外部中斷的使用,
    我們這邊要講的則是透過設定 Arduino 內部定时器(大多數Arduino板子有三個定时器),
    以便在某種狀況下對 Arduino 的 CPU 發出中斷請求。
    講白話一點, 就是設定(config) Arduino 內部某個鬧鈴(例如定时器 timer1),
    時間到(條件符合)就對 CPU 發動某種中斷請求,
    以便要求 CPU 處理一下特殊程序片段。

        這特殊程序片段稱為中斷服務程序(ISR, Interrupt Service Routine),
    又稱為中斷處理程序(Interrupt Handler)。
    這中斷處理程序的寫法比較特別,
    要使用 Arduino 開發環境預先定義的巨集 ISR( ),
    例如 ISR(TIMER1_OVF_vect) { } ISR(TIMER1_COMPA_vect) { } 等等。
    當然也會用到很多預先定義的符號名稱如 TCNT1, OCR1A, WGM12, CS11, CS12 等等。
    這些奇怪的名稱要看 ATmega328 datasheet:
        http://www.atmel.com/Images/doc8161.pdf

    這是因為 Arduino 大部份板子使用 ATmega328 這 CPU 微處理器(microcontroller),
    該 CPU 總共支援 25 種不同的中斷(Interrupt),
    包括外部中斷, 三個 timer 定时器數到 Overflow 溢位的中斷,
    或是定时器數到特定值的 CTC 中斷等等,
    當然也包括我們這使用的 timer1 數到特定值之 CTC 中斷:
       ISR(TIMER1_COMPA_vect) { }

    可是,何時會發生這中斷呢 ?
        當然就是必須對 timer1 (或 timer2 timer0)做一些設定,
    就像你可以設定鬧鈴早上六點半叫你起床那樣 !!
    所謂的 CTC 中斷,
    就是設定 timer1 每次被踢一下(tick)就把計數器TCNT1加 1,
    加到某個特定值(例如等於 OCR1A)就產生對CPU中斷並把計數器TCNT1歸零。


    何謂被"踢一下"呢(何謂一個 tick) ?

        Arduino 的板子大都採用時脈 16MHz,
    就是有個石英振盪器每秒振盪16000000 次,
    也就是每秒鐘滴答(tick) 16000000 次,
    這時脈主要是給 CPU 使用, 振盪一次稱為一個 tick 或一個 cycle;
    每個 tick (cycle)需要 1/16000000 秒 = 0.0000000625 秒;
    時脈當然也可順便給 timer 定时器使用,
    但是通常為了不要這麼快, 我們不會把這時脈直接拿來踢一下 timer,
    而是先經過一個除頻器, 除頻的分母由 timer 的 Prescaler 決定!
    每個 timer 可以用不同的 Prescaler 且是可以由程序中設定的 !
    就是說我們隨時可以設定 Prescaler 除頻來給各個 timer 定时器使用;
    例如把 Prescaler 設 64, 則頻率為 16000000 次/ 64 = 250000 Hz
    就是說 timer 每隔 1/250000 秒會被"踢一下", 就是 tick 一次,
    以 timer1 為例, 此時每個 tick (1/250000)秒 TCNT1 會被加 1,
    注意 timer1 是 16bit的定时器, 所以 TCNT1 和 OCR1A 最大是 65535,
    如果到了 65535 再加 1 就變成 0 即所謂 Overflow 溢位。

    由於我的範例是設定 CTC mode(Clear Timer on Compare mode),
    如果 TCNT1 與 OCR1A 內容相同,
    則 timer1 就進入 "match" 符合狀態,
    它會 把 TCNT1 清除為 0, 並拉動 CPU 的 IRQ 發動中斷請求。
    於是 CPU 就會暫停正在做的事(當然是在允許中斷的狀態),
    改為開始執行 ISR(TIMER1_COMPA_vect) { } 這個處理程序。
    再讓大家看看我在之前給的控制 timer1 定時做兩件事的範例:
    (各自負責閃爍 pin 13 LED 與 pin 8 LED)

    kittenblock中小学创客名师推荐的图形化编程软件

    // 控制 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
    /// 注意以下名稱是有意義的, 不可亂改 !
    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( ); 
    }
    unsigned long doP = 0;  // do print ?
    void loop() {
      //... 做其他事
      // if( ggyy == 1) ...
      if(doP) {
         doP = 0;
         Serial.println(ms);
      }
    }
    void myJobOne( ) {
      digitalWrite(ledPin, ggyy);  // ggyy 是 0 或 1
      ggyy = 1 - ggyy; //  給下次進入 用
      doP = 1; // tell him to print
    }
    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();  // 允許中斷
    }
    //////////////////////
    


    程序中最重要的是以下這函數 void setMyTimerOne( ) { }:
    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();  // 允許中斷
    }

    其工作就是設定 timer1 定时器的 Prescaler 為 64,
    且使用 CTC mode, 且 TOP 上限的 OCR1A 設為 myTOP 也就是 24,
    這樣, timer1 就會每 0.1 ms 發出中斷請求,
    而 CPU 就會暫時放下正在處理的程序(會記住位置),
    並開始優先處理(執行)中斷處理程序 ISR(TIMER1_COMPA_vect) { } 內的代碼。

    再來看看做完全一樣的事,
    但是改用 timer2 定时器幫忙發出中斷請求的該範例:

    kittenblock中小学创客名师推荐的图形化编程软件

    // 使用 timer2 定时器幫忙發出中斷請求
    // 控制 LED on pin 13亮滅, 每秒閃爍 2 次: 亮 0.25 秒滅 0.25 秒 ...
    // LED on pin 8 每秒閃爍 1 次: 亮 0.5 秒滅 0.5 秒 ...
    #define bbs(x)  (1<<x)
    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 uint8_t myTOP = 24;  // 0.0001 sec when Prescaler == 64
    ///// Interrupt Service Routine for TIMER1 CTC on OCR1A as TOP
    /// 注意以下名稱是有意義的, 不可亂改 !
    ISR(TIMER2_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
      setMyTimer2( );
    }
    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 setMyTimer2( ){
      cli();  // 禁止中斷
      TCCR2A = bbs(WGM21);  // CTC mode 2; Clear Timer on Compare, see p.158-162
      TCCR2B = bbs(CS22);  // Prescaler == 64; see p.162 in datasheet 
      ///// 注意 WGM22 在 TCCR2B, 但 WGM21 與 WGM20 在 TCCR2A; 
      ///// mode 由 WGM22, WGM21, WGM20 決定 (see datasheet p.158-162)
      OCR2A = myTOP;  // TOP count for CTC, 與 prescaler 有關
      TCNT2=0;  // counter 歸零 
      TIMSK2 |= bbs(OCIE2A);  // enable CTC for TIMER2_COMPA_vect
      sei();  // 允許中斷
    }
    


    程序中最重要的是以下這函數 void setMyTimer2( ){ } :

    void setMyTimer2( ){
      cli();  // 禁止中斷
      TCCR2A = bbs(WGM21);  // CTC mode 2; Clear Timer on Compare, see p.158-162
      TCCR2B = bbs(CS22);  // Prescaler == 64; see p.162 in datasheet
      ///// 注意 WGM22 在 TCCR2B, 但 WGM21 與 WGM20 在 TCCR2A;
      ///// mode 由 WGM22, WGM21, WGM20 決定 (see datasheet p.158-162)
      OCR2A = myTOP;  // TOP count for CTC, 與 prescaler 有關
      TCNT2=0;  // counter 歸零
      TIMSK2 |= bbs(OCIE2A);  // enable CTC for TIMER2_COMPA_vect
      sei();  // 允許中斷
    }

      其工作就是設定 timer2 定时器的 Prescaler 也是為 64,
    且使用 CTC mode, 且 TOP 上限的 OCR2A 設為 myTOP 也就是 24,
    這樣, timer2 就會每 0.1 ms 發出中斷請求,
    而 CPU 就會暫時放下正在處理的程序(會記住位置),
    並開始優先處理(執行)中斷處理程序 ISR(TIMER2_COMPA_vect) { } 內的代碼。
    程序中的 bbs(CS22) 其實就是 (1 << CS22), 因前面我有#define bbs() 巨集;
    至於 WGM21, CS22, TCNT2, OCR2A, TIMSK2 等這些都是事先被定義的符號,
    都有特定意義, 有些是代表整數, 有些是代表 timer 的特殊暫存器(寄存器)。

    // 關於 timer1 和 timer2 的設定為 CTC mode 與 Prescaler 之設定,
    // 在這再簡單做個總結:
    //    Prescaler 是用來除以 CPU 所用時脈的頻率以控制 timer 定時器;
    //    CTC mode 就是數到 TOP 值 (OCR1A, OCR2A)  就產生中斷並重新計數 !
    /////
    // TCCR1B 的 CS12, CS11, and CS10 這三個 bit 控制 timer1 的 Prescaler
    /// CS12 CS11 CS10 為 011 表示 Prescaler 為 64 (See datasheet p.137)
    /// 設定 CTC mode 要把 WGM12, WGM11, WGM10 設為 1 0 0
    ///  記得要先做   TCCR1A = 0;
    ///   WGM12 與 CS11 和 CS10 都在 TCCR1B 內!!
    ////// 至於 timer2 則略為不同.. (CTC mode 的設定也不同!)
    // TCCR2B 的 CS22, CS21, and CS20 這三個 bit 控制 timer2 的 Prescaler
    //  CS22 CS21 CS20 為 100 表示 Prescaler 為 64  (See datasheet p.162)
    /// 設定 CTC mode 要把 WGM22, WGM21, WGM20 設為 0 1 0
    /// 但是, 注意 WGM21 是在 TCCR2A 內!
    // For more detail, see:
    ///   http://www.engblaze.com/microcon ... -interrupts/#config
    /// And the datasheet for ATmega328 :
    ///   http://www.atmel.com/Images/doc8161.pdf  (P.158-162)
    /// ======================

    Q: 處理中斷程序時可不可以再受接新的中斷請求 ?
    A: 可以, 但原則上新來的中斷會進入排隊狀態 !
       這是因為進入 ISR 會立即自動禁止其他中斷請求,
       直到 ISR 程序全部做完才會再開放中斷請求 !!
       但你可以下達允許新中斷的指令, 以上述範例來說,
       如果你的 myJobOne( ) 或 myJobTwo( ) 做太久,
       希望在裡面允許中斷,
       則你可以在你的 myJobOne( ) 以及 myJobTwo( )裡面寫:
         sei( );
       這樣會允許其他中斷請求來打斷目前的處理程序,
       可是要注意, 如果中斷來得太快, 這樣很可能會因旯不及處理而出問題,
       而且還有重複進入同一個中斷處理程序還有變量被毀以及是否可共用的問題,
       當然後果要你自己負責囉

    Q: 如果同時有兩個定时器發出中斷請求會怎樣?
    A: CPU在每個指令做完之後如果允許中斷就會檢查是否有任何中斷請求,
       使用 16MHz 的CPU每個 tick (或稱 cycle)是0.0000000625 秒,
        每個基本指令大約兩三個 tick (cycle), 也就是大約0.0000002 秒以內;
        即使是比較花時間的中斷處理,
        從進入 ISR (23 ticks)到離開 ISR (19 ticks)(假設 ISR內都不做事),
        也才大約 42 ticks大約 0.0000027 秒 = 2.7 micro seconds,
       ( 參考  http://gammon.com.au/interrupts )
      這麼短的時間要同時發生兩個中斷請求的機率不高 !
       但既然說機率不高就表示總是有機會"同時發生" !
           這時 CPU 會優先處理優先權(Priority)比較高的中斷請求,
       也就是說 CPU 對於各種中斷請求的處理是有優先順序的,
       原則上 timer2 的中斷請求比 timer1 的中斷請求優先,
       而 timer1 的中斷請求又比 timer0 的中斷請求優先,
       還有, 計數器 Overflow 的中斷請求又比計數器 CTC 的中斷請求優先。
       詳細請看 ATmega328 datasheet:
          http://www.atmel.com/Images/doc8161.pdf
       或看這:
          http://gammon.com.au/interrupts#reply0

      如果你想使用外部中斷, 可以參看這:
        http://www.bristolwatch.com/arduino/arduino_irq.htm

      以及看看奈何大神寫的這篇有趣文章:
        http://www.arduino.cn/thread-2421-1-1.html


    Q: Timer定时器的 Prescaler 可以設多少?
    A: Prescaler 是一個整數,
       用來把原先給 CPU 的時脈(Clock)除頻之後給 timer 使用;
       各個 timer 的 Prescaler 各自可以設定不同的整數:
         timer0 可以設 1, 8, 64, 256, 1024
         timer1 可以設 1, 8, 64, 256, 1024
         timer2 可以設 1, 8, 32, 64, 128, 256,1024
        請看這 datasheet:
         http://www.atmel.com/Images/doc8161.pdf
        (a)timer1 see P.137 Table 15-5
        (b)timer2 see p.162 Table 17-9
        (c)timer0 see p.110 Table 14-9
       ** 我故意把 timer0 寫在第三項,
         因為通常我們不能亂改timer0的 Prescaler !
         Arduino開機後設 timer0 的 Prescaler 為64,
         這 timer0 控制 millis( ), micros( ), 以及 delay( ),
         在 16MHz 的clock下, 使得 timer0 Overflow 時間為 1.024 ms(每數256次),
         所以每隔1.024ms執行一次中斷程序 SIGNAL(TIMER0_OVF_vect);
         如果你改了Prescaler則這三個 function 都會變不準確 !

       (不過 delayMicroseconds( )不受影響, 因它不是靠中斷處理!)
    More Reference:
       http://letsmakerobots.com/node/28278



  • TA的每日心情
    郁闷
    2019-2-20 14:52
  • 签到天数: 172 天

    [LV.7]常住居民III

     楼主| 发表于 2015-2-25 21:28 | 显示全部楼层
    本帖最后由 tsaiwn 于 2015-2-25 21:32 编辑

    為了方便大家閱覽,
    我把在以下兩篇中對於暫停/繼續 timer1 和 timer2 的 CTC 中斷之補充也抓過來:
      "自己控制 timer1 計時器定時做多件事(教程)":
        http://www.arduino.cn/thread-12445-1-1.html
    以及
      "自己控制 timer2 定时器定時做多件事(教程)":
        http://www.arduino.cn/thread-12448-1-1.html

    補充如何暫時停止這 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 chanel A
    }
    void resumeT1( ) {
        TIMSK1 |= (1 << OCIE1A); // enable CTC for TIMER1_COMPA_vect
    }

    參看 ATmega328 的 datasheet:

      http://www.atmel.com/Images/doc8161.pdf  (P.134-139)

    ====================================

    補充如何暫時停止 timer2 的 CTC中斷 ?
    也是很簡單:
       TIMSK2 &= ( ~ (1 << OCIE2A) );  // 禁止 CTC for TIMER2_COMPA_vect

    那要如何又重新啟動  timer2 的CTC中斷 ?
    阿也是很簡單:
       TIMSK2 |= (1 << OCIE2A); // enable CTC for TIMER2_COMPA_vect

    或是寫成 function 方便使用:

    void stopT2( ) {
        TIMSK2 &= ( ~ (1 << OCIE2A) ); // 禁止 CTC for TIMER2_COMPA_vect
    }
    void resumeT2( ) {
        TIMSK2 |= (1 << OCIE2A);  // enable CTC for ISR(TIMER2_COMPA_vect)
    }

    參看 ATmega328 的 datasheet:

      http://www.atmel.com/Images/doc8161.pdf  (P.158-162)


    再提醒,
    不論是使用 timer1 或是 timer2 內部計時器(定时器),
    這裡的兩個範例都是設成每 0.1 ms 產生一次中斷 !

    如果你希望每隔 23.8 ms 做一次 yourJob( );
    那只要在 ISR( ) 內加入如下即可:
       static int y49 = 0;
       ++y49;
       if(y49 == 238) {  //
    238 * 0.1ms = 23.8 ms
          y49 = 0;
          yourJob( );
       }


      這兩個範例的中斷請求精準度是 0.1 ms,
    這是因為Prescaler 設 64,
    代表 TOP 的 OCR1A 和 OCR2A 設 24,
    以 CPU 使用時脈 16 MHz 來計算, 除頻 Prescaler 64, 結果:
    tick =  1 秒 / (16 000 000 / 64) = 1/250000 =  0.000004 sec / per cycle
    (24+1) * 0.000004 sec = 0.0001 sec = 0.1 ms

    因為以上除法並沒有餘數, 所以沒有誤差, 這 0.1ms 會很精準 !


      既然每隔 0.1ms 才來中斷處理程序一次,
    當然沒辦法設定每隔比 0.1ms 還小的時間做某事 !

       如果你想要比 0.1ms 還短的時間產生中斷一次,
    那你需要改 OCR1A 和 OCR2A and/or
    改變 Prescaler 以產生更快的中斷 !

  • TA的每日心情
    奋斗
    2019-10-29 17:14
  • 签到天数: 36 天

    [LV.5]常住居民I

    发表于 2015-3-3 17:33 | 显示全部楼层
    好文章,感谢分享,学习一下。
  • TA的每日心情
    郁闷
    2016-12-22 19:41
  • 签到天数: 9 天

    [LV.3]偶尔看看II

    发表于 2015-3-4 00:02 | 显示全部楼层
    大神,请教:
    1.如果设置Prescaler == 16 ,那么是不是每次中断间隔时间正好是 1 微秒?
    2.请问如何设置 Prescaler == 16 ?
    3.想自己做一个超声波发射,如果时间1微秒中断--控制13秒发射/截止,则周期为26微秒,频率为38k左右,因为接收端用cx20106红外38k接收芯片。不知道我理解的对不对,这样设计是否合理?
    请多指教,谢谢!
  • TA的每日心情
    郁闷
    2019-2-20 14:52
  • 签到天数: 172 天

    [LV.7]常住居民III

     楼主| 发表于 2015-3-4 10:29 | 显示全部楼层
    zjhyhky 发表于 2015-3-4 00:02
    大神,请教:
    1.如果设置Prescaler == 16 ,那么是不是每次中断间隔时间正好是 1 微秒?
    2.请问如何设置 Pr ...


    (1)Prescaler 沒有16, 且各個 timer 不相同:
       timer1 只有 1, 8, 64, 256, 1024
       timer2 只有 1, 8, 32, 64, 128, 256,1024
      請看這 datasheet:
      http://www.atmel.com/Images/doc8161.pdf
      (a)timer1 see P.137 Table 15-5
      (b)timer2 see p.162 Table 17-9
    (2)至於 timer0通常我們不能亂改它的 Prescaler
       Arduino開機後設timer0的 Prescaler為64,
       這 timer0 控制 millis( ), micros( ), 以及 delay( ),
       如果改了Prescaler則這三個都會變不準確 !
       (不過 delayMicroseconds( )不受影響, 因它不是靠中斷處理!)
       Timer0 的 Prescaler可以是 1, 8, 64, 256, 1024
       See p.110 Table 14-9
    (3)Prescaler 不是決定中斷時間的唯一因素:
       (a)如果是 Overflow mode, timer0 和 timer1 要乘以 256,
          因為要從 0 算到 255 再次加 1 變回 0 才發出中斷;
          如果是 timer1 要從0數到65535再加 1 變回 0 才發出中斷!
       (b)如果是 CTC mode, 數幾次才發中斷由OCR0A/OCR1A/OCR2A
          (或OCR0B/OCR1B/OCR2B)的值決定!
    (4)如果你說的 1 微秒是 1 micro second ( 1 us),
       則根本不可能!
       因為一個 ISR( )從發生中斷到跳入 ISR( ),
       即使都不做事, 立即離開 ISR( ),
       這樣加起來就要大約 42 clock大約 0.0000027 秒 = 2.7 micro seconds
       請仔細看我在這教程的 Q&A "Q: 如果同時有兩個定时器發出中斷請求會怎樣?"
    (5)Arduino用 16MHz 狀況下,
       每個 clock 是 0.0000000625 秒 = 0.0625us
       所以 42 * 0.0625 us = 2.7 us
    (6)timer1是 16 bit counter; timer2 和 timer0 都是 8 bit counter
       
  • TA的每日心情
    郁闷
    2016-12-22 19:41
  • 签到天数: 9 天

    [LV.3]偶尔看看II

    发表于 2015-3-4 17:01 | 显示全部楼层
    tsaiwn 发表于 2015-3-4 10:29
    (1)Prescaler 沒有16, 且各個 timer 不相同:
       timer1 只有 1, 8, 64, 256, 1024
       timer2 只有 1, 8, ...

    非常感谢大神的详细解答,英文看不太懂,只能继续请教:
    1.想做个超声波测距用在物理实验课,目标是每20毫秒测量一次,每次测量需要发射40khz的8个脉冲。我本来设想的是timer1用来产生40khz的脉冲,timer2用来控制20毫秒的测量频率。请问是否合理?
    2.如果按您所说,us级的时间无法控制,那么请问arduino要产生40khz的方波,应该怎么做?
  • TA的每日心情
    郁闷
    2019-2-20 14:52
  • 签到天数: 172 天

    [LV.7]常住居民III

     楼主| 发表于 2015-3-4 23:07 | 显示全部楼层
    zjhyhky 发表于 2015-3-4 17:01
    非常感谢大神的详细解答,英文看不太懂,只能继续请教:
    1.想做个超声波测距用在物理实验课,目标是每20 ...


    (1)發射脈沖可以設定 timer 去直接對某 pin 做,
       這樣可以很準, 類似 PWM 輸出
    (2)可以用 timer1 控制20毫秒的測量頻率(產生 CTC 中斷),
       然後設定 timer2 去直接控制 pin 11 或 pin 3 做類似 PWM 輸出
    (3)或是照你說的, 用 timer2 控制20毫秒的測量頻率(產生 CTC 中斷),
       然後設定 timer1 去直接控制 pin 9 或 pin 10 做類似 PWM 輸出
    請注意各 timer 所接的 pin 腳是固定的(如上所述)
    所以, 例如採用(2), 建議使用 pin 11,
    (A)先算好 用 timer2 發出方波所需的 Prescaler
       並對 timer2 設定
    (b)設定 timer1 的 Prescaler 與 CTC mode 所需的 OCR1A 並設定
       以便每 20毫秒 產生中斷去執行 ISR(TIMER1_COMPA_vect)
    (c)在 ISR(TIMER1_COMPA_vect) { } 內:
       啟動 timer2 對 pin 11 發出你要的方波8個脈沖
       然後根據 8個脈沖 所需時間就停止結束 ISR

    你可以參考這篇裡面的討論:
      http://forum.arduino.cc/index.php?topic=102430.0
    只能幫你到這囉
    加油 !
  • TA的每日心情
    郁闷
    2019-2-20 14:52
  • 签到天数: 172 天

    [LV.7]常住居民III

     楼主| 发表于 2015-3-11 15:16 | 显示全部楼层
    本帖最后由 tsaiwn 于 2015-3-11 15:33 编辑

    補充一下,
    有人來信問說設定了 0.1ms 產生一次中斷,
    並在中斷程序內只有做:
        v = analogRead( );
        ++n;
        tot += v;

    其中 v, n, tot 都是全局變量(Global variable), 都是 volatile,
    然後發現似乎時間很不準確 ?!
    問說為什麼 ?

    這個是當然會有問題了 !
    因為根據官網說明,
    每次 analogRead( ) 大約要 100us = 0.1ms,
    我自己實際測則每秒只能 analogRead( ) 大約 8920次, 每次超過 0.11ms
    這樣你把中斷設 0.1ms 來一次,
    那不是擺明了一定來不及 !?

    不過, 如果你要加快 analogRead( ), 請看以下我寫的這篇:
    如何加快analogRead速度提高采樣率Sampling Rate?
       http://www.arduino.cn/thread-12569-1-2.html

    该用户从未签到

    发表于 2016-3-7 18:38 | 显示全部楼层
    本帖最后由 Bayoscar 于 2016-3-7 18:53 编辑

    楼主您好,拜读了您的一些帖子,真是受益匪浅,大都是我正需要用到的。
    但我在计时器停止工作和回复工作时遇到了一些问题,您能帮我看看吗?

    void stopT1( ) {
        TIMSK1 &= ( ~ (1 << OCIE1A) ); // 禁止 CTC for chanel A
    }
    void resumeT1( ) {
        TIMSK1 |= (1 << OCIE1A); // enable CTC for TIMER1_COMPA_vect
    }
    这是您在这篇帖子下写的关闭停止CTC中断和恢复CTC中断的句子。
    但我在使用时发现这两句并没有起到作用,我本意是想使得pin9(与timer1相关联的引脚)停止输出方波(设置了CTC模式输出一定频率的方波)。
    结果我把这个stopT1()函数放到我语句中,发现连接PIN9的示波器并没有变化,仍然在输出方波
    是否是我错误地理解了您的CTC中断和恢复CTC中断的意思。

    在您要把OCIE1A位取0时,这样写不知道是否可以呢 ?TIMSK1 = ~ (1 << OCIE1A)
    我看您在前面还加了个位取0,这是必要的吗?


    因为按照一些人赋1的习惯直接就用
    TIMSK1 = _BV(OCIE1A)
    直接就把OCIE1A位赋1了,
    #define _BV(bit) (1 << (bit))
    还麻烦您不吝赐教。


  • TA的每日心情
    奋斗
    2016-6-14 13:12
  • 签到天数: 2 天

    [LV.1]初来乍到

    发表于 2016-6-13 16:41 | 显示全部楼层
    在您要把OCIE1A位取0时,这样写不知道是否可以呢 ?TIMSK1 = ~ (1 << OCIE1A)

    TIMSK1 = ~ (1 << OCIE1A)这个操作的结果会把OCIE1A置0,但也会把剩下的其他7位都置1。
    楼主的 TIMSK1 &= ( ~ (1 << OCIE1A) )是把OCIE1A位置0,其他7位保持原样。
    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    热门推荐

    新手求助,github上一个arduino开源CG重心称的问题
    新手求助,github上一个ar
    在github找到一个开源CG重心称的代码,并按文档说明搭建了硬件环境。发现程序在串口监
    求助为什么下载开发板出错???
    求助为什么下载开发板出错
    网络,网址都没错,为什么下载开发板出错???谢谢
    自制arduino时,usb周围电路搞不懂
    自制arduino时,usb周围电
    最近在画搭载atmega 32u4的arduino 兼容板,参考的arduino micro的设计,但电路知识贫
    关于DS1302.h
    关于DS1302.h
    去年买了个ARDUINO没怎么玩(就是不会玩),然后今天看见了例程中有一个DS1302.h时钟
    小爱控制ws2812,能开灯,调亮度、颜色,就是不能关灯
    小爱控制ws2812,能开灯,
    小爱控制ws2812,能开灯,调亮度、颜色,就是不能关灯,跑的例程不行,改了代码也是不
    Copyright   ©2015-2016  Arduino中文社区  Powered by©Discuz!   
    快速回复 返回顶部 返回列表