Управление светодиодный кубом.

uscr
Offline
Зарегистрирован: 17.08.2012

 Здравствуйте. Спаял кубик 2Х2, чисто для оценки возможности управления.
Примерно так выглядит схема куба:

 

То есть слой с общим анодом, "столбики" с общим катодом. Очевидно, что я не могу зажечь LED6 и LED3 отдельно, потому как "в нагрузку" загорятся LED2 и LED7. Это действительно так или я неверно понял схему по сборке кубика?

uscr
Offline
Зарегистрирован: 17.08.2012

Посмотрел статьи про светодиодные матрицы. Там ведь аналогичная проблема: нельзя зажечь светодиод в 1 столбце и 1 строке и вместе с ним 2 столбец, 2 строка, ведь вместе с ними загорятся 1 столбец, 2 строка и 2 столбец, 1 строка, так?

uscr
Offline
Зарегистрирован: 17.08.2012

 А ещё я нагуглил, что такое "динамическая индикация". Не допёр, правда, как это применить к кубу, но вопрос снят...

leshak
Offline
Зарегистрирован: 29.09.2011

Как вы расположите в пространстве диоды - не важно. Хоть кубом, хоть шаром, хоть пирамидой.

Главная проблема - невозможно зажигать одновременно.

Решается это примерно так: заводим массив в котором храним, для каждого диода, состояние (включен или выключен). И когда нам нужно "что-то зажечь" - меняем в этом массиве значение.

Далее, как можно чаще, в loop, пробегаемся  по этому массиву. Если видим что ячейка  содержит 1 - зажигаем соотвествующий ей диод. Ждем некоторое время. И гасим его. Переходим к следующей ячейке...  и так все по кругу.

В результате, у нас одномоментно горит только один диод. Но если достаточно быстро бегать по этому кругу и "зажигать диод" на не очень продолжительное время, то благодаря инерции зрения будеть казатся что диоды горят "одновременно".

Что-бы "понять" можете просто на три пина подключить три диода. Например на пины 5,6,7.

И выполнить такой скетч.

int pause=1000; // одна секунда
void setup(){
  for(byte i=5;i<=7;i++)pinMode(i,Output);
}

void loop(){

  for(byte i=5;i<=7;i++){
       digitalWrite(i,HIGH); // зажгли
       delay(pause); подождали какие-то время
       digitalWrite(i,LOW); // погасили
      
  }

  pause-=50; // уменьшили паузу на 50 мсек
  if(pause<50)pause=50; // но не меньше 50 мсек. что-бы все-таки было ему время "посветить" :)
}

Вначале вы увидите как диоды по очереди горят (по одной секунде), потом постепенно время горения будет сокращатся. все быстрее и быстрее будут менятся. Через какой-то время вам уже будет казатся что они светят все три одновременно.

uscr
Offline
Зарегистрирован: 17.08.2012

leshak пишет:

Как вы расположите в пространстве диоды - не важно. Хоть кубом, хоть шаром, хоть пирамидой.

Главная проблема - невозможно зажигать одновременно.

Решается это примерно так: заводим массив в котором храним, для каждого диода, состояние (включен или выключен). И когда нам нужно "что-то зажечь" - меняем в этом массиве значение.

Далее, как можно чаще, в loop, пробегаемся  по этому массиву. Если видим что ячейка  содержит 1 - зажигаем соотвествующий ей диод. Ждем некоторое время. И гасим его. Переходим к следующей ячейке...  и так все по кругу.

В результате, у нас одномоментно горит только один диод. Но если достаточно быстро бегать по этому кругу и "зажигать диод" на не очень продолжительное время, то благодаря инерции зрения будеть казатся что диоды горят "одновременно".

Что-бы "понять" можете просто на три пина подключить три диода. Например на пины 5,6,7.

И выполнить такой скетч.

int pause=1000; // одна секунда
void setup(){
  for(byte i=5;i<=7;i++)pinMode(i,Output);
}

void loop(){

  for(byte i=5;i<=7;i++){
       digitalWrite(i,HIGH); // зажгли
       delay(pause); подождали какие-то время
       digitalWrite(i,LOW); // погасили
      
  }

  pause-=50; // уменьшили паузу на 50 мсек
  if(pause<50)pause=50; // но не меньше 50 мсек. что-бы все-таки было ему время "посветить" :)
}

Вначале вы увидите как диоды по очереди горят (по одной секунде), потом постепенно время горения будет сокращатся. все быстрее и быстрее будут менятся. Через какой-то время вам уже будет казатся что они светят все три одновременно.

 

Я понимаю, как это работает. Я тут в соседней теме как раз "программный ШИМ" пытаюсь реализовать. Я просто не очень представляю, как это оптимально под куб закодить. Тоесть осмысленно вовремя зажигать и строки и столбцы. Но я соображу, думаю. 

Вернее я представляю, как это сделать с "прямым" подключением. Но у меня ещё сдвиговые регистры. Думаю сделать 2 массива (у меня 2 регистра) и в них записать "кадры", а потом просто пробегать по массивам и писать в регистры значения.

uscr
Offline
Зарегистрирован: 17.08.2012

 Прошу оценить говнокод (по опыту предущих подобных вопросов, я сейчас получу аналог в 10 раз короче :)).

Идея такая:
задаётся количество кадров, "атомов" в кадре, задержка между кадрами. "атомов" взял 8 - по числу светодиодов. В основном цикле по кругу на регистры передаются значения атомов, если атомы показываются уже столько времени, сколько указана задержка, переходим к следующему кадру.

"Атомный" подход, кажется, достойно реализует динамическую индикацию для моей конструкции.

