Мой сын крепко “подсел” на магнитный конструктор Magformers. Однажды просматривая серию Фиксиков где фигурировал такой же конструктор ребенок спросил: “Папа, а почему у фиксиков детальки светятся, а у нас нет?”.

Оказалось, что действительно существует набор “Magformers Neon LED Set”, где помимо обычных строительных блоков есть еще и элемент со светодиодом. Поскольку к этому времени у нас уже собрался целый ящик магнитиков всех возможных форм и размеров (как по мне, китайский магформерс ничуть не уступает оригиналу), покупать еще один набор только ради лампочки как-то не хотелось. Тем более, что этот набор стоил ощутимо дороже аналогичного без подсветки.

Прикинув, что компонентов там всего на пару баксов, бОльшая часть из которых у меня уже была, я решил собрать свою моргульку. Да еще и с эффектами, которых не было у оригинала.

Под катом Вы найдете вариант моргалки на ATTiny85 и светодиодной панели на светодиодах WS8212. Я расскажу о схемотехнике, как эту всю штуковину я запитывал от батареи, а также неочевидных проблем, которые я выгреб по пути. Я также в деталях расскажу о программной составляющей проекта.

Первые шаги


Мне показалось, что светяшка на обычном светодиоде (пускай даже RGB) это скучно и банально. А вот пощупать что нибудь вроде WS8212 показалось интересным. На ебее предлагались как отдельные светодиоды, так и матрицы размером до 16х16. Накупив несколько разных модулей свой выбор я остановил на матрице 4х4. В ней достаточно много светодиодов, чтобы побаловаться различными визуальными эффектами, при этом модуль сопоставим по размерам с окошком квадратного блока конструктора.



Для управления светодиодной матрицей достаточно всего одного пина микроконтроллера, так что даже ардуина нано выглядит как перебор (к тому же она не влезет в корпус). А вот клон digispark на контроллере ATTiny85 оказался в самый раз — в нем не очень много памяти и пинов, но более чем достаточно для светодиодной моргалки. Модуль отлично интегрируется с Arduino IDE и имеет на борту загрузчик по USB, поэтому программировать этот модуль очень просто и комфортно. Давно хотел его попробовать.

Начал с простейшей схемы.



В таком виде удалось достаточно быстро отладить все алгоритмы свечения/моргания (о них ниже). Но вот игрушка с проводным питанием это не дело — нужно подумать о питании от батарей. Причем чтобы не разорится на пальчиковых батареях (которые к тому же не влезают в габарит) решено было использовать литиевую. А раз есть литиевая батарея, то нужно думать как ее заряжать. В закромах как раз нашелся купленный по случаю “народный” контроллер заряда на микросхеме TP4056.

Только вот подключить его сразу не получилось. Схема модуля Digispark ATTiny85 не очень на такое рассчитана — там либо питание от USB, но тогда питание подается напрямую на микроконтроллер (по шине +5), либо от входа VIN, но тогда питание идет через линейный стабилизатор 7805. Вариант, когда модуль зарядки лития вставляется в разрыв между разъемом USB и микроконтроллером не предусмотрен. Пришлось доработать немного схему и выпаять лишние детали.



Так, теперь питание от USB поступает на ножку VIN и дальше уходит на вход зарядника. Выход зарядника (по сути аккумулятор подключается напрямую) заходит назад в плату через ножку 5V. И хотя на самом деле там будет от 3 до 4.2В (напряжение аккумулятора) это вполне нормально — диапазон рабочих напряжений микроконтроллера 1.8-5.5В. И даже светодиодный модуль нормально работает от 2.7В, хотя ниже 3.2В синему светодиоду немного не хватает и цвета немного “плывут” в желтый.

В целях экономии электроэнергии вечно горящий светодиод D2 я тоже выпаял. Общая схема теперь выглядит так



Питать схему можно было бы и через USB разъем в заряднике, но тогда бы потерялась возможность заливать прошивку через USB разъем на плате контроллера. Можно было бы оставить два USB разъема различного назначения — один для зарядки, другой для прошивки, но это как-то неправильно.

Аккумулятор размера 6х25х35 купил на ебее, но он оказался либо бракованный, либо я его убил коротким замыканием или большим током заряда (у платы по умолчанию ток заряда установлен в 1А и нужно перепаивать один резистор, чтобы уменьшить ток). В любом случае при подключении нагрузки даже в 10мА напряжение на аккуме падало до 1В. На время тестирования я переключился на полу-сдохшую LiPo батарею от мелкого квадрокоптера. Чуть позже заказал аккум у другого продавца и он оказался хорошим.

В принципе, на этом можно было бы и остановится, припаять соединительные провода и аккуратно затолкать все в какой нибудь корпус, но я решил измерить потребление схемы. И тут я прослезился. Ладно, что в рабочем состоянии (когда лампочки сияют на полную) эта штука жрет до 130мА, так в состоянии покоя потребление более 25мА! Т.е. мою батарею в 600мАч эта моргалка слопает менее чем за сутки!

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

Оставшиеся 15 мА потребляет микроконтроллер. Да, его можно уложить спать и согласно даташиту потребление будет измеряться микроамперами, но на деле меньше 1 мА получить не удалось. Я и АЦП отключал и пины переводил в input. Похоже где-то в схеме есть какая-то утечка, но моих скромных познаний в электронике недостаточно, чтобы ее найти и понять.

Усложняем схему


Тут я вспомнил, что я себе купил на пробу микросхему PT1502. Эта микросхема — контроллер заряда литиевого аккумулятора в комплекте с источником питания с несколькими управляющими входами. Единственная сложность — микросхема идет в корпусе QFN20 размером 4х4 мм и требует некоторой обвязки. Паять такое дома сложно, но можно. Плата получается сложной для обычного ЛУТа и нужно заказывать у китайцев. Но мы ведь не боимся сложностей, правда?

В нескольких квадратиках схему можно описать так.



В выключенном состоянии питание на контроллер и светодиоды не поступает. У устройства есть кнопка ‘Power’, которая включает моргалку (она же переключает режимы). Светодиод сияет, скажем, минуту и если пользовательской активности нет (никто не нажимает кнопку), то устройство выключается. Т.е. не просто уходит в сон, а именно отключает само себе питание сигналом Power Hold. Причем отключает все сразу — и микроконтроллер, и светодиоды. Функциональность включения и отключения питания реализуется внутри микросхемы PT1502

Осталось всего ничего: нарисовать принципиальную схему и сделать плату. Схема, по большей части, слизана с даташита PT1502, а также модуля Digispark ATTiny85. Микросхема контроллера питания PT1502 функционально делится на несколько частей, потому на схеме разбита на блоки.



