Мигаем светодиодами на AVR без Arduino. Подключение светодиодов к микроконтроллеру AVR Изменения частоты мигания светодиода на avr

На этом уроке Вы научитесь программировать свою плату Arduino на примере мигания встроенным светодиодом.

Необходимые элементы

Для данного примера Вам понадобится плата Arduino (в данном случае – Arduino Uno R3 , но Вы сможете проработать данный пример, имея в наличии и другую плату) и кабель USB (типа A (4х12 мм) – B (7х8 мм) – более подробно можно почитать на Вики).

Что такое ” L” светодиод


На Arduino Uno присутствуют ряды коннекторов типа мама по бокам платы, которые используются для подключения периферийных электронных устройств или “шилдов” .

Кроме того, на плате присутствует встроенный светодиод (англ. – LED), которым Вы можете управлять с помощью скетчей. Этот встроенный светодиод условно назовем “L” светодиод, как это принято на многих англоязычных ресурсах.

Расположение данного светодиода на плате отмечено на фото снизу.


Загрузка примера “Blink” (мигание) в Arduino IDE

При подключении новой платы к персональному компьютеру, обратите внимание, что светодиод начинает мигать, так как все платы от производителей поступают с уже “залитым” скетчем “Blink ”.

На этом уроке мы перепрограммируем нашу плату, изменив частоту мигания светодиода. Не забудьте настроить оболочку Arduino IDE и выбрать нужный серийный порт, по которому Вы подключили Вашу плату.

Пришло время проверить Ваше подключение и запрограммировать плату.

В оболочке Arduino IDE существует большая коллекция скетчей, которые уже готовы к использованию. Среди них находится и пример, который заставляет мигать “L” светодиод.

Откройте пример “Blink”, который находится в пункте меню File – Examples – 01.Basics

После открытия, расширьте окно оболочки Arduino IDE, чтобы Вы могли весь скетч в одно окне.

Скетчи из примеров, включенные в Arduino IDE предусматривают режим “только чтение” (“read only”). То есть, загрузить их на плату Вы сможете, но после изменения кода, Вы не сможете их сохранить в том же файле.

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

Вы сохранили копию скетча “Blink” в Вашей библиотеке. Теперь открыть этот файл Вы можете в любой момент, перейдя по вкладке File – Scetchbook.

Загрузка примера “Blink” (мигание) на плату

Подключите свою плату Arduino к компьютеру с помощью USB и проверьте тип платы (“Board type”) и серийный порт (“Serial Port”), по которому она подключена.

Текущие настройки отображаются внизу окна оболочки Arduino IDE

Кликните на кнопку “Загрузить” (“Upload”)

Во время загрузки в нижней части окна IDE появятся ползунок загрузки и сообщения. Вначале появляется фраза “Компилирование” (“Compiling scetch..”), что означает процесс конвертирования Вашего скетча в формат, подходящий для загрузки на плату Arduino.

В конце статус сменится на ”Загрузка завершена” (“Done uploading”). В сообщении, которое появится в текстовой строке отобразится информация о том, что загруженный скетч занимает 1,084 байта из 32,256 доступных.

Иногда при компиляции у Вас может возникнуть подобная ошибка:

Причин может быть несколько: Вы не подключили плату к компьютеру; Вы не установили необходимые драйвера; Вы выбрали некорректный серийный порт.

Пояснения к скетчу “Blink”

Ниже представлен код скетча “Blink”.

Turns on an LED on for one second, then off for one second, repeatedly.

This example code is in the public domain.

// Pin 13 has an LED connected on most Arduino boards.

// give it a name:

pinMode(led, OUTPUT);

delay(1000); // wait for a second

Первое, на что стоит обратить внимание: в данном скетче множество “комментариев”. Обратите внимание, что комментарии не являются инструкцией по работе программы. Это исключительно пояснения отдельных функций и задач, которые выполняются на определенном этапе кода. Это не обязательная часть кода. Все между символами /* и */ в верхней части скетча – это комментарии, в которых описаны задачи программы. Так же есть комментарии, которые ограничиваются одной строкой. Они начинаются с символов // и заканчиваются по умолчанию в конце строки. Первая важная, по сути, часть данного кода это строка:

