2015-06-05

Arduino筆記:以Sparkfun LED Strip(LED燈條)裝飾樂高聖誕樹

去年底應景做了『讓EV3播放聖誕音樂』,同時也以樂高零件搭配LED燈條組合成聖誕樹,展現的燈光效果確實很有年節的氛圍

樂高 LED燈飾聖誕樹
而其中LED燈條使用的是『Sparkfun RGB LED Strip』,共串接了32顆的 RGB LED,每一個LED各自由一顆 WS2801 驅動IC控制,因此可以獨立控制每一顆LED的顏色顯現(addressable),透過程式即可以展現出如下面影片中的特殊效果。


重要技術資訊

Sparkfun官網中的範例程式部分資訊有誤,以下則是經測試後確認功能確實可以正常運作:
  1. 燈條首尾分別為四芯的公與母接頭,可繼續延伸串接,四芯線與Arduino的接線方式為:
    ● 紅色 - 5V
    ● 藍色 - CKI (Data Clock Input)
    ● 綠色 - SDI (Serial Gray Scale data input)
    ● 黃色 - GND
     
  2. 每一顆LED以 24-bit 顏色值資料控制顏色的顯示:

     
  3. 使用WS2801控制LED顯示的步驟:
    (1) 開始傳送每一個data bit前,需先將CKI pin設成 LOW再由 SDI pin寫入 data bit,接著再將 CKI pin設成 HIGH。

    (2) 重複步驟 (1),每當送完24 bits的資料,WS2801會立即將資料送往下一個鄰近的WS2801 IC。

    (3) 當送完 32 個 WS2801的 data bits之後,將 CKI pin 設成 LOW並delay超過 500 us,WS2801 IC即會驅動 LED的顯示。

範例程式碼

A. 顯示指定的 LED


/* ************************************************************
 * WS2801_lighting_by_Address: Lighting specificed address of LED with specified color.
 * ledColor : Color of LED
 * ledAddr  : Address of LED (0-31, 0xFF: all LEDs) 
 * ************************************************************ */
void WS2801_lighting_by_Address (long ledColor, short ledAddr) {  
  for(short ledNo = 0; ledNo<STRIP_LENGTH; ledNo++) {   
    for(short color_bit = 23; color_bit >= 0 ; color_bit--) {      
      PORTD &= B11110111;       //digitalWrite(CKI, LOW);
      if(((ledNo == ledAddr) || ledAddr == 0xFF) && bitRead(ledColor, color_bit)) 
            PORTD |= B00000100; //digitalWrite(SDI, HIGH);
      else  PORTD &= B11111011; //digitalWrite(SDI, LOW);     
      PORTD |= B00001000;       //digitalWrite(CKI, HIGH);      
    }    
  } 
  //Pull clock low for 500us or more causes the IC to post the data.
  PORTD &= B11110111;           //digitalWrite(CKI, LOW);
  delay(1);   
}

B. 單向依序顯示指定數量的LED


/* ************************************************************
 * WS2801_sequencing_Multi_LED: Lights on specified number of 
 *                              continuous LEDs with specified color.
 * ledColor  : Color of specified LED
 * backColor : Color of non-specified LED
 * ledAddr   : Starting address of LED to be lighted with ledColor(0-31) 
 * ledCnt    : Number of continuous LED from ledAddr to be lighted on (range from 1 to 3). 
 * ************************************************************ */
void WS2801_sequencing_Multi_LED (long ledColor, long backColor, short ledAddr, short ledCnt) {
  if (ledCnt > 3) ledCnt = 3;
  else if (ledCnt < 1) ledCnt = 1;
  
  for(short ledNo = 0; ledNo<STRIP_LENGTH; ledNo++) {
    if (ledNo >= ledAddr && ledNo <= ledAddr+ledCnt-1) {
      for(short color_bit = 23; color_bit >= 0 ; color_bit--) {      
        PORTD &= B11110111;       //digitalWrite(CKI, LOW);
        if (bitRead(ledColor, color_bit))
             PORTD |= B00000100;  //digitalWrite(SDI, HIGH);
        else PORTD &= B11111011;  //digitalWrite(SDI, LOW);     
        PORTD |= B00001000;       //digitalWrite(CKI, HIGH);      
      }
    }
    else {
      for(short color_bit = 23; color_bit >= 0 ; color_bit--) {      
        PORTD &= B11110111;       //digitalWrite(CKI, LOW);
        if (bitRead(backColor, color_bit))
             PORTD |= B00000100;  //digitalWrite(SDI, HIGH);
        else PORTD &= B11111011;  //digitalWrite(SDI, LOW);     
        PORTD |= B00001000;       //digitalWrite(CKI, HIGH);      
      }
    }    
  } 
  //Pull clock low for 500us or more causes the IC to post the data.
  PORTD &= B11110111;             //digitalWrite(CKI, LOW);
  delay(1);   
}

C. 雙向依序顯示指定數量的 LED


