Светодиодный куб RGB 8x8x8
Компоненты и расходные материалы
![]() |
| × | 512 | |||
| × | 12 | ||||
| × | 1 | ||||
| × | 8 | ||||
![]() |
| × | 1 | |||
![]() |
| × | 1 | |||
![]() |
| × | 8 | |||
![]() |
| × | 8 | |||
![]() |
| × | 8 |
Приложения и онлайн-сервисы
![]() |
|
Об этом проекте
Видео
Сборка
Эта сборка была вдохновлена кубом Кевина Дарра RGB.

Глядя на телосложение Кевина, его терпение должно быть безграничным - к сожалению, мое терпение.
Я решил заменить 24 сдвиговых регистра, 192 транзистора и 640 резисторов на 12 светодиодных драйверов DM13a (около 1 доллара США каждый на eBay).

Сам куб был построен, как описывает Кевин в следующем видео:

После того, как куб был сделан, я создал печатную плату, используя метод тонера, чтобы удерживать микросхемы драйвера DM13A и сам куб. Вместо того, чтобы оплачивать стоимость серийно выпускаемой платы с покрытием для сквозных отверстий, я решил вручную подключить каждое соединение светодиода к соответствующему выводу DM13A с помощью проволочной обмотки. Включенные файлы Eagle содержат версию с ручной проводкой, а также версию с автоматической маршрутизацией (непроверенную).


На плате микропроцессора и анода также есть MSGEQ7 - семиполосный графический эквалайзер и микрофонный предусилитель, с которыми я планирую экспериментировать в будущем. Прямо сейчас они не используются. Если вы хотите использовать коммерческую плату UNO или Nano, все, что вам нужно, - это декодер от 3 до 8 строк 74HC138 и 8 полевых МОП-транзисторов с P-каналом и соответствующие резисторы. Вы можете просто подключить их к какой-нибудь прототипной плате, если хотите.
Блок питания 5В 20Вт был куплен на eBay. Я сделал корпус из выделанной сосны размером 40 х 9 мм.