В комментариях над строкой указано, что мы присваиваем имя пину, к которому подключен светодиод. На большинстве плат Arduino это будет 13 пин. Дальше используется функция “Setup”. Опять-таки, в комментариях указано, что функция срабатывает после нажатия кнопки “reset”. Также эта функция срабатывает, когда плата перезагрузится по каким-либо другим причинам. Например, подача питания или после загрузки скетча.

// the setup routine runs once when you press reset:

// initialize the digital pin as an output.

pinMode(led, OUTPUT);

Каждый скетч Arduino обязан включать в себя функцию “setup” и часть, в которую вы можете добавлять собственные инструкции, заключенные между { }. В нашем примере в функции присутствует только одна команда, в которой указано, что пин, который мы используем, настраивается на “вывод” (“Output”). Также обязательным для любого скетча является функция цикла “Loop”. В отличие от функции “Setup ”, которая отрабатывает один раз после перезагрузки, функция “Loop” после окончания работы команд, вновь запустится.

// the loop routine runs over and over again forever:

digitalWrite(led, HIGH); // turn the LED on (HIGH is the voltage level)

delay(1000); // wait for a second

digitalWrite(led, LOW); // turn the LED off by making the voltage LOW

delay(1000); // wait for a second

В теле функции “Loop” светодиод включается (HIGH), данное значение задерживается на 1000 миллисекунд (1 секунда), светодиод отключается (LOW) и остается выключенным на 1 секунду, после чего цикл повторится.

Изменение частоты мигания светодиода

Для того, чтобы обеспечить более частое мигание светодиода, необходимо изменить параметр, указываемый в скобках () в команде “delay ”.

Как уже было указано, период задержки указывается в миллисекундах. То есть, для того, чтобы заставить светодиод мигать в два раза чаще, необходимо изменить значение с 1000 на 500. В результате, пауза между включением/выключением светодиода составит половину секунды и светодиод будет мигать быстрее.

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

Пишем первую программу!

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

Алгоритм , (от имени персидского математика аль-Хорезми) - точный набор инструкций, описывающих порядок действий исполнителя для достижения результата решения задачи за определенное время.
Чтобы правильно писать программы нужно понять, как работает микроконтроллер, чем мы и будем заниматься, а пока составим первую программу на ассемблере.

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

Что делать с этим.hex файлом? А он нужен для программатора, чтобы записать код программы в ПЗУ (постоянное запоминающее устройство) микроконтроллера. Мы вместо программатора будем использовать программу Proteus , которая модулирует реальную работающую схему.
В AVR Studio 5 редактор, компилятор, отладчик и программатор все в одном флаконе!

Директива – это предписание редактору, с которым мы будем работать. В большинстве своем название директивы и ее функция в разных редакторах совпадают. Например, если сравнить редактор MPLAB IDE для микроконтроллеров PIC, то разницы практически нет!
А вот команды отличаются. Список команд формируется производителем и в какой-то степени зависит от технологии производства. Даже у одного производителя в разных семействах может быть разный список команд по мере изменения их структуры.
Команда – это единичная функция, с помощью которой мы можем управлять микроконтроллером.
Программа - это последовательность команд, соответствующая определенному алгоритму (составленная нами).
Любая программа начинается с “шапки”, где прописывается основные функции программы, ее автор, название контроллера, который будет использоваться, тактовая частота и другие данные. “Шапка” - это “правило хорошего тона” в программировании.
Обычно в “шапке” прописывается файл инициализации.includeххххdef.inc. директивы.device, .list, .nolist, и другие.
В AVR Studio 5 эти директивы прописаны по умолчанию. В предыдущей версии AVR Studio 4 эти директивы нужно было прописывать, поэтому я их оставил в виде комментариев.
Комментарии пишутся произвольно, как вам нравиться и главное, чтобы они для вас оставались понятными. Нередко, из-за неполных комментариев, через некоторый промежуток времени сам автор не может разобраться в собственной программе. Перед комментариями ставиться знак (;) .
Я ввел “шапку” предложенную Джоном Мортоном…

