1

Topic: Управление WS2812

Есть такие замечательные светодиоды с контроллером WS2812. Пытаюсь управлять ими через Rеmote_XY и Bluetooth соединение.
Если отправлять некие статические коды, например, с RGB-круга, то это проходит на ура.
Но если я запускаю какие-то эффекты, когда цвето постоянно меняется, то связь умирает наглухо.
BT подключен через библиотеку softwarserial.
Для управления WS2812 я использую библиотеку iarduino_NeoPixel. Как и многие похожие, она использует массив внутри ардуино для установки цвета, а потом с помощью команды write закидывает этот массив в ленту светодиодов. Вот эта команда write написана на ассемблере и еще отключает все прерывания. Т.е. поджирает под себя все ресурсы.
Однако у меня всего 14 светодиодов. Вот что написано про ws2812.

Команды управления подаются пакетами по 3 байта, по одному для каждого из трёх цветов. Между пакетами идет пауза длительностью 50 мкс, пауза более 100 мкс означает конец передачи.

Длительность любого бита – 1,25 мкс. Бит «1«кодируется импульсом длительностью 0,8 мкс и паузой в 0,45 мкс. Бит «0«– 0,4 и 0,85 мкс. Возможны расхождения по времени до 150 нс. Такой пакет должен быть отправлен для каждого пикселя в светодиодной ленте.

Таким образом мне надо передать 14*3*8 = 336 бит. Каждый бит 1,25 мкс. 336*1.25 = 420 мкс. Т.е. не так уж и много. Но, как я уже писал, связь пропадает. Т.е. устройство не откликается на команды с телефона.
В общем я чего-то не понимаю. Помогите разобраться, плз.
Ниже привожу код программы. Главная проблема при RemoteXY.select_Mode = 2.

#define REMOTEXY_MODE__SOFTSERIAL
#include <SoftwareSerial.h> 

#include <RemoteXY.h> 

// настройки соединения  
#define REMOTEXY_SERIAL_RX 9 
#define REMOTEXY_SERIAL_TX 10 
#define REMOTEXY_SERIAL_SPEED 9600 


// конфигурация интерфейса   
#pragma pack(push, 1) 
uint8_t RemoteXY_CONF[] = 
  { 255,5,0,17,0,51,0,8,161,1,
  6,0,10,52,43,43,2,26,2,1,
  2,2,22,11,191,26,93,31,79,78,
  0,79,70,70,0,3,131,16,29,31,
  11,164,26,67,1,2,15,59,10,107,
  161,16,65,42,45,3,9,9 }; 
   
// структура определяет все переменные вашего интерфейса управления  
struct { 

    // input variable
  uint8_t rgb_1_r; // =0..255 значение Красного цвета 
  uint8_t rgb_1_g; // =0..255 значение Зеленого цвета 
  uint8_t rgb_1_b; // =0..255 значение Синего цвета 
  uint8_t switch_on; // =1 если переключатель включен и =0 если отключен 
  uint8_t select_Mode; // =0 если переключатель в положении A, =1 если в положении B, =2 если в положении C, ... 

    // output variable
  char text_Mode[16];  // =строка UTF8 оканчивающаяся нулем 
  uint8_t led_On_g; // =0..255 яркость зеленого цвета индикатора 

    // other variable
  uint8_t connect_flag;  // =1 if wire connected, else =0 

} RemoteXY; 
#pragma pack(pop) 

///////////////////////////////////////////// 
//           END RemoteXY include          // 
///////////////////////////////////////////// 

#define PIN_SWITCH_ON 13

uint8_t rgb_r = 0; // =0..255 значение Красного цвета 
uint8_t rgb_g = 0; // =0..255 значение Зеленого цвета 
uint8_t rgb_b = 0; // =0..255 значение Синего цвета 
uint8_t rgb_bright = 255; // =0..255 значение Синего цвета 

#define PIN 7
#define LED_NUMBER 14

#include <iarduino_NeoPixel.h>           //  Подключаем библиотеку iarduino_NeoPixel для работы со светодиодами NeoPixel
iarduino_NeoPixel led(PIN, LED_NUMBER ); //  Объявляем объект LED указывая (№ вывода Arduino к которому подключены светодиоды NeoPixel, количество используемых светодиодов)