// 1----------2
// |          |
// |          |
// |          |
// |          |
// |          |
// 3----------4
//
// red: 1 126(01111110)-2 2 190(10111110)-2 3 222(11011110)-2 4 238(11101110)-2
// green: 1 246(11110110)-2 2 250(11111010)-2 3 252(11111100)-2 4 126(1111110)-1
// blue: 1 190(10111110)-1 2 222(11011110)-1 3 238(11101110)-1 4 246(11110110)-1
//

byte mcrDel = 100;
unsigned long del = 300000; //В микросекундах
boolean atoms_repeat = true;
unsigned long time = 0;
byte j=0;

#define atom_in_frame 8
#define framecount 4

byte animation1[framecount][atom_in_frame] = 
{255, 254, 254, 255, 255, 254, 254, 255,
254, 127, 255, 254, 254, 127, 255, 254,
191, 222, 239, 246, 191, 222, 239, 246,
254, 254, 254, 254, 254, 254, 254, 254};
byte animation2[framecount][atom_in_frame] = 
{126, 239, 191, 222, 126, 239, 191, 222,
247, 254, 250, 253, 247, 254, 250, 253,
254, 255, 254, 255, 254, 255, 254, 255,
254, 254, 254, 254, 254, 254, 254, 254};

//Пин подключен к ST_CP входу 74HC595 зелёный
int latchPin = 8; 
//Пин подключен к SH_CP входу 74HC595 жёлтый
int clockPin = 12;
//Пин подключен к DS входу 74HC595 синий
int dataPin = 11;

int rstPin1 = 7;
int rstPin2 = 6;

void setup() {
Serial.begin(9600);
  pinMode(latchPin, OUTPUT);
  pinMode(clockPin, OUTPUT);
  pinMode(dataPin, OUTPUT);
  pinMode(rstPin1, OUTPUT);
  pinMode(rstPin2, OUTPUT);

digitalWrite(rstPin1, LOW);
  digitalWrite(rstPin1, HIGH);
  digitalWrite(rstPin2, LOW);
  digitalWrite(rstPin2, HIGH);

}

void loop() {
  
  for (int i=0; i<framecount; i++) {
    atoms_repeat = true;
    time = micros();
    j=0;
    while ( atoms_repeat ) {
      digitalWrite(latchPin, LOW);
      shiftOut(dataPin, clockPin, MSBFIRST, animation1[i][j]);
      shiftOut(dataPin, clockPin, MSBFIRST, animation2[i][j]);
      digitalWrite(latchPin, HIGH);
      
      j++;
      if ( j == atom_in_frame ) { j=0; }
      
      if ((micros() - time) < del ) {
        delayMicroseconds(mcrDel);
      } else {
        atoms_repeat = false;
      }
     
    }
  }
  
}

 

P.S. Предложение leshak: "заводим массив в котором храним, для каждого диода, состояние" я отверг по той причине, что накладные расходы по преобразованию значения из массива в байт, который будет передан на регистр превзойдут мой текущий код в несколько раз (или я просто не умею чего то?). Плюс, мой вариант легко оборачивается в "кодогенератор" на любом скриптовом языке, что бы "руками" не подбирать значения в массиве для создания желаемых эффектов.

AlexFisher
AlexFisher аватар
Offline
Зарегистрирован: 20.12.2011

Данные действительно  нужно хранить кадрами. 1 кадр - 8, 16 или 32 бита, каждому биту соответствует светодиод. Типы масивов будут uint8_t, uint16_t или uint32_t соответственно. Это зависит от количества включенных последовательно регистров сдвига.

У Вас заданы двумерные массивы, которые инициализируются одномерными - ошибка. Достучаться до отдельных битов через индекс массива нельзя!

Посмотрите http://arduino.ru/Reference/ShiftOut - там есть "замечания по использованию", где показано, как выводятся многобайтовые величины.

uscr
Offline
Зарегистрирован: 17.08.2012

AlexFisher пишет:

Данные действительно  нужно хранить кадрами. 1 кадр - 8, 16 или 32 бита, каждому биту соответствует светодиод. Типы масивов будут uint8_t, uint16_t или uint32_t соответственно. Это зависит от количества включенных последовательно регистров сдвига.

Вы предлагаете хранить состояние каждого светодиода в кубе? Тоесть я могу написать нечто вроде "uint16_t cube_state = 13107" (11001100110011), передать это с помощью shiftOut и установить значения сразу на 2 регистрах? В примере есть оператор >>, нагуглилось, что это побитовый сдвиг вправо. Я правильно понимаю, что "cube_state >> 8" выводит 11001100 (старший байт), а затем, при повторной передаче в shiftOut cube_state будет выведен "остаток"?

AlexFisher пишет:

У Вас заданы двумерные массивы, которые инициализируются одномерными - ошибка. Достучаться до отдельных битов через индекс массива нельзя!

А как правильно инициализировать двумерный массив? Такую конструкцию я подсмотрел в статье про подключение светодиодной матрицы. Она мне тоже показалась странной, но...она работает.

AlexFisher
AlexFisher аватар
Offline
Зарегистрирован: 20.12.2011

По поводу сдвига - Вы правильно поняли.

По поводу объявления массива,  запись var[][] фактически воспринимается как **var и при инициализации линейным массивом компилятор раскладывает значения построчно. Правильно записывать:

uint8_t var[2][2]={{0,0},{0,0}};

Самое логичное - куб выводить послойно. Один слой (он же кадр) - один элемент массива. Для куба 4х4х4 нужно 2 регистра, массив, хранящий текущее состояние куба будет 

uint16_t cube[4];

Индекс массива - номер слоя.

Ардуино не поддерживает битовый формат.