使用TimerOne程式庫改寫Arduino交流電調光器程式

本文旨在修訂《超圖解Arduino互動設計入門》附錄D的交流電調光器程式。最近再次做實驗時,發現書本的diyD_4.ino程式檔,有時候會讓燈泡不停閃爍,我不確定是否是delayMicroseconds()指令的時間誤差所導致(請參閱3-7頁)。後來在arduino.cc官方論壇上,看到AC Light Dimming這篇討論,並採用TimerOne程式庫來改寫交流電調光器程式,問題就解決了。

Arduino的ATmega328微處理器內部具有三個計時器(timer),TimerOne程式庫集合了一組用於設置和運用微處理器Timer1計時器的程式碼,最基本的用法就是讓程式定時去觸發執行某一項工作。

定時點滅LED

本節將以TimerOne程式庫提供的 LED閃爍程式,來說明此程式庫的使用方式。請先下載TimerOne程式庫,將它解壓縮之後,重新命名成“TimerOne”,存入Arduino應用程式路徑裡的”libraries”資料夾裡面。 Mac電腦的使用者,請存放在「文件」裡面的“Arduino/libraries”路徑底下:

安裝TimerOne程式庫

使用TimerOne程式庫所提供的各項指令之前,必須先執行底下的敘述,進行初始化:

Timer1.initialize(微秒);

其中的「微秒」參數,最大可能值是8388480(約8.3秒),若不設定參數,則採用預設值1000000(1秒)。

初始化之後,即可透過attachInterrupt(直譯為「附加中斷」)指令,設定要定時觸發的自訂函數(或者說「中斷常式」),第二個「微秒」參數是選擇性的,可不填寫。

Timer1.attachInterrupt(自訂函數, 微秒);

請選擇 Arduino主功能表的「檔案→範例→TimerOne→ISRBlink」範例程式,其主程式片段如下:

TimerOne範例程式的主程式碼

透過XOR(互斥或)來達成切換開關功能

ISRBlink範例的timerIsr()自訂函數當中,包含一段開、關第13腳LED的敘述,它把目前第13腳的狀態(0或1),和1做XOR運算(指令寫法:^),因此每一次執行這個敘述,第13腳的輸出就會和上一次相反

XOR的邏輯符號及其意義,說明如下:

XOR(互斥或)運算

底下是自訂函數timerIsr()的內容說明:

自訂函數timerIsr()的內容

補充說明,Timer1計時器也負責控制數位9和10腳Arduino Mega板則是11, 12和13腳)的PWM頻率,所以,採用此程式碼時,不要將控制輸出接在這些數位腳。

使用TimerOne改寫交流電調光程式

本單元程式的原理如下(請搭配《超圖解Arduino互動設計入門》附錄D,D-11頁上面的圖說),我們將設置一個每隔65微秒觸發執行的程式,每執行一次,就將變數i值加1,並且判斷i值是否等於或大於調光變數dim。

TimerOne交流電訊號觸發TRIAC說明

如果i值大於或等於dim變數值,隨即觸發TRIAC,否則, TRIAC維持在關閉狀態。完整的程式碼如下(電路和附錄 D-13頁相同):

/*
 本範例程式改寫自arduino.cc官方論壇的這一篇文章:
 http://forum.arduino.cc/index.php?PHPSESSID=1mlmloei1vpish99327r5gfol0&topic=22512.15
   
 原者:
 Ryan McLaughlin <ryanjmclaughlin@gmail.com> 
*/
   
#include <TimerOne.h>

/*
 設定一個充當計算關閉TRIAC的延遲時間的「計數器」,
 其值將在每65微秒增加1,
 當i的值增加到大於或等於dim(調光值)時,
 再令TRIAC開啟。
*/
volatile int i=0;
volatile boolean zeroCross=0;  // 儲存零交越狀態的變數
const byte acPin = 3;          // TRIAC訊號輸出接腳
const byte potPin = A0;        // 可變電阻的接腳
int dim = 64;                  // 調光器的階段值 (0-128) , 128代表關閉。