Редактор AVR Studio 5 – понимает написанные, директивы и команды выделяя их в синий цвет, числовые данные остаются черными, непонятные выражения выделяет в красный, комментарии в зеленый.

Прежде чем начать, что-то писать рассмотрим директивы ассемблера микроконтроллеров AVR.
Перед каждой директивой ставиться точка:
Директива Описание.BYTE Резервировать байты в ОЗУ.CSEG Сегмент программы.DB Определить байт – константу во Flash-памяти или EEPROM .DEF Назначить регистру символическое имя.DEVICE Определяет устройство, для которого компилируется программа.DSEG Сегмент данных.DW Определяет слово во Flash-памяти или EEPROM .ENDM Конец макроса.ENDMACRO Конец макроса.EQU Установить постоянное выражение.ESEG Сегмент EEPROM .EXIT Выход из файла.INCLUDE Вложить другой файл.LIST Включить генерацию листинга.LISTMAC Включить разворачивание макросов в листинге.MACRO Начало макроса.NOLIST Выключить генерацию листинга.ORG Установить положение в сегменте.SET Установить для переменной эквивалентное выражение

Полное описание команд и директив ассемблера в русском переводе Руслана Шимкевича можно посмотреть тут:
🕗 24/09/11 ⚖️ 397,28 Kb ⇣ 244 Здравствуй, читатель! Меня зовут Игорь, мне 45, я сибиряк и заядлый электронщик-любитель. Я придумал, создал и содержу этот замечательный сайт с 2006 года.
Уже более 10 лет наш журнал существует только на мои средства.

Хорош! Халява кончилась. Хочешь файлы и полезные статьи - помоги мне!

--
Спасибо за внимание!

Мы еще не раз вернемся к директивам ассемблера, а сейчас попробуем написать небольшой текст программы, где будем включать светодиоды.
Для удобства выберем микроконтроллер ATtiny2313A . Если кто-то пойдет дальше и будет экспериментировать в “железе”, этот контроллер один из самых доступных, очень часто применяется в различных конструкциях, которые можно найти в бескрайних просторах сети.

--
Спасибо за внимание!
Игорь Котов, главный редактор журнала «Датагор»


Нужно лишь распаковать и стартовать его (при условии, что Proteus уже установлен).
Должна появиться вот такая картинка:

Запустим AVR Studio 5 , и в редакторе напишем небольшую программу с подробными комментариями:
.def temp=r16 ; директива.def назначает регистру r16 имя temp ;==================================================== ; Начало программы.cseg ; директива.cseg определяет начало сегмента, где будет расположен; основной код программы. В AVR Studio 5 это директива не; обязательна.org 0 ; начало первой строки программы rjmp Start ; относительный переход к метке Start (в PIC соответствует; команде goto) ; ==================================================== Start: ser temp; устанавливает все биты регистра temp в 1 out DDRB,temp; переводит все биты out DDRD,temp; порта B и D на вывод clr temp; обнуляет регистр temp (устанавливает все биты регистра temp в 0) out PortB,temp; отключает подтягивающие резисторы out PortD,temp; портов B и D Cicle: ldi temp,0b11001100; включает светодиоды out PortB, temp; порта B rjmp Cicle; Возвращаемся к метке Cicle, зацикливаемся

Скомпилируем ее, кликнув по F7
В окошке Output появятся информация о проведенной компиляции, в конце должна быть надпись Build succeeded, которая подтверждает удачную сборку.hex файла.


Этот файл по умолчанию размещен в Моих документах, в проекте AVR Studio 5.

Переходим в Proteus , кликаем на рисунок контроллера и вводим путь, где находиться.hex файл:




Запускаем эмуляцию программы.
После нажатия кнопки Старт видим результат!



Чтобы остановить эмуляцию – кликаем Стоп.
Если прошить микроконтроллер программатором и собрать схему, то результат будет таким-же.