Это, собственно, контроллер заряда литиевой батареи со своей обвязкой. Светодиод LED1 показывает состояние заряда — горит, значит идет заряд. Резистор R6 задает ток заряда в 470мА. Поскольку у меня батарея на 600мАч, в принципе можно поднять ток и до 600мА поставив резистор на 780-800 Ом. Впрочем я не уверен в особом качестве моей батареи — пусть лучше заряжает медленнее, но дольше проживет.

Рассмотрим схему управления питанием



Кнопка SW1 запускает всю систему — микросхема PT1502 просыпается сама и затем запускает все источники питания (которых у нее 3). Когда питание установится микросхема запустит микроконтроллер, отпустив сигнал RESET. Для удобства отладки я еще добавил отдельную кнопку Reset.

Сигнал HOLD используется для выключения всей системы. Когда микроконтроллер запустится он должен выставить на этой линии единицу. Когда пора закругляться, микроконтроллер выставляет на линии HOLD ноль и микросхема питания PT1502 остановит все источники питания.

Можно было бы еще отслеживать низкий заряд батареи с помощью вывода BAT_LOW, но в этой поделке я на это забил — никаких данных сохранять не нужно и ничего не взорвется если вовремя не заметить севшую батарею. Сдохнет так сдохнет. Но на всякий случай на плате предусмотрел контакт под это дело.

Вернемся на секунду к кнопке SW1. Я решил не делать 2 отдельные кнопки для включения и для управления. Поэтому та же кнопка подключена еще и к ATTiny85 и во время работы переключает режимы моргания. Номиналы делителя R7-R8 подобраны так, чтобы не спалить порт микроконтроллера PB2. При всех диапазонах напряжений батареи (3,3 — 4.2В) на ногу контроллера будет поступать напряжение в оговоренных даташитом пределах (0.7*VCC — VCC+0.5В)

Рассмотрим источник питания



Это импульсный DC-DC преобразователь. Напряжение на выходе задается резисторами R10-R11 и согласно формуле из даташита настроено на 3.3В. Все остальное — несложная обвязка.

По хорошему такой навороченный источник питания не особо то и нужен — можно было микроконтроллер бы вообще запитать напрямую от батареи. Просто этот источник уже реализован в микросхеме PT1502 и он может включаться/выключаться когда нам будет нужно — почему бы этим не воспользоваться?



В микросхеме также имеются 2 линейных стабилизатора, но я их использовать не буду. К сожалению, как выяснилось, подавать входное напряжение на этот источник все равно нужно, иначе микросхема думает что питание все еще недостаточно стабильно и не запускает микроконтроллер (это знание мне далось неделей перепаивания тестовой платы туда-сюда — никак не мог понять почему оно не работает)

Перейдем к логической части.



Обвязка USB слизана с платы Digispark без изменений. Это нужно для согласования напряжений USB (по которому бегает 3.3В) и сигналов микроконтроллера (который в оригинале питается от 5В). Поскольку в моем случае микроконтроллер также питается от 3.3В, то схему можно было бы и упростить, но на всякий случай я развел на плате оригинальную схему.



В обвязке микроконтроллера ничего интересного.

Финальный штрих это разъем



По сути у меня получилась такая себе отладочная плата на ATTiny85 с поддержкой USB и контроллером питания от литиевой батареи. Потому я не стал ограничиваться только выводом линии на светодиод. Вместо этого я вывел все линии микроконтроллера на гребенку — заодно и к программатору удобно подключать.

И пускай почти все линии жестко привязаны к определенному функционалу (PB1 — линия Hold, PB2 — кнопка включения, PB3/PB4 — USB, PB5 — Reset) в будущем можно будет в некоторых пределах обойти. Например, не распаивать обвязку USB и освободить линии PB3/PB4. Или, например, отказаться от ресета и освободить PB5. Ну а пока свободным остается только PB0 — к нему и подключим наш светодиод.

Переходим к плате. Учитывая ограничения по размерам платы в 40х40мм, количество компонентов и QFN20 корпус микросхемы PT1502, я даже не стал рассматривать изготовление платы в домашних условиях. Поэтому я сразу стал разводить максимально компактную двухслойную плату. Вот что у меня получилось



Для удобства использования на обратной стороне подписал все возможные функции выводов (идею слямзил с платы Digispark)



Плату заказывал на JLCPCB . Качеством, если честно, не очень доволен — если много раз перепаивать микросхему, то маска возле мелких контактов PT1502 чуток облазит. Ну и мелкие надписи немного поплыли. Впрочем, если все запаять с первого раза, то норм.

Для пайки QFN20 понадобится паяльный фен, все остальное можно при определенной сноровке запаять обычным паяльником. Вот так выглядит распаянная плата



Корпус


Пора переходить к корпусу. Его я напечатал на 3Д принтере. Дизайн без излишеств — коробка и кнопка. На коробке предусмотрены специальные зацепы, чтобы устанавливать светяшку в стандартный квадратный модуль конструктора.



В корпусе живет основная плата и батарея.





Светодиодная панель крепится на крышку, которая в свою очередь привинчивается шурупами к основной коробке

Сначала я думал прикручивать светодиодную панель к к крышке шурупами, но в итоге просто приклеил на двусторонний скотч. Получилось вот так



В таком виде устройством уже можно пользоваться, но выглядит пока еще некрасиво — не хватает рассеивателя.

Первый вариант рассеивателя я пробовал сделать по технологии усадки ПЭТ бутылок строительным феном (подсмотрено у авиамоделистов).

Итак, для начала нужна болванка. Ее я сделал из гипса, который залил в форму, которую напечатал на 3д принтере. В первом варианте форма была неразъемная и я так и не смог вытянуть из нее отлитую болванку. Поэтому пришлось сделать форму из двух частей.



Идея метода в следующем. Надеваешь бутылку из-под детского йогурта на болванку и усаживаешь строительным феном. Вот только перепортив штук 20 разных емкостей из-под различной молочки у меня так и не получилось усадить эту штуку красиво, без складок и пузырей. По видимому нужно городить какую-то вакуумную установку и усаживать листовой пластик. В общем, оказалось слишком сложно для такой поделки.

Пошуршав по сусекам я нашел пробник пластика Verbatim PET Transparent в пару метров. Решил попробовать рассеиватель просто напечатать. И хотя на входе в принтер пластик кажется кристально прозрачным реальная деталь получается матово мутная. Вероятно это из-за внутренней структуры, т.к. слои не заполняют объем полностью а накладываются с промежутками и щелям. Более того если попробовать обработать деталь наждачкой для более гладкой поверхности то получаем еще большее матирование. Впрочем, это как раз то, что мне и было нужно.

