Мой комп - Железо. Windows. Интернет. Программы

Мигание светодиодом на atmega8. Мигание встроенным на плату Arduino светодиодом

  • Перевод

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

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

Устройство относительно простое. В стандартном 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 программа, эмулирующая поведение этого светодиода,

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

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

Также после запуска настроим в качестве отладчика симулятор. Также, чтобы нам не мучиться с кодом и не вводить его заново, мы его скопируем с прошлого занятия, открыв в проекте с прошлого занятия файл main.c в любом редакторе и скопировав весь код в буфер обмена. Удалим весь код из файла main.c в новом проекте и вставим из буфера обмена код.

Теперь начнем исправления. То есть мы создадим новый код, а старый у нас будет нетронутым в прошлом проекте. Порт D мы полностью весь установим в 0, изменив 1 на 0 в следующей строке

PORTD = 0b00000000;

В принципе, у нас уже все ножки порта по умолчанию установлены в ноль, но все равно сделаем это для порядка.

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

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

Логические операции мы будем производить над восьмибитовыми числами или байтами, которые мы представим в виде прямоугольников, состоящих из восьми ячеек. Первые три операции будут выполняться над двумя числами. Возьмём, к примеру вот такие:

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

Первая — это операция "И ", обозначаемая в языке C/C++ знаком "& ". Любой бит в результате будет установлен в единицу только в том случае, если оба соответствующих ему бита в операндах будут единицами . В любом другом случае мы получаем ноль .

Поэтому результат у нас будет следующий

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

Сделующая логическая операция — это операция "ИЛИ ", обозначаемая вертикальной чертой "| ". В результате данной операции мы получим единицу , если хотя бы один бит в сравниваемых одноименных битах будет установлен в единицу . Таким образом, ноль мы получим только тогда, когда в обеих сравниваемых битах будет ноль .

Поэтому здесь будет уже вот такой результат

Третий вид операции — операция "Исключающее ИЛИ ". Эта операция обозначается знаком "^ ". Здесь шансы у бита стать нулём или единицей уравниваются. Единицей результирующий бит станет тогда, когда сравниваемые биты обязательно будут разными — один будет нулём , другой — единицей . А нулём будет результирующий бит тогда, когда сравниваемые биты будут одинаковыми.

Итак, получим следующий результат

А четвертая операция — операция "НЕ ", обозначаемая знаком тильда — "~ ", проделывается над одним байтом. В результате данной операции все биты меняются обратный. То есть ноль становится единицей , а единица нулём . Данную операцию ещё называют операцией инвертирования .

Вот такой вот получим результат

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

while (1)

PORTD |=(1<<(PORTD0 ));

Первый непонятный оператор в данной команде — это сдвоенный оператор "|= ". Такого рода операторов существует несколько. В данном случае сначала результат, который получится в правой части, сразу не присваивается переменной. которая находится в правой части, а складывается логически по "ИЛИ " со значением, хранящемся до этого в перменной, которая находится справа. Затем уже результат, полученный после применения данной логической операции, присваивается переменной, находящейся в правой части. Также существуют подобные операции типа "+= и "-= ", которыми мы в дальнейшем будем очень часто пользоваться.

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

Битовые сдвиги бывают двух видов:

"<< " — сдвиг влево,

">> " — сдвиг враво.

Давайте остановимся на них немного поподробнее.

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

Давайте проделаем данные операции над определённым байтом

Ну, я думаю, со сдвигом всё ясно. Давайте проделаем данную операцию над нашим примером в коде

Единичка, стоящая слева — это число, над которым проводится операция, а не число, на которое мы сдвигаем байт побитно, как очень многие путают. Запомните это накрепко! То есть в нашем случае — это единица или в двоичном выражении 0b00000001 . А PORTD0 — это константа, которая определена в макросе в файле io.h . Вот разновидности данных констант

#define PORTD7 7

#define PORTD6 6

#define PORTD5 5

#define PORTD4 4

#define PORTD3 3

#define PORTD2 2

#define PORTD1 1

#define PORTD0 0

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

Дальше мы делаем операцию "ИЛИ" между значением, находящимся в переменной PORTD и данным результатом, равным единице. Так как у нас в порте D в данный момент находится ноль на всех ножках, то мы только установим единицу на нулевой ножке, так как 0b00000000 и 0b00000001 в результате данной операции дадут на 0b00000001 . Но если бы в регистре порта D находилось бы какое-нибудь другое число, то мы также бы установили бы единицу в нулевом бите, а остальные биты бы не тронулись. То есть мы в результате команды получили и лояльную к остальным лапкам операцию, и в то же время эта операция у нас имеет очень наглядный и читабельный вид.

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

