Espruino:Примеры/Индивидуальный доступ к светодиодам
Индивидуальный доступ к светодиодам[1]
В последнее время светодиоды с индивидуальным доступом становятся всё дешевле и дешевле. Эти светодиоды (помимо самого RGB-светодиода) оснащены маленьким управляющим чипом, с помощью которого и задается нужный цвет. Эти управляющие чипы умеют принимать и отправлять последовательные данные и их можно последовательно подсоединить друг к другу (по принципу гирляндного подключения, которое также называют «daisy-chain»). Это позволяет управлять большим количеством светодиодов при помощи всего нескольких управляющих проводов, т.е. избавляет вас от необходимости использовать по проводу для каждого светодиода.
Сейчас есть два главных типа светодиодов с индивидуальным доступом – с 3 и 4 проводами.
Светодиоды вроде WS2801 оснащены 4 проводами – для заземления, питания, тактовой частоты и передачи данных. Ими можно управлять с помощью шины SPI, которая есть на большинстве микроконтроллеров. Но у этого метода есть пара маленьких недостатков:
- Между каждой парой светодиодов должно быть 4 провода, что увеличивает затраты
- Устройству, состоящему из управляющего чипа и светодиода, нужно больше проводов, что увеличивает его размер и затраты на него
Многие более современные управляющие чипы используют 3 провода – например, WS2811/WS2812/APA104/APA106 и SK6812. Управление ими осуществляется при помощи 1-проводного протокола последовательной передачи данных, в котором (как подсказывает его название) для передачи данных используется только 1 провод. Это значит:
- Для каждого светодиода нужно только 3 провода
- Управляющий чип будет более компактным, как и его печатная плата. Соответственно, само устройство тоже будет меньше
- У многих светодиодов (по сути, у всех, кроме WS2811) управляющий чип находится внутри самого светодиода
В результате проект, создаваемый при помощи 3-проводных RGB-светодиодов, будет значительно дешевле проекта, создаваемого с помощью светодиодов WS2801.
В этом руководстве мы расскажем, как управлять этими 3-проводными светодиодами при помощи Espruino. Управление ими будет осуществляться при помощи серии импульсов на частоте 800 КГц, где коротким импульсом будет бит 0, а длинным – бит 1. Для управления этими устройствами есть специальная библиотека neopixel.
Нам понадобятся
- Цепь RGB-светодиодов вроде WS2811. Я воспользуюсь не светодиодной лентой, а 25 отдельными светодиодами, подключенными друг к другу
- Плата Espruino
Подсоединение
Ознакомьтесь документацией к библиотеке neopixel – светодиоды вроде WS2811 могут быть очень привередливы касаемо напряжения на линии для передачи данных. Если вы используете Espruino Original или Pico, то проблем возникнуть не должно, но при использовании других плат может понадобиться чуть более высокое напряжение.
У цепи светодиодов WS2811 должно быть два конца по 3 провода на каждом (некоторые из них могут быть продублированы) – красный, белый и зелёный. Красный – это 5-вольтовое питание, белый – это заземление, а зелёный – это передача сигнала (данных). Если к вашей светодиодной цепи есть какая-то документация, то советуем сначала ознакомиться с ней.
Сигнальный провод с одной стороны будет использоваться для ввода данных, а с другой – для вывода. Так вы сможете подключить друг к другу более 50 светодиодов. На моих светодиодах коннектор типа «мама» был входным. Но если на ваших светодиодах есть пометки «DI»/«DO» или в документации есть какое-то специальное упоминание об обозначениях, то используйте их.
Итак, само подключение выполняется очень просто.
Просто подсоедините белый (0 вольт) и красный (5 вольт) провода к 5-вольтовому источнику питания. Наверно, USB лучше не использовать, потому что когда всё будет включено, 50 светодиодов будут тянуть около 1 ампера, а это очень много!
Подключите белый провод к контакту GND, а зелёный – к контакту SPI MOSI на Espruino. Для этих целей подойдёт, например, B15 (но есть и другие варианты). На платах Discovery это обычно A7, а на платах Maple/Olimexino – D11. Если сомневаетесь, лучше сверьтесь с документацией платы.
Код
Теперь, когда всё подключено, давайте попробуем отправить какие-нибудь данные. Например, на первый светодиод. Передаваемые данные будут трёхсоставными – по байту для красного, зелёного и синего значений:
require("neopixel").write(B15, [255,0,0]);
В результате первый светодиод загорится красным цветом.
Обратите внимание, что в строчке кода выше используется контакт B15. Если вы хотите использовать какой-то другой контакт (см. раздел «Подсоединение» выше), то этот аргумент нужно будет поменять, но на большинстве устройств вам также надо будет убедиться, что нужный контакт можно использовать как MOSI-контакт для аппаратной SPI. При использовании Puck.js подойдёт абсолютно любой контакт.
Разумеется, мы можем задать и другие цвета:
require("neopixel").write(B15, [0,255,0]); // зелёный
require("neopixel").write(B15, [0,0,255]); // синий
require("neopixel").write(B15, [255,255,255]); // белый
Теперь давайте дополнительно зададим цвета для второго и третьего светодиодов. Для этого нам просто нужно отправить больше данных:
require("neopixel").write(B15, [255,0,0, 0,255,0, 0,0,255]);
Теперь мы можем создать функцию с циклом for(), которая будет создавать цвета для всех светодиодов. Код ниже делает так, чтобы все 50 светодиодов загорались всё более ярким белым цветом (первый светодиод будет выключен):
var rgb = new Uint8ClampedArray(25*3);
function getPattern() {
for (var i=0;i<rgb.length;i+=3) {
rgb[i ] = i*10;
rgb[i+1] = i*10;
rgb[i+2] = i*10;
}
}
Обратите внимание, что в самом верху при помощи класса Uint8ClampedArray мы задали массив rgb. Мы можем воспользоваться и обычным массивом, но типизированные массивы быстрее и занимают меньше памяти, если вам нужно хранить лишь значения в диапазоне между 0 и 255. Использование Uint8ClampedArray также означает, что все значения больше 255 и меньше 0 будут «обрезаны». При использовании Uint8Array у значения будут просто удалены старшие биты, из-за чего 256 станет 0, 257 станет 1 и так далее.
Затем мы можем создать функцию, которая будет отправлять эту информацию светодиодам, а затем вызывать её:
function doLights() {
getPattern();
require("neopixel").write(B15, rgb);
}
doLights();
Теперь у нас получился приятный оттенок серого. Но его также можно заанимировать:
var pos = 0;
function getPattern() {
pos++;
for (var i=0;i<rgb.length;i+=3) {
var col = (Math.sin(i+pos*0.2)+1) * 127;
rgb[i ] = col;
rgb[i+1] = col;
rgb[i+2] = col;
}
}
doLights();
В коде выше используется синусоида, с помощью которой светодиод занимает промежуточное положение между максимальной яркостью и выключением. Позиция анимации хранится в переменной pos. Теперь при каждом вызове doLights() цвет будет меняться. Мы можем заанимировать это при помощи setInterval().
setInterval(doLights,50);
Теперь давайте попробуем что-нибудь ещё. Например, мы можем воспользоваться синусоидными волнами разной частоты, чтобы сделать анимацию радужными цветами:
function getPattern() {
pos++;
for (var i=0;i<rgb.length;i+=3) {
rgb[i ] = (1 + Math.sin((i+pos)*0.1324)) * 127;
rgb[i+1] = (1 + Math.sin((i+pos)*0.1654)) * 127;
rgb[i+2] = (1 + Math.sin((i+pos)*0.1)) * 127;
}
}
Или мы можем воспользоваться генератором случайных чисел, чтобы светодиод попеременно мигал разными случайными оттенками синего.
function getPattern() {
for (var i=0;i<rgb.length;i+=3) {
rgb[i ] = 0;
rgb[i+1] = 0;
rgb[i+2] = Math.random()*255;
}
}
Теперь мы можем сохранить все наши разные функции getPattern() в массив, а затем переключаться между ними при нажатии на кнопку на Espruino:
var patterns = [];
patterns.push(function() {
pos++;
for (var i=0;i<rgb.length;i+=3) {
var col = (Math.sin(i+pos*0.2)+1) * 127;
rgb[i ] = col;
rgb[i+1] = col;
rgb[i+2] = col;
}
});
patterns.push(function() {
pos++;
for (var i=0;i<rgb.length;i+=3) {
rgb[i ] = (1 + Math.sin((i+pos)*0.1324)) * 127;
rgb[i+1] = (1 + Math.sin((i+pos)*0.1654)) * 127;
rgb[i+2] = (1 + Math.sin((i+pos)*0.1)) * 127;
}
});
patterns.push(function() {
for (var i=0;i<rgb.length;i+=3) {
rgb[i ] = 0;
rgb[i+1] = 0;
rgb[i+2] = Math.random()*255;
}
});
var patternNumber = 0;
function changePattern() {
patternNumber = (patternNumber+1) % patterns.length;
getPattern = patterns[patternNumber];
}
setWatch(changePattern, BTN, { repeat: true, edge:'falling' });
Теперь, если нажать на кнопку на Espruino (не кнопку сброса!), паттерн изменится. Вы можете вернуться и отредактировать getPattern(), чтобы создать собственный паттерн:
edit(getPattern);
Отредактируйте его, перейдите к концу функции при помощи клавиши «Стрелочка вниз» или Page Down и нажмите ↵ Enter . Паттерн мигания светодиодов изменится. Теперь впишите строчку ниже и ваш новый паттерн будет сохранен в массив с паттернами (и в результате он тоже будет активирован при нажатии на кнопку Espruino, когда до него дойдёт очередь):
patterns.push(getPattern)
Весь код полностью (если вы просто хотите разом скопировать его и вставить) выглядит вот так:
var rgb = new Uint8ClampedArray(25*3);
var pos=0;
var patterns = [];
patterns.push(function() {
pos++;
for (var i=0;i<rgb.length;i+=3) {
var col = (Math.sin(i+pos*0.2)+1) * 127;
rgb[i ] = col;
rgb[i+1] = col;
rgb[i+2] = col;
}
});
patterns.push(function() {
pos++;
for (var i=0;i<rgb.length;i+=3) {
rgb[i ] = (1 + Math.sin((i+pos)*0.1324)) * 127;
rgb[i+1] = (1 + Math.sin((i+pos)*0.1654)) * 127;
rgb[i+2] = (1 + Math.sin((i+pos)*0.1)) * 127;
}
});
patterns.push(function() {
for (var i=0;i<rgb.length;i+=3) {
rgb[i ] = 0;
rgb[i+1] = 0;
rgb[i+2] = Math.random()*255;
}
});
var getPattern = patterns[0];
function doLights() {
getPattern();
require("neopixel").write(B15, rgb);
}
var patternNumber = 0;
function changePattern() {
patternNumber = (patternNumber+1) % patterns.length;
getPattern = patterns[patternNumber];
}
setWatch(changePattern, BTN, { repeat: true, edge:'falling' });
setInterval(doLights,50);
Вот и всё! Если вы создали какой-то клёвый паттерн, можете поделиться им с другими!
О том, где купить WS2811, читайте последнем разделе этой статьи. Там же можно почитать о том, какие типы светодиодов тоже работают по этому принципу.
См.также
Внешние ссылки