Попробуем разобраться, с программой:

Строка:
.def temp=r16 ; директива.def назначает регистру r16 имя temp
Для удобства мы назначили регистру r16 имя temp, это значит что в любом месте программы, при написании слова temp программа будет обращаться к регистру r16.
Что это за регистр – r16?
Он относится к регистрам общего назначения, которые обозначаются как r0….r31, т.е их всего 32 по 8 бит. Вторая половина регистров общего назначения r16…r31 выполняют функцию временного хранения информации (как аккумуляторы, могут принимать и могут отдавать информацию, для сравнения - у PIC контроллеров всего один аккумулятор W). Последние 6 регистров общего назначения условно объединены в три 16-битных:

Обратите внимание, что старший байт пишется раньше, младший позже.

r27:r26 называется регистром X,
r29:r28 называется регистром Y,
r31:r30 называется регистром Z
Программа будет так же работать если слово temp будет присвоено любому другому регистру общего назначения от r16 до r31 например.def temp=r20. После компиляции (клавиша F7 в AVR Studio 5), разницы в работе программы никакой нет.
(Можете попробовать, теперь вы знаете, как это сделать!)

Строка:
.cseg ; директива.cseg определяет начало сегмента, где будет расположен основной код программы
В AVR Studio 5 это директива прописана по умолчанию.
В микроконтроллерах AVR выделяют разные области памяти, для хранения программного кода, данных в постоянной памяти или EEPROM, для обозначения этой области существуют директории:
.cseg - Программный сегмент
.dseg – Сегмент данных
.eseg – сегмент EEPROM

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

Строка:
rjmp Start ; относительный переход к метке Start (в PIC соответствует команде goto)
Это команда относительного перехода к метке Start.
Программа выполняется последовательно от верхней стоки к нижней. Если требуется перейти в другую область программы то используется команда rjmp

Строки:
ser temp ; устанавливает все биты регистра temp в 1 clr temp ; обнуляет регистр temp (устанавливает все биты регистра temp в 0)
Команда ser выполняется с регистрами r16…r31, а команда clr может выполнятся ко всем регистрам, в том числе и регистрам ввода-вывода (регистрам специального назначения). Назначение этих регистров мы рассмотрим позже.

Строка:
ldi temp,0b11001100
Команда ldi записывет в регистр temp двоичное число 11001100 (не будет ошибкой если вместо 0b11001100 написать 204 или CCh, в одном случае в десятичной форме в другом в шестнадцатеричной)

Строка:
out DDRB,temp
Команда out выводит значение регистра temp в регистр порта DDRB.

Что такое порт и как он работает?

Если упростить до минимума, то порт можно представить как ножку-вывод микроконтроллера, на который в режиме вывода можно произвольно подать напряжение или отключить, а в режиме ввода определить, есть ли напряжение, поданное извне или нет.
Есть три режима работы порта: это вывод, ввод и отключенное состояние.
Управляют портами регистры PinX, PortX, DDRX.
Х – означает порт, которым управляют эти регистры.
Регистр DDRX управляет режимами ввода-вывода, как реле – включили (записали 1) – подключился на линию вывода, отключили (записали 0) – переключился на линию ввода (по умолчанию).

Чтобы вывести данные в порт Х, нужно регистр DDRX переключить в режим вывода (прописать 1) а данные переслать в регистр PortX.
Считать значение порта Х можно, если регистр DDRX переключить в режим ввода (прописать 0), из регистра PinX. Чтобы подключить подтягивающие резисторы в режиме ввода, надо отправить данные в PortX.
Подтягивающие резисторы избавляют нас от необходимости подключать внешние резисторы на положительный провод и с помощью одной команды сами подают логическую единицу на вход.

Читательское голосование

Статью одобрили 23 читателя.

Для участия в голосовании зарегистрируйтесь и войдите на сайт с вашими логином и паролем. разработчик 80-го уровня 17 июля 2015 в 11:33

Мигаем светодиодами на AVR без Arduino

  • DIY или Сделай сам *

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

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

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