unsigned long DiscoTime = 0;


void setup()  
{ 
  RemoteXY_Init ();  
   
  pinMode (PIN_SWITCH_ON, OUTPUT);
  led.begin();
  clrColor();
  
  RemoteXY.select_Mode = 0;

  Serial.begin(9600);
  Serial.println("Candel begin...");
} 

void loop()  
{  
  RemoteXY_Handler (); 
  
  digitalWrite(PIN_SWITCH_ON, (RemoteXY.switch_on==0)?LOW:HIGH);
  RemoteXY.led_On_g = 200*RemoteXY.switch_on;

  if(!RemoteXY.switch_on){
    //Состояние ВЫКЛ
    strcpy (RemoteXY.text_Mode, "Candel off");
    clrColor();
    return;
  }

  //Состояние ВКЛ
  switch (RemoteXY.select_Mode){
  case 0:
    strcpy (RemoteXY.text_Mode, "Light");
    //clrColor();
    rgb_r = 255;
    rgb_g = 255;
    rgb_b = 255;
    setRGB(NeoPixelAll, 255, 255, 255);
    break;
    
  case 1:
    strcpy (RemoteXY.text_Mode, "Color");
    setColorFromDim();
    break;
    
  case 2:
    strcpy (RemoteXY.text_Mode, "Discoteka");
    ChangeColor();
    break;
  }

}

void setRGB(uint16_t NumPix, uint8_t rgbRed, uint8_t rgbGreen, uint8_t rgbBlue){
  bool NeedWrite = false;
  if(NumPix=NeoPixelAll){
    for (uint16_t i=0; i<LED_NUMBER; i++){
      if( (led.getPointer()[i+0] != rgbRed) || (led.getPointer()[i+1] != rgbGreen) || (led.getPointer()[i+2] != rgbBlue)) {
        /*
        Serial.print("setRGB(");
        Serial.print(i,DEC);
        Serial.print(") ");
        Serial.print(led.getPointer()[i+0], DEC);
        Serial.print(" ");
        Serial.print(rgbRed, DEC);
        Serial.print(",");
        Serial.print(led.getPointer()[i+1], DEC);
        Serial.print(" ");
        Serial.print(rgbGreen, DEC);
        Serial.print(",");
        Serial.print(led.getPointer()[i+2], DEC);
        Serial.print(" ");
        Serial.println(rgbBlue, DEC);
        */
        NeedWrite = true;
        break;
      }//if
    } //for
  }else{
      if( (led.getPointer()[NumPix+0] != rgbRed) || (led.getPointer()[NumPix+1] != rgbGreen) || (led.getPointer()[NumPix+2] != rgbBlue)) {
        /*
        Serial.print("setRGB(");
        Serial.print(NumPix,DEC);
        Serial.print(") ");
        Serial.print(led.getPointer()[NumPix+0], DEC);
        Serial.print(" ");
        Serial.print(rgbRed, DEC);
        Serial.print(",");
        Serial.print(led.getPointer()[NumPix+1], DEC);
        Serial.print(" ");
        Serial.print(rgbGreen, DEC);
        Serial.print(",");
        Serial.print(led.getPointer()[NumPix+2], DEC);
        Serial.print(" ");
        Serial.println(rgbBlue, DEC);
        */
        NeedWrite = true;
      }//if
    }//else
  if(NeedWrite){
  //Serial.println("setRGB");
    led.setColor(NumPix, byte(rgbRed), byte(rgbGreen), byte(rgbBlue));  //  Устанавливаем цвет для светодиодов (номер,R,G,B)
    led.write(); //  Записываем
  }
}

void setColorFromDim(){
    if( (rgb_r != RemoteXY.rgb_1_r) || (rgb_g != RemoteXY.rgb_1_g) || (rgb_b != RemoteXY.rgb_1_b)) {
      Serial.print("R: ");
      Serial.print(RemoteXY.rgb_1_r, DEC);
      Serial.print(" G: ");
      Serial.print(RemoteXY.rgb_1_g, DEC);
      Serial.print(" B: ");
      Serial.println(RemoteXY.rgb_1_b, DEC);
      
      rgb_r = RemoteXY.rgb_1_r;
      rgb_g = RemoteXY.rgb_1_g;
      rgb_b = RemoteXY.rgb_1_b;
  
      setRGB(NeoPixelAll, byte(rgb_r), byte(rgb_g), byte(rgb_b));  //  Устанавливаем цвет для всех светодиодов (все,R,G,B)
    }
}