/* ************************************************************
 * WS2801_2waySequencing_Multi_LED: Two directions to sequence light specified number of 
 *                                  continuous LEDs with specified color.
 * ledColor  : Color of specified LED
 * backColor : Color of non-specified LED
 * ledCnt    : Number of continuous LED from ledAddr to be lighted on (range from 1 to 3).
 * ************************************************************ */
void WS2801_2waySequencing_Multi_LED (long ledColor, long backColor, short ledCnt) {
  if (ledCnt > 3) ledCnt = 3;
  else if (ledCnt < 1) ledCnt = 1;
  
  for(short ledAddr=0; ledAddr<STRIP_LENGTH; ledAddr++) {
    for(short ledNo=0; ledNo<STRIP_LENGTH; ledNo++) {
      if ((ledNo >= ledAddr                         && ledNo <= ledAddr+ledCnt-1) || 
          (ledNo >= STRIP_LENGTH-1-ledAddr-ledCnt+1 && ledNo <= STRIP_LENGTH-1-ledAddr))
      {
        for(short color_bit=23; color_bit>=0 ; color_bit--) {      
          PORTD &= B11110111;       //digitalWrite(CKI, LOW);
          if (bitRead(ledColor, color_bit))
               PORTD |= B00000100;  //digitalWrite(SDI, HIGH);
          else PORTD &= B11111011;  //digitalWrite(SDI, LOW);     
          PORTD |= B00001000;       //digitalWrite(CKI, HIGH);      
        }
      }
      else {
        for(short color_bit=23; color_bit>=0 ; color_bit--) {      
          PORTD &= B11110111;       //digitalWrite(CKI, LOW);
          if (bitRead(backColor, color_bit))
               PORTD |= B00000100;  //digitalWrite(SDI, HIGH);
          else PORTD &= B11111011;  //digitalWrite(SDI, LOW);     
          PORTD |= B00001000;       //digitalWrite(CKI, HIGH);      
        }
      }        
    } 
    //Pull clock low for 500us or more causes the IC to post the data.
    PORTD &= B11110111;             //digitalWrite(CKI, LOW);
    delay(1);
    delay(50);
  }    
}


D. 逐次增加LED的顯示數量


/* ************************************************************
 * WS2801_stepping_LED: Stepping light on LED with specified color.
 * ledColor     : Color of LED
 * speedMillis  : Address of LED (0-31, 0xFF: all LEDs) 
 * ************************************************************ */
void WS2801_stepping_LED (long ledColor, boolean stepForward, long speedMillis) {
  boolean isCompleted = false;
  short ledAddr;
  ledAddr = (stepForward?0:STRIP_LENGTH-1);  
  
  //for(short ledAddr=0; ledAddr<STRIP_LENGTH; ledAddr++) {
  while(!isCompleted) {  
    for(short ledNo=0; ledNo<STRIP_LENGTH; ledNo++) {   
      for(short color_bit = 23; color_bit >= 0 ; color_bit--) {      
        PORTD &= B11110111;       //digitalWrite(CKI, LOW);
        if(((stepForward  && ledNo <= ledAddr) && bitRead(ledColor, color_bit)) ||
           ((!stepForward && ledNo >= ledAddr) && bitRead(ledColor, color_bit)))            
              PORTD |= B00000100; //digitalWrite(SDI, HIGH);
        else  PORTD &= B11111011; //digitalWrite(SDI, LOW);     
        PORTD |= B00001000;       //digitalWrite(CKI, HIGH);      
      }    
    } 
    //Pull clock low for 500us or more causes the IC to post the data.
    PORTD &= B11110111;           //digitalWrite(CKI, LOW);
    delay(1);
    delay(speedMillis);
    if(stepForward) {
       ledAddr++;
       if(ledAddr>STRIP_LENGTH-1) isCompleted = true;
    }
    else {
       ledAddr--;
       if(ledAddr<0) isCompleted = true;
    }      
  }   
}

E. 隨機閃爍LED


/* ************************************************************
 * WS2801_twinkling_LED: Twinkling LED with random color.
 * delayMillis : Delay of milliseconds to light on
 * ************************************************************ */
void WS2801_twinkling_LED (long delayMillis) {  
  for(short ledNo = 0; ledNo<STRIP_LENGTH; ledNo++) {
    long xledColor = ledSColor[random(7)];      
    for(short color_bit = 23; color_bit >= 0 ; color_bit--) {      
      PORTD &= B11110111;       //digitalWrite(CKI, LOW);
      if(bitRead(xledColor, color_bit)) 
            PORTD |= B00000100; //digitalWrite(SDI, HIGH);
      else  PORTD &= B11111011; //digitalWrite(SDI, LOW);     
      PORTD |= B00001000;       //digitalWrite(CKI, HIGH);      
    }    
  } 
  //Pull clock low for 500us or more causes the IC to post the data.
  PORTD &= B11110111;           //digitalWrite(CKI, LOW);
  delay(1);
  delay(delayMillis);  
}


沒有留言:

張貼留言