Первым делом чертим схему. Схема не моя, свою потерял, поэтому исходника для протеус не будет.

Для тек кто не знает что это за программа, очень советую, очень удобно наблюдать как все работает без пайки и прошивки контроллера. Микроконтроллер фирмы Atmel Atmega8. Почему именно атмел, предлагаю обсудить в комментариях.

Собственно сам код:

#include //Включаем библиотеку для работы с микроконтроллером ATMega8 #include //Включаем библиотеку для организации задержек #include //Включаем библиотеку для генерации псевдослучайных чисел (rand) void main(void) //Обязательный заголовок (тело) { char i; //Объявляем переменную (i) PORTD=0x00; //Выставляем все выходы порта D на 0, то есть, выключаем весь порт D DDRD=0xFF; //Делаем порт D, как выход, чтобы на выходах порта было напряжение 5В PORTC=0x00; //Выставляем все выходы порта C на 0, то есть, выключаем весь порт C DDRC=0xFF; //Делаем порт C, как выход, чтобы на выходах порта было напряжение 5В PORTB=0x00; //Выставляем все выходы порта B на 0, то есть, выключаем весь порт B DDRB=0xFF; //Делаем порт B, как выход, чтобы на выходах порта было напряжение 5В while (1) //Добавляем бессконечный цикл, код ниже выполняется в цикле { i = rand() % 22 + 1; /*Присваеваем переменной функцию, которая принимает значения от 0 до 32767. (В нашем случае делим по модулю)*/ delay_ms(5); //Исполюзуем задержку 5мс if (i==1){PORTD.0=1;} else PORTD.0=0; //Если переменная (i) равна (1) то 0-вой бит порта (D) равен (1), if (i==2){PORTD.1=1;} else PORTD.1=0; // если иначе, то 0-лю if (i==3){PORTD.2=1;} else PORTD.2=0; //Во всех остальных ниже условиях по аналогии if (i==4){PORTD.3=1;} else PORTD.3=0; if (i==5){PORTD.4=1;} else PORTD.4=0; if (i==6){PORTD.5=1;} else PORTD.5=0; if (i==7){PORTD.6=1;} else PORTD.6=0; if (i==8){PORTD.7=1;} else PORTD.7=0; if (i==9){PORTC.0=1;} else PORTC.0=0; if (i==10){PORTC.1=1;} else PORTC.1=0; if (i==11){PORTC.2=1;} else PORTC.2=0; if (i==12){PORTC.3=1;} else PORTC.3=0; if (i==13){PORTC.4=1;} else PORTC.4=0; if (i==14){PORTC.5=1;} else PORTC.5=0; if (i==15){PORTB.0=1;} else PORTB.0=0; if (i==16){PORTB.1=1;} else PORTB.1=0; if (i==17){PORTB.2=1;} else PORTB.2=0; if (i==18){PORTB.3=1;} else PORTB.3=0; if (i==19){PORTB.4=1;} else PORTB.4=0; if (i==20){PORTB.5=1;} else PORTB.5=0; if (i==21){PORTB.6=1;} else PORTB.6=0; if (i==22){PORTB.7=1;} else PORTB.7=0; }; }

Код писал 2 дня, пока понял что к чему. Опишу кратко. Подключаем необходимые библиотеки, в цикле с помощью функции rand() генерируем числа, условия открывают закрывают порты в зависимости от значения переменной «i». А как бы сделали вы? Можно ли сократить код?

Для проверки работоспособности кода перед заливкой в микроконтроллер рекомендую запустить в протеусе и посмотреть что происходит.

После необходимо прошить сам микроконтроллер. Тут такая же ситуация. Существует всеми хвалимая программа AVRDUDE, но мне понравилась KhazamaAVRProgrammer. В качестве программатора выступал обычный USBASP:

На протяжении всей статьи я пропускал очень важные моменты. Я не показывал скриншоты програм, что и куда нажимать. Напоследок хочется сказать: если вы сами разберетесь тогда вы будете это уметь.

Печатную плату в формате lay можете скачать

  • Перевод