Мне лень было возиться с креплением для рассеивателя, потому я присобачил его на термоклей. Так что конструкция у меня теперь условно разборная. Я бы мог заморочиться и с изобретением каких нибудь защелок, но у меня уже закончился пробник прозрачного пластика. Так что пусть будет термоклей.





Прошивка


Для светодиодной моргалки особо сильно в периферию микроконтроллера погружаться не нужно — достаточно парочки функций по работе с GPIO. Но раз уж модуль стыкуется с платформой Ардуино, то почему бы этим не воспользоваться?

Для начала несколько определений и констант

// Number of total LEDs on the board. Mine has 4x4 LEDs
#define NUM_HW_PIXELS 16

// Pin number where LED data pin is attached
#define DATA_PIN 0
// Pin number where mode switch button is attached
#define BUTTON_PIN 2
// Power Enabled pin
#define POWER_EN_PIN 1

// Max brightness (dimming the light for debugging)
#define MAX_VAL 255

Тут определяется количество пикселей в моей матрице, номера пинов и максимальная яркость светодиодов (во время отладки удобно было ее ставить на уровне 50, чтобы не слепила глаза)

Светодиоды в моей матрице расположены достаточно неочевидным образом — зигзагом. Потому для разных эффектов пришлось сделать перенумерацию.

// LED indexes for different patterns
uint8_t circleLEDIndexes[] = {0, 1, 2, 3, 4, 11, 12, 13, 14, 15, 8, 7};
uint8_t beaconLEDIndexes[] = {6, 5, 10, 9};
uint8_t policeLEDIndexes[] = {7, 6, 10, 11, 4, 5, 9, 8};

Для управлением светодиодов я не стал изобретать велосипед и взял готовую библиотеку для работы со светодиодами WS8211. Интерфейс библиотеки слегка побелил-покрасил. Некоторые вспомогательные функции (например конвертация HSV в RGB) также оттуда слямзил.

Для начала плату и библиотеку WS8211 нужно проинициализировать

// Driver
Ai_WS2811 ws2811;

void setup()
{
    // Set up power
    pinMode(POWER_EN_PIN, OUTPUT);
    digitalWrite(POWER_EN_PIN, HIGH);

    // initialize LED data pin
    pinMode(LED_PIN, OUTPUT);

    // Initialize button pin
    pinMode(BUTTON_PIN, INPUT);

    // Initialize WS8211 library
    static CRGB ledsBuf[NUM_HW_PIXELS];
    ws2811.init(DATA_PIN, NUM_HW_PIXELS, ledsBuf);

    // Set the watchdog timer to 2 sec
    wdt_enable(WDTO_2S);
}

Первым делом нужно выставить сигнал POWER HOLD в единицу — это будет сигналом микросхеме PT1502, что микроконтроллер завелся и работает исправно. Микросхема в свою очередь будет исправно поставлять электричество микроконтроллеру и светодиодам до тех пор, пока сигнал HOLD выставлен в единицу.

Далее конфигурируются ножки управления светодиодом на выход и кнопки на вход. После этого можно инициализировать библиотеку WS8211.

Поскольку это достаточно автономное устройство нельзя допустить, чтобы микроконтроллер залип в непонятном состоянии и сожрал всю батарею. Для этого я запускаю watchdog таймер на 2 секунды. Таймер будет перезапускаться в основном цикле программы.

Теперь нужно определить парочку вспомогательных функций. Библиотека WS8211 хранит в себе буфер с цветовыми значениями каждого светодиода. Работать с буфером напрямую не очень удобно, потому я написал простую функцию записи RGB значения в определенный светодиод

void setRgb(uint8_t led_idx, uint8_t r, uint8_t g, uint8_t b)
{
    CRGB * leds = ws2811.getRGBData();
    leds[led_idx].r = r;
    leds[led_idx].g = g;
    leds[led_idx].b = b;
}

Но в большинстве случаев в цветовой модели RGB считать цвета не очень удобно, а то и вообще невозможно. Например при рисовании всяких радуг удобнее работать с цветовой моделью HSV. Цвет каждого пикселя задается задается значением цветового тона и яркостью. Значение насыщенности для простоты опущено (используется максимальное). Значения цветового тона (hue) сведены к диапазону 0-255 (вместо стандартных 0-359).

/**
* HVS to RGB conversion (simplified to the range 0-255)
**/
void setHue(uint8_t led_idx, int hue, int brightness)
{
	//this is the algorithm to convert from RGB to HSV
	double r = 0;
	double g = 0;
	double b = 0;

	double hf = hue/42.6; // Not /60 as range is _not_ 0-360

	int i=(int)floor(hue/42.6);
	double f = hue/42.6 - i;
	double qv = 1 - f;
	double tv = f;

	switch (i)
	{
		case 0:
			r = 1;
			g = tv;
			break;
		case 1:
			r = qv;
			g = 1;
			break;
		case 2:
			g = 1;
			b = tv;
			break;
		case 3:
			g = qv;
			b = 1;
			break;
		case 4:
			r = tv;
			b = 1;
			break;
		case 5:
			r = 1;
			b = qv;
			break;
	}

    brightness = constrain(brightness, 0, MAX_VAL);

    setRgb(led_idx, 
        constrain(brightness*r, 0, MAX_VAL),
        constrain(brightness*g, 0, MAX_VAL),
        constrain(brightness*b, 0, MAX_VAL)
    );
}

Функция взята из библиотеки Ai_WS8211 и слегка подпилена. В оригинальном варианте этой функции из библиотеки было парочку багов из-за чего цвет на радугах показывался с рывками.

Перейдем к реализации различных эффектов. Каждая функция вызывается из главного цикла для отрисовки одного “кадра”. Поскольку каждый эффект оперирует разными параметрами между вызовами они сохраняются в статических переменных.

Это самый простой эффект — все светодиоды заливаются одним цветом, который плавно меняется.

void rainbow()
{
    static uint8_t hue = 0;
    hue++;
    
    for (int led = 0; led < NUM_HW_PIXELS; led++)
        setHue(led, hue, MAX_VAL);
   
    ws2811.sendLedData();
    delay(80);
}

Следующий эффект поинтереснее — он выводит радугу по контуру матрицы, а цвета в радуге постепенно смещаются по кругу.

void slidingRainbow()
{
    static uint8_t pos = 0;
    pos++;
    
    for (int led = 0; led < ARRAY_SIZE(circleLEDIndexes); led++)
    {
        int hue = (pos + led*256/ARRAY_SIZE(circleLEDIndexes)) % 256;
        setHue(circleLEDIndexes[led], hue, MAX_VAL);
    }
   
    ws2811.sendLedData();
    delay(10);
}