Delay_ms (число в милисекундах )

Delay_us (число в микросекундах )

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

#include

#include

Мы воспользуемся первой командой и применим задержку на 500 милисекунд

PORTD |=(1<<(PORTD0 ));

_delay_ms (500);

Теперь можно погасить светодиод. Делается это с помощью следующей команды

Delay_ms (500);

PORTD &=~(1<<(PORTD0 ));

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

Давайте разберём данную команду. Результат справа нам известен — это 0b00000001 , если мы его инвертируем то получим соответственно 0b11111110 . Тепрь его нужно умножить логически на значение в переменной справа. В данный момент там у нас также 0b00000001 . Соответственно получим мы все нули, так как сравниваемые биты в операндах у нас разные. А если бы было какое-то другое чесло в переменной PORTD, то у нас бы сбросился только нулевой бит, остальные бы не тронулись. Поэтому вот такой операцией в будущем мы и будем сбрасывать определенные биты в регистрах и переменных. Также можно сбрасывать сразу несколько битов или устанавливать, но об этом позже, а то и так мы слишком много уже узнали. Осталось нам добавить ещё такую же задержку, чтобы светодиод находился в погасшем состоянии столько же. сколько и в светящемся

PORTD &=~(1<<(PORTD0 ));

_delay_ms (500);

Но это ещё не всё. Чтобы задержка корректно работала, мы обязаны ещё в начале модуля или файла main.c написать макрос, говорящий компилятору о том, с какой частотой работает наш контроллер, иначе он неправлиьно применит задержку, и нам даст предупрежедение. Напишем данный макрос

#define F_CPU 8000000

#include

Соберём проект. У нас ошибок и предупреждений нет

Теперь мы можем посмотреть результат работы сначала в протеусе, затем на живом светодиоде и контроллере.

Чтобы нам заново не создавать схему, мы файл с проектом для проетуса из папки с проектом из прошлого урока скопируем в папку с новым проектом и переименуем в Test02.pdsprj .

Запустим его. Щёлкнем двойным щелчком по изображению контроллера и выберем там другую прошивку, соответствующую новому проекту, а также выставим в свойствах соответствующую частоту, иначе светодиод будет мигать в 8 раз медленнее

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

Затем прошьём настоящий контроллер, и также убедимся, что светодиод мигает и там.

Смотреть ВИДЕОУРОК

Post Views: 11 097


Мигающий светодиод проект на AVR


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

Надеюсь что с установкой и скачкой утилиты вопросов не возникает. Поэтому после инсталляции и перезагрузки ПК запускаем программу и создаем новый проект. Для этого нажимаем в меню File->New. Появится окно "Createe New File" (Создать новый файл). В нем выбираем File Type (Тип файла) - Project (проект) и нажимаем Ok. Появляется новое окно "Вы хотите создать новый проект и использовать генератор кода?" Отвечаете Да (Yes). В окне генератора кода мы выбираем что будет проинициализировано нашим микроконтроллером перед стартом основной программы. Начнем с вкладки Chip , в ней выбираем наш МК: Atmega 8 с частотой 4 МГЦ.

Переходим в следующую вкладку Ports . Здесь в Port B нулевой бит переключаем с In на Out. Значение по умолчанию на выходе оставляем нулевым. Это значит что при запуске МК на порту B в нулевом разряде будет логический ноль. Далее сохраняем наш проект жесткий диск, например в C:/my_cvavr/project_led.

Для генерации стартового кода жмем на иконку "шестеренка" (немного правее иконки сохранить) и дважды под тем же именем сохраняем проект. Давайте теперь рассмотрим отдельные моменты полученного кода.


Строка: include mega8.h в ней мы говорим компилятору о том, что требуется подключить файл с описаниями всех регистров ATmega8

Строка: PORTB=0x00; по умолчанию установлен логический ноль на выходе. Правда этим выражением выводится нули в весь порт. А можно чуть по проще PORTB.0=0x00;

Строка: DDRB=0x01 ; Вспоминаем статью " ". Так как цифра 0x01 в hex равна 0b00000001 и bin. переведя это выражение из шестнадцатеричной системы в двоичную, мы поймем, что в нулевой разряд регистра направления DDRB записали 1, т.е будем выводить данные через нулевой разряд порта В.

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