Дешевые электронные «свечи» в последнее время, кажется, повсюду. Я не обращал на них особого внимания, пока не заметил, что на самом деле в них используется особый светодиод - со встроенным «моргательным» контроллером. Теперь-то совсем другое дело: кому не нравятся таинственные светодиоды? Полчаса спустя я уже набрал охапку мерцающих светодиодов китайского производства.

Конечно, самый интересный вопрос - как они работают? Учитывая, что стоят они буквально по несколько центов за штуку, там внутри не может быть какой-то дорогой электроники. В связи с этим возникает еще один вопрос: правда ли эти светодиоды хуже, чем многочисленные «свечи» на микроконтроллерах, схем которых полно в интернете?

Устройство относительно простое. В стандартном 5-миллиметровом корпусе размещены кристалл светодиода и микросхема, которая чуть больше первого по размеру. Схема контроллера соединена как с положительным, так и с отрицательным выводами. Третьей перемычкой к ней подключен анод светодиода, в то время как катодом он «сидит» на отрицательном выводе.

В блоге Evil Mad Scientist недавно был рассказ о похожих светодиодах. Там было показано, как они «поют», если преобразовать изменения яркости в звук. А также - как с их помощью управлять более мощным диодом. Подобные трюки основаны на том, что светодиод потребляет больший ток в те моменты, когда контроллер зажигает его ярче. Обычный светодиод, включенный последовательно с мерцающим, показывает очень похожие изменения яркости. Иными словами, падение напряжения на добавочном резисторе изменяется пропорционально яркости.


Это я и использовал, чтобы извлечь управляющий сигнал контроллера и завести его на логический анализатор (см. схему выше). Подстройкой переменного резистора я добился того, чтобы анализатор воспринимал броски тока как «нули» и «единицы», а светодиод при этом нормально работал.


На диаграмме выше показаны изменения яркости диода в течение примерно минуты, записанные с частотой выборки 1 МГц. Заметны интервалы, когда светодиод непрерывно включен, и периоды, когда его яркость каким-то образом модулируется. Светодиод никогда не выключается надолго. Это разумно, ведь настоящая свеча тоже ярко светит большую часть времени, снижая яркость на короткие периоды мерцания.


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

Любопытно, что частота сигнала - примерно 440 Гц, как у стандартного камертона (нота Ля первой октавы - прим. перев. ). Совпадение? Или разработчик просто взял генератор из какой-то музыкальной схемы? Так что есть доля правды в рассказах о «музыкальности» этих светодиодов. Каждый «кадр» постоянной яркости составляет ровно 32 такта и длится около 72 мс. Это соответствует 13-14 кадрам в секунду.

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


График яркости в зависимости от времени наводит на некоторые мысли: изменения яркости случайны, дискретны и имеют неравномерное распределение. Кажется, существуют 16 уровней яркости, низшие 4 из которых используются очень редко. Им соответствуют только 13 из 3600 отсчетов.


Постороение гистограммы открывает всю картину: фактически используется только 12 уровней яркости. Ровно половина кадров имеет максимальную яркость, остальные значения распределены примерно поровну.

Как это может быть реализовано на аппаратном уровне? Вполне вероятно, используется генератор равномерно распределенных случайных чисел, которые пропускают через простую функцию-формирователь. Для того распределения, которое мы наблюдаем, требуется как минимум 12x2=24 дискретных уровня. Половина из них отображаются в один. Это весьма любопытно, так как генератор, скорее всего, выдает двоичные числа. Наиболее логичной была бы разрядность числа 5 бит, а это 32 состояния. Отобразить 32-уровневую дискретную случайную величину в 24 уровня, не изменив распределения, не так просто, как кажется. Не забываем также, что это совсем не критичная схема, и у разработчика, вероятно, не было много времени на красивое решение. Поэтому он применил самое простое, своего рода хак.