А этот эффект заливает всю матрицу случайным цветом, который сначала плавно загорается, а потом также плавно гаснет.

void randomColorsFadeInOut()
{
    static uint8_t color = 0;
    static bool goesUp = false;
    static uint8_t curLevel = 0;

    if(curLevel == 0 && !goesUp)
    {
        color = rand() % 256;
        goesUp = true;
    }

    if(curLevel == MAX_VAL && goesUp)
    {
        goesUp = false;
    }

    for(int led = 0; led < NUM_HW_PIXELS; led++)
        setHue(led, color, curLevel);

    if(goesUp)
        curLevel++;
    else
        curLevel--;

    ws2811.sendLedData();
    delay(10);    
}

Следующая группа эффектов рисует разные проблесковые маячки. Так, например, ребенок любит строить из магнитиков бульдозер и оранжевая мигалка там будет очень кстати.

void orangeBeacon()
{
    const int ORANGE_HUE = 17;

    static uint8_t pos = 0;
    pos+=3;
    
    for (int led = 0; led < ARRAY_SIZE(circleLEDIndexes); led++)
    {
        int brightness = brightnessByPos(pos, led*255/ARRAY_SIZE(circleLEDIndexes), 70);
        setHue(circleLEDIndexes[led], ORANGE_HUE, brightness);
    }
   
    ws2811.sendLedData();
    delay(1);
}

Технически эффект выглядит как яркая точка, которая двигается по матрице. Но чтобы выглядело красиво соседние светодиоды плавно угасают по мере отдаления от основной точки. Поэтому мне понадобилась функция, которая вычисляет эту самую яркость.

int brightnessByPos(int pos, int ledPos, int delta)
{
    int diff = abs(pos - ledPos);
    if(diff > 127)
        diff = abs(256-diff);
    int brightness = MAX_VAL - constrain(MAX_VAL*diff/delta, 0, MAX_VAL);
    return brightness;
}

Pos это некоторая условная позиция светящейся точки яркость, отображенная на закольцованный диапазон 0-255. ledPos это позиция светодиода (отображенная на тот же диапазон) яркость которого нужно вычислить. Если разница позиций больше delta, то светодиод не горит, а чем ближе к позиции, тем ярче он светится.

Или вот, например, полицейский красно-синий проблесковый маяк

void policeBeacon()
{
    const int RED_HUE = 0;
    const int BLUE_HUE = 170;
    
    static uint8_t pos = 0;
    pos += 2;
    
    for (int led = 0; led < ARRAY_SIZE(policeLEDIndexes); led++)
    {
        int ledPos = led*255/ARRAY_SIZE(policeLEDIndexes);
        int brightness = brightnessByPos(pos, ledPos, 50);
        setHue(policeLEDIndexes[led], RED_HUE, brightness);

        if(brightness == 0)
        {
            brightness = brightnessByPos((pos+100) % 256, ledPos, 50);
            setHue(policeLEDIndexes[led], BLUE_HUE, brightness);
        }
    }
   
    ws2811.sendLedData();
    delay(1);
}

Раз уж речь зашла про машины, то и светофор тут реализовать не проблема.

Это функции, которые включают различные сигналы светофора на различных позициях

void clearPixels()
{
    for(int i=0; i<NUM_HW_PIXELS; i++)
    {
        setRgb(i, 0, 0, 0);
    }
}

void redTrafficLights()
{
    for(int i=0; i<4; i++)
        setRgb(i, MAX_VAL, 0, 0);
    ws2811.sendLedData();
}

void yellowTrafficLights()
{
    for(int i=4; i<8; i++)
        setRgb(i, MAX_VAL, MAX_VAL, 0);
    ws2811.sendLedData();
}

void greenTrafficLights()
{
    for(int i=8; i<16; i++)
        setRgb(i, 0, MAX_VAL, 0);
    ws2811.sendLedData();
}

Пора это оживить. Светофор работает по специальной программе, заданной в чем-то вроде байткода. В табличке описан режим и время на которое этот режим нужно включить.

enum TRAFFIC_LIGHTS
{
    NONE, RED, YELLOW, GREEN
};

struct trafficLightState
{
    uint8_t state;
    uint16_t duration;
};

const trafficLightState trafficLightStates[] = {
    {NONE, 1},         // clear yellow
    {RED, 7000},     // red
    {YELLOW, 2000},      // red + yellow
    {NONE, 1},         // clear red+yellow
    {GREEN, 7000},     // green
    {NONE, 300},       // Blinking green
    {GREEN, 300},       // Blinking green
    {NONE, 300},       // Blinking green
    {GREEN, 300},       // Blinking green
    {NONE, 300},       // Blinking green
    {GREEN, 300},       // Blinking green
    {NONE, 1},         // clear green
    {YELLOW, 2000},      // yellow
};

Собственно функция, которая это все обрабатывает

void trafficLights()
{
    static uint8_t curStateIdx = 0;
    static unsigned long curStateTimeStamp = 0;

    // Switch to a new state when time comes
    if(millis() - curStateTimeStamp > (unsigned long)trafficLightStates[curStateIdx].duration)
    {
        curStateIdx++;
        curStateIdx %= ARRAY_SIZE(trafficLightStates);
        curStateTimeStamp = millis();
    }
    
    switch(trafficLightStates[curStateIdx].state)
    {
        case NONE:
            clearPixels();
            ws2811.sendLedData();
            break;
        case RED:
            redTrafficLights();
            break;
        case YELLOW:
            yellowTrafficLights();
            break;
        case GREEN:
            greenTrafficLights();
            break;
        default:
            break;
    }

    // Just waiting
    delay(10);
}

По достижению заданного временнОго интервала включается следующий режим светофора и опять начинается отсчет времени.

Последний эффект на который хватило моего воображения это звездочки. 5 случайных светодиодов плавно загораются на случайную яркость и затем плавно гаснут. Если одна звездочка потухла, то загорится другая в случайном месте.

void stars()
{
    const uint8_t numleds = 5;
    static uint8_t ledIndexes[numleds] = {0};    
    static uint8_t curVal[numleds] = {0};
    static uint8_t maxVal[numleds] = {0};

    for(int i=0; i<numleds; i++)
    {
        if(ledIndexes[i] == 0)
        {
            uint8_t led = rand() % (NUM_HW_PIXELS+1);
            CRGB * leds = ws2811.getRGBData();
            if(leds[led].r == 0)
            {
                ledIndexes[i] = led;
                maxVal[i] = rand() % (MAX_VAL-1) + 1;
                curVal[i] = 0;
            }
        }
        else
        {
            uint8_t led = ledIndexes[i];
            if(curVal[i] < maxVal[i])
                curVal[i]++;
            else
            if(curVal[i] == maxVal[i])
                maxVal[i] = 0;
            else
            if(curVal[i] == 0 || --curVal[i] == 0)
                ledIndexes[i] = 0;

            setRgb(led-1, curVal[i], curVal[i], curVal[i]);
        }
    }

    ws2811.sendLedData();
    delay(80);    
}