void clrColor(){
  Serial.println("clrColor");
  rgb_r = 0;
  rgb_g = 0;
  rgb_b = 0;
  
  setRGB(NeoPixelAll, 0, 0, 0);
}

void ChangeColor(){
  static uint8_t j;                                     //  Объявляем переменную для хранения значения сдвига спектра цветов для всех светодиодов (от 0 до 255)
  uint8_t k;                                     //  Объявляем переменную для хранения положения сдвига спектра цвета для каждого светодиода на спектре j (зависит от количества светодиодов)
  uint8_t r, g, b;                               //  Объявляем переменную для хранения цветов RGB для каждого светодиода
  const uint8_t z=50;                                   //  Определяем константу указывающую задержку в мс (чем выше значение, тем медленнее перелив цветов) 

/*
    j++;                                             //  Смещаем спектр цветов для всех светодиодов
    for(uint16_t i=0; i<led.count(); i++){           //  Проходим по всем светодиодам
        k=((uint16_t)(i*256/led.count())+j);         //  Определяем положение очередного светодиода на смещённом спектре цветов
        if(k<85) {        b=0; r=k*3; g=255-r;}else  //  Перелив от зелёного к красному, через жёлтый
        if(k<170){k-=85;  g=0; b=k*3; r=255-b;}else  //  Перелив от красного к синему  , через фиолетовый
                 {k-=170; r=0; g=k*3; b=255-g;}      //  Перелив от синего   к зелёному, через голубой
        led.setColor(i, r,g,b);                      //  Устанавливаем выбранный цвет для очередного светодиода
    }   led.write();                                 //  Записываем цвета всех светодиодов
    delay(z);                                        //  Устанавливаем задержку
*/
   unsigned long curTime = millis();
   if(curTime - DiscoTime < z) return;
   DiscoTime = curTime;
   j++;                                         //  Смещаем спектр цветов для всех светодиодов
   k=((uint16_t)(256/led.count())+j);           //  Определяем положение очередного светодиода на смещённом спектре цветов
   if(k<85) {        b=0; r=k*3; g=255-r;}else  //  Перелив от зелёного к красному, через жёлтый
   if(k<170){k-=85;  g=0; b=k*3; r=255-b;}else  //  Перелив от красного к синему  , через фиолетовый
            {k-=170; r=0; g=k*3; b=255-g;}      //  Перелив от синего   к зелёному, через голубой
   led.setColor(NeoPixelAll, r,g,b);            //  Устанавливаем выбранный цвет для очередного светодиода
   led.write();                                 //  Записываем цвета всех светодиодов
}

2

Re: Управление WS2812

Что-то никто не отвечает.
Задам другой вопрос.
Как часто нужно вызывать процедуру RemoteXY_Handler?
Вот думаю, может сделать разделение по времени и делать вызов каждые n mc?

3

Re: Управление WS2812

RemoteXY_Handler необходимо вызывать максимально часто. Это связано с тем, что эта функция обрабатывает ввод информации через порт, это запросы от приложения. Если в какой то момент входная информация переполнит внутренний буфер порта, произойдет обрыв связи (точнее он произойдет если такое переполнение повторится несколько раз, но если программа этого не учитывает то так и происходит).
При разработке приложения вы не сможете точно определить сколько времени потребуется что бы переполнился буфер, для этого достаточно нескольких миллисекунд. По этому желательно что бы вызов RemoteXY_Handler происходил в каждом цикле loop. А вся программа была разработана таким образом, что бы в цикле loop не было каких либо задержек и длительных операций. Для различных пауз и таймингов необходимо использовать системное время, например millis ().

4 (edited by Guillaume 2017-12-31 17:26:14)

Re: Управление WS2812

I suggest to limit the refresh rate of your LEDs when connect_flag == 1, so that LEDs take less resources and give a chance to RemoteXY to do its work properly. When RemoteXY is not connected, go to full refresh rate if you wish.

I recommend FastLED library.

CFastLED::setMaxRefreshRate