Я добавил пару анимаций в программу куба Кевина, но в основном она осталась без изменений.
Заключение
Вы должны иметь возможность купить светодиоды RGB с общим анодом 600 x 5 мм на eBay примерно за 30 долларов США. Даже после упрощения электроники создание куба было очень трудоемким, но, в конце концов, полезным.
Код
- Куб_8x8x8_V1.ino
Cube_8x8x8_V1.ino C / C ++
/ * Светодиодный куб 8x8x8 RGB, автор Джон Брэднам, на основе работы Кевина Дарра, последнее V12, 17 апреля 2013 г. Примечания к выпуску:V11 - Исправлена ошибка с синхронизацией BAM - Установка пустого контакта перемещена в ISR, поэтому контакты не работают до тех пор, пока они не будут записаны в V12побитовая операция для установки pins LOW был неверным, должен быть PORTx &=~ (1 <// Библиотека SPI используется для синхронизации данных с регистрами сдвига # define LATCH_PIN 2 // может использовать любой вывод, который вы хотите заблокировать регистры сдвига # определить BLANK_PIN 4 // то же самое, можно использовать любой вывод, который вы хотите для этого, просто убедитесь, что вы потянули через от 1k до 5V # define DATA_PIN 11 // используется SPI, должен быть контакт 11 # define CLOCK_PIN 13 // используется SPI, должен быть 13 # define LAYER_A 5 // 74138 A Input # define LAYER_B 6 // 74138 A Input # define LAYER_C 7 // 74138 A Input # define SWITCH_PGM 10 // PB2 #define SWITCH_SEQ 9 // PB1 #define CUBE_SIZE 8 // Количество столбцов, строк или уровней в кубе # define CUBE_MAX (CUBE_SIZE - 1) // Максимальное количество кубов в dex # define LEDS_PER_LEVEL (CUBE_SIZE * CUBE_SIZE) // Количество светодиодов на уровень // *** переменные *** переменные *** переменные *** переменные *** переменные *** переменные *** переменные *** переменные // Эти переменные используются мультиплексированием и кодом модуляции битового угла // Так сохраняется яркость каждого светодиода, // Каждому светодиоду нужен только бит, чтобы знать, должен ли он быть включен или выключен, поэтому 64 байта дает you 512 бит =512 светодиодов // Поскольку мы модулируем светодиоды, используя 4-битное разрешение, каждый цвет имеет 4 массива по 64 бита в каждом байте red0 [LEDS_PER_LEVEL], red1 [LEDS_PER_LEVEL], red2 [LEDS_PER_LEVEL], red3 [LEDS_PER_LEVEL]; byte синий0 [LEDS_PER_LEVEL], синий1 [LEDS_PER_LEVEL], синий2 [LEDS_PER_LEVEL], синий3 [LEDS_PER_LEVEL]; байт зеленый0 [LEDS_PER_LEVEL], зеленый1 [LEDS_PER_LEVEL], зеленый2 [LEDS_PER_LEVEL], зеленый3 [LEDS_PER_LEVEL], увеличится разрешение] больше вашего драгоценного RAMint level =0; // отслеживает, на какой уровень мы смещаем данные int anodeLevel =0; // это увеличивается через уровни анода int BAM_Bit , BAM_Counter =0; // Переменные модуляции битового угла для отслеживания событийint animation =0; // Отслеживает анимацию в основном цикле // **** setup **** setup **** setup **** setup **** setup **** setup **** setup **** setup **** setup **** setup **** setup **** setup **** setupvoid setup () {SPI.setBitOrder (MSBFIRST); // Первый старший бит SPI.setDataMode (SPI_MODE0); // Режим 0 Нарастающий фронт данных, сохраняйте низкую частоту SPI.setClockDivider (SPI_CLOCK_DIV2); // Запускаем данные с частотой 16 МГц / 2-8 МГц //Serial.begin(115200);// если вам это нужно? noInterrupts (); // уничтожаем прерывания до тех пор, пока все не будут настроены // Мы используем Таймер 1 для обновления куба TCCR1A =B00000000; // Регистрируем все 0, так как мы не переключаем никакие контакты TCCR1B =B00001011; // бит 3 установлен для перехода в режим CTC вызовет прерывание при совпадении счетчика // биты 0 и 1 настроены для деления тактовой частоты на 64, поэтому 16MHz / 64 =250kHz TIMSK1 =B00000010; // бит 1 установлен для вызова прерывания на OCR1A соответствует OCR1A =30; // вы можете поиграть с этим, но я установил его на 30, что означает:// наши часы работают на частоте 250 кГц, что составляет 1/250 кГц =4 мкс // с OCR1A, установленным на 30, это означает, что прерывание будет вызываться каждые ( 30 + 1) x4us =124us, // что дает частоту мультиплексирования около 8 кГц // наконец настраиваем выводы pinMode (LATCH_PIN, OUTPUT); // фиксируем pinMode (DATA_PIN, OUTPUT); // MOSI DATA pinMode (CLOCK_PIN, OUTPUT); // SPI Clock pinMode (LAYER_A, OUTPUT); // 74138 A Вход pinMode (LAYER_B, OUTPUT); // 74138 B Вход pinMode (LAYER_C, OUTPUT); // 74138 C Вход digitalWrite (LAYER_A, LOW); digitalWrite (СЛОЙ_B, НИЗКИЙ); digitalWrite (LAYER_C, LOW); pinMode (SWITCH_PGM, INPUT); // Переключение PGM 1 / PGM 2 pinMode (SWITCH_SEQ, INPUT); // Переключение SEQ / COLOR // pinMode (BLANK_PIN, OUTPUT); // Разрешение вывода важно, чтобы сделать это последним, поэтому светодиоды делают не мигает при загрузке SPI.begin (); // запускаем библиотеку SPI interrupts (); // позвольте шоу начаться, это позволяет начать мультиплексирование} // *** start loop *** start loop *** start loop *** start loop *** start loop *** start loop *** start loop *** start loop *** start loopvoid loop () {// Каждая анимация, находящаяся в подпрограмме // Для управления LED, вы просто:// LED (уровень, который вы хотите 0-CUBE_MAX, строка, которую вы хотите 0-CUBE_MAX, столбец, который вы хотите 0-CUBE_MAX, яркость красного 0-15, яркость зеленого 0-15, яркость синего 0-15); если (digitalRead (SWITCH_PGM) ==HIGH) test_leds (); else {clean (); анимация =анимация + 1; переключатель (анимация) {case 1:rainVersionTwo (20); ломать; случай 2:папка (10); ломать; случай 3:sinwaveTwo (15); ломать; случай 4:randomColor (10); ломать; случай 5:wipe_out (10); ломать; случай 6:bouncyvTwo (15); ломать; case 7:color_wheelTWO (10); ломать; случай 8:harlem_shake (); ломать; корпус 9:рябь (10); ломать; случай 10:анимация =0; ломать; }}} // **** LED Routine **** LED Routine **** LED Routine **** LED Routinevoid LED (int level, int row, int column, byte red, byte green, byte blue) { // Здесь все начинается. либо 0, либо 7 для местоположения, и 0 или 15 для уровня яркости =ограничение (level, 0, CUBE_MAX); строка =ограничение (строка, 0, CUBE_MAX); столбец =ограничение (столбец, 0, CUBE_MAX); красный =ограничение (красный, 0, 15); зеленый =ограничение (зеленый, 0, 15); синий =ограничение (синий, 0, 15); // В кубе есть светодиоды (CUBE_SIZE * CUBE_SIZE * CUBE_SIZE), поэтому, когда мы пишем на уровень 2, столбец 5, строку 4, это нужно преобразовать в число от 0 до (CUBE_SIZE * CUBE_SIZE * CUBE_SIZE) - 1 // Светодиоды первого уровня идут первыми в последовательности, затем 2-й уровень, затем третий и так далее // Для куба 4 x 4 x 4 (level * (4 * 4)) - это то, что индексирует начальную позицию уровня, поэтому уровень 0 - это светодиоды 0-15, уровень 1 - светодиоды 16-31 и так далее // если вы смотрели на куб, а смотрели только на нижний уровень // 00 01 02 03 // 04 05 06 07 / / 08 09 10 11 // 12 13 14 15 // Для куба 8 x 8 x 8 (level * (8 * 8)) - это то, что индексирует начальную позицию уровня, поэтому уровень 0 - это светодиоды 0-63, уровень 1 это светодиоды 64-127 и т. д. // если вы смотрели на куб сверху вниз и смотрели только на нижний уровень // 00 01 02 03 04 05 06 07 // 08 09 10 11 12 13 14 15 // 16 17 18 19 20 21 22 23 // 24 25 26 27 28 29 30 31 // 32 33 34 35 36 37 38 39 // 40 41 42 43 44 45 46 47 // 48 49 50 51 52 53 54 55 // 56 57 5 8 59 60 61 62 63 // Затем, если вы увеличили уровень, верхний правый угол сетки выше начнется с (CUBE_SIZE * CUBE_SIZE) // Причина этого в том, что вам не нужно запоминать число для каждого светодиода, что позволяет использовать уровень, строку, столбец // А как насчет деления на 8? //... ну, у нас 8 бит на байт, и у нас есть 64 байта в памяти для всех 512 бит, необходимых для каждого светодиода, поэтому // мы делим только что найденное число на 8 и берем его целое число, Итак, мы знаем, в каком байте находится этот бит // запутались? это нормально, давайте возьмем пример, если бы мы хотели записать в светодиод до последнего светодиода в кубе, мы бы написали 7, 7, 7 // давая (7 * 64) + (7 * 8) =7 =511, что верно, но теперь давайте разделим его на 8, 511/8 =63,875 и возьмем int, чтобы получить 63, // это последний байт в массиве, что верно, поскольку это последний светодиод // Получить номер светодиода 0-511 int allbyte =(level * LEDS_PER_LEVEL) + (row * CUBE_SIZE) + column; // Получаем индекс в массив. Каждая проиндексированная ячейка содержит один байт или 8 бит; int whichbyte =int (целый байт / 8); int whichbit =(весь байт &7); // Все это обретет смысл через секунду // Это 4-битное цветовое разрешение, поэтому каждый цвет содержит массивы x4 по 64 байта, объяснение ниже:bitWrite (red0 [whichbyte], whichbit, bitRead (red, 0)); bitWrite (красный1 [whichbyte], whichbit, bitRead (красный, 1)); bitWrite (красный2 [whichbyte], whichbit, bitRead (красный, 2)); bitWrite (red3 [whichbyte], whichbit, bitRead (красный, 3)); bitWrite (green0 [whichbyte], whichbit, bitRead (зеленый, 0)); bitWrite (зеленый1 [whichbyte], whichbit, bitRead (зеленый, 1)); bitWrite (зеленый2 [whichbyte], whichbit, bitRead (зеленый, 2)); bitWrite (зеленый3 [whichbyte], whichbit, bitRead (зеленый, 3)); bitWrite (blue0 [whichbyte], whichbit, bitRead (синий, 0)); bitWrite (blue1 [whichbyte], whichbit, bitRead (синий, 1)); bitWrite (blue2 [whichbyte], whichbit, bitRead (синий, 2)); bitWrite (blue3 [whichbyte], whichbit, bitRead (синий, 3)); // Вы еще больше запутались? Не должно быть! Теперь это начинает обретать смысл. Обратите внимание на то, что каждая строка представляет собой bitWrite, // bitWrite (байт, который вы хотите записать, бит байта, который нужно записать, и 0 или 1, которые вы хотите записать) // Это означает, что 'whichbyte' это байт от 0 до 63, в котором бит соответствует светодиоду от 0 до 511 // Теперь имеет смысл, почему мы это сделали? принимает значение от 0 до 511 и преобразует его в значение от 0 до 63, поскольку каждый светодиод представляет собой // бит в массиве из 64 байтов. // Затем следующая строка - это какой бит 'allbyte- (8 * whichbyte)' // Это просто берет значение светодиода 0-511 и вычитает его из БАЙТА, его бит был расположен в 8 раз // Подумайте об этом, байт 63 будет содержать светодиоды от 504 до 511, поэтому, если вы взяли 505- (8 * 63), вы получите 1, что означает, что // светодиод номер 505 находится в бите 1 байта 63 в массиве // это то, что Это? Нет, вам все равно нужно выполнить бит Считывание яркости 0-15, которую вы пытаетесь записать, // если вы написали 15 в КРАСНЫЙ, все 4 массива для этого светодиода будут иметь 1 для этого бита, что означает, что он будет включен 100% // Вот почему четыре массива читают 0-4 значения, введенного для КРАСНОГО, ЗЕЛЕНОГО и СИНЕГО // надеюсь, все это имеет какой-то смысл?} // *** MultiPlex BAM *** MultiPlex BAM ** * MultiPlex BAM *** MultiPlex BAM *** MultiPlex BAM *** MultiPlex BAM *** MultiPlex BAMISR (TIMER1_COMPA_vect) {// Эта процедура вызывается в фоновом режиме автоматически с частотой, установленной OCR1A // В этом коде я установил OCR1A до 30, поэтому это вызывается каждые 124 мкс, давая каждому уровню в кубе 124 мкс времени включения // Есть 8 уровней, поэтому у нас есть максимальная яркость 1/8, так как уровень должен отключиться до того, как будет достигнут следующий уровень. включен // Частота мультиплексирования равна 124 мкс * 8 =992 мкс, или 1/992 мкс =около 1 кГц PORTD | =1 < =CUBE_SIZE) {fx =CUBE_MAX; fxm =-1; } ломать; случай 1:fy =fy + fym; если (fy <0) {fy =0; fym =1; } если (fy> =CUBE_SIZE) {fy =CUBE_MAX; фим =-1; } ломать; случай 2:fz =fz + fzm; если (fz <0) {fz =0; fzm =1; } если (fz> =CUBE_SIZE) {fz =CUBE_MAX; fzm =-1; } ломать; } переключатель (случайный (3)) {case 0:ftx =ftx + ftxm; если (ftx <0) {ftx =0; ftxm =1; } если (ftx> =CUBE_SIZE) {ftx =CUBE_MAX; ftxm =-1; } ломать; случай 1:fty =fty + ftym; если (fty <0) {fty =0; ftym =1; } если (fty> =CUBE_SIZE) {fty =CUBE_MAX; ftym =-1; } ломать; случай 2:ftz =ftz + ftzm; если (ftz <0) {ftz =0; ftzm =1; } если (ftz> =CUBE_SIZE) {ftz =CUBE_MAX; ftzm =-1; } ломать; }} // while clean ();} // wipeout // **** rainVersionTwo **** rainVersionTwo **** rainVersionTwo **** rainVersionTwo **** rainVersionTwovoid rainVersionTwo (int runtimeInSeconds) {int x [LEDS_PER_LEVEL ], y [LEDS_PER_LEVEL], z [LEDS_PER_LEVEL], ledcolor; int xx [LEDS_PER_LEVEL], yy [LEDS_PER_LEVEL], zz [LEDS_PER_LEVEL], xold [LEDS_PER_LEVEL], yold [LEDS_PER_LEVEL], zold [LEDS_PER_LEVEL]; для (int addr =0; addr =200 &&ledcolor <300) {for (int addr =0; addr =300 &&ledcolor <400) {} if (ledcolor> =500 &&ledcolor <600) {} ledcolor ++; если (ledcolor> =300) ledcolor =0; для (int addr =0; addr
Схема


Производственный процесс
- Сигнал тревоги для растений, предупреждающий о жажде
- Светодиодный куб 5x5x5
- Итальянские часы со словами
- Счетчик кВтч Sigfox
- Цветовой микшер Arduino RGB
- Монитор температуры Bluetooth
- Светодиодный уличный светодиод DMX RGB
- Блокировка, управляемая жестами
- Изолированный аналоговый вход для Arduino
- Измерьте время реакции