Где-то тут закрался злобный баг. Иногда звездочки резко загораются, или наоборот резко гаснут. Но мне, если честно, лень было с этим разбираться — выглядит оно вполне нормально.

Пора подумать об экономии батареи. Я уже приводил значения потребления этой всей штуки. Если не подумать об отключении питания, то светодиоды съедят батарейку за пару часов. Вот эта функция занимается отключением питания через 90 секунд бездействия. Изначально было 60 секунд, но при реальной игре этого оказалось маловато, а 2 минуты уже как-то долго.

void shutdownOnTimeOut(bool resetTimer = false)
{
    static unsigned long periodStartTime = 0;

    if(periodStartTime == 0 || resetTimer)
    {
        periodStartTime = millis();
        return;
    }

    if(millis() - periodStartTime >= 90000UL)
    {   
        periodStartTime = 0;
        shutDown();
    }
}

Собственно отключение питания происходит так.

void shutDown()
{
    clearPixels();
    ws2811.sendLedData();

    wdt_disable();
    digitalWrite(POWER_EN_PIN, LOW);

    // No power after this point
    while(true)
        ;
}

Если пользователь нажимает на кнопки, то таймер сбрасывается. По истечении установленного времени функция выставляет сигнал HOLD в ноль, что является командой PT1502 на отключение питания. Watchdog, кстати, тоже остановить нужно, иначе через 2 секунды он разбудит систему и включит питание опять.

Наконец, главный цикл, который это все запускает

// List of pointers to functions that serve different modes
void (*Modes[])() = 
{
    rainbow,
    slidingRainbow,
    orangeBeacon,
    policeBeacon,
    trafficLights,
    stars,
    randomColorsFadeInOut
};

void loop()
{
    static uint8_t mode = eeprom_read_byte( (uint8_t*) 10 );
    static bool waitingForBtnUp = false;
    static long btnPressTimeStamp;

    // Button switches mode
    if(digitalRead(BUTTON_PIN) == HIGH && !waitingForBtnUp)
    {
        delay(20);
        if(digitalRead(BUTTON_PIN) == HIGH)
        {
            mode++;
            mode %= ARRAY_SIZE(Modes); // num modes
            
            clearPixels();
            ws2811.sendLedData();
            delay(1);

            eeprom_write_byte( (uint8_t*) 10, mode );

            waitingForBtnUp = true;
            btnPressTimeStamp = millis();

            shutdownOnTimeOut(true);
        }
    }

    // Shut down on long press over 5s
    if(digitalRead(BUTTON_PIN) == HIGH && waitingForBtnUp && millis() - btnPressTimeStamp > 5000)
        shutDown();

    // Detect button release
    if(digitalRead(BUTTON_PIN) == LOW && waitingForBtnUp)
        waitingForBtnUp = false;

    // display LEDs according to current mode
    Modes[mode]();

    // pong shutdown timer
    shutdownOnTimeOut();

    // Yes, we still alive
    wdt_reset();
}

Нажатие кнопки переключает режимы и сбрасывает таймер автовыключения. В зависимости от текущего режима запускается одна из функций-эффектов из списка Modes. На каждом цикле также сбрасывается watchdog.

Если ребенок, скажем, играл в полицейскую машину и через 1.5 минуты мигалка отключилась, то скорее всего после повторного включения сын захочет продолжить играть в полицейскую машину. Для этого выбранный режим сохраняется в EEPROM (ячейка номер 10 выбрана от балды).

Вот видео, которое показывает как это все работает.


Бутлоадер


Почти все готово. Но есть еще одна штука, которую нужно подпилить — бутлоадер. Дело в том, что стандартный бутлоадер нам не подходит.

Во-первых, при включении питания он ждет целых 6 секунд — авось в него прошивку вливать начнут. Только после этого управление передается основной прошивке. Это удобно на стадии разработки, но будет раздражать в готовом устройстве.

А во-вторых, стандартный загрузчик ничего не знает про микросхему PT1502, которой неплохо было бы подать сигнал HOLD. Без этого сигнала микросхема думает, что микроконтроллер либо не завелся, либо наоборот хочет выключаться. А раз так, то через несколько миллисекунд PT1502 отрубит питание всей схеме.

Благо исправить обе проблемы не составляет труда. В плате digispark ATTiny85 используется загрузчик micronucleus. Этот загрузчик достаточно просто подпилить под наши нужды. Нужно только подправить соответствующие дефайны в файле конфигурации.

Первым делом я скопировал стандартную конфигурацию firmware\configuration\t85_default в свою собственную директорию и в ней уже делал все изменения. Так будет в случае чего легко откатиться на оригинальный загрузчик.

В файле bootloaderconfig.h есть выбор способа входа в загрузчик. Из того, что предлагается из коробки нам ничего не подходит, но ближе всего вариант ENTRY_JUMPER. В этом варианте вход в загрузчик происходит только если на определенном пине появляется определенный уровень (на плате замыкают джампер).

#define ENTRYMODE ENTRY_JUMPER

Джампера у нас нет, но есть кнопка на ноге PB2. Пускай вход в загрузчик будет происходить если при включении питания кнопку держат в течении 5-7 секунд. А вот если нажали и отпустили, то переход в основную прошивку происходит сразу.

Нам нужно определить 3 функции — инициализации, деинициализации и собственно проверка, а не пора ли входить в бутлоадер. В оригинале они все простые и реализованы макросами. У нас простыми будут только первые 2

#define HOLD_PIN      PB1
#define JUMPER_PIN    PB2
#define JUMPER_PORT   PORTB 
#define JUMPER_DDR    DDRB 
#define JUMPER_INP    PINB 

#define bootLoaderInit()   {JUMPER_DDR &= ~_BV(JUMPER_PIN); JUMPER_DDR |= _BV(HOLD_PIN); JUMPER_PORT &= ~_BV(JUMPER_PIN); JUMPER_PORT |= _BV(HOLD_PIN); _delay_ms(1);}
#define bootLoaderExit()   {;}

bootLoaderInit() настраивает пин кнопки (JUMPER_PIN) на вход и выключает на нем подтяжку. Подтяжка у нас уже есть на плате, причем к земле, а при нажатии на кнопку на пине наоборот будет единица. Заодно можно и сразу сконфигурировать сигнал HOLD на вывод и выставить на нем единицу…