void setup() {
   pinMode(acPin, OUTPUT);                     // TRIAC的控制輸出腳
   attachInterrupt(0, zeroCrossISR, RISING);   // 偵測零交越訊號
   /* 
     執行TimerOne程式庫裡的Timer1定時觸發程式,
     參數65代表定時器的運作週期是65微秒。
   */
   Timer1.initialize(65); 
   // 設定讓定時器每隔65微秒,自動執行dim_check函數。
   Timer1.attachInterrupt(dim_check); 
}
   
// 每當偵測到零交越點,底下的函數就會被執行
void zeroCrossISR() {
   zeroCross = true; 
   i=0;
   digitalWrite(acPin, LOW);  // 關閉TRIAC
}

// 底下的函數將每隔65微秒觸發一次
void dim_check() { 
   if(zeroCross) {      	    // 若已經過零交越點.... 
      if(i>=dim) {      	    // 判斷是否過了延遲觸發時間...
         digitalWrite(acPin, HIGH); // 開啟TRIAC 
         i=0;                       // 重設「計數器」 
         zeroCross=false;
      } else {
         i++;  	// 增加「計數器」 
      } 
   } 
}

void loop() {
   // 讀取可變電阻的值(0~1023),除以8可得到128階段值。 
   dim = analogRead(potPin) / 8;
}

延伸閱讀

Posts created 467

