Эстетика навесного монтажа обладает особой притягательной силой. Есть в этом что-то одновременно и от скульптуры, и от handcraft ювелирки. Вот и у меня давно "чесались руки" собрать какой-нибудь незамысловатый но симпатичный электронный гаджетик в таком стиле. И тут же хочется добавить: "… так что, как только я сложил вместе рамку и сердечко, мое воображение немедленно дорисовало все остальные компоненты и мне ничего не оставалось, кроме как соединить их воедино". Но нет. В действительности затея эта пылилась у меня в столе почти десяток лет, пока не дождалась неожиданного в своей простоте "инсайта", вдохновившего меня доделать некогда задуманное.
Итак, все началось почти декаду назад, когда я, пребывая в меланхоличном расположении духа, решил сделать подарок жене в форме светящегося кулончика. Вот такого...
Я даже, помнится, публиковал на "несуществующем" заметку про эту затею. Опубликовал и на какое-то время забыл. В целом, штучка простая: attiny10 + светодиод, да питание от трех крохотных батареек. Прошивка в полторы строки. Я еще ловко пробросил 5 ног в проводке с разъемчиками, чтобы можно было бы при случае перепрограммировать.
Основная сложность на тот момент заключалась в процессе пайки миниатюрных деталек и размещении получившейся сборки внутри "камушка" и потом еще нужно было закрыть дырочки боковыми блестящими заглушками не повредив сам проводок и не оторвав пайку.
Изрядно помучившись с первой горжеткой решил я тогда, что будет куда проще в следующий раз сделать форму и залить всю сборку каким-нибудь двухкомпонентным компаундом. For proof of concept я взял диоды, слепил из разных материалов формочки, приклеил все тщательно и залил. Но на поверку все оказалось еще сложнее. Компаунд застывал с пузырьками и никак не хотел оставаться прозрачным. Это я потом выяснил, что нужна вакуумная камера и что есть другие компонентные полимеры, которые могут и без вакуумной камеры давать приличный результат. Но на тот момент мне удалось сделать единственный более-менее приемлемый экземпляр.
Вот это другие варианты, худший справа, для сравнения. Надо бы из него тоже что-нибудь соорудить. Там как раз тоже есть диод :)
Решив было, что столь сложный процесс не годится, я поздабросил эту затею и сосредоточился на своих основных проектах. Так это сердечко у меня и валялось, время от времени попадаясь на глаза.
Тут должна быть пауза и потом параллельная склейка, как в кино…
Будучи как-то в IKEA я прихватил с собой рамку 6x6cm (пытался только-что отыскать название, но, по-моему, этот тот самый уникальный случай, когда Интернет, похоже, напрочь забыл про эту икеевскую поделку). Знаете, как это бывает, когда ты проходишь мимо стеллажей и глаз цепляется за какую-то бессмысленную, на первый взгляд, вещицу, которая тебе, в целом, не особо нужна, но что-то внутри подсказывает, что из этой штуки можно что-то сконструировать. Так случилось и тогда. Я подхватил эту рамку.
Каждый раз проходя мимо я думал, — Вот эти мелкие рамки, что в них можно запихать? Картинку туда нормально не вставишь. А вот какую-то поделку мелкую или талисманчик, в принципе — в самый раз. В общем, нужно ли говорит, что упомянутую рамку ждала примерно та же участь — валяться в коробке… до поры до времени.
И вот, некоторое время назад, аккурат после прочтения очередной статьи про клеточные автоматы (ссылка будет внизу), разбирая завалы в столе, я наткнулся на эту рамку и на сердечко одновременно и моментально понял, что они созданы друг для друга и что именно я хочу из этого соорудить.
Начало
В общем, сперва я все это зарисовал, чтобы приблизительно было понятно, как размещать компоненты с учетом длинны проводников и композиционного равновесия.
Потом аккуратненько спаял все. Просверлил дырочки в подложке, просунул и возрадовался, как все аккуратно легло. Чуть позже стало понятно, что стоило бы "приглушить" остальные элементы, что я и сделал, закрасив все аккуратно черным.
Вот тут я начал было уже закрашивать, но подумал, что не плохо было бы опубликовать это все на Хабре, и решил все документировать.
С тыльной стороны разместился разъем micro USB, разъем для прошивки и переключатель питания (attiny10 требует 5 вольт для прошивки, так что пришлось предусмотреть, но оно и хорошо, потому что это еще и выключатель, что удобно для перезапуска, например).
А вот так все выглядит в сборке, с подключенным программатором. Дополнительный двойной переключатель на проводке переключает ногу 3 на UART RX для удобства отладки, а так же размыкает RTS и DTR программатора, чтобы не подтягивать соответствующие ноги почем зря.
Я догадывался, что выбранный мною зуммер может не давать шить прошивку из за своего низкого импеданса, работая как маленький конденсатор, так что я решил прибавить пару сотен пикофарад. Но все оказалось еще хуже, эта штука с дырочкой оказалась самым что ни на есть низкоомным динамиком. Мне бы, конечно, все это замерить заранее. Но я хотел во что бы то ни стало успеть к годовщине нашего романтичного знакомства, так что был тороплив и невнимателен. Однако, где-то подсознательно я, должно быть, догадывался о такой вероятности, поэтому предусмотрел слева пространство для второго классического пьезоизлучателя. В итоге туда я его и приделал. Микро-динамик же я оставил на месте, покуда он там красиво прижился и дополняет композицию. (Тут мне, к слову, только что подумалось, можно попробовать почитать ADC на этой ноге. Может его можно использовать в качестве генератора энтропии (об этом ниже), или датчика громких хлопков :).
После того, как я окончательно разобрался с источником звука, я вырезал держалку для разъемов на лазерном резаке, прикрутил маленькими шурупчиками USB, собрал все воедино и включил.
И в этот момент стало понятно — затея того стоит.
Предвосхищая критическое "Это же всего-навсего мигающий диодик в рамке!". Нет, дорогой читатель, ты в плену внешней простоты. И прямо сейчас я тебе расскажу, что же за тайные смыслы сокрыты в этой, с виду, банальной безделушке.
Третий "компонент" и немного теории
Насколько тривиально выглядел бы светящийся светодиод в рамке, если бы внутри этой штуки не бегала бы специально придуманная для нее прошивка. Поэтому вот вам немного теоретической подоплеки.
Думаю, все здесь присутствующие в той или иной степени знакомы с концепцией клеточных автоматов. Эта тема, несмотря на свою кажущуюся простоту, с самого своего рождения не дает покоя исследователям и является предметом вдохновения для многих энтузиастов. Стивен Вольфрам вот даже целую Теорию Всего пытается на основе нее сконструировать и вывести из нее всю квантовую механику вместе с квантовой гравитацией. В общем, и ваш покорный слуга — не исключение. Мне тоже нравится эта красивая модель во всех своих проявлениях.
Так вот, я подумал, что такой простой кристалл как ATtiny10 — лучшая платформа для запуска крошечного одномерного автомата. Про одномерные автоматы известно, что большинство правил в основном порождают повторяющиеся паттерны, некоторые просто быстро "умирают", но вот несколько правил дают крайне любопытные результаты. Например правило 110 дает вот такую неоднозначную картинку,
и, как выясняется, является полным по Тьюрингу. А вот 22, 30, 126, 150 и 182 порождают хаотичные паттерны и их даже используют в криптографии для генерации псевдослучайных последовательностей. А еще правило 30 порождает треугольнички, очень похожие на сердечки. Так что я решил, коль скоро, правила порождающего непосредственно сердечки нет (хотя, наверное, было бы заманчиво его получить, вот так, например), то возьму-ка я 30 и буду использовать эту его особенность.
***** ***** ** * ****** ** ** ** * * ** ** ** *
* * * * ** * * ** * * * ** ** ** * ** * ** ***
* *** ** * * ** ***** ****** ** ** * * * * * * ***
*** ** * ** * **** * ** * * * ********* **** * * *
* * *** * *** * * * * *** ***** * * * ****
** ** * * * ** ** * ** ** * ** ** *** ** *
* * ** ** ** ** *** * * * ****** * ** * ** *** ** *
****** * * * * *** **** * ** ** ** ** *** *** ***
* *************** * ** ** * *** * ** * *** *
*** ** * *** ** * ** *** * * * ****** * ***
* * ** * ** * *** **** * ** ** * * *** *
**** * ** ** **** *** **** ** * * ** ** ** *
**** * ** *** *** * ** * ****** * * ** * ** *
* ** ** ** * * ** *** * * ** * * * * * * ***
* ** * ** * ** ****** * *** * **** ** ** * **** * *
** * **** ** ** * * * * ** * *** * ** * * * **
* **** *** *** **** ** **** * ** ** **** *** ** ** *
**** * ** * * * ** * **** * * ** * * *** ***
* ** * * *** *** ***** **** ** *** * * * *** ** * ***
** * * * * * * * *** * ** * * **** * **** *
** **** * ** *** *** *** ** * * * ** * * * ***** ****
Вероятность выпадения треугольника большего размера, на вскидку, должна увеличиваться пропорционально количеству прошедших эпох. Так что я решил, что пусть веселое пиликанье и мигание знаменует рождение нового самого большого треугольника в моей вселенной. Позднее, правда, после того как я прогнал несколько миллионов эпох и посчитал статистику выпадения разноразмерных треугольников, выяснилось, что такая вероятность не шибко велика, и количество находок не превышает 32 для 10? эпох.
А учитывая, что я собирался сделать 8 минутную паузу на сон после каждой эпохи, чтобы, в частности, максимально экономить батарею и не сделать мигание чрезмерно навязчивым, 32 писка приходилось аккурат на 120 лет работы сердечка. Рассудив, что это как-то слишком too long, я снизил условия для мигания, введя нижний порог срабатывания по размеру треугольничка. Так что мигает он почаще, а пиликает редко, как это подобает настоящей вселенной со столь редкими событиями.
Программирование
Attiny10 — процессор крохотный, не только внешне но и на уровне "железа". Там всего-то килобайт ПЗУ и 32 байта ОЗУ. Особо не развернешься. Поэтому я сперва написал отдельную утилитку, на которой максимально оптимизировал основной алгоритм + за одно, проверил статистику, чтобы выбрать оптимальное поведение.
Чтобы было удобно отлаживать мою микровселенную я сразу добавил снипет для UART TX. Аппаратного-то у ATtiny10 не предусмотрено, так что пришлось свой небольшой соорудить.
// ---------------------- simple UART TX -----------------------
#ifdef UART
void put_char( uint8_t c ){
c = ~c;
output_low( PORTB, SND );
for( uint8_t i = 10; i; i-- ){ // 10 bits
_delay_us( 1000000 / 9600 ); // bit duration / (8MHz, 9600, 104 us) Delay 832 cycles
if( c & 1 ) output_low( PORTB, SND ); else output_high( PORTB, SND ); // data bit 0 / data bit 1 or stop bit
c >>= 1;
}
}
void put_str_P(PGM_P str){
static PGM_P s;
s=str;
while (pgm_read_byte(s)) put_char( pgm_read_byte(s++) );
}
void put_num( uint8_t num ){
uint8_t div = 1;
for (div = 1; num / div >= 10 ; div *= 10);
do {
put_char( 48 + (( num / div ) % 10 ));
div /= 10;
} while ( div > 0 );
}
#endif
Далее я было уже собрался приделать PWM, но не тут то было. Я, конечно, заранее на всякий случай удостоверился, что ADC у меня на измерении заряда, а аппаратное управление таймером есть на всех ногах. На всех… кроме одной. И я, конечно же, припаял светодиод именно к этой ноге. Все потому что я сперва думал, что микроконтроллер у меня будет перевернут, как контроллер зарядки (см картинку), но потом я, все же, перерисовал его в нормальном положении, а про ногу забыл. В общем, что делать… От управления пищалкой на прерывании пришлось отказаться совсем, а управление светодиодом сделать на прерываниях переполнения, по известному рецепту.
// ---------------------- LED PWM ------------------------------
#ifndef UART
#define INT_PWM_DISABLE ({ TIMSK0 &= ~((1<<OCIE0A) | (1<<TOIE0)); }) // compare interrupt off
#define INT_PWM_ENABLE ({ TIMSK0 |= ((1<<OCIE0A) | (1<<TOIE0)); }) // compare interrupt on
ISR ( TIM0_COMPA_vect ){
output_low(PORTB, LED);
}
ISR ( TIM0_OVF_vect ){
output_high(PORTB, LED);
}
void ledPWMInint(){
TCCR0A = (1<<WGM01);
TIMSK0 = (1<<OCIE0A); // Eneable interrupt (since I'm uisng PB2, I can't use integrated pin :( )
ICR0 = 1000; // Set top to 1000
TCCR0B = (1<<CS01) | (1<<WGM03) | (1<<WGM02); // Start timer with prescaler 1:8 and set upper mode bits. WGM0 = 1110 Fast PWM ICR0 TOP TOP
}
#endif
Да и это даже к лучшему, потому что в процессе отладки пищалки я придумал финт с тональностью, которая сейчас выбирается как результат деления длинны "треугольника" по модулю 5 и звучит прямо как как R2D2, каждый раз по-новому (ну и за одно звуковой индикатор разряда батареи использует эту же переменную, памяти-то там с гулькин нос).
// playing current epoch byte sequence as score or 'battery low' message
do {
m = uni[ cnt % LEN ] % 5;
j = 255;
while ( j-- ){
invert(PORTB, SND);
k = 30*(m + 1);
while ( k-- ) { //500ns
asm volatile (
" rjmp 1f \n"
"1: rjmp 2f \n"
"2: \n"
);
}
}
_delay_ms(60);
} while ( cnt-- );
ну и напоследок осталось разобраться с чтением ADC. И вот тут у меня до сих пор нет четкого понимания, как это получилось. Как заметит внимательный читатель, там у меня на ноге 1 стоит делитель, который должен, вроде бы, скармливать часть напряжения на ADC, а тот, в свою очередь, замерять его падение. И все бы ничего. Но у ATtiny нет опорного внешнего входа, а у меня нет стабилизатора. Т.е., если верить datasheet, опорное напряжение для ADC это либо напряжение питания, либо GND на выбор. Т.е. по логике в моем случае результат ADC должен быть стабильным, даже при падении напряжения.
И что я тут предпринял, спросите вы? Да ничего. Я просто про это не подумал. Приделал батарею (маленькую для теста), поставил на ночь разряжаться и утром посмотрел логи.
Вот результаты тестирования разрядки батареи со встроенным делителем
И все, как видите, норм. Тем не менее, с утра на свежую голову я посмотрел на это еще разок, и осознал, что так работать не должно. Почитал даташит для уверенности. И все верно — замер делается относительно опорного внутреннего напряжения. Но ведь оно падает!
В общем, я попробовал тупо резистором покрутить питание. Показания меняются. Попробовал сделать относительно земли. Тоже меняются. Так что я пока склоняюсь к мысли, что ATtiny имеет внутри какой-то стабилизатор или что-то, что ограничивает напряжение питания. Возможно, только для ADC. Может быть кто-то из коллег мне подскажет, что тут за магия. Так или иначе, все работает и разрядку батареи замерять можно.
Так что мне осталось только добавить засыпание и пробуждение по watchdog прерыванию, зарядить батарею и поставить гаджетик на стол любимой, пока она сладко спит :)
Вот так оно делает время от времени. Если мигнуло, значит в моей микровселенной родился большой треугольник. Количество миганий показывает его длину в основании (точнее, превышение длинны относительно константы). А если еще и запиликало, то это и вовсе рекордсмен, где высота тона пропорциональна значению байта, считываемого последовательно по периметру моего замкнутого компактного 32 битного одномерного многообразия, взятому по модулю 5 (ну, чтобы не шибко разбег по высоте был).
А да… отдельная проблема с рандомизацией исходного состояния. Я было думал привычным способом читать младшие биты того же АЦП и их использовать, но в attiny АЦП восмибитный всего то. И дает весьма стабильный (т.е. грубый) результат. Тем не менее результат этот колеблется на единичку изредка. Поэтому я решил сделать такую смешную "пробежку" по ПЗУ еще в дополнение.
// ----------- initial randomizer -----------------
while( i-- ) {
readADC();
uni[ i ] = pgm_read_byte( uni[ i+1 ] ) + ADCL;
}
Благо само правило 30 чувствительно к небольшим изменениям + можно предположить, что степень разрядки батареи тоже не будет всегда одинаковой при включении. Так что, вроде бы, энтропии должно хватать для моей скромной задачи.
А на будущее, стоило бы, наверное, приделать маленькую антенку и/или индуктивность, дабы ловить энтопию из электромагнитного эфира непосредственно. Плюс, выглядело бы еще круче. Ну или попробовать читать значение ноги №3.
Использованные материалы и полезные для вдохновения ссылки
- Elementary_cellular_automaton, wiki
- JS движок для простейшего автомата (oshibka404)
- Эволюционирующие клеточные автоматы (xcont)
- A_New_Kind_of_Science by Stephen Wolfram
- Permutation City by Greg Egan
Исходники
Post scriptum
PS тут меня со всех сторон атакуют, мол, Шальнов, что там с babooshka.tv. Я честно признаюсь, у меня катастрофически не хватает времени. Но я, тем не менее, не забросил ничего и в настоящий момент доделываю досочку, из которой должен получиться приятный во всех отношениях и полезный для разного рода использования готовый девайс. Не только для борьбы с медийной монополией какой-нибудь отдельно взятой информационной автократии, но и для всевозможных других медиа-затей. Вот только закончу с разводкой, и обещаю опубликовать статью сразу.
А пока вот вам таинственная картинка для привлечения внимания.
farafonoff
Все прекрасно, маленький комментарий: «И все верно — замер делается относительно опорного внутреннего напряжения. Но ведь оно падает!». Внутренний vref это в большинстве случаев просто диод, на котором падает всегда определенное напряжение. Теория тут www.ti.com/lit/eb/slyc147a/slyc147a.pdf
Gengenid
У attiny10 нет внутреннего ИОНа и входа для внешнего нет. Он только относительно напряжения питания измеряет.
aureliano_b Автор
в том-то и дело, что в нем нет внутреннего VRef (о чем ниже коллега, вот, тоже написал). В даташите об этом прямо написано. Используйте GND или VCC и привет :)
Так что, вопрос остается открытым.