За пояснением битовой арифметики ходить, например, сюда, а понимание регистров настройки GPIO в контроллерах AVR можно почерпнуть, например, отсюда.

Функция bootLoaderExit() пустая, т.к. выставленная конфигурация вполне годится для последующего перехода к основной прошивке

Функцию bootLoaderStartCondition() которая отвечает за вход в бутлоадер в формат макроса уже не влезла, а потому стала полноценной функцией

#ifndef __ASSEMBLER__
// Bootloader condition is to hold the button for 5 seconds
inline unsigned char  bootLoaderStartCondition()
{
  long int i;
  for(i=0; i<10000000; i++)
    if( !(JUMPER_INP & _BV(JUMPER_PIN)))
      return 0;

  return 1;
}
#endif

Функция в течении нескольких секунд (по факту около 6-7) проверяет состояние кнопки. Если кнопку отпустили раньше, то входить в бутлоадер нам не нужно. Терпеливых и настойчивых пускают дальше в загрузчик.

Как оказалось файл bootloaderconfig.h участвует в компиляции ассемблерных файлов и сишный код в этом файле вызывает ошибки. Пришлось функцию поместить в блок #ifndef __ASSEMBLER__

Еще один параметр, который я подправил, указывает бутлоадеру что делать если его не подключили к USB — выходить через одну секунду. Дело в том, что во время обкатки сын часто нажимал кнопку и нечаянно заходил в бутлоадер. Я не знаю каким чудом, но бутлоадер если не видел USB соединения мог случайным образом затирать некоторые страницы памяти. Потому если нет соединения будем просто выходить в основную программу.

/*
 * Define bootloader timeout value. 
 * 
 *  The bootloader will only time out if a user program was loaded.
 * 
 *  AUTO_EXIT_NO_USB_MS        The bootloader will exit after this delay if no USB is connected.
 *                             Set to 0 to disable
 *                             Adds ~6 bytes.
 *                             (This will wait for an USB SE0 reset from the host)
 *
 *  All values are approx. in milliseconds
 */
#define AUTO_EXIT_NO_USB_MS    1000

Компилируем… и получаем ошибку, что код не влезает в отведенное ему пространство бутлоадера. Поскольку флеш памяти в контроллере очень мало, то бутлоадер ужимают по максимуму, чтобы оставить побольше места основной программе. Но это легко исправить в файле Makefile.inc следуя инструкции

# hexadecimal address for bootloader section to begin. To calculate the best value:
# - make clean; make main.hex; ### output will list data: 2124 (or something like that)
# - for the size of your device (8kb = 1024 * 8 = 8192) subtract above value 2124... = 6068
# - How many pages in is that? 6068 / 64 (tiny85 page size in bytes) = 94.8125
# - round that down to 94 - our new bootloader address is 94 * 64 = 6016, in hex = 1780
BOOTLOADER_ADDRESS = 1940

Тут я просто уменьшил стартовый адрес бутлоадера одну страницу (64 байта) тем самым увеличив место под загрузчик.

В остальном компиляция и заливка бутлоадер с помощью программатора USBAsp не составила проблем.

Заключение


Это был весьма интересный путь от прототипа на макетке до законченного устройства. Вроде выглядит как обычная моргалка из урока по ардуине, а на самом деле в процессе работы пришлось решить целую кучу интересных проблем — тут и борьба с потреблением, и выбор элементной базы, и проектирование корпуса, и доведение до ума прошивки с бутлоадером. Я искренне надеюсь, что мой опыт кому нибудь пригодится.

Можно ли было проще? Конечно можно. Я думаю все можно было бы сделать с помощью транзистора. К сожалению вот эту статью я прочитал уже после того как спаял плату. Увидел бы статью раньше — сделал бы все на том же народном TP4056 — его паять легче. Все равно DC-DC преобразователь, который есть внутри PT1502 в этом устройстве, по хорошему, не нужен. Впрочем, практическое исследование микросхемы PT1502 мне пригодится для моего другого проекта, как и умение паять микросхемы в корпусе QFN20.

Напоследок вот ссылки на мой проект:

Код прошивки
Схема и плата
Модель корпуса и рассеивателя
Готовые STL модели для печати