Так как светодиодом мы управляем через нулевой разряд порта "В", то пишем:

PORTB.0 = 0x01;
PORTB.0 = 0x00;

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

Что же нам делать? Как вариант, можно вставить паузы длинной в пол секунды или использовать аппаратный таймер с прерыванием. Но раз уж мы мы работаем в утилите CodeVisionAVR, а в ней есть отличная библиотека полезных функций, в которой можно выбрать паузу delay_ms(int x) или delay_us(int x) . В первом случае будет пауза длинной х миллисекунд, а в другом х микросекунд. Но чтоб ей пользоваться придется вернуться на самый верх кода и после строки #include допишем под ней #include , т.е подключим библиотеку и можем пользоваться функциями пауз. Допишем наш код.

PORTB.0 = 0x01;
delay_ms(500);
PORTB.0 = 0x00;
delay_ms(500);

Для сборки проекта. В меню нажимаем Project->Build All, После сборки мы увидим окно в котором говорится, что все сделано без ошибок и нет различных предупреждений. Две строки: No errors и No warnings.

Добавлено: 28.06.2017 в 13:00

В этом примере мы напишем нашу первую программу на Си для микроконтроллера ATtiny13. Предполагается, что у нас уже подготовлено к работе всё необходимое: среда разработки, компилятор и т.д. Подопытным у меня будет самодельная отладочная плата , соответственно весь код буду приводить применительно к ней.
В качестве тестовой программы напишем классический, простейший пример "blink", который будет мигать светодиодом с определённой частотой.

Итак, создаём новый проект и приступаем. Приведу сразу полный код программы, а затем поясню всё более подробно:

/* * tiny13_board_blink * Демо-прошивка отладочной платы на ATtiny13 * с целью проверки работоспособности МК. * Мигаем светодиодом. */ #define F_CPU 1200000UL // Указываем тактовую частоту МК #define LED PB2 // Используем светодиод, подключенный к PB2 (7 пин) #include // Подключаем определения ввода/вывода #include // Подключаем библиотеку функций задержки int main(void) { // Светодиод DDRB |= (1<

В самом начале задаём значения констант и подключаем заголовочные файлы и библиотеки.
Файл avr/io.h подключает определения ввода/вывода для конкретного типа микроконтроллера (тип МК указывается в виде опции для компилятора).
Библиотеку util/delay.h подключаем для использования функций задержки, в нашем случае: _delay_ms() . Для работы функций задержки мы должны указать тактовую частоту процессора. Поэтому ДО подключения util/delay.h определяем константу F_CPU (в данном случае - 1,2МГц).

Затем у нас идёт основная функция main - это, собственно, тело нашей программы. Здесь мы должны, сначала, описать все действия, которые будут происходить при старте микроконтроллера, а затем, в бесконечном цикле запустить выполнение основной программы:

while (1) { ... }

Для начала, мы должны сконфигурировать порт ввода/вывода. В МК AVR старших моделей портов в/в может быть несколько (A, B, C, D). К каждому порту может быть подключено до восьми ножек. Каждая из ножек может быть настроена как на вход, так и на выход. ATtiny13 имеет только один порт (B), к которому подключены шесть ножек (PB0-PB5, см. datasheet). По умолчанию все ножки настроены на вход, а чтобы управлять светодиодом, мы должны использовать соответствующую ножку как выход. В микроконтроллерах AVR вся аппаратная часть настраивается посредством восьмибитных регистров. Направление (вход-выход) устанавливается битами регистров DDRx (где x - буква порта, в нашем случае B). Значение бита "0" - соответствует входу, "1" - выходу. Таким образом, чтобы использовать ножку PB2, как выход, мы должны установить второй бит регистра DDRB в единицу:

DDRB |= (1<

Для управления состоянием выхода предназначены регистры PORTx. Например, чтобы выключить светодиод, подключенный к ножке PB2 (подать низкий уровень сигнала), мы должны записать ноль во второй бит регистра PORTB :

PORTB &= ~(1<

Чтобы включить (подать высокий уровень сигнала) - соответственно, записываем единицу:

PORTB |= (1<

Теперь, когда порт в/в сконфигурирован, мы запускаем основной цикл, в котором будем инвертировать состояние выхода PB2 (чередовать высокий и низкий уровень сигнала) с задержкой 500мс. Таким образом, светодиод у нас будет мигать с частотой 1 раз в секунду.

разработчик 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 можете скачать