POV-бегущая строка из 8 светодиодов
- Платформы: Iskra Mini
- Языки программирования: Arduino (C++)
- Тэги: Персистенция, акселерометр, рисуем светодиодами в воздухе, обман зрения.
Что это?
При быстром вращении зажжённого факела, вместо нескольких раздельных его положений, мы видим светящийся огненный круг, а когда смотрим кино отчетливо видим плавно двигающуюся картинку, а не отдельные кадры. Этот эффект называется персистенция — способность человеческого глаза соединять быстро сменяющиеся изображения в одно. Мы расскажем как выводить текстовые сообщения и картинки в воздухе, опираясь на этот эффект .
Что нам понадобится?
- Светодиоды красные 5 мм (8 шт.)
- Резисторы 220 Ом (8 шт.)
Как собрать?
- Так как акселерометр общается с управляющей электроникой по протоколу I²C, а на плате Iskra Mini эти пины не выведены, сделаем это сами. Для этого возьмите штырьковые соединители, откусите от них два пина и припаяйте с обратной стороны платы Iskra Mini к пинам
A4 (SDA)
иA5 (SCL)
. - Установите платформу Iskra Mini на макетную плату.
- Пины питания
+5V
и землиGND
платформы Iskra Mini соедините с помощью проводов «папа-папа» с рельсами питания и земли макетной платы соответственно. - Установите акселерометр на Breadbord и с помощью проводов подключите следующим образом:
- Пины питания
V
и землиG
акселерометра с помощью проводов «папа-папа» соедините с рельсами питания и земли макетной платы соответственно. - Сигнальные пины
D
иC
акселерометра с помощью проводов «папа-мама» соедините с пинамиA4 (SDA)
иA5 (SCL)
платы Iskra Mini.
- Возьмите 8 красных светодиодов и установите их в ряд на макетную плату.
- Далее с помощью резисторов
220 Ом
соедините катоды светодиодов с рельсой земли макетной платы. - В продолжение используя провода «папа-папа» соедините аноды светодиодов со
2
по9
пин платы Iska Mini. - Переверните макетную плату. Возьмите кабель питания от батарейки Крона и соедините одну часть с Кроной, а вторую — с гнездом питания 2,1 мм с клеммником. Далее подключите в клеммник два провода «папа-папа». Теперь аккуратно отклейте защитную плёнку от breadbord и закрепите всю собранную конструкцию на липкой части макетки.
- В заключение подключите выведенные от клеммника два провода к платформе Iskra Mini таким образом:
- контакт
+
клеммника — наVin
платы Iskra Mini; - контакт
−
клеммника — наGND
платы Iskra Mini.
В итоге получилась схема, приведенная ниже.
Алгоритм
- Сразу после подачи питания считываем направления и величину ускорения по оси Y.
- Если ускорение превышает норму и направленно слева направо выводим буквы в обычном порядке.
- Если ускорение превышает норму и направленно справа налево выводим буквы в обратно порядке зеркально отраженные.
- Гасим все светодиоды и повторяем весь выше описанный цикл.
Исходный код
- povdisplay.ino
// библиотека для работы I²C #include <Wire.h> // библиотека для работы с модулями IMU #include <TroykaIMU.h> // задержка между столбцами одного символа // ширина символа #define TIMER_COLUMS 3 // задержка между символами // ширина между символами #define TIMER_FRAMES 8 // количество светодиодов, высота символа #define ROW 8 // количество столбцов в символе #define COL 5 // массив пинов, к которым подключены светодиоды int pins[] = {2, 3, 4, 5, 6, 7, 8, 9}; // создаём объект для работы с акселерометром Accelerometer accel; // переменная для хранения // направления и величины ускорения по оси Y float y = 0; // переменная для хранения кол-во раз // подряд положительных значений направленного ускорения int y_pol = 0; // переменная для хранения кол-во раз // подряд отрицательных значений направленного ускорения int y_neg = 0; // двумерный массив символов const unsigned char image[][COL] = { {0x00, 0x00, 0x00, 0x00, 0x00}, // space {0x00, 0xF6, 0xF6, 0x00, 0x00}, // ! {0x00, 0xE0, 0x00, 0xE0, 0x00}, // " {0x28, 0xFE, 0x28, 0xFE, 0x28}, // # {0x00, 0x64, 0xD6, 0x54, 0x08}, // $ {0xC2, 0xCC, 0x10, 0x26, 0xC6}, // % {0x4C, 0xB2, 0x92, 0x6C, 0x0A}, // & {0x00, 0x00, 0xE0, 0x00, 0x00}, // ' {0x00, 0x38, 0x44, 0x38, 0x00}, // ) {0x00, 0x82, 0x44, 0x38, 0x00}, // ) {0x88, 0x50, 0xF8, 0x50, 0x88}, // * {0x08, 0x08, 0x3E, 0x08, 0x08}, // + {0x00, 0x00, 0x05, 0x06, 0x00}, // , {0x08, 0x08, 0x08, 0x08, 0x08}, // - {0x00, 0x00, 0x06, 0x06, 0x00}, // . {0x02, 0x0C, 0x10, 0x60, 0x80}, // / {0x7C, 0x8A, 0x92, 0xA2, 0x7C}, // 0 {0x00, 0x42, 0xFE, 0x02, 0x00}, // 1 {0x42, 0x86, 0x8A, 0x92, 0x62}, // 2 {0x44, 0x82, 0x92, 0x92, 0x6C}, // 3 {0x10, 0x30, 0x50, 0xFE, 0x10}, // 4 {0xE4, 0xA2, 0xA2, 0xA2, 0x9C}, // 5 {0x3C, 0x52, 0x92, 0x92, 0x0C}, // 6 {0x80, 0x86, 0x98, 0xE0, 0x80}, // 7 {0x6C, 0x92, 0x92, 0x92, 0x6C}, // 8 {0x60, 0x92, 0x92, 0x94, 0x78}, // 9 {0x00, 0x00, 0x36, 0x36, 0x00}, // : {0x00, 0x00, 0x35, 0x36, 0x00}, // ; {0x10, 0x28, 0x44, 0x82, 0x00}, // < {0x28, 0x28, 0x28, 0x28, 0x28}, // = {0x00, 0x82, 0x44, 0x28, 0x10}, // > {0x40, 0x80, 0x8A, 0x90, 0x60}, // ? {0x7C, 0x82, 0xBA, 0xBA, 0x62}, // @ {0x3E, 0x48, 0x88, 0x48, 0x3E}, // A {0xFE, 0x92, 0x92, 0x92, 0x6C}, // B {0x7C, 0x82, 0x82, 0x82, 0x44}, // C {0xFE, 0x82, 0x82, 0x82, 0x7C}, // D {0xFE, 0x92, 0x92, 0x92, 0x82}, // E {0xFE, 0x90, 0x90, 0x90, 0x80}, // F {0x7C, 0x82, 0x82, 0x8A, 0x4E}, // G {0xFE, 0x10, 0x10, 0x10, 0xFE}, // H {0x82, 0x82, 0xFE, 0x82, 0x82}, // I {0x84, 0x82, 0xFC, 0x80, 0x80}, // J {0xFE, 0x10, 0x28, 0x44, 0x82}, // K {0xFE, 0x02, 0x02, 0x02, 0x02}, // L {0xFE, 0x40, 0x20, 0x40, 0xFE}, // M {0xFE, 0x60, 0x10, 0x0C, 0xFE}, // N {0x7C, 0x82, 0x82, 0x82, 0x7C}, // O {0xFE, 0x90, 0x90, 0x90, 0x60}, // P {0x7C, 0x82, 0x82, 0x86, 0x7E}, // Q {0xFE, 0x90, 0x98, 0x94, 0x62}, // R {0x64, 0x92, 0x92, 0x92, 0x4C}, // S {0x80, 0x80, 0xFE, 0x80, 0x80}, // T {0xFC, 0x02, 0x02, 0x02, 0xFC}, // U {0xF8, 0x04, 0x02, 0x04, 0xF8}, // V {0xFC, 0x02, 0x0C, 0x02, 0xFC}, // W {0xC6, 0x28, 0x10, 0x28, 0xC6}, // X {0xC0, 0x20, 0x1E, 0x20, 0xC0}, // Y {0x86, 0x8A, 0x92, 0xA2, 0xC2}, // Z {0x00, 0x00, 0xFE, 0x82, 0x00}, // [ {0x00, 0x00, 0x00, 0x00, 0x00}, // \ {0x80, 0x60, 0x10, 0x0C, 0x02}, // ] {0x20, 0x40, 0x80, 0x40, 0x20}, // ^ {0x01, 0x01, 0x01, 0x01, 0x01}, // _ {0x80, 0x40, 0x20, 0x00, 0x00}, // ` {0x04, 0x2A, 0x2A, 0x2A, 0x1E}, // a {0xFE, 0x12, 0x22, 0x22, 0x1C}, // b {0x1C, 0x22, 0x22, 0x22, 0x14}, // c {0x1C, 0x22, 0x22, 0x12, 0xFE}, // d {0x1C, 0x2A, 0x2A, 0x2A, 0x18}, // e {0x10, 0x7E, 0x90, 0x80, 0x40}, // f {0x18, 0x25, 0x25, 0x25, 0x1E}, // g {0xFE, 0x10, 0x10, 0x10, 0x0E}, // h {0x00, 0x12, 0x5E, 0x02, 0x00}, // i {0x02, 0x01, 0x01, 0x11, 0x5E}, // j {0xFE, 0x08, 0x08, 0x14, 0x22}, // k {0x00, 0x82, 0xFE, 0x02, 0x00}, // l {0x3E, 0x20, 0x1C, 0x20, 0x1E}, // m {0x3E, 0x20, 0x20, 0x20, 0x1E}, // n {0x1C, 0x22, 0x22, 0x22, 0x1C}, // o {0x3F, 0x24, 0x24, 0x24, 0x18}, // p {0x18, 0x24, 0x24, 0x3F, 0x01}, // q {0x3E, 0x10, 0x20, 0x20, 0x10}, // r {0x12, 0x2A, 0x2A, 0x2A, 0x04}, // s {0x00, 0x10, 0x3C, 0x12, 0x04}, // t {0x3C, 0x02, 0x02, 0x02, 0x3E}, // u {0x30, 0x0C, 0x02, 0x0C, 0x30}, // v {0x38, 0x06, 0x18, 0x06, 0x38}, // w {0x22, 0x14, 0x08, 0x14, 0x22}, // x {0x38, 0x05, 0x05, 0x05, 0x3E}, // y {0x22, 0x26, 0x2A, 0x32, 0x22}, // z {0x00, 0x10, 0x6C, 0x82, 0x82}, // { {0x00, 0x00, 0xFF, 0x00, 0x00}, // | {0x82, 0x82, 0x6C, 0x10, 0x00}, // } {0x08, 0x10, 0x18, 0x08, 0x10}, // ~ {0x38, 0x7c, 0x3e, 0x7c, 0x38}, // Love 127 }; void setup() { // инициализация акселерометра accel.begin(); // устанавливаем чувствительность accel.setRange(RANGE_2G); // устанавливаем пины из массива pins[] в режим выхода for (int i = 0; i < ROW; i++) { pinMode(pins[i], OUTPUT); } // гасим все светодиоды ledsAllOff(); } void loop() { // считываем направления и величину ускорения по оси Y y = accel.readAY()); // если ускорение превысило допустимое значение // и направлено с права налево if (y > 15) { y_pol++; if (y_pol == 60) { y_pol = 0; // так как ускорение направлено справа налево // выводим буквы в обратном порядке // функцией которая их зеркально отражает showRL('a'); showRL('k'); showRL('r'); showRL('e'); showRL('p'); showRL('m'); showRL('A'); delay(10); } // если ускорение превысило допустимое значение // и направлено с лево на право } else if (y < - 15) { y_neg++; if (y_neg == 60) { y_neg = 0; // так как ускорение направлено слева направо // выводим буквы в обычном порядке showLR('A'); showLR('m'); showLR('p'); showLR('e'); showLR('r'); showLR('k'); showLR('a'); delay(10); } } } // функция вывода символа слева направо void showLR(unsigned char c) { // перебор столбцов матрицы for (int i = 0; i < COL; i++) { // перебор строк матрицы (светодиодов) ledsSet(c, i); // задержка задающая ширину символа delay(TIMER_COLUMS); } // гасим все светодиоды ledsAllOff(); // задержка задающая ширину между символами delay(TIMER_FRAMES); } // функция вывода символа справа налево void showRL(unsigned char c) { // перебор столбцов матрицы for (int i = COL - 1; i >= 0; i--) { // перебор строк матрицы (светодиодов) ledsSet(c, i); // задержка задающая ширину символа delay(TIMER_COLUMS); } // гасим все светодиоды ledsAllOff(); // задержка задающая ширину между символами delay(TIMER_FRAMES); } // функция гасящая все светодиоды void ledsAllOff() { for (int i = 0; i < ROW; i++) { digitalWrite(pins[i], LOW); } } // функция включения светодиодов в текущем столбце void ledsSet(unsigned char c, int i) { for (int j = 0; j < ROW; j++) { digitalWrite(pins[j], bitRead(image[c-32][i], j)); } }
Демонстрация работы устройства
Что дальше?
В этот примере мы показали самое простое применение персистенции. Добавив механическую конструкцию, увеличив количество и разнообразив цвета светодиодов можно собрать более сложные и интереснее устройства. А если использовать RGB-светодиоды, можно собрать полноценный виртуальный дисплей в воздухе.