Комментарии (44)


  1. prs123
    05.01.2019 20:38

    Очень интересный кейс и здорово, что использовали для этого подходящий контроллер.
    Раз печатали серую часть корпуса на принтере, можно было бы и не мучаться с бутылкой, а взять пластик прозрачный Филаментарно (надеюсь за рекламу не сочтут).
    Когда плату на JLCPCB заказывали, доставка дороже плат не вышла или вы сразу пачку заказывали? На сколько хорошо дорожки сделали? а то народ жаловался на качество дорожек


    1. grafalex Автор
      05.01.2019 20:48

      а взять пластик прозрачный

      Так я же так и сделал.
      Упомнянутый Verbatim PET Transparent это и есть прозрачный пластик для 3Д принтера.
      Выклянчил пробник на какой-то выставке…

      Когда плату на JLCPCB заказывали, доставка дороже плат не вышла или вы сразу пачку заказывали?

      Изготовление 5 плат — $2, доставка — $10.8. Но в заказе я объединил несколько своих проектов на одну плату 100х100, после чего разрезал их ножницами по металлу. Если бы заказывал только под этот проект, то доставка была бы долларов 5
      Дорожки не отваливались, только маска, да и то после второй-третьей перепайки микросхемы в QFN20


      1. prs123
        05.01.2019 20:51

        Так я же так и сделал.
        Упомнянутый Verbatim PET Transparent это и есть прозрачный пластик для 3Д принтера.
        Выклянчил пробник на какой-то выставке…

        каюсь, не заметил
        Изготовление 5 плат — $2, доставка — $10.8. Но в заказе я объединил несколько своих проектов на одну плату 100х100

        А вы указывали это как одну? то есть стоимость не менялась?


        1. grafalex Автор
          05.01.2019 21:32

          Слышал, что некоторые производители трепетно относятся к таким вещам и если видят, что на заготовке больше одной платы поднимают цену.

          В данном случае я не заказывал V-Cut (фрезеровка между элементами платы, чтобы удобнее было ломать). Вместо этого я просто налепил на одну панель несколько своих проектов и шелкографией нарисовал границы плат. Прокатило — цена осталась на уровне акционных 2 долларов


          1. sashz
            06.01.2019 09:57

            некоторые производители трепетно относятся к таким вещам и если видят, что на заготовке больше одной платы поднимают цену.

            pcbway этим увлекается, принудительно рисуя V-Cut и задирая цену, если видят несколько разобщенных плат на одной большой.


          1. Max-812
            06.01.2019 17:54

            Я для такой компоновки рисую границы контурным слоем, между плат делаю зазор 2 мм, и между каждой из соседних плат делаю мелкий мостик миллиметра 3 шириной, в котором сразу же размечаю несколько мелких отверстия по середине. В итоге платы просто ломаются руками, несколько зазубринок остается аккуратно убрать напильником. Пока вопросов у производителя не было, хотя регулярно в один 100 на 100 вписывается от 2 до 5 различных плат.


            1. grafalex Автор
              06.01.2019 19:22

              между плат делаю зазор 2 мм

              Зазор с фрезеровкой? за фрезеровку доп плату берут?


              1. Max-812
                07.01.2019 20:41

                Да, с фрезеровкой. Пока ни разу не брали. По сути, за счет мостиков у нас получается просто одна плата со сложным контуром.


                1. grafalex Автор
                  07.01.2019 23:27

                  Спасибо, возьму на вооружение


    1. Gluzer
      06.01.2019 13:54

      народ жаловался на качество дорожек
      Качество изготовления плат на JLCPCB разнится от случая к случаю.


      1. grafalex Автор
        06.01.2019 13:55

        Вот для нового проекта заказал, посмотрим что приедет


  1. grafalex Автор
    05.01.2019 21:31

    del


  1. customtema
    05.01.2019 22:30
    -1

    Этот проект называется Arduino.

    Извините.


    1. grafalex Автор
      05.01.2019 22:32

      Мммммм, не совсем понял у чему Вы придрались


      1. customtema
        05.01.2019 22:42
        -1

        К названию.


    1. BSW
      05.01.2019 22:35

      Подскажите. Сколько потребляет PT1502 в ''выключенном'' состоянии?


      1. grafalex Автор
        05.01.2019 22:39

        Микроамперы… Когда-то мерял, но не записал.
        К сожалению светяшку заклеил термоклеем и без особой необходимости туда не очень хотелось бы лезть. Но если вдруг полезу — обязательно замерю


      1. grafalex Автор
        05.01.2019 23:54

        Глянул в даташит. Typical — 1uA, Max — 5uA
        Так что батарейки должно хватить на годы.


  1. REPISOT
    05.01.2019 23:30

    Можете ответить, чем обусловлено вот это в разводке? При том, что сделано, вроде, для фабричного изготовления, а не одноразовая поделка для ЛУТа.


    1. grafalex Автор
      05.01.2019 23:43
      +2

      Моей невнимательностью и маленьким опытом в электронике (я программист).
      Спасибо за конструктивный комментарий, я обязательно учту в будущих проектах.


      1. olartamonov
        06.01.2019 01:27
        +1

        Самый большой ляп — никогда, ни при каких обстоятельствах не разводите землю обычной дорожкой, если абсолютно точно не знаете, зачем вы так делаете.

        Полигон.


        1. olartamonov
          06.01.2019 01:36
          +2

          Второй большой ляп — USB.

          1) никогда не используйте для защиты USB стабилитроны. Во-первых, стабилитроны вообще не для защиты, для этого есть TVS, во-вторых, у них конская ёмкость, до сотен пикофарад, которая может убить вам даже 12 Мбит/с. Для Full Speed подойдут многие типовые TVS, например, SP0503 — там как раз сборка из трёх TVS (третий на +5 В), для High Speed — только специализированные TVS с низкой паразитной ёмкостью, например, USBLC6.

          2) USB — дифференциальная шина, как минимум ведите обе дорожки на плате параллельно, выдерживайте их длину примерно равной, не допускайте стабов (тупиковых ответвлений, вот как у вас на R3) и т.п. Для High Speed и/или длины больше 3-5 см уже обязателен расчёт пары под импеданс 90 Ом.


          1. grafalex Автор
            06.01.2019 10:41

            Спасибо, это тоже учту.

            Как я написал в статье эту часть я слямзил с платы от DigiSpark. Думаю, что стабилитроны тут не для защиты, а для конвертации уровней 5<->3.3В. Насколько я понимаю эта схемотехника рекомендуется создателями библиотеки V-Usb www.obdev.at/products/vusb/index.html — это полностью программная эмуляция USB Low Speed (1.5MBit)

            Что не так с R3? насколько я понимаю что Low Speed, что Full Speed требуют подтяжки одной из шин к питанию (High Speed пока не копал)


            1. olartamonov
              06.01.2019 15:23

              Вопрос не в подтяжке, вопрос в дорожке.



              Слева правильно, справа — нет. На высокочастотных и/или дифференциальных линиях стараются не делать глухие ответвления (стабы), т.к. это дополнительная ёмкость, дополнительные отражения сигнала, дополнительная разница в длине и индуктивности дорожек в случае диффпары и т.п.

              По той же причине стараются на них не делать переходных отверстий, а на совсем чувствительных штуках — делают дорожки постоянной ширины, включая повороты, площадки деталей и т.п.

              В вашем случае последнее не нужно, но минимальную гигиену (без стабов, обе линии в паре близки по длине) соблюдать стоит всегда.


            1. olartamonov
              06.01.2019 15:24

              Про стабилитроны — кривая реализация, на Low Speed оно ещё может работать, но если вы это перенесёте на контроллер с аппаратным USB — уже на High Speed отвалится практически гарантированно, ну или как минимум работоспособность будет очень сильно зависеть от используемого USB-шнурка.


              1. grafalex Автор
                06.01.2019 16:57

                Для контроллера с аппаратным USB скорее всего не понадобится согласовывать уровни, а значит стабилитроны и не понадобятся.

                Спасибо за разъяснение про нюансы разводки диф-линий. Как раз в другом своем проекте дошел до разводки платы. Там я собираюсь подключать STM32 к USB без всяких стабилитронов, только через STF202-22 для защиты.


        1. grafalex Автор
          06.01.2019 10:28

          Спасибо, буду знать


    1. Demon_i
      05.01.2019 23:52
      +2

      С нижней и средней, вроде понятно, а с верхней что не так? Так, чтобы знать.


      1. rstepanov
        06.01.2019 00:55
        +2

        Прямой угол, ну и в целом не оптимально.


      1. olartamonov
        06.01.2019 01:28
        +1

        Ничего критичного в данном случае, но хорошим тоном является минимизация длины дорожек. Это такая разница между просто работающей разводкой и красивой разводкой.


    1. sashz
      06.01.2019 10:01

      Eagle авторазводчик такие косяки допускает. Если руками не поправить, то так и останется.


  1. Psychosynthesis
    06.01.2019 07:06

    Спасибо, интересная статья и результат радует.

    Единственная сложность — микросхема идет в корпусе QFN20 размером 4х4 мм и требует некоторой обвязки. Паять такое дома сложно, но можно.

    Поверьте, паять такое паяльником без фена это дело лишь небольшой привычки. Бывают корпуса, у которых на боковых сторонах выводов вообще нет, только снизу (как BGA), вот там даже с феном всё проклинаешь, только печь.


    1. olartamonov
      06.01.2019 15:26

      LGA феном совершенно спокойно ставятся.

      BGA в общем тоже, но там уже вопросы подготовки шариков, площадок, совмещения этого — в то время как LGA можно просто залудить и как-то так примерно ровно сверху положить.


      1. Psychosynthesis
        06.01.2019 16:10

        LGA? Это же разъём, нет?

        Вообще я же не отрицаю, что при должной сноровке что угодно можно сделать, но всё-таки если выводы под корпусом, без печи с подобранным терморежимом это не так просто делается как в иных случаях — отсутствие визуального контроля многого стоит. Достаточно какой-нибудь один контакт не пропаять и привет. Тут всё упирается в опыт и подбор золотой середины — когда и пропаялось всё как следует и чип не перегрет.

        В общем случае считаю бессмысленно спорить, что корпуса с выводами по бокам паяются проще корпусов с выводами снизу.


        1. olartamonov
          06.01.2019 16:25

          LGA — это вот такое, площадки на нижней поверхности чипа:



          Можно грубо представить как QFN, у которого ноги на боковую сторону не выходят.

          Сложность пайки с QFN одинаковая, и определяется по большей части шагом ножек и их количеством.

          И да, печь тут вообще глубочайше вторична и не даёт никак преимуществ — дефекты пайки возникают не из-за прогрева, а из-за нехватки или избытка припоя на ногах. То есть, хороший трафарет и хороший расстановщик рулят.

          Но если их нет, а припаять надо всего 1-2-5 штук, то тот же LGA облуживается снизу одним движением паяльника, после чего просто ставится с феном на площадку, без паяльной пасты, только с тонким слоем жидкого флюса. Результат будет 100-процентный, в общем-то.


          1. Psychosynthesis
            06.01.2019 16:46

            Хз, я пробовал, получалось через раз. Но согласен, количество припоя или флюса решает.


  1. stifff
    06.01.2019 07:28

    All, а подскажи. Есть ли какой-нибудь софт для расчета/моделирования рассеивателя вроде того, что в устройстве? Чтобы свечение отдельных светодиодов на плате не так сильно бросалось в глаза


    1. Gluzer
      06.01.2019 15:39

      Софт для моделирования оптических элементов конечно же есть. Из профессиональных можно отметить TracePro и LightTools. Но для расчета светорассеивателя в них придется немало попотеть, т.к. софт исключительно узкоспециализированный. Для сохранения минимальной толщины оптического элемента лучше всего использовать конструктив линзы Френеля.


  1. SHmoX
    06.01.2019 11:47

    отличная статья, отдельное спасибо за Amorphis.


  1. VT100
    06.01.2019 13:12

    … но на деле меньше 1 мА получить не удалось. Я и АЦП отключал и пины переводил в input. Похоже где-то в схеме есть какая-то утечка, но моих скромных познаний в электронике недостаточно, чтобы ее найти и понять.

    Была-бы схема того варианта — можно было-бы покумекать над поиском утечек. Несомненно — она была по цепи R3-D1.
    Но, скорее, выбран не тот режим энергосбережения (например — Idle вместо Power Down) и что-то не так с выводами. Вместо Input надо, как минимум, Input w. Pull-up. Выход P0 надо переводить в 0, что-бы не запитывать WS8212 через защитный диод. И над PB3, PB4 надо подумать: перевод их в «1» или Pull-up — будет давать утечки через D1, D2, а перевод в «0» — вызывать конфликт на USB.

    Хоть в итоге и получился некий overkill — хорошо. Набитые шишки не будут лишними.


    1. grafalex Автор
      06.01.2019 13:29

      Дык в статье приводил схему. Вот она же без моих каракулей
      image

      Если честно, не понимаю как по R3-D1 что нибудь может утекать, если ЮСБ не подключен, а вывод контроллера переведен в input.

      То же самое непонимание про перевод P0 в ноль. Как WS8212 можно этим запитать?

      По режимам уже не помню, статья пролежала «в столе» с марта пока я думал из чего рассеиватель делать.


  1. VT100
    06.01.2019 14:57

    Дык в статье приводил схему.

    Нет, это схема Digispark'а. Я имел в виду полную схему варианта «Digispark без U1 и D3» + TP4056 + WS8212.
    Если честно, не понимаю как по R3-D1 что нибудь может утекать, если ЮСБ не подключен, а вывод контроллера переведен в input.

    Если я правильно понимаю, то в этой версии на линии 5V был аккумулятор. Вот и утекало через R3 пропорционально разнице напряжений аккумулятора и защитного стабилитрона. Можно было-бы переключить верхний вывод R3 на V+, ведь он нужен только для USB.
    Не говоря уж про указанные olartamonov косяки с ёмкостью, хотя для LS это и прокатывает.
    То же самое непонимание про перевод P0 в ноль. Как WS8212 можно этим запитать?

    Если перевести (или оставить) P0 в «1», то будет утечка через верхний диод внутрь WS8212.
    image
    Паразитное питание. Автор, правда, насилует кое-какие факты. Но, в целом, — достаточно объясняет суть явлений.
    Подобным способом, например, питаются 1-wire микросхемы Maxim/Dallas.


    1. grafalex Автор
      06.01.2019 16:48

      Спасибо за разъяснение.

      Если я правильно понимаю, то в этой версии на линии 5V был аккумулятор. Вот и утекало через R3 пропорционально разнице напряжений аккумулятора и защитного стабилитрона. Можно было-бы переключить верхний вывод R3 на V+, ведь он нужен только для USB.

      Да, Вы правы. R3 получается был подключен к батарейке, но течет через него максимум (4.2-3.6)/1500 = 40мкА.

      Версия с паразитным питанием светодиодов выглядит более правдоподобно. В смысле там могло больше утекать.

      В моей текущей схеме, получается, я допустил сразу 2 ошибки. Во-первых R3 нужно было подключать к VIN (5V). Я же его подключил к 3.3В, а значит резистор нужно было бы другой. Я точно не уверен, но вроде спецификация USB требует подтяжки именно через 1.5к резистор и именно к 5В.


      1. holomen
        07.01.2019 18:15

        Спецификация конечно требует, но на практике у многих bluepill нормально работает с вообще 10К на 5В. Некоторые эту самую пилюлю «улучшают», перепаивая 10к на 4К7 и рассказывают что перепаяли «по уму» и оно теперь работает стабильнее… %)