55 thoughts on “使用TimerOne程式庫改寫Arduino交流電調光器程式

  1. 趙先生:
    I had read your book “超圖解 Arduino 互動設計入門”. I plan to add SDcard into my system to record the temperature and humility data. Can you share your idea about what kind of hardware and Arduino code can meet the requirement?
    Thanks!
    Best Regards,
    Phillip

  2. 請問一下如要要控制的燈泡大於2個的話
    如何去執行?
    我嘗試過似乎都只抓到一個

    1. 是的 接兩個燈泡 在控制兩個模組這樣可行嗎?

      謝謝解答

    2. 接兩組交流電調光器的情況,零交越偵測電路只需要一組(因為它們都接到相同的交流電來源,觸發時機一致),因此第二組應該只需要如下的電路,但我沒有驗證過:

      TRIAC調光器控制電路

      每一組都需要個別計算TRIAC開啟和關閉的時間,所以要設定兩組相關變數。即便使用兩組相同的TRIAC控制電路,只需要將其中一組的零偵測輸出,接到中斷腳(數位2),此外:

      假設第一組的TRIAC訊號輸出接在數位腳3;可變電阻接在A0。
      假設第二組的TRIAC訊號輸出接在數位腳5;可變電阻接在A1。

      程式修改如下(編譯無誤,但我手邊沒有多餘的TRIAC控制模組,因此未實際驗證):

      #include 
      
      volatile int i=0;
      volatile int j=0;
      volatile boolean zeroCross1=false;  // 儲存零交越狀態1的變數
      volatile boolean zeroCross2=false;  // 儲存零交越狀態2的變數
      const byte acPin1 = 3;          // TRIAC 1訊號輸出接腳
      const byte acPin2 = 5;          // TRIAC 2訊號輸出接腳
      const byte potPin1 = A0;        // 可變電阻的接腳
      const byte potPin2 = A1;        // 可變電阻的接腳
      int dim1 = 64;                  // 調光器的階段值 (0-128) , 128代表關閉。
      int dim2 = 64;                  // 調光器的階段值 (0-128) , 128代表關閉。
      
      void setup() {
         pinMode(acPin1, OUTPUT);       // TRIAC 1的控制輸出腳
         pinMode(acPin2, OUTPUT);       // TRIAC 2的控制輸出腳
         attachInterrupt(0, zeroCross1ISR, RISING);   // 偵測零交越訊號(中斷0,數位腳2)
         /* 
           執行TimerOne程式庫裡的Timer1定時觸發程式,
           參數65代表定時器的運作週期是65微秒。
         */
         Timer1.initialize(65); 
         // 設定讓定時器每隔65微秒,自動執行dim_check函數。
         Timer1.attachInterrupt(dim_check); 
      }
         
      // 每當偵測到零交越點,底下的函數就會被執行
      void zeroCross1ISR() {
        // 第一組TRIAC
         zeroCross1 = true; 
         i=0;
         digitalWrite(acPin1, LOW);  // 關閉TRIAC 1
        // 第二組TRIAC   
         zeroCross2 = true; 
         j=0;
         digitalWrite(acPin2, LOW);  // 關閉TRIAC 2
      }
      
      // 底下的函數將每隔65微秒觸發一次
      void dim_check() { 
         if(zeroCross1) {      	    // 若已經過零交越點.... 
            if(i>=dim1) {      	    // 判斷是否過了延遲觸發時間...
               digitalWrite(acPin1, HIGH); // 開啟TRIAC 
               i=0;                       // 重設「計數器」 
               zeroCross1=false;
            } else {
               i++;  	// 增加「計數器」 
            } 
         }
        // 第二組
        if(zeroCross2) {      	    // 若已經過零交越點.... 
            if(j>=dim2) {      	    // 判斷是否過了延遲觸發時間...
               digitalWrite(acPin2, HIGH); // 開啟TRIAC 
               j=0;                       // 重設「計數器」 
               zeroCross2=false;
            } else {
               j++;  	// 增加「計數器」 
            } 
         }
      }
      
      void loop() {
         // 讀取可變電阻的值(0~1023),除以8可得到128階段值。 
         dim1 = analogRead(potPin1) / 8;
         dim2 = analogRead(potPin2) / 8;
      }
      

      thanks,
      jeffrey

    1. hi zhang:

      如果你可以用一般的可調光燈具控制那個LED燈泡的話,用這個電路也行,但我沒有測試過。請再告訴我結果,謝謝!

      thanks,
      jeffrey

    2. 沒問題
      目前是考慮這顆http://24h.pchome.com.tw/prod/DEAX3T-A82854337

    3. 感謝告知!不過,我以為這LED燈泡的操作結果會跟普通燈泡一樣,原來只分成三段調光啊~

      thanks,
      jeffrey

  3. 記得以前電力電子學教的TRIAC好像是只要觸發他導通,就會持續導通,直到流過
    的電流為零時就自動OFF掉了,也就是通過交越點就不導通了

    1. hi jian:

      是的,電晶體的B極必須要持續流入電流才能讓C, E極導通;TRIAC和SCR的閘極(G)則類似拴鎖,只要觸發一下,它就維持開啟狀態,直到A1和A2極的電流為0就自動關閉了。

      所以程式只需要計算從零交越點到觸發閘極的延遲時間,進而控制TRIAC的導通時間。

      thanks,
      jeffrey

    2. 請教
      有可能設定成外部中斷時(交越時)TIMER1才開始起動計時,計時器溢位中斷時停止計時再觸發TRIAC一小段時間
      然後計時的時間長度是可變的,我想以紅外線遙控器按上下或+ – 來控制觸發延遲的時間
      不曉得這樣可行嗎?

    3. 上文程式的亮度值由dim變數決定,而該變數值的來源是可變電阻的分壓值,您只要將它改成透過紅外線訊號改變dim值即可。

      thanks,
      jeffrey

  4. 遙控器控制調光器試做成功了..
    但是我照圖接triac不會動,似乎是書頁D-10中左手邊A1,A2
    錯誤造成的,我對調2腳就OK了
    書本的腳位與DATASHEET不同喲..
    BTA12-600B,MOC3020M我只買到BTA12-600C,MOC3020

    1. 感謝告知!TRIAC是雙向導通,而且我們也無法預知是正半波或負半波先導通,所以它們應該沒有分別。

      thanks,
      jeffrey

  5. 您好,請問一下
    如果以1N4004替代,電阻45k以兩顆91K並聯代替,但是最後輸出沒有電壓(輸入有110V,並聯電阻也有),可能是什麼原因。

    1. hi bj:

      請問4N25有輸出訊號嗎?可在上文程式第44行:

      if(zeroCross) {

      底下加入一個Serial.println()敘述,在序列埠監控視窗隨意輸出一些訊息,看看中斷是否有作用。

      thanks,
      jeffrey

  6. 您好,請問該如何在if(zeroCross) {底下加入一個Serial.println(),能詳細說明嗎

    另外好像在BTA12-600B的A1時還有電壓(接A1燈泡會量)但接A2燈泡不會亮
    是什麼原因

    謝謝

    1. 接A2腳不會亮,是因為TRIAC沒有導通;請將setup()函數改成:

      void setup() {
         Serial.begin(9600);  // 初始化序列埠
         pinMode(acPin, OUTPUT);
         attachInterrupt(0, zeroCrossISR, RISING);
      
         Timer1.initialize(65); 
         Timer1.attachInterrupt(dim_check); 
      }
      

      dim_check()函數改成:

      void dim_check() { 
         if(zeroCross) {
            Serial.println("crossed!"); // 輸出"crossed!"
            if(i>=dim) {
               digitalWrite(acPin, HIGH);
               i=0; 
               zeroCross=false;
            } else {
               i++;
            } 
         } 
      }
      

      如果電路正常,序列埠監控視窗將會不停地呈現”crossed!”訊息。

      關於序列埠以及相關指令應用,請參閱5-17頁。

      thanks,
      jeffrey

  7. Hi Jeffrey,

    我看了你的調光程式,是用市電60HZ頻率分割128等份,每等份65us. 若是這個程式要能同時使用在不同頻率的電源下. 可否請教您的想法及建議. 謝謝.

    1. hi andy:

      我認為有兩個作法,一個是先用兩個變數儲存間隔時間,然後在電路上安排一個切換開關,決定要採用50Hz或60Hz模式。

      另一個可行的方式,程式碼可以從零交越點的觸發時間,回推得知目前的交流電頻率,再去計算相關的延遲時間,這樣應該能達成自動辨別與切換的功能。

      thanks,
      jeffrey

  8. 請問我如何把可變電阻的值改成速度的值,然後利用TimerOne函示庫結合再一起,如何計算顯示出超音波的速度與時間??以下是我利用超音波程式與TimerOne結合 是哪裡出問題了??
    #include

    /*
    設定一個充當計算關閉TRIAC的延遲時間的「計數器」,
    其值將在每65微秒增加1,
    當i的值增加到大於或等於dim(調光值)時,
    再令TRIAC開啟。
    */
    volatile int i=0;
    volatile boolean zeroCross=0; // 儲存零交越狀態的變數
    const byte acPin = 12; // TRIAC訊號輸出接腳
    const byte potPin = A0; // 可變電阻的接腳
    #define trigPin 12
    #define echoPin 13
    int time=0;
    int dim = 64; // 調光器的階段值 (0-128) , 128代表關閉。

    void setup() {
    Serial.begin (9600);
    pinMode(trigPin, OUTPUT);
    pinMode(echoPin, INPUT);
    pinMode(acPin, OUTPUT); // TRIAC的控制輸出腳
    attachInterrupt(0, zeroCrossISR, RISING); // 偵測零交越訊號
    /*
    執行TimerOne程式庫裡的Timer1定時觸發程式,
    參數65代表定時器的運作週期是65微秒。
    */
    Timer1.initialize(65);
    // 設定讓定時器每隔65微秒,自動執行dim_check函數。
    Timer1.attachInterrupt(dim_check);

    }

    // 每當偵測到零交越點,底下的函數就會被執行
    void zeroCrossISR() {
    zeroCross = true;
    i=0;
    digitalWrite(acPin, LOW); // 關閉TRIAC
    }

    // 底下的函數將每隔65微秒觸發一次
    void dim_check() {
    if(zeroCross) { // 若已經過零交越點….
    if(i>=dim) { // 判斷是否過了延遲觸發時間…
    digitalWrite(acPin, HIGH); // 開啟TRIAC
    i=0; // 重設「計數器」
    zeroCross=false;
    } else {
    i++; // 增加「計數器」
    }
    }
    }

    void loop() {
    float duration, distance;
    int intervaltime=500; //每幾毫秒測量一次
    digitalWrite(trigPin, HIGH);
    delayMicroseconds(1000);
    digitalWrite(trigPin, LOW);
    duration = pulseIn(echoPin, HIGH);
    distance = (duration/2) / 29.1;
    Serial.print(“distance,”);
    Serial.print(time);
    Serial.print(“,”);
    Serial.println(distance);
    time = time + intervaltime;
    dim = analogRead(potPin) / 8;

    delay(intervaltime);

    }

    1. hi kevin:

      我猜想你的意思是,要把超音波感測器測得的距離,代換成燈光亮度。從你的程式碼看來,只是把兩個不同功能的程式片段貼在一起,兩者並沒有交集。比方說,你已經決定不再使用可變電阻,那loop()函式裡面就不應該出現底下的敘述:

      dim = analogRead(potPin) / 8;

      我覺得在寫這個程式之前,應該要先規劃好距離跟亮度的比例關係。例如,當感測距離低於30cm時,亮度最高;當感測距離大於或等於120cm時,燈光熄滅。

      按照這個邏輯,只要把距離透過map()函式,置換成對應的dim值即可。

      have fun!
      jeffrey

  9. Jeffrey,您好:
    請問書上的調光器電路,有辦法用 MOC3063 來簡化嗎?
    另外,依照您設計的調光器電路,若是輸出端要驅動兩支 110V 15A 的燈管,程式碼與電路的部分要如完成呢?謝謝。

    1. 我沒嘗試過,但我想沒問題,重點是你要把MOC3063的零交越信號輸出給Arduino的中斷0,程式碼不用改。這個電路無法控制日光燈管,相關的電路再麻煩您搜尋一下。

      thanks,
      jeffrey

    2. 謝謝 Jeffrey 的回覆。
      是這樣的,目前手邊有許多台飛利浦黑晶爐,它別於電磁爐設計,內部是兩條鹵素燈管,燈管通電後產生光與熱,再透過表面的玻璃產生熱能。負責導通燈管的是兩顆 BT139X ,終端由 MCU 來控制,火力六段可以調整,我想用 Arduino 來模擬它的設計。
      另外,以下是我用 Arduino 設計的致冷晶片電路,濕度高於 45%就會啟動致冷片,但是當濕度介於 45~46%的時候,繼電器就會跳來跳去減少壽命,請問程式碼的部分要如何更改較妥當?謝謝。
      連結:http://bbs.pigoo.com/thread-56641-1-1.html

      #include
      #include
      #define dhtread A0
      #define led 3
      #define fan 2
      float display;
      dht DHT;
      LiquidCrystal lcd(9, 8, 7, 6, 5, 4);

      void setup()
      {
      pinMode (fan, OUTPUT);
      lcd.begin(16, 2);
      lcd.setCursor(4, 0);
      lcd.print(“Temp”);
      lcd.setCursor(0, 1);
      lcd.print(“Humidity”);
      pinMode(led, OUTPUT);
      Serial.begin(9600);
      delay(300);
      Serial.println(“Humidity and Tempture Test \n\n”);
      delay(700);
      }

      void loop() {

      lcd.setCursor(9, 0);
      lcd.print((float)DHT.temperature, 2);
      lcd.print((char) 0xDF);
      lcd.print(“C”);

      lcd.setCursor(9, 1);
      lcd.print((float)DHT.humidity, 2);
      lcd.print(“%”);
      delay(2000);

      DHT.read11(dhtread);
      Serial.print(“Humidity = “);
      Serial.print((float)DHT.humidity);
      Serial.print(“% “);

      Serial.print(“Temperature = “);
      Serial.print((float)DHT.temperature);
      Serial.println(“C “);
      delay(1000);

      display = DHT.humidity;
      if (display >= 45) {
      digitalWrite (led, HIGH);
      digitalWrite (2, HIGH);
      } else {
      digitalWrite(2, LOW);
      digitalWrite(led, LOW);
      }

      }

    3. 因為外在環境(溫濕度、亮度、重力加速度…等)經常會有些微的變化、感測器本身有誤差,加上雜訊,所以感測值經常處於變動狀態。

      為了避免感測值在臨界值附近震盪,導致輸出開開關關,程式不要只依賴一個臨界值;您可以參閱「動手做6-2:使用光敏電阻製作小夜燈」單元,設定一個感測區間值,高於某數值時啟動、低於某數值時關閉。

      或者,您可以嘗試在一段時間內讀取數筆感測資料(通常取5筆),然後再予以平均;或是每次取其中最大或最小值,這樣就能降低取樣數據波動的情況。

      thanks,
      jeffrey

  10. 您好~~~我照著書本上的範例, 以可變電阻來控制燈泡亮度有成功了(我的情形跟上面回覆的一樣, A1和A2腳需對調)~~
    我現在想改成以Serial.read()輸入值來改變dim值, 但都不成功…後來我有把dim值Print出來, 發現跟輸入值不同…想請教一下, 是什麼地方還需要做變更嗎?!我的程式碼只有將loop()裡讀取可變電阻值來改變dim值改掉而以. 程式如下:

    /*
     本範例程式改寫自arduino.cc官方論壇的這一篇文章:
     http://forum.arduino.cc/index.php?PHPSESSID=1mlmloei1vpish99327r5gfol0&topic=22512.15
       
     原者:
     Ryan McLaughlin  
    */
       
    #include 
     
    /*
     設定一個充當計算關閉TRIAC的延遲時間的「計數器」,
     其值將在每65微秒增加1,
     當i的值增加到大於或等於dim(調光值)時,
     再令TRIAC開啟。
    */
    volatile int i=0;
    volatile boolean zeroCross=0;  // 儲存零交越狀態的變數
    const byte acPin = 3;          // TRIAC訊號輸出接腳
    // const byte potPin = A0;        // 可變電阻的接腳
    int dim = 64;                  // 調光器的階段值 (0-128) , 128代表關閉。
     
    void setup() {
       Serial.begin(9600);
       pinMode(acPin, OUTPUT);                     // TRIAC的控制輸出腳
       attachInterrupt(0, zeroCrossISR, RISING);   // 偵測零交越訊號
       /* 
         執行TimerOne程式庫裡的Timer1定時觸發程式,
         參數65代表定時器的運作週期是65微秒。
       */
       Timer1.initialize(65); 
       // 設定讓定時器每隔65微秒,自動執行dim_check函數。
       Timer1.attachInterrupt(dim_check); 
    }
       
    // 每當偵測到零交越點,底下的函數就會被執行
    void zeroCrossISR() {
       zeroCross = true; 
       i=0;
       digitalWrite(acPin, LOW);  // 關閉TRIAC
    }
     
    // 底下的函數將每隔65微秒觸發一次
    void dim_check() { 
       if(zeroCross) {      	    // 若已經過零交越點.... 
          if(i>=dim) {      	    // 判斷是否過了延遲觸發時間...
             digitalWrite(acPin, HIGH); // 開啟TRIAC 
             i=0;                       // 重設「計數器」 
             zeroCross=false;
          } else {
             i++;  	// 增加「計數器」 
          } 
       } 
    }
     
    void loop() {
       // 讀取可變電阻的值(0~1023),除以8可得到128階段值。 
       //dim = analogRead(potPin) / 8;
       if(Serial.available())
       {
         dim=Serial.read();
         Serial.println(dim);
       }
    }
    

    不知道是哪裡出了錯誤呢?!

    1. hi kevin:

      問題出在loop()函式的讀取序列值程式碼:

      if(Serial.available()) 
      {
        dim=Serial.read();
        Serial.println(dim);
      }
      

      請參閱5-23頁說明,序列接收到的值是字元的ASCII碼,如果要將輸入字串轉換成數字,請參閱10-10頁說明。

      thanks,
      jeffrey

  11. 原來如此~~~哈哈~~~
    感謝您耐心的回答哦~~~
    PS:這本書真的讓我學到了很多知識~~~解釋很詳細也很直覺~~~真的是很好的一本讀物~~~

  12. Hi Jeffrey
    若我想要用的Timer中斷時間為10分鐘(或以上)
    請問該怎麼辦呢

    小弟不成材
    剛開始起步探索arduino的世界
    還請Jeffrey指教

    1. hi oreo:

      我不太了解你的意思,如果你是想要寫一個延時10分鐘的程式,請參閱這個留言,使用SimpleTimer程式庫撰寫會比較容易。

      把第36行的敘述改成:

      timer.setTimeout(600000L, turnOff);

      就是10分鐘了。

      thanks,
      jeffrey

  13. 您好,
    燈泡未開啟時數值01010101的變動,燈泡開啟時數值都為1
    請問這該如何來做判斷
    謝謝

  14. 請問接腳2,是TimerOne.h裡定義的嗎?因為我在上面程式碼裡沒看到接腳2
    謝謝

  15. 您好!
    我想把可變電阻手動控制明滅 改成自動用程式碼控制
    但初入arduino不知道該如何寫QQ

    這是我網上參考的目前的程式碼
    但無法順利運行呼吸燈….

    unsigned char acPin1 = 11;
    unsigned char dimming = 3;
    unsigned char i;

    void setup(){
    pinMode(acPin1, OUTPUT);
    attachInterrupt(1, zero_crosss_int, RISING);
    }

    void zero_crosss_int(){

    int dimtime = (100*dimming);
    delayMicroseconds(dimtime);
    digitalWrite(acPin1, HIGH);
    delayMicroseconds(100);
    digitalWrite(acPin1, LOW);
    }

    void loop() {

    for (i=0;i0;i–)
    {
    dimming=i;
    delay(20);
    }
    }

    還有想用一個arduino板控制多個燈泡(運行模式不同)
    想請您指點一下……>< 作品做不出來…..非常感謝QQ

    1. 首先,你要清楚知道你究竟要傳達哪些操作步驟給電腦。

      上文調光程式的重點是改變dim變數的值,這裡只是用類比輸入腳當作參考點:

      // 讀取可變電阻的值(0~1023),除以8可得到128階段值。 
      dim = analogRead(potPin) / 8;
      

      拿出筆和紙規劃程式的執行步驟,逐漸調昇或調降dim變數值,達到呼吸燈效果。所以關鍵在於如何每隔一小段延遲時間之後,調昇或調降某個變數值?很多程式都有答案。

      thanks,
      jeffrey

  16. CUBIE.
    感謝你無私的分享..
    我是個新手剛介入Arduino不久, 今看到你的調光器程式裡有些地方不太明白,
    不知是否可以相您請教..
    程式中; 在程序段中 dim_chrck , if(zeroCross) { }
    請問題為何這個 if ,else 判斷中的 (zeroCross) 只有宣告名, 卻不需要寫入條件成立與否?
    隨後那個 if(i>=dim) {
    digitalWrite(acPin, HIGH);
    i=0;
    zeroCross=false;
    } 我就明白 i => dim 就執行後面的程序

    請問 if(zeroCross) { } 它是在判斷什麼? 還是說程式 語法就是如此寫法?
    似乎跟我理解的不太一樣..一個條件成立 後 才執行 , 否則 else {
    i++;

    void dim_check() {
    if(zeroCross) { // 若已經過零交越點….
    if(i>=dim) { // 判斷是否過了延遲觸發時間…
    digitalWrite(acPin, HIGH); // 開啟TRIAC
    i=0; // 重設「計數器」
    zeroCross=false;
    } else {
    i++; // 增加「計數器」
    }
    }
    }

    1. 因為zeroCross變數值只有true和false兩個可能值,所以條件判斷敘述:

      if(zeroCross == true)

      可寫成:

      if(zeroCross)

      意思一樣。

      thanks,
      jeffrey

  17. jeffrey
    感謝你抽空回覆,我還以我的留言是失敗的..因為一直看不到自己的留言問題!!

    CUBIE
    2019/02/24 at 10:55 下午
    因為zeroCross變數值只有true和false兩個可能值,所以條件判斷敘述:
    if(zeroCross == true)
    可寫成:
    if(zeroCross)
    意思一樣

    我呈述一下您的意思, 您幫我看看是不是這個解釋!
    原程序段截取.
    void zeroCrossISR() {
    zeroCross = true; ***************<< 因為在這個void zeroCrossISR()函數中,zeroCross = true;已經是成立了.
    i=0;
    digitalWrite(acPin, LOW);
    }

    void dim_check() {
    if(zeroCross) { **************所以到了這個函數void dim_check() 中 ,這個if(zeroCross),仍然是判斷上個
    函數zeroCross = true; 有效..對嗎??

    那我又有一個問題想問, 如果按照很多書上說的一樣,都說程式是由上往下執行, 所以我可以明白在程式最一開始時.
    所下的宣告,在後面任何一段函數程式內都是有效的,
    那麼如果說, 我在某一段函數中臨時宣告一個變數的話..是不是該段函數中的變數,只能給予該段程式或是以下程式
    使用? 而先前的跑過的程序就不能使用?
    我先隨便比喻一下..

    void TEST0 () {
    請問在下面 TEST 1 中的 val = 10的變數 , 在TEST0 這TEST0函數中如果也用到 val 時, 算有效的嗎?
    }

    void TEST 1 () {
    int val ;
    val = 10
    }

    void TEST2 () {
    請問上面val = 10 在函數TEST2 皆段裡是不是就是 你說的那個意思, 因程序之前已經先宣告, 所以以下程式都可以使用, 這樣也就符合程式從上往下執行的原則?

    jeffrey 很抱歉!!
    我的問題很笨,因為對程式這東西剛上手, 也不是很懂得它的運作情況..就好比說…

    Timer1.initialize(65);
    // 設定讓定時器每隔65微秒,自動執行dim_check函數。
    Timer1.attachInterrupt(dim_check);

    這個在我的理解裡, 好像是說後面的程序都會按照撰寫者的順序一直執行下去, 但是程式執行到某一個條件出現時, 卻又可以在整體時間內多出65微秒的時間, 來暫停後面的程序, 而先跑回到程式頭端來執行Timer1.attachInterrupt(dim_check); 執行完後, 又可以回到原先的停止點繼續執行後面未完的程序..
    這就不免會讓我會以為, 程式可以同時執行不同段的程式碼,如果真是這樣,好像很不合理..
    不過還是很謝謝你願意分享你的所長, 讓更多的學習者能跟著你的腳步慢慢進步.

    1. 程式基本上是由上而下逐行執行沒錯,但「中斷」敘述可打破這個規則。
      函式內部的敘述可以改變全域變數的值。

      thanks,
      jeffrey

  18. 你好, 想請教一下, 在這個調光電路裡, 如果我想用BTA41-600B去替換掉BTA12-600B的話, 其它元件(電阻和電容)我有需要做更換嗎?! 因為想拿來控制大電流的元件.
    順便也想請教一下, 這些電阻值是怎麼計算出來的呢?!

    1. 直接替換Triac元件即可。

      驅動Triac元件的光耦合元件的輸出端電阻值,可以參考原廠的應用設計說明文件(Application Note AN-3003)。這份文件的第2頁提到,如果驅動電阻式負載(Resistive Loads),例如,白熾燈泡,採用圖2的電路,只要在MOC30xx系列的第6腳串接一個電阻即可。MOC30xx系列的差異在於驅動端的耐電壓值,MOC301X是250V,MOC302X則是400V。

      第2頁的Triac Driving Requirements(Triac驅動要求)單元有計算式說明,基本公式為:

      R1 (最低值) = 輸入交流電峰值/光耦的輸入電流峰值

      交流電壓通常以rms(根均方)值標示,例如,台灣家庭室內的交流電rms值是110V;換算成峰值(瞬間最大振幅)要乘上開根號2(即:1.414):110V×1.414=155.54V。

      該說明文件假設的輸入電壓rms值是115V,峰值是180V(應該是162V左右);MOC3011M的輸入電流峰值是1.2A(應該是1A,參閱MOC30xx系列技術文件第3頁的ITSM欄),R1電阻最低值為:

      R1 (min) = Vin(pk)/1.2A = 150 ohms.

      用110V和1A計算,R1電阻最低值為156Ω。

      110 * 1.414 = 155.54

      這可說明為何許多MOC30xx系列的Triac驅動電路採用180Ω阻值。

      thanks,
      jeffrey

  19. 感謝你如此詳細的解說~也很慶幸我第一本Arduino 的入門書,是你的超圖解系列,讓我這種非電子科系的人,少走了很多冤枉路~也讓我可以將想做的物品實體化~目前三本超圖解系列,是我最常拿出來一翻再翻的書籍,也很期待之後的新作~感謝~

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *

Related Posts

Begin typing your search term above and press enter to search. Press ESC to cancel.

Back To Top