Единственный простой способ, что приходит на ум - просто отбрасывать неподходящие значения и брать следующее случайное число. Нежелательные значения можно легко отделить по битовой маске. Так как схема синхронная, есть только конечное число попыток, пока не начнется следующий кадр. Если контроллер не уложился в заданное количество попыток, он застрянет на «неправильном» значении. Помните редкие выбросы на графике яркости?

Реализация на ANSI-C могла бы выглядеть так:
char attempts=0; char out; while(attempts++15) out=15; // верхняя половина диапазона соответствует максимальной яркости

Можно узнать, сколько делается попыток? По статистике, доля a=0,25 всех чисел должн быть отброшена и сгенерирована заново. Вероятность того, что за n попыток не будет выбрано «правильное» число, равна a n .
n=1 0,25 n=2 0,0625 n=3 0,015625 n=4 0,003906

Доля аномально низких уровней яркости составляет 13/3600=0,0036 , что хорошо совпадает с вариантом n=4 . Таким образом, MAX_ATTEMPTS==4 .

Обратите внимание, что более простым решением было бы просто использовать значение из предыдущего кадра, если встретилось недопустимое число. Этот вариант можно было бы исключить, исходя из автокорреляции (см. ниже). Наиболее же простое, вероятно, решение - изменить схему ШИМ - не было здесь использовано.

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


Я рассчитал автокорреляцию всей последовательности значений. Самоподобия не было найдено вплоть до 3500 кадров (на графике выше показано только 1200), что означает уникальность мерцания на протяжении по меньшей мере 4 минут. Неясно, наблюдалось ли дальнейшее повторение последовательности, или логический анализатор автора просто не позволял записывать дольше - прим. перев. Поскольку на каждый кадр нужно как минимум 5 бит случайных данных (а учитывая механизм отбрасывания нежелательных чисел - еще больше), псевдослучайная последовательность имеет длину по меньшей мере 17500 бит. Для этого потребуется регистр разрядности не менее 17, либо настоящий аппаратный генератор случайных чисел. В любом случае, интересно, как много внимания при разработке уделили тому, чтобы картина мерцания не повторялась.

В заключение ответим на вопросы, заданные в начале статьи. Мерцающий светодиод оказался гораздо сложнее, чем я ожидал (также я не ожидал потратить на него 4 часа). Многие микроконтроллерные реализации свечей просто подают биты с генератора псевдослучайных чисел на ШИМ-выход. Покупной светодиод использует более хитрый алгоритм изменения яркости. Безусловно, определенное внимание было уделено разработке алгоритма, и при этом использован кристалл почти минимально возможной площади. Доля цента потрачена не зря.

Каков же лучший алгоритм мерцания? Можно ли улучшить этот?

Дополнение: Я наконец нашел время написать эмулятор. Написанная на ANSI-C программа, эмулирующая поведение этого светодиода,

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

Для начала нужно выбрать микроконтроллер. В моем случае это ATmega8535. В данном случае микроконтроллер можно брать любой, так как данная задача легко реализуется под любой МК. Писать программу для микроконтроллера можно на Ассемблере, СИ, Pascal-е и Bascom. Я использовал язык СИ, все эти языки разные.
Конкретную разницу между Си и Паскалем можно увидеть ниже.

//Мигающий светодиод void main() { ddrB = 0b11111111; //задаём порты B на выход portB = 0b11111111; //по умолчанию всё выключено while(1) { portB = ˜portB; //переключаем состояние светодиода на обратное delay_ms(100); //делаем задержку на 100 миллисекунд } }

Program First; begin ddrB:= $FF; //задаём порт B на выход portB:= $FF; //по умолчанию ничего не горит while(1) do begin portB:= not(portB); //переключаем состояние светодиода на обратное delay_ms(100); //делаем небольшую задержку end; end.

Список радиоэлементов

Обозначение Тип Номинал Количество Примечание Магазин Мой блокнот
U1 МК AVR 8-бит

ATmega8535

1 В блокнот
R1-R8 Резистор

220 Ом - 1 кОм

8 В блокнот
R9-R11 Резистор

10 кОм

3 В блокнот
V1-V8 Светодиод 8 В блокнот
Тактовая кнопка 3