Управление светодиодный кубом.
- Войдите на сайт для отправки комментариев
Чт, 13/09/2012 - 18:20
Здравствуйте. Спаял кубик 2Х2, чисто для оценки возможности управления.
Примерно так выглядит схема куба:

То есть слой с общим анодом, "столбики" с общим катодом. Очевидно, что я не могу зажечь LED6 и LED3 отдельно, потому как "в нагрузку" загорятся LED2 и LED7. Это действительно так или я неверно понял схему по сборке кубика?
Посмотрел статьи про светодиодные матрицы. Там ведь аналогичная проблема: нельзя зажечь светодиод в 1 столбце и 1 строке и вместе с ним 2 столбец, 2 строка, ведь вместе с ними загорятся 1 столбец, 2 строка и 2 столбец, 1 строка, так?
А ещё я нагуглил, что такое "динамическая индикация". Не допёр, правда, как это применить к кубу, но вопрос снят...
Как вы расположите в пространстве диоды - не важно. Хоть кубом, хоть шаром, хоть пирамидой.
Главная проблема - невозможно зажигать одновременно.
Решается это примерно так: заводим массив в котором храним, для каждого диода, состояние (включен или выключен). И когда нам нужно "что-то зажечь" - меняем в этом массиве значение.
Далее, как можно чаще, в 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 мсек. что-бы все-таки было ему время "посветить" :) }Вначале вы увидите как диоды по очереди горят (по одной секунде), потом постепенно время горения будет сокращатся. все быстрее и быстрее будут менятся. Через какой-то время вам уже будет казатся что они светят все три одновременно.
Как вы расположите в пространстве диоды - не важно. Хоть кубом, хоть шаром, хоть пирамидой.
Главная проблема - невозможно зажигать одновременно.
Решается это примерно так: заводим массив в котором храним, для каждого диода, состояние (включен или выключен). И когда нам нужно "что-то зажечь" - меняем в этом массиве значение.
Далее, как можно чаще, в 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 регистра) и в них записать "кадры", а потом просто пробегать по массивам и писать в регистры значения.
Прошу оценить говнокод (по опыту предущих подобных вопросов, я сейчас получу аналог в 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: "заводим массив в котором храним, для каждого диода, состояние" я отверг по той причине, что накладные расходы по преобразованию значения из массива в байт, который будет передан на регистр превзойдут мой текущий код в несколько раз (или я просто не умею чего то?). Плюс, мой вариант легко оборачивается в "кодогенератор" на любом скриптовом языке, что бы "руками" не подбирать значения в массиве для создания желаемых эффектов.
Данные действительно нужно хранить кадрами. 1 кадр - 8, 16 или 32 бита, каждому биту соответствует светодиод. Типы масивов будут uint8_t, uint16_t или uint32_t соответственно. Это зависит от количества включенных последовательно регистров сдвига.
У Вас заданы двумерные массивы, которые инициализируются одномерными - ошибка. Достучаться до отдельных битов через индекс массива нельзя!
Посмотрите http://arduino.ru/Reference/ShiftOut - там есть "замечания по использованию", где показано, как выводятся многобайтовые величины.
Данные действительно нужно хранить кадрами. 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 будет выведен "остаток"?
У Вас заданы двумерные массивы, которые инициализируются одномерными - ошибка. Достучаться до отдельных битов через индекс массива нельзя!
А как правильно инициализировать двумерный массив? Такую конструкцию я подсмотрел в статье про подключение светодиодной матрицы. Она мне тоже показалась странной, но...она работает.
По поводу сдвига - Вы правильно поняли.
По поводу объявления массива, запись var[][] фактически воспринимается как **var и при инициализации линейным массивом компилятор раскладывает значения построчно. Правильно записывать:
uint8_t var[2][2]={{0,0},{0,0}};Самое логичное - куб выводить послойно. Один слой (он же кадр) - один элемент массива. Для куба 4х4х4 нужно 2 регистра, массив, хранящий текущее состояние куба будет
Индекс массива - номер слоя.
Ардуино не поддерживает битовый формат.