Таймер на atmega8: AVR использование таймеров ( ATMega8 TIMER0)

Содержание

Таймер на ATmega8 и светодиодных индикаторах

Микроконтроллеры и Технологии каталог схем и прошивок

  • Вход на сайт
  • Создать аккаунт

Имя пользователя

Пароль

Запомнить меня

  • Забыли пароль?
  • Забыли логин?
  • Создать аккаунт
  • Создать аккаунт
  • Вход на сайт
  1. Главная|
  2. Устройства|
  3. AVR|
  4. Таймер на ATmega8 и светодиодных индикаторах
Просмотров: 75962

ATmega8 Таймер Семисегментный индикатор

Этот таймер предназначен для установки выдержек от 5 секунд до 100 минут. На его выходе имеется достаточно мощное электромагнитное реле, позволяющее коммутировать ток до З0А при напряжении 12V и ток до 10А при напряжении 220V. Благодаря применению электромагнитного реле таймер может управлять не только нагревательными или осветительными приборами, но и электронными приборами, критичными к форме питающего переменного напряжения. Трансформаторное питание, в сочетании с реле, обеспечивает полную гальваническую развязку электронной схемы таймера от сети.

Для общения таймера с оператором есть четырехразрядный светодиодный индикатор, в нем очень старые 7-сегментные матрицы АЛ304 в количестве четырех штук, соединены в матрицу путем соединения вместе одноименных сегментных выводов. Конечно можно использовать и более современные светодиодные индикаторы, и даже готовые матрицы по четыре разряда под динамическую индикацию.

Управляют таймером кнопками S1, S2, S3, S4. При нажатии кнопки S1 происходит включение   нагрузки и   запуск таймера. Чтобы установить время, в течение которого должна работать нагрузка, нужно нажать S4.На дисплее будут мигать два старших разряда (минуты). Теперь кнопками S2 и S3 можно установить значение минут. Затем нужно еще раз нажать S4. Теперь будут мигать младшие разряды и кнопками S2 и S3 можно установить секунды. Чтобы сохранить установки нужно еще раз нажать S4. Теперь индикатор будет показывать установленную выдержку. Чтобы запустить таймер нужно нажать S1. Нагрузка включается, а показания индикатора начинают убывать. Как только заданное время иссякнет на индикаторе появляется надпись «OFF», а нагрузка выключается электромагнитным реле. Чтобы повторить нужно дважды нажать кнопку S1. При первом нажатии «OFF» сменится на показание заданного времени, а при втором произойдет запуск таймера. Управление реле по выводу 23 D1. Включение — логической единицей. Ключ на VT5 и VT6 управляет электромагнитным реле К1. Такие реле используются в схемах автомобильных сигнализаций. Они могут коммутировать как постоянный ток (12V) так и переменный (220V), поскольку обладают хорошей изоляцией.

Источник питания выполнен на маломощном трансформаторе. Поскольку вторичная обмотка трансформатора имеет отвод от середины (12-0-12), то выпрямитель сделан не по мостовой, а по двухполупериодной схеме на двух диодах VD2 и VD3. Если трансформатор будет с обмоткой 12V без отводом, то нужен выпрямительный мост. Реле питается непосредственно с выхода выпрямителя, а остальная схема через стабилизатор А1 напряжения 5V.

При прошивке нужно задать на работу с внутренним генератором 8 МГц.

Схема собрана на покупной макетной печатной плате, на её одной стороне расположены микросхема и другие детали, а кнопки и индикаторы на другой стороне. Трансформатор питания за пределами платы.

Транзисторы КТ315 можно заменить на КТ3102 или любые аналоги. Транзистор КТ815 можно заменить на КТ817, КТ604. Диод КД521 — практически любой аналог. Диоды в выпрямителе КД209 — любые диоды выпрямительные на постоянный ток не ниже 150 мА. Интегральный стабилизатор 7805 можно заменить любым 5-вольтовым, например, КР142ЕН5А.

Или сделать стабилизатор по параметрической схеме на двух транзисторах и стабилитроне на 5V. По поводу индикаторов сказано выше. Это могут быть любые семисегментные индикаторы с общим анодом(катодом).


Архив для статьи «Таймер на Atmega8 и светодиодных индикаторах»
Описание: Файлы прошивок
Размер файла: 5.58 KB Количество загрузок: 5 034 Скачать

Понравилась статья? Поделиться с друзьями:

  • Назад
  • Вперед

Таймер на atmega8 с часовым модулем. Программирование микроконтроллеров AVR

Этот таймер предназначен для установки выдержек от 5 секунд до 100 минут.

На его выходе имеется достаточно мощное электромагнитное реле, позволяющее коммутировать ток до З0А при напряжении 12V и ток до 10А при напряжении 220V. Благодаря применению электромагнитного реле таймер может управлять не только нагревательными или осветительными приборами, но и электронными приборами, критичными к форме питающего переменного напряжения. Трансформаторное питание, в сочетании с реле, обеспечивает полную гальваническую развязку электронной схемы таймера от сети.

Для общения таймера с оператором есть четырехразрядный светодиодный индикатор, в нем очень старые 7-сегментные матрицы АЛ304 в количестве четырех штук, соединены в матрицу путем соединения вместе одноименных сегментных выводов. Конечно можно использовать и более современные светодиодные индикаторы, и даже готовые матрицы по четыре разряда под динамическую индикацию.

Управляют таймером кнопками S1, S2, S3, S4. При нажатии кнопки S1 происходит включение нагрузки и запуск таймера. Чтобы установить время, в течение которого должна работать нагрузка, нужно нажать S4.

На дисплее будут мигать два старших разряда (минуты). Теперь кнопками S2 и S3 можно установить значение минут. Затем нужно еще раз нажать S4. Теперь будут мигать младшие разряды и кнопками S2 и S3 можно установить секунды. Чтобы сохранить установки нужно еще раз нажать S4. Теперь индикатор будет показывать установленную выдержку. Чтобы запустить таймер нужно нажать S1. Нагрузка включается, а показания индикатора начинают убывать. Как только заданное время иссякнет на индикаторе появляется надпись «OFF», а нагрузка выключается электромагнитным реле. Чтобы повторить нужно дважды нажать кнопку S1. При первом нажатии «OFF» сменится на показание заданного времени, а при втором произойдет запуск таймера. Управление реле по выводу 23 D1. Включение — логической единицей. Ключ на VT5 и VT6 управляет электромагнитным реле К1. Такие реле используются в схемах автомобильных сигнализаций. Они могут коммутировать как постоянный ток (12V) так и переменный (220V), поскольку обладают хорошей изоляцией.

Источник питания выполнен на маломощном трансформаторе. Поскольку вторичная обмотка трансформатора имеет отвод от середины (12-0-12), то выпрямитель сделан не по мостовой, а по двухполупериодной схеме на двух диодах VD2 и VD3. Если трансформатор будет с обмоткой 12V без отводом, то нужен выпрямительный мост. Реле питается непосредственно с выхода выпрямителя, а остальная схема через стабилизатор А1 напряжения 5V.

При прошивке нужно задать на работу с внутренним генератором 8 МГц.

Схема собрана на покупной макетной печатной плате, на её одной стороне расположены микросхема и другие детали, а кнопки и индикаторы на другой стороне. Трансформатор питания за пределами платы.

Транзисторы КТ315 можно заменить на КТ3102 или любые аналоги. Транзистор КТ815 можно заменить на КТ817, КТ604. Диод КД521 — практически любой аналог. Диоды в выпрямителе КД209 — любые диоды выпрямительные на постоянный ток не ниже 150 мА. Интегральный стабилизатор 7805 можно заменить любым 5-вольтовым, например, КР142ЕН5А. Или сделать стабилизатор по параметрической схеме на двух транзисторах и стабилитроне на 5V. По поводу индикаторов сказано выше. Это могут быть любые семисегментные индикаторы с общим анодом(катодом).

Архив для статьи «Таймер на Atmega8 и светодиодных индикаторах»
Описание:
Файлы прошивок
Размер файла: 5.58 KB Количество загрузок: 4 319

Этот таймер предназначен для установки выдержек от 5 секунд до 100 минут. На его выходе имеется достаточно мощное электромагнитное реле, позволяющее коммутировать ток до З0А при напряжении 12V и ток до 10А при напряжении 220V. Благодаря применению электромагнитного реле таймер может управлять не только нагревательными или осветительными приборами, но и электронными приборами, критичными к форме питающего переменного напряжения. Трансформаторное питание, в сочетании с реле, обеспечивает полную гальваническую развязку электронной схемы таймера от сети.

Для общения таймера с оператором есть четырехразрядный светодиодный индикатор, в нем очень старые 7-сегментные матрицы АЛ304 в количестве четырех штук, соединены в матрицу путем соединения вместе одноименных сегментных выводов. Конечно можно использовать и более современные светодиодные индикаторы, и даже готовые матрицы по четыре разряда под динамическую индикацию.

Управляют таймером кнопками S1, S2, S3, S4. При нажатии кнопки S1 происходит включение нагрузки и запуск таймера. Чтобы установить время, в течение которого должна работать нагрузка, нужно нажать S4.На дисплее будут мигать два старших разряда (минуты). Теперь кнопками S2 и S3 можно установить значение минут. Затем нужно еще раз нажать S4. Теперь будут мигать младшие разряды и кнопками S2 и S3 можно установить секунды. Чтобы сохранить установки нужно еще раз нажать S4. Теперь индикатор будет показывать установленную выдержку. Чтобы запустить таймер нужно нажать S1. Нагрузка включается, а показания индикатора начинают убывать. Как только заданное время иссякнет на индикаторе появляется надпись «OFF», а нагрузка выключается электромагнитным реле. Чтобы повторить нужно дважды нажать кнопку S1. При первом нажатии «OFF» сменится на показание заданного времени, а при втором произойдет запуск таймера. Управление реле по выводу 23 D1. Включение — логической единицей. Ключ на VT5 и VT6 управляет электромагнитным реле К1. Такие реле используются в схемах автомобильных сигнализаций. Они могут коммутировать как постоянный ток (12V) так и переменный (220V), поскольку обладают хорошей изоляцией.

Источник питания выполнен на маломощном трансформаторе. Поскольку вторичная обмотка трансформатора имеет отвод от середины (12-0-12), то выпрямитель сделан не по мостовой, а по двухполупериодной схеме на двух диодах VD2 и VD3. Если трансформатор будет с обмоткой 12V без отводом, то нужен выпрямительный мост. Реле питается непосредственно с выхода выпрямителя, а остальная схема через стабилизатор А1 напряжения 5V.

При прошивке нужно задать на работу с внутренним генератором 8 МГц.

Схема собрана на покупной макетной печатной плате, на её одной стороне расположены микросхема и другие детали, а кнопки и индикаторы на другой стороне. Трансформатор питания за пределами платы.

Транзисторы КТ315 можно заменить на КТ3102 или любые аналоги. Транзистор КТ815 можно заменить на КТ817, КТ604. Диод КД521 — практически любой аналог. Диоды в выпрямителе КД209 — любые диоды выпрямительные на постоянный ток не ниже 150 мА. Интегральный стабилизатор 7805 можно заменить любым 5-вольтовым, например, КР142ЕН5А. Или сделать стабилизатор по параметрической схеме на двух транзисторах и стабилитроне на 5V. По поводу индикаторов сказано выше. Это могут быть любые семисегментные индикаторы с общим анодом(катодом).

Архив для статьи «Таймер на Atmega8 и светодиодных индикаторах»
Описание: Файлы прошивок
Размер файла: 5. 58 KB Количество загрузок: 4 319

Таймер обратного отсчёта поможет вам точно отмерять интервал времени в диапазоне от 1 секунды до 24 часов.

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

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

– компактная конструкция и простая схемотехника;

– оперативное кнопочное управление;

– при управлении кнопками, дублирование действий на ЖКИ;

– задание времени с точностью до секунды;

– диапазон отсчета от 1 секунды до 24 часов;

– функция старт, пауза;

– функция сброса отсчета и выставленных значений времени;

– при достижении значения 00.00.00, включается исполнительное устройство;

Все поставленные задачи были реализованы в этом проекте.

Описание режимов работы таймера

После включения таймера, можно выставлять время, которое нам требуется. Назначение кнопок видно на схеме. После установки, нажимаете кнопку СТАРТ-пауза отсчет начинается. Максимальное выставляемое время 23.59.59.

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

Как только время достигает 00.00.00, – включается светодиод (в данный момент это имитация включения исполнительного устройства или просто можно пищалку с генератором).

Если при его работе таймера нажать кнопку старт-ПАУЗА, таймер остановит отсчет, двойное нажатие кнопки СТАРТ-пауза приводит к возобновлению остановленного отсчета.

Чтобы выключить нагрузку после включения, нужно нажать СБРОС, данные таймера установятся 00.00.01 – нагрузка выключиться. Или выставить новый период отсчета и двойное нажатие кнопки СТАРТ-пауза.

Отображение на ЖКИ символов ! > означает, что нагрузка отключена (PD3) и при однократном нажатии кнопки СТАРТ начнется обратный отсчет установленного времени.

Кварц внешний 8 MHz, для точности счета.

Для программатора в PonyProg и CodeVisionAVR нужно убрать все галочки с настройки тактового генератора.

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

Внимание! Что касается FUSE-битов. Это основной источник ошибок, приводящих к залочиванию контроллера.

– CKSEL3…0 должны быть НЕзапрограммированы.

В PonyProg и CodeVisionAVR стоят так:

ЖК должен быть на базе контроллера HD4480

16х1, для него в архиве имеется прошивка V-1

или 8х2, в архиве прошивка V-2.

Работу схемы можно протестировать в proteus’е.

Если при симуляции проекта в proteus’е некорректно отображаются символы кириллицы на ЖКИ, то для правильного отображения кириллицы на ЖК индикаторе распаковать библиотеку LCDrus .zip (приложена в архиве проекта) в папку models proteus’а.

Данное устройство будет полезно любому человеку, которому приходится постоянно готовить, и надежно защитит вашу кухню от чрезмерного количества дыма. Этот прибор, как следует из названия — кухонный таймер. Он предназначен для отсчитывания интервалов при приготовлении различных блюд. Таймер имеет несколько клавиш, при помощи которых можно легко установить время от 1 минуты до 99 часов. Обратный отсчет начинается автоматически, через 3 секунды после установки времени. Благодаря громкой пищалке вы точно услышите когда блюдо готово. Устройство собрано на основе микроконтроллера ATMega8.

Описание конструкции

Сердцем устройства является микроконтроллер U1 (ATMega8) с кварцевым резонатором X1 (16 МГц) и обвязкой из двух конденсаторов C1 (22 пФ) и C2 (22 пФ). Стабилизатор U2 (7805) с обвязкой из конденсаторов C3 (100 мкФ) и C4 (47 мкФ) стабилизирует напряжение питания 5В, необходимое для корректной работы микроконтроллера и связанных компонентов. На разъем Zas подается напряжение 7-12В. Если у Вас имеется блок питания с напряжением 5-6В, можно исключить из схемы стабилизатор напряжения. Зуммер B1 и аноды дисплея W1 управляются транзисторами T1 — T5 (BC556), с обвязкой из резисторов R1 — R8 (3. 3 кОм), R17 (3.3 кОм) и R18 (3.3 кОм). Резисторы R9 — R16 (330 Ом) ограничивают ток через сегменты дисплея. Разъем Prog и один вывод R используются для подключения программатора. Клавиатура таймера подключена к разъему Sw.

Изготовление

Рисунок печатной платы для устройства есть в архиве в конце статьи. Установка деталей начинается с пайки двух перемычек. Затем устанавливаются все резисторы и прочие элементы в порядке от меньшего к большему. Кварц X1 должен быть «низкий» — он устанавливается под дисплей — в противном случае он просто не поместится туда. Зуммер B1 можно припаять на плату, как это показано на фотографиях, но позже выяснилось, что после закрытия корпуса звук слишком тихий (несмотря на отверстия, просверленные в корпусе). Лучше приклеить зуммер на одну из сторон корпуса (так, как это показано на последнем фото), и соединить проводами с платой. Клавиатура состоит из 5 кнопок без фиксации 12х12мм непосредственно на лицевой части корпуса, так что их толкатели находятся чуть выше поверхности корпуса. Для этого устройства в качестве блока питания хорошо использовать зарядное устройство для телефона, из-за его небольшого веса и размеров.

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

ATmega8A-AU

1В блокнот
U2Линейный регулятор

LM7805CT

1В блокнот
T1-T5Биполярный транзистор

BC556

5В блокнот
C1, C2Конденсатор22 пФ1В блокнот
C3100 мкФ1В блокнот
С4Конденсатор электролитический47 мкФ1В блокнот
R1-R8, R17, R18Резистор3. 3 кОм10В блокнот
R9-R16Резистор330 Ом8В блокнот
W17-сегментный индикаторAF-05643FG-B1Или с аналогичной распиновкойВ блокнот
B1Пьезодинамик с генератором1

На нашем сайте, посвящённом различным электронным самоделкам, уже неоднократно публиковались схемы . Конечно они уступают современным промышленным аналогам, где имеется дисплей, возможность программирования и другие сервисные функции. И вот пришло время разместить такую схему, которая на равных будет конкурировать с лучшими фирменными образцами. Цифровой таймер используются для управления работой электрических устройств, по запрограммированному графику. Этот программируемый таймер делается на основе микроконтроллера PIC16F628A , который может быть запрограммирован, чтобы составить расписание включения и выключения электрического прибора, подключенного к нему, который управляется через реле. Таймер позволяет вручную задать время включения и выключения. Максимальный интервал времени, который можно настроить для включения и выключения, составляет 99 часов 59 минут. Проект разработан под использование 16х2 ЖК-дисплея и 4 кнопки.

Здесь 5 вольтовое реле управляется транзистором PN2222, который, в свою очередь, управляется RB3 PIC16F628A. Цифровые входы из 4 кнопок читаются с помощью порта ввода/вывода RA2, RA3, RA4, и RB0. Стандартный 16?2 символьный ЖК-дисплей используется для отображения состояния устройства, программы, меню и времени. ЖК работает в 4-битном режиме, поэтому только 6 выводов I/O PIC16F628A необходимы для работы. Пьезоэлектрический зуммер дает звуковой сигнал, когда таймер запускается и останавливается. Он также подает звуковой сигнал, когда устройство включено или выключено. Напряжение питания схемы поступает от стабилизатора LM7805. На вход её подается 9 В от сетевого адаптера. Подсветка LED повышает читаемость дисплея LCD в условиях низкой освещенности состоянии.

Таймер получает команды от 4-х кнопок. Их функции следующие:

Время : позволяет задать время включения и выключения. Когда таймер изначально включен, устройство находится в выключенном состоянии, и время 0. Нажав эту кнопку, можно переключаться между on и off на дисплее.

Выбор : позволяет выбрать между on и off параметрами, а также часовой и минутной цифрой. Выбранная цифра увеличивается нажатием кнопки ON/OFF.

Ввод : когда соответствующее время выбрано, нажатие данной кнопки завершит установки.

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

AVR. Учебный курс. Таймеры | Электроника для всех

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

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

И такой счетчик есть, даже не один — это периферийные таймеры. В AVR их может быть несколько штук да еще с разной разрядностью. В ATmega16 три, в ATmega128 четыре. А в новых МК серии AVR может даже еще больше, не узнавал.

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

Что умееют таймеры

  • Тикать с разной скоростью, подсчитывая время
  • Считать входящие извне импульсы (режим счетчика)
  • Тикать от внешнего кварца на 32768гц
  • Генерировать несколько видов ШИМ сигнала
  • Выдавать прерывания (по полудесятку разных событий) и устанавливать флаги

Разные таймеры имеют разную функциональность и разную разрядность. Это подробней смотреть в даташите.

Источник тиков таймера
Таймер/Счетчик (далее буду звать его Т/С) считает либо тактовые импульсы от встроенного тактового генератора, либо со счетного входа.

Погляди внимательно на распиновку ног ATmega16, видишь там ножки T1 и T0?

Так вот это и есть счетные входы Timer 0 и Timer 1. При соответствующей настройке Т/С будет считать либо передний (перепад с 0-1), либо задний (перепад 1-0) фронт импульсов, пришедших на эти входы.

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

Кроме того, Т/С2 способен работать в асинхронном режиме. То есть Т/С считает не тактовые импульсы процессора, не входящие импульсы на ножки, а импульсы своего собственного собственного генератора, работающего от отдельного кварца. Для этого у Т/С2 есть входы TOSC1 и TOSC2, на которые можно повесить кварцевый резонатор.

Зачем это вообще надо? Да хотя бы организовать часы реального времени. Повесил на них часовой кварц на 32768 Гц да считай время — за секунду произойдет 128 переполнений (т.к. Т/С2 восьми разрядный). Так что одно переполнение это 1/128 секунды. Причем на время обработки прерывания по переполнению таймер не останавливается, он также продолжает считать. Так что часы сделать плевое дело!

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

То есть еще до попадания в счетный регистр частота импульсов будет делиться. Делить можно на 8, 32, 64, 128, 256, 1024. Так что если повесишь на Т/С2 часовой кварц, да пропустишь через предделитель на 128, то таймер у тебя будет тикать со скоростью один тик в секунду.

Удобно! Также удобно юзать предделитель когда надо просто получить большой интервал, а единственный источник тиков это тактовый генератор процессора на 8Мгц, считать эти мегагерцы задолбаешься, а вот если пропустить через предделитель, на 1024 то все уже куда радужней.

Но тут есть одна особенность, дело в том, что если мы запустим Т/С с каким нибудь зверским предделителем, например на 1024, то первый тик на счетный регистр придет не обязательно через 1024 импульса.

Это зависит от того в каком состоянии находился предделитель, а вдруг он к моменту нашего включения уже досчитал почти до 1024? Значит тик будет сразу же. Предделитель работает все время, вне зависимости от того включен таймер или нет.

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

Например первый таймер работает на выводе 1:64, а второй на выводе 1:1024 предделителя. У второго почти дотикало в предделителе до 1024 и вот вот должен быть тик таймера, но тут ты взял и сбросил предделитель, чтобы запустить первый таймер точно с нуля. Что произойдет? Правильно, у второго делилка тут же скинется в 0 (предделитель то единый, регистр у него один) и второму таймеру придется ждать еще 1024 такта, чтобы получить таки вожделенный импульс!

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

Для сброса предделителей достаточно записать бит PSR10 в регистре SFIOR. Бит PSR10 будет сброшен автоматически на следующем такте.

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

Причем тут есть подвох, если в восьмиразрядный регистр надо положить число, то нет проблем OUT TCNT0,Rx и никаких гвоздей, то с двухбайтными придется поизвращаться.

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

Чувствуете лажу? Вот! Таймер точное устройство, поэтому грузить его счетные регистры надо одновременно! Но как? А инженеры из Atmel решили проблему просто:
Запись в старший регистр (TCNTxH) ведется вначале в регистр TEMP. Этот регистр чисто служебный, и нам никак недоступен.

Что в итоге получается: Записываем старший байт в регистр TEMP (для нас это один хрен TCNTxH), а затем записываем младший байт. В этот момент, в реальный TCNTxH, заносится ранее записанное нами значение. То есть два байта, старший и младший, записываются одновременно! Менять порядок нельзя! Только так

Выглядит это так:

1
2
3
4
	CLI 			; Запрещаем прерывания, в обязательном порядке!
	OUT	TCNT1H,R16	; Старшей байт записался вначале в TEMP
	OUT	TCNT1L,R17	; А теперь записалось и в старший и младший!
	SEI 			; Разрешаем прерывания

Зачем запрещать прерывания? Да чтобы после записи первого байта, прога случайно не умчалась не прерывание, а там кто нибудь наш таймер не изнасиловал. Тогда в его регистрах будет не то что мы послали тут (или в прерывании), а черти что. Вот и попробуй потом такую багу отловить! А ведь она может вылезти в самый неподходящий момент, да хрен поймаешь, ведь прерывание это почти случайная величина. Так что такие моменты надо просекать сразу же.

Читается все также, только в обратном порядке. Сначала младший байт (при этом старший пихается в TEMP), потом старший. Это гарантирует то, что мы считаем именно тот байт который был на данный момент в счетном регистре, а не тот который у нас натикал пока мы выковыривали его побайтно из счетного регистра.

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

Итак, главным регистром является TCCRx
Для Т/С0 и Т/С2 это TCCR0 и TCCR2 соответственно, а для Т/С1 это TCCR1B

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

У разных таймеров немного по разному, поэтому опишу биты CS02. .CS00 только для таймера 0

  • 000 — таймер остановлен
  • 001 — предделитель равен 1, то есть выключен. таймер считает тактовые импульсы
  • 010 — предделитель равен 8, тактовая частота делится на 8
  • 011 — предделитель равен 64, тактовая частота делится на 64
  • 100 — предделитель равен 256, тактовая частота делится на 256
  • 101 — предделитель равен 1024, тактовая частота делится на 1024
  • 110 — тактовые импульсы идут от ножки Т0 на переходе с 1 на 0
  • 111 — тактовые импульсы идут от ножки Т0 на переходе с 0 на 1

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

За прерывания от таймеров отвечают регистры TIMSК, TIFR. А у более крутых AVR, таких как ATMega128, есть еще ETIFR и ETIMSK — своего рода продолжение, так как таймеров там поболее будет.

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

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

  • TOIE0 — разрешение на прерывание по переполнению таймера 0
  • TOIE1 — разрешение на прерывание по переполнению таймера 1
  • TOIE2 — разрешение на прерывание по переполнению таймера 2

О остальных фичах и прерываниях таймера мы поговорим попозжа, когда будем разбирать ШИМ.

Регистр TIFR это непосредственно флаговый регистр. Когда какое то прерывание срабатывает, то выскакивает там флаг, что у нас есть прерывание. Этот флаг сбрасывается аппаратно когда программа уходит по вектору. Если прерывания запрещены, то флаг так и будет стоять до тех пор пока прерывания не разрешат и программа не уйдет на прерывание.

Чтобы этого не произошло флаг можно сбросить вручную. Для этого в регистре TIFR в него нужно записать 1!

А теперь похимичим
Ну перекроим программу на работу с таймером. Введем программный таймер. Шарманка так и останется, пускай тикает. А мы добавим вторую переменную, тоже на четыре байта:

1
2
3
4
; RAM ========================================================
		.DSEG
CCNT:	.byte	4
TCNT:	.byte	4

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

Делаем RJMP на обработчик с вектора.

1
2
3
4
5
6
         .ORG $010
         RETI			; (TIMER1 OVF) Timer/Counter1 Overflow
         . ORG $012
         RJMP	Timer0_OV	; (TIMER0 OVF) Timer/Counter0 Overflow
         .ORG $014
         RETI			; (SPI,STC) Serial Transfer Complete

Добавим обработчик прерывания по переполнению таймера 0, в секцию Interrupt. Так как наш тикающий макрос активно работает с регистрами и портит флаги, то надо это дело все сохранить в стеке сначала:

Кстати, давайте создадим еще один макрос, пихающий в стек флаговый регистр SREG и второй — достающий его оттуда.

1
2
3
4
5
6
7
8
9
10
11
12
		.MACRO PUSHF
		PUSH	R16
		IN	R16,SREG
		PUSH	R16
		.ENDM
 
 
		.MACRO POPF
		POP	R16
		OUT	SREG,R16
		POP	R16
		.ENDM

Как побочный эффект он еще сохраняет и R16, помним об этом 🙂

1
2
3
4
5
6
7
8
9
10
11
12
13
Timer0_OV:	PUSHF
		PUSH	R17
		PUSH	R18
		PUSH	R19
 
		INCM	TCNT
 
		POP	R19
		POP	R18
		POP	R17
		POPF
 
		RETI

Теперь инициализация таймера. Добавь ее в секцию инита локальной периферии (Internal Hardware Init).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
; Internal Hardware Init  ======================================
	SETB	DDRD,4,R16		; DDRD.4 = 1
	SETB	DDRD,5,R16		; DDRD.5 = 1
	SETB	DDRD,7,R16		; DDRD.7 = 1
 
	SETB	PORTD,6,R16		; Вывод PD6 на вход с подтягом
	CLRB	DDRD,6,R16		; Чтобы считать кнопку
 
	SETB	TIMSK,TOIE0,R16 	; Разрешаем прерывание таймера
 
	OUTI	TCCR0,1<<CS00		; Запускаем таймер. Предделитель=1
					; Т.е. тикаем с тактовой частотой.
 
	SEI				; Разрешаем глобальные прерывания
; End Internal Hardware Init ===================================

Осталось переписать наш блок сравнения и пересчитать число. Теперь все просто, один тик один такт. Без всяких заморочек с разной длиной кода. Для одной секунды на 8Мгц должно быть сделано 8 миллионов тиков. В хексах это 7A 12 00 с учетом, что младший байт у нас TCNT0, то на наш счетчик остается 7А 12 ну и еще старшие два байта 00 00, их можно не проверять. Маскировать не нужно, таймер мы потом переустановим все равно.

Одна только проблема — младший байт, тот что в таймере. Он тикает каждый такт и проверить его на соответствие будет почти невозможно. Т.к. малейшее несовпадение и условие сравнение выпадет в NoMatch, а подгадать так, чтобы проверка его значения совпала именно с этим тактом… Проще иголку из стога сена вытащить с первой попытки наугад.

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

Тогда наша секунда обеспечивается с точностью 8000 000 плюс минус 256 тактов. Не велика погрешность, всего 0,003%.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
; Main =========================================================
Main:		SBIS	PIND,6		; Если кнопка нажата - переход
		RJMP	BT_Push
 
		SETB	PORTD,5	; Зажгем LED2
		CLRB	PORTD,4	; Погасим LED1
 
Next:		LDS	R16,TCNT	; Грузим числа в регистры
		LDS	R17,TCNT+1
 
		CPI	R16,0x12	; Сравниванем побайтно.  Первый байт
		BRCS	NoMatch	; Если меньше -- значит не натикало.
		CPI	R17,0x7A	; Второй байт
		BRCS	NoMatch	; Если меньше -- значит не натикало.
 
; Если совпало то делаем экшн
Match:		INVB	PORTD,7,R16,R17	; Инвертировали LED3	
 
; Теперь надо обнулить счетчик, иначе за эту же итерацию главного цикла
; мы сюда попадем еще не один раз -- таймер то не успеет натикать 255 значений,
; чтобы число в первых двух байтах счетчика изменилось и условие сработает.
; Конечно, можно обойти это доп флажком, но проще сбросить счетчик :) 
 
		CLR	R16			; Нам нужен ноль
 
		CLI 				; Доступ к многобайтной переменной
						; одновременно из прерывания и фона
						; нужен атомарный доступ.  Запрет прерываний
 
		OUTU	TCNT0,R16		; Ноль в счетный регистр таймера
		STS	TCNT,R16		; Ноль в первый байт счетчика в RAM
		STS	TCNT+1,R16		; Ноль в второй байт счетчика в RAM
		STS	TCNT+2,R16		; Ноль в третий байт счетчика в RAM
		STS	TCNT+3,R16		; Ноль в первый байт счетчика в RAM
		SEI 				; Разрешаем прерывания снова. 
 
; Не совпало - не делаем :) 
NoMatch:	NOP
 
		INCM	CCNT		; Счетчик циклов по тикает
					; Пускай, хоть и не используется.
		JMP	Main
 
 
BT_Push:	SETB	PORTD,4	; Зажгем LED1
		CLRB	PORTD,5	; Погасим LED2
 
		RJMP	Next
; End Main =====================================================

Скачать проект с этим примером

Вот как это выглядит в работе

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

Можно еще немного оптимизировать процесс проверки. Сделать его более быстрым.

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

А что если надо точней? Ну тут вариант только один — заюзать обработку события прям в обработчике прерывания, а значение в TCNT:TCNT0 каждый раз подстраивать так, чтобы прерывание происходило точно в нужное время.

AVR. Описание работы Timer/Counter1




Email автора.

Copyright: Сухарев Евгений, 2005


Описание работы таймера/счётчика 1.
Прерывания от TC1


Таймер/счётчик 1 (TC1) представляет из себя 16-битный модуль, содержащий 10 8-битных регистров. Эти регистры фактически являются набором из 5 16-битных регистров. Счёт происходит в регистрах TCNT1H (Timer counter 1 High byte) и TCNT1L (Low byte), вместе составляющих 16-битный регистр TCNT1. ВНИМАНИЕ! Если использовать прямое чтение 8-битных регистров TCNT1H и TCNT1L, то нельзя быть уверенным, что эти регистры прочитались одновременно. Может произойти следующая ситуация: Счётчик содержал значение $01FF, Вы считали TCNT1H (содержащий значение 01 в какую-то переменную). За это время произошёл счётный импульс, и содержимое TCNT1L стало равно $00, а в TCNT1H записалось значение $02. Теперь Вы читаете значение TCNT1L в другую переменную, получаете в этой переменной значение $00 (ведь таймер-счётчик уже произвёл счёт). 16-битное значение этих переменных получилось $0100, но на момент считывания старшего байта содержимое счётчика было $01FF, и младший байт у Вас должен был прочитаться как FF. Для предотвращения такой ситуации служит временный регистр, содержащийся в блоке таймера-счётчика. Этот регистр прозрачный, т.е. действует автоматически. При считывании значения регистра TCNT1L в переменную, содержимое TCNT1H попадает в этот регистр. Затем при чтении старшего байта в переменную, считывается значение временного регистра. Временный регистр абсолютно прозрачен для пользователя, но для его корректной работы необходимо соблюдать такую последовательность действий:
Для 16-битной операции записи, старший байт должен быть записан первым. Младший — вторым.
Для операции 16-битного чтения, младший байт должен быть прочитан первым, а содержимое старшего байта считывается вторым.
Регистр TCCR1A служит для задания режимов работы таймера/счётчика 1:

Биты COM1A1,COM1A0, COM1B1 и COM1B0 — контролируют поведение выводов OC1A и OC1B.
Биты FOC1A, FOC1B, WGM11 и WGM10 служат для задания работы ТС1 как широтно-импульсного модулятора.
Скорость счёта ТС1 можно установить в регистре TCCR1B:

Где биты ICNC1, ICES1, WGM13 и WGM12 также служат для PWM, а CS12, CS11 и CS10 настраивают скорость счёта следующим образом:

В случае, если в эти биты записаны значения 000, ТС0 остановлен. Если записано 001, то тактовая частота процессора подаётся через схему делителя без изменений, и на каждый такт процессора ТС1 увеличивает значение в регистре TCNT1. Соответственно, если в CSxx Записано 101, то увеличение значения в TCNT1 происходит на каждый 1024-ый такт процессора.

16-битные регистры OCR1A и OCR1Bслужат для задания значения, при достижении которого в режиме счёта, ТС1 генерирует соответствующие прерывания.

Обработка прерываний от TC1

ТС1 при переполнении значения TCNT1 посылает процессору сигнал Timer/Counter 1 Overflow. Также процессору посылается сигнал Timer/Counter 1 A или B Compare Match при совпадении значений в регистрах TCNT1 и OCR1A и OCR1B соответственно. Реакция процессора на эти сигналы (вызов соответствующих прерываний) зависит от значения регистров TIMSK и флага I в Status регистре процессора.
Для задания реакции на события TC1 в регистре TIMSK служат четыре бита:

Бит 2 — TOIE1 — Когда этот бит установлен в 1 и разрешены прерывания, процессор реагирует на сигнал переполнения ТС1 и вызывает прерывание по вектору $010 (OVF1addr).
Бит 3 — OCIE1B — Когда этот бит установлен в 1 и разрешены прерывания, процессор реагирует вызовом прерывания по вектору $00E (OC1Baddr) на событие совпадения счёта с константой в регистре OCR1B. Бит 4 — OCIE1A — Когда этот бит установлен в 1 и разрешены прерывания, процессор реагирует вызовом прерывания по вектору $00C (OC1Aaddr) на событие совпадения счёта с константой в регистре OCR1A. Бит 5 — TICIE1 — Если установлен этот бит и разрешены прерывания, разрешено прерывание захвата ТС1, расположенного по вектору $00A (ICP1addr).

Ассемблер для микроконтроллера с нуля. Часть 5. Периферия МК. » Журнал практической электроники Датагор

Сегодня мы рассмотрим работу следующих модулей периферии:
• порта ввода-вывода,
• таймера
• аналого-цифрового преобразователя,
• PWM для nRF52832
и, обобщив все полученные знания, реализуем проект «lightControl» для управления состоянием двух светодиодов.

Содержание / Contents

Камрад, рассмотри датагорские рекомендации

🌼 Полезные и проверенные железяки, можно брать

Опробовано в лаборатории редакции или читателями.

Трансформатор R-core 30Ватт 2 x 6V 9V 12V 15V 18V 24V 30V

Паяльная станция 80W SUGON T26, жала и ручки JBC!

Отличная прочная сумочка для инструмента и мелочей

Хороший кабель Display Port для монитора, DP1. 4

Конденсаторы WIMA MKP2 полипропилен

Трансформатор-тор 30 Ватт, 12V 15V 18V 24V 28V 30V 36V

SN-390 Держатель для удобной пайки печатных плат

Панельки для электронных ламп 8 пин, керамика

Как уже говорилось в 

первой части нашей эпопеи

, в плоскости общения МК с внешним миром назначение его периферии состоит в следующем:

а) Порт ввода-вывода позволяет вывести на тот или иной вывод МК или считать с него цифровые данные (логические 1 или 0).
Это может быть обособленная работа одного вывода, в частности с целью:
• включить/выключить светодиод или реле,
• определить состояние кнопки,

Либо — согласованное взаимодействие группы выводов, к примеру для:
• управления шаговым двигателем,
• чтения комбинации состояний ножек энкодера,
• реализации программного варианта одного из протоколов связи (I2c, SPI, UART).

б) Посредством АЦП можно измерить и преобразовать в численное значение уровень аналогового сигнала на соответствующем выводе МК.

в) Таймер обеспечивает своевременность чтения/записи информации с точностью до одного периода его тактового импульса. Дополнительно, таймеры ATtiny85, ATmega8 и STM32F401 можно использовать для генерации аппаратного ШИМ-сигнала. Аналогичная функция для nRF52832 реализуется посредством модуля PWM.

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

Для проверки примеров кода осуществим следующие подготовительные действия:
1. Соберём схему, представленную на Рисунке 1.

Рисунок 1. Схема устройства проекта lightControl.

Распиновка устройства в зависимости от выбранного вами МК приведена на Рисунке 2.

Рисунок 2. Распиновка устройства проекта lightControl.

2. Создадим папку нового проекта lightControl, куда скопируем шаблонные файлы. Напомню, что актуальный вариант шаблонных хидер-файлов можно скачать из архива в четвёртой части статьи.

3. В папке lightControl создадим парные файлы led.h/led.S, timer.h/timer.S, adc./adc.S и, только для nRF52832, pwm.h/pwm.S. Во вспомогательные хидер-файлы (led.h, timer.h и т. д.) мы будем выносить не системные макроопределения и иную информацию по соответствующему модулю. На начальном этапе использование таких файлов может показаться избыточным, но, поверьте мне, в конечном итоге сэкономит вам немало времени и нервов.

4. Внесём в Makefile следующие изменения и дополнения:
ATtiny85

all:
  avr-as main.S -mmcu=attiny85 -o main.o
  avr-as led.S -mmcu=attiny85 -o led.o
  avr-as timer.S -mmcu=attiny85 -o timer.o
  avr-as adc.S -mmcu=attiny85 -o adc.o
  avr-ld main.o led.o adc.o timer.o -T LinkerScript.ld  -o lightControl.elf
  avr-objcopy lightControl.elf -j .text -j .data -O ihex lightControl.hex
  avrdude -p attiny85 -c usbasp -P usb -e -U flash:w:lightControl. hex:i

ATmega8

all:
  avr-as main.S -mmcu=atmega8 -o main.o
  avr-as led.S -mmcu=atmega8 -o led.o
  avr-as timer.S -mmcu=atmega8 -o timer.o
  avr-as adc.S -mmcu=atmega8 -o adc.o	
  avr-ld main.o led.o timer.o adc.o -T LinkerScript.ld  -o lightControl.elf
  avr-objcopy lightControl.elf -j .text -j .data -O ihex lightControl.hex
  avrdude -p atmega8 -c usbasp -P usb -e -U flash:w:lightControl.hex:i

STM32F401

all:		
  arm-none-eabi-as main.S -mcpu=cortex-m4 -o main.o
  arm-none-eabi-as led.S -mcpu=cortex-m4 -o led.o
  arm-none-eabi-as timer.S -mcpu=cortex-m4 -o timer.o
  arm-none-eabi-as adc.S -mcpu=cortex-m4 -o adc.o
  arm-none-eabi-ld main.o led.o timer.o adc.o -T LinkerScript.ld -o lightControl.elf
  arm-none-eabi-objcopy lightControl.elf -S -O ihex lightControl.hex
  openocd -f interface/stlink.cfg -f target/stm32f4x.cfg -c "init" -c "reset init" -c "flash write_image erase lightControl. hex" -c "reset" -c "exit"

nRF52832

all:		
  arm-none-eabi-as main.S -mcpu=cortex-m4 -o main.o
  arm-none-eabi-as led.S -mcpu=cortex-m4 -o led.o
  arm-none-eabi-as timer.S -mcpu=cortex-m4 -o timer.o
  arm-none-eabi-as adc.S -mcpu=cortex-m4 -o adc.o
  arm-none-eabi-as pwm.S -mcpu=cortex-m4 -o pwm.o
  arm-none-eabi-ld main.o led.o timer.o adc.o pwm.o -T LinkerScript.ld -o lightControl.elf
  arm-none-eabi-objcopy lightControl.elf -S -O ihex lightControl.hex
  openocd -f interface/stlink.cfg -f target/nrf52.cfg -c "init" -c "reset init" -c "flash write_image erase lightControl.hex" -c "reset" -c "exit"

Как видите, мы включили в процесс компиляции созданные нами ассемблер-файлы led, adc, timer и pwm (для nRF52832). Не смотря на то, что пока указанные файлы — пустые, GCC будет успешно компилировать их в объектные файлы, а затем собирать последние в единый elf-файл.

Кроме того, в Makefile изменены имена elf и hex файлов с main на lightControl. Делать это не обязательно, однако, если все отладочные и прошивочные файлы ваших проектов будут иметь одно и то же имя main, то в конце концов вы запутаетесь в них.

Поскольку ATtiny85 и ATmega8 располагают разным составом периферии, выберем те из модулей, функционал которых, а также названия регистров и их битов схожи, что позволит объединить объяснения для обоих МК:
• порт ввода-вывода B,
• 8-битный таймер TIMER0 для ATtiny85 и 16-битный таймер TIMER1 для ATmega8,
• канал ADC2 аналого-цифрового преобразователя.

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

Описание работы портов ввода-вывода ATtiny85 и ATmega8 вы найдёте в Разделе «I/O Ports» (страница даташита 53 и 51, соответственно), информацию же о назначении регистров и их битов — в пункте «Register Description» того же раздела, а также в Таблице «Register Summary».

ATtiny85 в силу своих размеров располагает всего одним портом (порт B), в то время как для ATmega8 их предусмотрено три — порты B, C и D.

За работу порта отвечают три регистра: DDRx, PORTx и PINx, где х — B, C или D.
На Рисунке 3 приведены несколько изменённые и объединённые вместе выдержки из Таблиц «Register Summary» для порта В обоих МК.

Рисунок 3. Адреса и имена битов регистров порта ввода-вывода B МК ATtiny85 и ATmega8.

Обратите внимание, что:
1. Имена битов (PORTBn, DDBn и PINBn) заменены на однообразное РBn, где n — номер бита. Такая замена ничего не меняет в плане корректной записи в регистры и чтения из них, зато наглядно увязывает новые имена с распиновкой МК и втрое уменьшает количество макроопределений номеров битов.

2. Поскольку регистры обоих МК сведены в одну таблиц, информация по битам 6—7 действительна только для ATmega8.

Прежде, чем перейти к назначению регистров и битов порта, добавим макроопределения их имён и абсолютных адресов в шаблонный хидер-файл (attiny85.h или atmega8.h).

/* Адреса и номера битов регистров порта В */
PINB = 0×36
DDRB = 0×37
PORTB = 0×38
  PB7 = 7 /* только для ATmega8 */
  PB6 = 6 /* только для ATmega8 */
  PB5 = 5
  PB4 = 4
  PB3 = 3
  PB2 = 2
  PB1 = 1
  PB0 = 0

Теперь о том, как всё это работает.
а) Регистр DDRB определяет направление работы связанных с его битами выводов МК: если значение бита равно 1, то соответствующий вывод МК будет настроен как выход, в противном случае — как вход.

б) Регистр PORTB имеет двойное назначение:
• если вывод МК настроен как выход, то запись 1 или 0 в связанный с ним бит регистра PORTB обусловит логические 1 или 0, соответственно, на этом выводе.
• если вывод МК настроен как вход, то запись 1 в связанный с ним бит регистра PORTB повлечёт за собой подтяжку вывода к питанию через внутренний резистор сопротивлением около 50 кОм, а 0 — придаст входу состояние Hi-Z с высоким внутренним сопротивлением.

в) Регистр PINB хранит текущее состояние пинов РВn.

Ниже приведён пример кода настройки и записи/чтения для пина РВ0 (вывод 5 и 14 для ATtiny85 и ATmega8, соответственно) с применением макросов из macro.h.

/* Настроить пин PB0 как выход и
   установить в состояние 1 */
setBit DDRB, PB0
setBit PORTB, PB0
/* Настроить пин PB0 как выход и
   установить в состояние 0 */
setBit DDRB, PB0
clearBit PORTB, PB0
/* Настроить пин PB0 как выход и установить
   в состояние, противоположное от текущего */
setBit DDRB, PB0
LDS r16, PORTB
LDI r17, (1 ≪ PB0)
EOR r16, r17
STS PORTB, r16
/* Настроить пин PB0 как вход с подтяжкой к питанию.
   Считать PINB в r16 и проверить состояние PB0.
   Если состояние — низкое, перейти к метке label */
clearBit DDRB, PB0
setBit PORTB, PB0
LDS r16, PINB
ANDI r16, (1 ≪ PB0)
CPI r16, (1 ≪ PB0)
BRNE label
/* Настроить пин PB0 как вход Hi-Z. 
   Считать PINB в r16 и проверить состояние PB0.
   Если состояние — высокое, перейти к метке label */
clearBit DDRB, PB0
clearBit PORTB, PB0
LDS r16, PINB
ANDI r16, (1 ≪ PB0)
CPI r16, (1 ≪ PB0)
BREQ label

Пропишем в хидер-файле led.h всю вспомогательную информацию для настройки выводов управления обоими светодиодами из Рисунка 1, а также для включения, выключения и переключения в противоположное состояние жёлтого светодиода.

ATtiny85

.include "attiny85.h"
.include "macro.h"
YELLOW_LED_DDR 	= DDRB
YELLOW_LED_PORT = PORTB
YELLOW_LED      = PB2
GREEN_LED_DDR 	= DDRB
GREEN_LED_PORT  = PORTB
GREEN_LED       = PB0
/* Глобальные функции */
.global ledsInit, yellowLedOn, yellowLedOff, yellowLedToggle

ATmega8

.include "atmega8.h"
.include "macro.h"
YELLOW_LED_DDR 	= DDRB
YELLOW_LED_PORT = PORTB
YELLOW_LED      = PB0
GREEN_LED_DDR 	= DDRB
GREEN_LED_PORT 	= PORTB
GREEN_LED       = PB1
/* Глобальные функции */
. global ledsInit, yellowLedOn, yellowLedOff, yellowLedToggle

Как видите:
а) Директивы .include включения шаблонных хидер-файлов и файла macro.h перенесены в led.h, что способствует уменьшению объёма текста в ассемблер-файле.

б) Для адресов и номеров битов регистров порта B применены двойные макроопределения. Такой подход позволяет, при необходимости, менять выводы управления светодиодами, не затрагивая код в файле led.S.

в) Объявления функций ledsInit, yellowLedOn, yellowLedOff и yellowLedToggle глобальными также вынесены в led.h и оформлены все вместе через запятую одной директивой .global, что разрешено правилами GCC.

Оформим в файле led.S вышеупомянутые функции.

.include "led.h"
  .text
    ledsInit:
      /* Настроить выводы упраления светодиодами как выходы */
      setBit   YELLOW_LED_DDR, YELLOW_LED
      setBit   GREEN_LED_DDR, GREEN_LED
      /* Возврат из функции */
      RET
    yellowLedOn:
      /* Включить жёлтый светодиод */
      setBit   YELLOW_LED_PORT, YELLOW_LED
      /* Возврат из функции */
      RET
    yellowLedOff:
      /* Выключить жёлтый светодиод */
      clearBit YELLOW_LED_PORT, YELLOW_LED
      /* Возврат из функции */
      RET
    yellowLedToggle:
      /* Изменить состояние жёлтого светодиода на противоположное */
      LDS      r18, YELLOW_LED_PORT
      LDI      r19, (1 << YELLOW_LED)
      EOR      r18, r19
      STS      YELLOW_LED_PORT, r18
      /* Возврат из функции */
      RET
. end

А затем в main.S — программу, которая настроит оба вывода управления светодиодами и заставит мигать жёлтый светодиод.

.include  "attiny85.h" /* или "atmega8.h" */
.include "macro.h"
  .data
  .text
    .org   Reset_vector
      RJMP  main			
    .global  main
    main:
      /* Указать на вершину стека */
      stackPointerInit
      /* Настроить выводы упраления светодиодами */
      RCALL  ledsInit				
    main_loop:
      RCALL  yellowLedToggle
      RJMP   main_loop
.end

Вспомним из предыдущей части статьи, что дефолтная частота FCPU тактирования процессора для обоих МК составляет 1 МГц и, ориентируясь на столбец «Cloks» Таблицы «Instruction Set Summary» даташита, прикинем период сигнала на выводе управления жёлтым светодиодом.

В цикле main_loop основной функции присутствуют две инструкции — RCALL (3 тика) и RJMP (2 тика).
Функция yellowLedToggle состоит из следующих инструкций:
LDS — 2 тика
LDI — 1 тик
EOR — 1 тик
STS — 2 тика
RET — 4 тика
Итого 15 тиков.

Время одного тика — 1 / 1 МГц = 1 мкс, следовательно полу-период сигнала будет составлять 15 тиков х 1 мкс = 15 мкс, а период — 30 мкс.

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

Увеличим период сигнала, подключив к проекту таймер.

Подробности настройки и функционирования таймера TIMERn (сейчас и впредь, n = 0 и 1 для таймера ATtiny85 и ATmega8, соответственно) изложены на странице 65 и 75 даташита для ATtiny85 и ATmega8, соответственно.

Львиная доля обращений при настройке и эксплуатации таймера приходится на 7 регистров:
а) Два регистра контроля TCCRnA и TCCRnB.
б) Два регистра сравнения OCRnA и OCRnB.
в) Счётчик TCNTn.
г) Регистр флагов TIFR.
д) Регистр контроля за прерываниями TIMSK.

Добавим в шаблонные хидер-файлы макроопределения:
• указанных регистров и тех их битов, которые будут упоминаться либо использоваться в примерах ниже (оставшиеся вам придётся вносить самостоятельно, по мере необходимости),
• векторов прерываний таймера TIMERn по переполнению и сравнению с регистром OCRnA.

attiny85.h

/* Таблица векторов */
Reset_vector = 0×00
INT0_vector = 0×01
TIMER0_OVF_vector = 0×05
TIMER0_COMPA_vector = 0×0A
ADC_vector = 0×08

/* Адреса и номера битов регистров таймера TIMER0 */
OCR0B = 0×48
OCR0A = 0×49
TCCR0A = 0×4A
  COM0A1 = 7
  COM0A0 = 6
  COM0B1 = 5
  COM0B0 = 4
  WGM01 = 1
  WGM00 = 0
TCNT0 = 0×52
TCCR0B = 0×53
  CS02 = 2
  CS01 = 1
  CS00 = 0
TIFR = 0×58
  OCF0A = 4
  OCF0B = 3
  TOV0 = 1
TIMSK = 0×59
  OCIE0A = 4
  OCIE0B = 3
  TOIE0 = 1

atmega8. h

/* Таблица векторов */
Reset_vector = 0×00
TIMER1_COMPA_vector = 0×06
TIMER1_OVF_vector = 0×08
TIMER0_OVF_vector = 0×09
ADC_vector = 0×0E

/* Адреса и номера битов регистров таймера TIMER1 */
OCR1B_L = 0×48
OCR1B_H = 0×49
OCR1A_L = 0×4A
OCR1A_H = 0×4B
TCNT1_L = 0×4C
TCNT1_H = 0×4D
TCCR1B = 0×4E
  WGM13 = 4
  WGM12 = 3
  CS12 = 2
  CS11 = 1
  CS10 = 0
TCCR1A = 0×4F
  COM1A1 = 7
  COM1A0 = 6
  COM1B1 = 5
  COM1B0 = 4
  WGM11 = 1
  WGM10 = 0
TIFR = 0×58
  OCF1A = 4
  OCF1B = 3
  TOV1 = 2
TIMSK = 0×59
  OCIE1A = 4
  OCIE1B = 3
  TOIE1 = 2

В первую очередь разберёмся с механизмом тактирования таймера и преобразования FCPU в FPERIPHERAL (в данном случае — в FTIMER). Реализуется он посредством битов CS регистра TCCRnB. Выдержка из Таблицы «Clock Select Bit Description» (страница 80 и 99 даташита для ATtiny85 и ATmega8, соответственно), поясняющая связь между комбинацией указанных битов и значением FTIMER, приведена на Рисунке 4, где clkI/O — и есть FCPU,

Рисунок 4. Выбор делителя таймера TIMERn через биты CS регистра TCCRnB МК ATtiny85 и ATmega8.

Из Рисунка 4 следует, что, пока значение всех битов CS равно нулю (дефолтное значение), тактирование таймера отключено, т. е. последний фактически не работает. Любая другая комбинация указанных битов включает тактирование с частотой FCPU, делённой на соответствующее значение.

Помимо приведённых на Рисунке 4, для обоих МК возможны также комбинации 110 и 111 битов CS для случая, когда таймер тактируется от собственного внешнего источника, подключённого к пину Tn (выводы 7 и 11 для ATtiny85 и ATmega8, соответственно). Данный вариант тактирования таймера выходит за рамки вопросов, рассматриваемых в настоящей статье, однако вы, уверен, сможете самостоятельно разобраться с ним, учитывая что соответствующей информации в сети — предостаточно.

Таймер TIMERn обоих МК поддерживает несколько режимов работы, выбор между которыми осуществляется посредством битов WGM регистров TCCRnА и TCCRnB. Полный набор режимов таймера можно увидеть в Таблице «Waveform Generation Mode Bit Description» на странице 79 и 97 даташита для ATtiny85 и ATmega8, соответственно. Мы же рассмотрим три из них, представленные на Рисунке 5.

Рисунок 5. Отдельные режимы работы таймера TIMERn МК ATtiny85 и ATmega8.

Значения BOTTOM из таблиц на Рисунке 5 для всех режимов таймера обоих МК равны 0, а TOP и MAX — будут приводиться ниже, при рассмотрении каждого режима работы.

Все биты WGM регистров TCCRnА и TCCRnB равны 0. Данные значения являются дефолтными, т. е. по сбросу/подаче питания таймеры обоих МК и без дополнительных настроек находятся в режиме Normal.

TOP = MAX = 255 и 65535 для ATtiny85 и ATmega8, соответственно.

Счётчик TCNTn таймера в режиме Normal увеличивает своё значение на 1 с каждым поступающим тактовым импульсом и по достижению TOP = MAX, т. е. переполнению, сбрасывается в ноль.
При этом:
а) Будет сгенерировано прерывание таймера по переполнению (timer overflow interrupt), если оно разрешено глобально (через бит I регистра SREG) и локально (посредством бита TOIEn регистра TIMSK).

б) Установится в 1 бит TOVn регистра TIFR, который сбрасывается обратно в 0 либо записью из программы в него единицы, либо автоматически при исполнении обработчика прерывания переполнения.

Режим Normal применяется для отсчёта временных интервалов, равных периоду переполнения счётчика TCNTn, путём отслеживания состояния TOVn, причём делать это можно двумя способами: проверкой в main_loop и через соответствующее прерывание таймера.
Реализуем второй вариант, предварительно посчитав подходящее значение делителя таймера.

Как было сказано выше, дефолтный период одного тика тактового сигнала процессора обоих МК составляет 1 мкс.
Выберем для 8-битного таймера TIMER0 МК ATtiny85 комбинацию 101 битов CS, что даст деление FCPU на 1024. Следовательно на переполнение счётчика таймера потребуется 1 мкс х 1024×255 = 261.12 мс.
Счётчик таймера TIMER1 МК ATmega8 — 16-битный, т. е. переполняется, досчитав до 65535, поэтому для него достаточно значение делителя 8 (комбинация 010 битов CS), который обусловит время переполнения в 1 мкс х 8×65535 = 524.28 мс.

Оформим в файлах timer код работы TIMERn.

ATtiny85
timer.h

.include "attiny85.h"
.include "macro.h"

.global TIMER0_OVF_Handler, timerInit

timer.S

.include "timer.h"
  .text
    TIMER0_OVF_Handler:
      /* Переключить жёлтый светодиод в противоположное состояние */
      RCALL  yellowLedToggle
      /* Возврат из обработчика */		
      RETI

    timerInit:
      /* Разрешить прерывание по переполнению */
      setBit  TIMSK, TOIE0
      /* Включить тактирование с делителем 1024 */
      setBit  TCCR0B, CS00
      setBit  TCCR0B, CS02
      /* Возврат из функции */
      RET
. end

ATmega8
timer.h

.include "atmega8.h"
.include "macro.h"
.global TIMER1_OVF_Handler, timerInit

timer.S

.include "timer.h"
  .text
    TIMER1_OVF_Handler:
      /* Переключить жёлтый светодиод в противоположное состояние */
      RCALL  yellowLedToggle			
      /* Возврат из обработчика */
      RETI

    timerInit:
      /* Разрешить прерывание по переполнению */
      setBit  TIMSK, TOIE1
      /* Включить тактирование с делителем 8 */
      setBit  TCCR1B, CS11		
      /* Возврат из функции */
      RET
.end

А затем перепишем вышеприведённый код в main.S следующим образом:

ATtiny85

.include  "attiny85.h"
.include "macro.h"
  .data

  .text	
    .org Reset_vector
      RJMP   main			
    .org TIMER0_OVF_vector * 2
      RJMP   TIMER0_OVF_Handler
				
    . global main		
    main:
      /* Указать на вершину стека */
      stackPointerInit
      /* Настроить выводы управления светодиодами */
      RCALL  ledsInit
      /* Настроить таймер */
      RCALL  timerInit
      /* Разрешить прерывания глобально */
      SEI
    main_loop:
      /* Перейти к метке main_loop */
      RJMP   main_loop
.end

ATmega8

.include "atmega8.h"
.include "macro.h"
  .data

  .text	
    .org Reset_vector
      RJMP   main			
    .org TIMER1_OVF_vector * 2
      RJMP   TIMER1_OVF_Handler
				
    .global main
    main:
      /* Указать на вершину стека */
      stackPointerInit
      /* Настроить выводы управления светодиодами */
      RCALL  ledsInit
      /* Настроить таймер */
      RCALL  timerInit
      /* Разрешить прерывания глобально */
      SEI				
      main_loop:					
      /* Перейти к метке main_loop */			
      RJMP   main_loop
. end

Теперь жёлтый светодиод будет мигать с периодом чуть больше 261.12 мс х 2 = 522.4 мс для ATtiny85 и 524.28 мс х 2 = 1048.56 мс для ATmega8, соответственно.
Чтобы посчитать точное время, вам придётся учесть время исполнения инструкций, как мы это делали выше. Однако, поскольку исполняются они процессором с частотой 1 МГц, общий период сигнала увеличится не намного.

Комбинация битов WGM регистров TCCRnА и TCCRnB:
• 010 для ATtiny85
• 0100 для ATmega8.

TOP = OCRnA.
MAX = 255 и 65535 для ATtiny85 и ATmega8, соответственно.

В режиме CTC счётчик TCNTn таймера, увеличивая на 1 своё значение с каждым тактовым импульсом, сбрасывается в ноль по достижению TOP (т.е. числа, записанного в регистр OCRnA), вследствие чего:
а) Генерируется прерывание таймера по сравнению (timer compare match interrupt), если установлены в 1 бит I регистра SREG и бит OCIEnA регистра TIMSK.

б) Устанавливается в 1 бит OCFnA регистра флагов TIFR, сбросить в 0 который можно записью в него единицы из программы или автоматически исполнением обработчика прерывания.

Так же, как и Normal, режим СTC можно использовать для отсчёта времени, записывая подходящее число в регистр OCRnA. Тот факт, что значение этого числа можно менять в диапазоне от 0 до максимального значения счётчика TCNTn, предопределяет преимущество режима CTC над Normal, заключающееся в возможности регулировать период счёта с точностью до периода 1 тика тактового сигнала таймера.

Кроме отсчёта времени, через комбинацию 01 битов COM регистра TCCRnА (вариант «Toggle OCnA/OCnB on Compare Match» в Таблице «Compare Output Mode, non-PWM» на странице 78 и 96 даташита для ATtiny85 и ATmega8, соответственно) вы можете реализовать аппаратное изменение состояние пина ОСnА (выводы PB0 и РВ1 для ATtiny85 и ATmega8, соответственно), предварительно настроенного как выход, при каждом обнулении счётчика таймера. Скважность полученного таким образом периодического сигнала на выводе ОСnА не регулируется и всегда будет равна 0.5, не зависимо от частоты сигнала. Полупериод сигнала вычисляется как произведение значения OCRnA на период тактового импульса таймера.

Ниже приведён пример, демонстрирующий оба варианта использования режима СТС таймера, когда:
• Состояние вывода управления жёлтым светодиодом меняется через прерывание по сравнению.
• Состояние вывода управления зелёным светодиодом меняется аппаратно, через настройку битов COM регистра TCCRnА.

ATtiny85
timer.h

.include "attiny85.h"
.include "macro.h"

OCR0A_VALUE = 200

.global	TIMER0_COMPA_Handler, timerInit

timer.S

.include "timer.h"
  .text
    TIMER0_COMPA_Handler:
      /* Переключить жёлтый светодиод в противоположное состояние */
      RCALL   yellowLedToggle
      /* Возврат из обработчика */		
      RETI

    timerInit:
      /* Включить режим CTC */
      setBit  TCCR0A, WGM01
      /* Разрешить сигнал на пине OC0A, т. е. на
         выводе управления зелёным светодиодом */
      setBit  TCCR0A, COM0A0
      /* Записать в OCR0A значение сравнения */
      LDI     r16, OCR0A_VALUE
      STS     OCR0A, r16
      /* Разрешить прерывание по сравнению с OCR1A */
      setBit  TIMSK, OCIE0A
      /* Включить тактирование с делителем 1024 */
      setBit  TCCR0B, CS00
      setBit  TCCR0B, CS02
      /* Возврат из функции */
      RET
.end

main.S

.include "attiny85.h"
.include "macro.h"
  .data

  .text
    .org   Reset_vector
      RJMP   main			
    .org TIMER0_COMPA_vector * 2
      RJMP   TIMER0_COMPA_Handler
				
    .global main		
    main:
      /* Указать на вершину стека */
      stackPointerInit
      /* Настроить выводы управления светодиодами */
      RCALL  ledsInit
      /* Настроить таймер */
      RCALL  timerInit
      /* Разрешить прерывания глобально */
      SEI
    main_loop:
      /* Перейти к метке main_loop */
      RJMP   main_loop
. end

Учитывая, что в регистр OCR0A записывается число 200, после загрузки программы в МК вы должны получить на выводе OCnA (PB0) управления зелёным светодиодом сигнал со скважностью 0.5 и полупериодом 200×1024×1мкс = 204800 мкс.
Полупериод сигнала на выводе PB2 управления жёлтым светодиодом будет больше на время исполнения с частотой FCPU инструкций обработчика прерывания.

ATmega8
timer.h

.include "atmega8.h"
.include "macro.h"

OCR1A_VALUE = 50000

.global	TIMER1_COMPA_Handler, timerInit

timer.S

.include "timer.h"
  .text
    TIMER1_COMPA_Handler:
      /* Переключить жёлтый светодиод в противоположное состояние */
      RCALL   yellowLedToggle
      /* Возврат из обработчика */
      RETI

    timerInit:
      /* Включить режим CTC */
      setBit  TCCR1B, WGM12
      /* Разрешить сигнал на пине OC1A, т. е.
         на выводе управления зелёным светодиодом */
      setBit  TCCR1A, COM1A0
      /* Записать в OCR1A значение сравнения */
      LDI     r17, hi8(OCR1A_VALUE)
      LDI     r16, lo8(OCR1A_VALUE)
      STS     OCR1AH, r17
      STS     OCR1AL, r16	
      /* Разрешить прерывание по сравнению с OCR1A */
      setBit  TIMSK, OCIE1A					
      /* Включить тактирование с делителем 8 */
      setBit  TCCR1B, CS11
      /* Возврат из функции */
      RET
.end

Обратите внимание на порядок записи числа в регистр OCR1A: сначала производится запись в старший байт регистра, а затем — в младший. Чтение из этих регистров осуществляется в обратном порядке: в первую очередь считывается младший байт и вслед за ним — старший.
Дело в том, что регистры TCNT1, OCR1A/B и ICR1 таймера TIMER1 МК Atmega8 — шестнадцати-битные. Поскольку восьми бит РОН не хватает для обращения к указанным регистрам за один раз, производитель определил строгий порядок доступа к ним, изложенный в Разделе «Accessing 16-bit Registers» на странице 77 даташита.

main.S

.include  "atmega8.h"
.include  "macro.h"
  .data

  .text	
    .org Reset_vector
      RJMP   main			
    .org TIMER1_COMPA_vector * 2
      RJMP   TIMER1_COMPA_Handler

    .global main
    main:
      /* Указать на вершину стека */
      stackPointerInit
      /* Настроить выводы управления светодиодами */
      RCALL  ledsInit
      /* Настроить таймер */
      RCALL  timerInit
      /* Разрешить прерывания глобально */
      SEI				
    main_loop:					
      /* Перейти к метке main_loop */			
      RJMP   main_loop
.end

В случае с ATmega8 регистр OCR1A — двух-байтный, что позволяет записать в него число 50000. С учётом значения 8 делителя тактовой частоты таймера полу-период сигнала на выводе OC1A (PB1) управления зелёным светодиодом составит 1 мкс х 8×50000 = 400 мс.
Полу-период сигнала на выводе PB0 управления жёлтым светодиодом будет, как и в случае с ATtiny85, чуть больше: на время, необходимое для исполнения инструкций обработчика прерывания.

Следует отметить, что для ATmega8 предусмотрен ещё один вариант режима СТС, когда сравниваются значения регистров TCNTn и ICR1. Данная модификация режима используется, в частности, для подсчёта количества изменений состояния пина ICP1 (вывод 14 МК). Настройка и эксплуатация обоих модификаций настолько похожи, что вам не сложно будет реализовать при необходимости вторую, ориентируясь на пример выше.

Оба МК поддерживают несколько модификаций режима Fast PWM, из которых мы рассмотрим по одной для каждого микроконтроллера, выбираемой посредством комбинации битов WGM регистров TCCRnА и TCCRnB:
• 011 для ATtiny85.
• 0101 для ATmega8.

При выборе вышеуказанной комбинации битов WGM таймер TIMER1 ATmega8 как-бы «урезается» до размеров 8-битного таймера так, что MAX для обоих микроконтроллеров равен 255.
TOP определяется числом, записанным в OCRnA, и может принимать значения от 0 до 255.

Из названия режима понятно, что он позволяет реализовать полноценный ШИМ-сигнал с регулируемой скважностью на пинах OCnA/OCnB микроконтроллера. Осуществляется это посредством битов COM (смотрите Таблицу «Compare Output Mode, Fast PWM» на странице 78 и 96 даташита для ATtiny85 и ATmega8, соответственно). При этом, период ШИМ-сигнала задаётся значением MAX, а скважность — соотношением TOP/MAX.

В режиме Fast PWM по ходу работы счётчика TCNTn таймера происходит несколько событий.

Когда значение счётчика сравнивается со значением числа, записанного в OCRnA:
а) Генерируется прерывание таймера по сравнению, если установлены в 1 бит I регистра SREG и бит OCIEnA регистра TIMSK.
б) Устанавливается в 1 бит OCFnA регистра флагов TIFR.
в) Состояние пинов OCnA/OCnB меняется на противоположное, если разрешена генерация ШИМ-сигнала.

При этом счётчик продолжает считать до значения MAX, лишь по достижению которого сбрасывается в ноль, в связи с чем:
а) Будет сгенерировано прерывание таймера по переполнению, если оно разрешено глобально и локально.
б) Установится в 1 бит TOVn регистра TIFR.
в) Состояние пинов OCnA/OCnB меняется на противоположное, если разрешена генерация ШИМ-сигнала.

Выберем для обоих МК следующие настройки:
• Комбинация битов COM — 10, что разрешит не-инвертированный ШИМ-сигнал на выводе управления зелёным светодиодом.
• Делитель частоты тактирования — 8, обуславливающий период одного тика таймера в 8 мкс.
• Число, записываемое в OCRnA — 50.

Тогда, период ШИМ-сигнала составит 8мкс х 255 = 2040 мкс, время нахождения вывода OCnA в высоком состоянии — 8мкс х 50 = 400 мкс, а скважность — 50 / 255 = 0.196.

Теперь об управлении жёлтым светодиодом.
Чтобы период сигнала на пине YELLOW_LED не зависел от значения OCRnA, из двух возможных выберем прерывание по переполнению. Значение самого периода составит 8мкс х 255×2 = 4080 мкс, что опять делает сигнал невидимым для глаза. Поэтому, уменьшим частоту сигнала посредством одного из свободных РОН (например, r21), оформив обработчик прерывания следующим образом:
1. При каждом прерывании значение r21 увеличивается на единицу.
2. По достижению числа в r21 порогового значения (к примеру, 200), состояния вывода YELLOW_LED переключается на противоположное, а сам r21 — обнуляется.

Фактически, мы разделим частоту сигнала на 200, заставив мигать жёлтый светодиод с полу-периодом 8мкс х 255×200 = 408 мс.

ATtiny85
timer.h

.include "attiny85.h"
.include "macro.h"

OCR0A_VALUE = 50
COUNTER_MAX = 200

.global	TIMER0_OVF_Handler, timerInit

timer.S

.include "timer.h"
  .text
    TIMER0_OVF_Handler:
      /* Увеличить на 1 значение r21 и сравнить с COUNTER_MAX */
      INC     r21
      CPI     r21, COUNTER_MAX
      /* Если  меньше, перейти к метке return */
      BRNE    return
      /* В противном случае, обнулить r21 и переключить */
      /* жёлтый светодиод в противоположное состояние */
      CLR     r21
      RCALL   yellowLedToggle
     return:
      /* Возврат из обработчика */		
      RETI

    timerInit:
      /* Включить режим FastPWM */
      setBit  TCCR0A, WGM01
      setBit  TCCR0A, WGM00
      /* Разрешить ШИМ-сигнал на пине OC0A, т. е. на
         выводе управления зелёным светодиодом */
      setBit  TCCR0A, COM0A1
      /* Записать в OCR0A значение сравнения */
      LDI     r16, OCR0A_VALUE
      STS     OCR0A, r16
      /* Разрешить прерывание по переполнению */
      setBit  TIMSK, TOIE0
      /* Включить тактирование с делителем 8 */
      setBit  TCCR0B, CS01
      /* Возврат из функции */
      RET
.end

main.S

.include  "attiny85.h"
.include "macro.h"
  .data

  .text	
    .org Reset_vector
      RJMP   main
    .org TIMER0_OVF_vector * 2
      RJMP   TIMER0_OVF_Handler
				
    .global main
    main:
      /* Указать на вершину стека */
      stackPointerInit
      /* Настроить выводы управления светодиодами */
      RCALL  ledsInit
      /* Настроить таймер */
      RCALL  timerInit
      /* Разрешить прерывания глобально */
      SEI
    main_loop:
      /* Перейти к метке main_loop */
      RJMP   main_loop
. end

ATmega8
timer.h

.include "atmega8.h"
.include "macro.h"

OCR1A_VALUE = 50
COUNTER_MAX = 200

.global	TIMER1_OVF_Handler, timerInit

timer.S

.include "timer.h"
  .text
    TIMER1_OVF_Handler:
      /* Увеличить на 1 значение r21 и сравнить с COUNTER_MAX */
      INC     r21
      CPI     r21, COUNTER_MAX
      /* Если  меньше, перейти к метке return */
      BRNE    return
      /* В противном случае, обнулить r21 и переключить */
      /* жёлтый светодиод в противоположное состояние */
      CLR     r21
      RCALL   yellowLedToggle
     return:					
      /* Возврат из обработчика */
      RETI

    timerInit:
      /* Включить режим FastPWM, 8-bit */
      setBit  TCCR1B, WGM12
      setBit  TCCR1A, WGM10
      /* Разрешить ШИМ-сигнал на пине OC1A, т.е. на
         выводе управления зелёным светодиодом */
      setBit  TCCR1A, COM1A1
      /* Записать в OCR1A значение сравнения */
      LDI     r17, hi8(OCR1A_VALUE)
      LDI     r16, lo8(OCR1A_VALUE)
      STS     OCR1AH, r17
      STS     OCR1AL, r16
      /* Разрешить прерывание по переполнению */
      setBit  TIMSK, TOIE1		
      /* Включить тактирование с делителем 8 */
      setBit  TCCR1B, CS11
      /* Возврат из функции */
      RET
. end

Как видите, не смотря на то, что число 50, меньше, чем 255, запись производится, согласно упоминавшегося выше Раздела «Accessing 16-bit Registers», в оба байта регистра OCR1A: 0 — в OCR1AH и 50 — в OCR1AL.

main.S

.include  "atmega8.h"
.include  "macro.h"
  .data

  .text
    .org Reset_vector
      RJMP   main
    .org TIMER1_OVF_vector * 2
      RJMP   TIMER1_OVF_Handler

    .global main
    main:
      /* Указать на вершину стека */
      stackPointerInit
      /* Настроить выводы управления светодиодами */
      RCALL  ledsInit
      /* Настроить таймер */
      RCALL  timerInit
      /* Разрешить прерывания глобально */
      SEI				
    main_loop:					
      /* Перейти к метке main_loop */			
      RJMP   main_loop
.end

В подобной реализации проект имеет кое-какие недостатки:

• Во-первых, частота ШИМ-сигнала слишком высока, чтобы можно было наблюдать его, не вооружившись осциллографом.

• Во-вторых, чтобы изменить скважность сигнала, придётся в timer.h каждый раз прописывать новое значение OCRnA_VALUE, а затем компилировать программу и загружать её в МК.

Исправим ситуацию, приведя скважность ШИМ-сигнала к зависимости от уровня аналогового напряжение на пине ADC2.

Оба рассматриваемые нами МК располагают 10-разрядным аналого-цифровым преобразователем, нюансы настройки и функционирования которого можно узнать из Раздела «Analog to Digital Converter» на странице 122 и 189 даташита, соответственно.

Следует особо отметить, что питание аналоговой части ATmega8 — отдельное, поэтому необходимо соединить между собой оба вывода GND (8 и 22), а также выводы VCC и AVCC (7 и 20).
Для ATtiny85 питание аналоговой и цифровой частей МК — единое.

АЦП ATtiny85 имеет 4 канала ADC0 — ADC3 для измерения уровня аналогового напряжения на выводах 1 — 3 и 7 МК, в то время как для ATmega8 их число составляет 6 — с ADC0 по ADC5 (выводы 23 — 28 МК).

Для нашего проекта достаточно использования трёх регистров АЦП: двух регистров настройки (ADCSRA и ADMUX) и регистра данных ADC, макроопределения адресов и номеров битов которых следует внести в шаблонный хидер-файл.

attiny85.h/atmega8.h

/* Адреса и номера битов регистров модуля АЦП */
ADCL = 0×24
ADCH = 0×25
ADCSRA = 0×26
  ADEN = 7
  ADSC = 6
  ADATE = 5 /* ADFR — для ATmega8 */
  ADIF = 4
  ADIE = 3
  ADPS2 = 2
  ADPS1 = 1
  ADPS0 = 0
ADMUX = 0×27
  ADLAR = 5
  MUX1 = 1

Объявим во вспомогательном хидер-файле adc.h глобальными:
• обработчик прерывания ADC_Handler,
• функцию adcInit инициализации АЦП,
• функцию adcStartConversion однократного измерения уровня аналогового напряжения.

adc.h

.include "attiny85.h" /* или "atmega8.h" */
.include "macro.h"

.global	ADC_Handler, adcInit, adcStartConversion

И оформим их в adc. S.

В обработчике прерывания необходимо лишь копировать содержимое регистра данных ADC аналого-цифрового преобразователя в регистр сравнения OCRnA таймера.

Запуск однократного измерения в осуществляется установкой в 1 бита ADSC регистра ADCSRA.

Теперь о настройке АЦП.
Для начала выберем в качестве рабочего канал ADC2, что согласно Таблицы «Input Channel Selections» (страница 135 и 199 даташита для ATtiny85 и ATmega8, соответственно) реализуется комбинацией 0010 битов MUX регистра ADMUX, т. е. установкой в 1 бита MUX1.

Кроме того, установим в 1 бит ADLAR того же регистра, что обусловит выравнивание 10-битного результата измерения по левому краю регистра данных ADC: 8 старших битов результата будут размещаться в ADCH, а 2 младших — в ADCL.

Подобный выбор продиктован следующими соображениями.
Как вы помните, регистр OCR0A таймера ATtiny85 — 8-битный.
Для таймера ATmega8 мы выбрали 8-битный режим Fast PWM, поэтому в генерации ШИМ-сигнала принимает участие лишь младший байт OCR1AL регистра OCR1A.
Учитывая изложенное, необходимо привести 10-битный результат измерения АЦП в соответствие с восемью битами регистра OCRnA таймера. Можно сделать это с помощью пропорций. Однако, есть более простой путь.
Дело в том, что два младших бита результата измерения АЦП — наименее значащие. Более того, показания реального АЦП обычно скачут, т. е. значения младших разрядов — не стабильные, поэтому мы можем не учитывать их, используя только старшие 8 битов результата.
Таким образом, выровняв результат измерения по левому краю, мы будем использовать лишь значение старшего байта ADCH регистра ADC.

Далее нужно определить источник опорного напряжения посредством комбинации битов REFS того же регистра ADMUX.
Мы воспользуемся комбинацией 00 указанных битов, т. е. выберем в качестве ИОН:

а) Напряжение на выводе питания для ATtiny85.

б) Напряжение на выводе AREF (21) для ATmega8. Чтобы не озадачиваться поиском дополнительного источника напряжения, просто соединим указанный вывод с выводом питания.

Поскольку комбинация 00 является дефолтной, прописывать в коде обнуление с помощью макроса clearBit битов REFS, значение которых и без того по сбросу/подаче питания равно нулю, не имеет смысла.

Частота FADC тактирования АЦП, как следует из Таблицы «ADC Prescaler Selections» на странице 136 (ATtiny85) и 201 (ATmega8) даташита, как минимум в два раза меньше FCPU. При желании можно ещё уменьшить её, выбрав соответствующую комбинацию битов ADPS регистра ADCSRA. Остановимся на значении 4 делителя, установив в 1 бит ADPS1 указанного регистра.

Остаётся лишь установить в 1 два бита того же регистра ADCSRA:
• Бит ADIE регистра, разрешив тем самым локально прерывание АЦП по завершению измерения,
• Бит ADEN, что обусловит включение АЦП.

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

ATtiny85
adc. S

.include "adc.h"
  .text
    ADC_Handler:
      /* Скопировать в OCR0A значение ADCH */
      PUSH    r17
      LDS     r17, ADCH
      STS     OCR0A, r17
      POP     r17
      /* Возврат из обработчика */
      RETI

    adcInit:
      /* Выбрать канал ADC2(PB4) и установить
         выравнивание данных по левому краю */
      setBit  ADMUX, MUX1
      setBit  ADMUX, ADLAR
      /* Разрешить локально прерывание АЦП */
      setBit  ADCSRA, ADIE
      /* Разрешить тактирование АЦП с частотой F_CPU / 4 */
      setBit  ADCSRA, ADPS1
      setBit  ADCSRA, ADEN
      /* Возврат из функции */
      RET

    adcStartConversion:
      /* Запустить измерение */
      setBit  ADCSRA, ADSC
      /* Возврат из функции */
      RET
.end

main.S

.include "attiny85.h"
.include "macro.h"
  .data

  . text	
    .org Reset_vector
      RJMP   main
    .org TIMER0_OVF_vector * 2
      RJMP   TIMER0_OVF_Handler
    .org ADC_vector * 2
      RJMP   ADC_Handler

    .global main
    main:
      /* Указать на вершину стека */
      stackPointerInit
      /* Настроить выводы управления светодиодами */
      RCALL  ledsInit
      /* Настроить таймер */
      RCALL  timerInit
      /* Настроить АЦП */
      RCALL  adcInit
      /* Разрешить прерывания глобально */
      SEI
    main_loop:
      /* Перейти к метке main_loop */
      RJMP   main_loop
.end

Видео 1. Результат работы проекта lightControl для МК ATtiny85.

ATmega8
adc.S

.include "adc.h"
  .text
    ADC_Handler:
      /* Скопировать в OCR1A значение ADCH */
      PUSH    r16
      PUSH    r17
      LDS     r16, ADCL
      LDS     r17, ADCH
      STS     OCR1AH, r16
      STS     OCR1AL, r17
      POP     r17
      POP     r16
      /* Возврат из обработчика */      
      RETI

    adcInit:
      /* Выбрать канал ADC2(PС2) и установить
         выравнивание данных по левому краю */
      setBit  ADMUX, MUX1
      setBit  ADMUX, ADLAR
      /* Разрешить локально прерывание АЦП */
      setBit  ADCSRA, ADIE
      /* Разрешить тактирование АЦП с частотой F_CPU / 4 */
      setBit  ADCSRA, ADPS1
      setBit  ADCSRA, ADEN
      /* Возврат из функции */
      RET

    adcStartConversion:
      /* Запустить измерение */
      setBit  ADCSRA, ADSC
      /* Возврат из функции */
      RET
. end

main.S

.include "atmega8.h"
.include "macro.h"
  .data

  .text
    .org Reset_vector
      RJMP   main
    .org TIMER1_OVF_vector * 2
      RJMP   TIMER1_OVF_Handler
    .org ADC_vector * 2
      RJMP   ADC_Handler

    .global main
    main:
      /* Указать на вершину стека */
      stackPointerInit
      /* Настроить выводы управления светодиодами */
      RCALL  ledsInit
      /* Настроить таймер */
      RCALL  timerInit
      /* Настроить АЦП */
      RCALL  adcInit
      /* Разрешить прерывания глобально */
      SEI
    main_loop:
      /* Перейти к метке main_loop */
      RJMP   main_loop
.end

Видео 2. Результат работы проекта lightControl для МК ATmega8.

Примеры кода для ATtiny85 и Atmega8 выложены в архив в подвале статьи.

Вся информация о периферии этого МК собрана в Reference manual, за исключением Таблицы 9 «Alternate function mapping», размещённой на странице 44 даташита и содержащей информацию об альтернативных функциях выводов.

Из всего многообразия предусмотренной производителем периферии нами будут рассмотрены:
• Порт ввода-вывода А.
• Таймер общего назначения TIM5.
• Аналого-цифровой преобразователь ADC1.

Как вы помните из Раздела 1.2 предыдущей части статьи, для каждого модуля периферии в карте памяти данных STM32F401 выделен сектор, границы которого представлены в Таблице 1 «STM32F401x register boundary addresses» на странице 38 Reference manual.

Напомню также, что для обращения к регистрам практически любого модуля периферии STM32F401 необходимо, прежде всего, включить тактирование этого модуля через соответствующий регистр модуля RCC, макроопределения регистров и номеров битов которого мы внесли в stm32f401.h в прошлый раз.

Выдержки из упомянутых выше таблиц с информацией, имеющей отношение к рассматриваемым нами модулям и выводам, приведена на Рисунке 6.

Рисунок 6. Выдержка из Таблицы 1 Reference manual и Таблицы 9 даташита МК STM32F401.

Всего для STM32F401 предусмотрено шесть портов: с GPIOA по GPIOH.
За настройку портов отвечает более десятка регистров, полный перечень которых можно увидеть в Разделе «GPIO registers» на странице 158 Reference manual.

Оформим макроопределения и рассмотрим назначение наиболее часто используемых регистров, представленных на Рисунке 7.

Рисунок 7. Некоторые регистры портов ввода-вывода МК STM32F401.

В предыдущей части статьи мы прописали в stm32f401.h макроопределения адресов нижних границ секторов памяти данных, выделенных под порты ввода-вывода, а также смещений адресов отдельных регистров и номеров некоторых битов.
Учитывая, что в проекте lightControl задействованы выводы с номерами 0, 1 и 4 (PA0, PA1 и PA4), добавим в шаблонный хидер-файл макроопределения ещё не внесённых туда регистров из Рисунка 7, а также номеров битов, ответственных за настройку указанных выводов.

В итоге, фрагмент stm32f401.h, касающийся портов ввода-вывода, будет иметь следующий текущий вид.

/* Адреса и номера битов регистров модуля GPIO */
GPIOH = 0×40021C00
GPIOE = 0×40021000
GPIOD = 0×40020C00
GPIOC = 0×40020800
GPIOB = 0×40020400
GPIOA = 0×40020000
  MODER = 0×00
    MODER_15 = 30
    MODER_14 = 28
    MODER_4 = 8
    MODER_1 = 2
    MODER_0 = 0
  PUPDR = 0×0C
    PUPDR_4 = 8
    PUPDR_1 = 2
    PUPDR_0 = 0
  IDR = 0×10
    IDR_4 = 4
    IDR_1 = 1
    IDR_0 = 0
  ODR = 0×14
    ODR_4 = 4
    ODR_1 = 1
    ODR_0 = 0
  AFRH = 0×24
    AFRH_15 = 28
    AFRH_14 = 24
    AFRH_9 = 4
    AFRH_8 = 0
  AFRL = 0×20
    AFRL_7 = 28
    AFRL_6 = 24
    AFRL_4 = 16
    AFRL_1 = 4
    AFRL_0 = 0

Регистр MODER, как уже говорилось в предыдущей части статьи, ответственен за выбор назначения вывода. На настройку каждого пина порта-ввода-вывода выделено по два бита указанного регистра, комбинация значений которых и определяет текущее назначение.
При выборе режима альтернативной функции необходимо, дополнительно к комбинации соответствующих битов регистра MODER, ещё и определить конкретную альтернативную функцию посредством одного из регистров AFR, исходя из данных Таблицы 9 на Рисунке 6.

Ниже приведён пример настроек различных функций для выводов PA0PA4.

/* Включить тактирование модуля GPIOA */
setBit (RCC + AHB1ENR), GPIOAEN
/* Настроить пин PA0 как вход */
LDR r0, =(GPIOA + MODER)
LDR r1, =(0b00 ≪ MODER_0)
STR r1, [r0]
/* Настроить пин PA1 как выход */
LDR r0, =(GPIOA + MODER)
LDR r1, =(0b01 ≪ MODER_1)
STR r1, [r0]
/* Настроить пин PA4 на аналоговый режим */
LDR r0, =(GPIOA + MODER)
LDR r1, =(0b11 ≪ MODER_4)
STR r1, [r0]
/* Настроить пин PA4 на альтернативную функцию */
LDR r0, =(GPIOA + MODER)
LDR r1, =(0b10 ≪ MODER_4)
STR r1, [r0]
/* и выбрать функцию USRT2_CK */
LDR r0, =(GPIOA + AFRL)
LDR r1, =(0b0111 ≪ AFRL_4)
STR r1, [r0]

Регистры PUPDR и IDR понадобятся вам при работе с пином, настроенным как вход: биты первого обуславливают подтяжку входа к питанию или земле через внутренний резистор сопротивлением до 50 кОм, а второго — хранят текущее состояние входа.

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

/* Включить тактирование модуля GPIOA */
setBit (RCC + AHB1ENR), GPIOAEN
/* Настроить пин PA0 как вход */
LDR r0, =(GPIOA + MODER)
LDR r1, =(0b00 ≪ MODER_0)
STR r1, [r0]
/* с подтяжкой к питанию */
LDR r0, =(GPIOA + PUPDR)
LDR r1, =(0b01 ≪ PUPDR_0)
STR r1, [r0]
/* Считать значение IDR в r1 и проверить состояние PA0
   Если состояние — высокое, перйти к метке label */
LDR r0, =(GPIOA + IDR)
LDR r1, [r0]
AND r1, (1 ≪ IDR_0)
CMP r1, 0
BNE label

Соответствующие биты регистра ODR определяют текущее состояние пина, настроенного как выход:

/* Настроить пин PA1 как выход */
LDR r0, =(GPIOA + MODER)
LDR r1, =(0b01 ≪ MODER_1)
STR r1, [r0]
/* и установить в 1 */
setBit (GPIOA + ODR), ODR_1

Оформим в файлах led функции настройки выводов управления зелёным и жёлтым светодиодами, а также установки последнего в разные состояния.
Так же, как и в случае с AVR-8, для гибкости кода применим двойные макроопределения адресов регистров, а с целью минимизации объёма ассемблер-файла вынесем директиву .global во вспомогательный хидер-файл.

led.h

.include "stm32f401.h"
.include "macro.h"

LEDS_GPIO        = GPIOA
LEDS_GPIOEN      = GPIOAEN
YELLOW_LED_MODER = MODER_1
YELLOW_LED_ODR   = ODR_1
GREEN_LED_MODER  = MODER_0
GREEN_LED_ODR    = ODR_0
.global ledsInit, yellowLedOn, yellowLedOff, yellowLedToggle

led.S

.include "led.h"
  .text
    ledsInit:
      PUSH     {LR}
      /* Включить тактирование модуля GPIOA */
      setBit   (RCC + AHB1ENR), LEDS_GPIOEN
      /* Настроить вывод управления жёлтым светодиодом как выход */
      setBit   (LEDS_GPIO + MODER), YELLOW_LED_MODER
      /* Настроить вывод управления зелёным светодиодом
      на альтернативную функцию */
      LDR      r0, = (LEDS_GPIO + MODER)
      LDR      r1, [r0]
      LDR      r2, =(0b10 << GREEN_LED_MODER)
      ORR      r1, r2
      STR      r1, [r0]
      /* и выбрать функцию AF02 - ШИМ канала Ch2 таймера TIM5*/
      LDR      r0, =(GPIOA + AFRL)
      LDR      r1, [r0]
      ORR      r1, (0b0010 << AFRL_0)
      STR      r1, [r0]
      /* Возврат из функции */
      POP      {PC}
    yellowLedOn:
      PUSH     {LR}
      /* Включить жёлтый светодиод */
      setBit   (LEDS_GPIO + ODR), YELLOW_LED_ODR
      /* Возврат из функции */
      POP      {PC}
    yellowLedOff:
      PUSH     {LR}
      /* Выключить жёлтый светодиод */
      clearBit (LEDS_GPIO + ODR), YELLOW_LED_ODR
      /* Возврат из функции */
      POP      {PC}
    yellowLedToggle:
      PUSH     {LR}
      /* Изменить состояние жёлтого светодиода на противоположное */
      LDR      r0, = (LEDS_GPIO + ODR)
      LDR      r1, [r0]
      EOR      r1, (1 << YELLOW_LED_ODR)
      STR      r1, [r0]
      /* Возврат из функции */
      POP      {PC}		
. end

Заметьте, что пин PA0 управления зелёным светодиодом настроен на генерацию ШИМ-сигнала. Как реализовать сам сигнал, вы узнаете ниже.

Обеспечим периодическое изменение состояние жёлтого светодиода, откомпилировав и загрузив в МК нижеследующую программу.

.include "stm32f401.h"
.include "macro.h"
  .data

  .text
    .org 0
      /* Указать на вершину стека */
      .word  RAMEND		
    .org Reset_vector	
      .word  main  + 1
			
    .global main
    main:
      /* Настроить выводы управления светодиодами */
      BL  ledsInit
    main_loop:
      /* Переключить жёлтый светодиод в противоположное состояние */
      BL  yellowLedToggle
      /* Перейти к метке main_loop */
      B   main_loop
.end   

Как вы помните, дефолтная частота FCPU тактирования процессора STM32F401 — 16 МГц, т. е. период одного тика составляет 1 / 16 МГц = 62. 5 наносекунд.

Как следует из столбца «Cycles» Таблицы «Instructions Set Summary» на странице 3-30 Cortex M-4 Technical Reference Manual, исполнение инструкций одного цикла основной программы, включая функцию yellowLedToggle, займёт:
BL — 2
PUSH — 2
LDR — 2
LDR — 2
EOR — 1
STR — 2
POP — 2
B — 2
Итого: 15 тиков или 15×62.5 нс = 938 нс

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

С помощью таймера увеличим период сигнала на выводе управления жёлтым светодиодом и организуем ШИМ–сигнал на выводе PA0.

В состав периферии STM32F401 входят несколько таймеров разного типа, в том числе и таймер общего назначения TIM5, возможности которого мы применим в нашем проекте.
Информация о работе группе таймеров TIM2TIM5 и назначении их регистров изложена на страницах 316—352 и 353—375, соответственно, в Разделе «General-purpose timers (TIM2 to TIM5)» Reference manual.

Таймер TIM5 поддерживает несколько режимов работы, из которых, в рамках проекта lightControl, мы рассмотрим:
• Режим сравнения количества поступивших на таймер тактовых импульсов с заданным числом, при равенстве которых генерируется соответствующее прерывание.
• Режим генерации ШИМ-сигнала на определённом выводе МК, в данном случае на PA0.

Реализовать указанные режимы можно посредством регистров и их битов (отмечены красным), представленных на Рисунке 8.

Рисунок 8. Некоторые регистры группы таймеров TIM2TIM5 МК STM32F401.

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

Для настройки режима сравнения потребуется:
1. Выбрать подходящий делитель частоты, записав его значение в регистр PSC. Остановимся на значении 1600, что даст частоту тактирования таймера FTIMER = FCPU / 1600 = 10 кГц или период одно тика в 100 мкс.

2. Установить посредством записи соответствующего числа (пусть это будет 255) в регистр ARR значение, по достижению которого сбрасывается в ноль счётчик таймера и генерируется прерывание.

Умножив 255 на 100 мкс, мы получим 25.5 мс — значение периода генерации прерывания таймера, а также периода ШИМ-сигнала на выводе управления зелёным светодиодом.

Кроме того, учитывая, что в обработчик прерывания таймера мы будут помещены:
• вызов оформленной выше функции yellowLedToggle,
• вызов предстоящей к оформлению ниже функции запуска измерения АЦП,

указанные 25.5 мс являются ещё и:
• полу-периодом сигнала на выводе управления жёлтым светодиодом,
• периодом сэмплирования АЦП.

Минимальный набор настроек ШИМ-сигнала включает в себя:
1. Выбор одного из двух режимов — PWM1 или PWM2. Мы выберем PWM1 путём записи комбинации 110 в группу битов OC1M регистра CCMR1.

2. Установку скважности, значение которой определяется соотношением чисел, записанных в регистры CCR1 и ARR. Позднее скважность ШИМ-сигнала будет увязана нами с уровнем аналогового напряжения на выводе PA4, пока же запишем в CCR1 число 100, которое даст скважность 100 / 255 = 0.39.

3. Разрешение ШИМ-сигнала на соответствующем выводе МК. В нашем случае такое разрешение для сигнала на пине PA0 достигается установкой в 1 бита CC1E регистра CCER.

После того, как все требуемые настройки произведены, остаётся:
а) Разрешить прерывание таймера глобально (через регистр ISER1 модуля NVIC) и локально (посредством бита UIE регистра DIER).
б) Включить счётчик таймера, записав 1 в бит CEN регистра CR1.

Оформим всё вышеизложенное в файлах timer.
timer.h

.include "stm32f401.h"
.include "macro.h"

TIM5_PSC_VALUE  = 1600
TIM5_ARR_VALUE  = 255
COUNTER_MAX     = 20
PWM_DUTY_CYCLE	= 100

.global TIM5_Handler, timerInit

timer.S

.include "timer.h"
  .text
    TIM5_Handler:
      PUSH     {LR}
      /* Очистить флаг прерывания */
      clearBit (TIM5 + TIM_SR), UIF
      /* Увеличить на 1 значение r5 и сравнить с COUNTER_MAX */
      ADD      r5, 1
      CMP      r5, COUNTER_MAX
      /* Если меньше, перейти к метке return */
      BNE      return
      /* В противном случае, обнулить r5 и */
      LDR      r5, =0
      /* изменить состояние жёлтого светодиода на противоположное */
      BL       yellowLedToggle
     return:
      /* Возврат из обработчика */
      POP      {PC}

    timerInit:
      PUSH     {LR}
      /* Включить тактирование таймера TIM5 */
      setBit   (RCC + APB1ENR), TIM5EN
      /* Выбрать значение делителя частоты тактирования */
      LDR      r0, =(TIM5 + TIM_PSC)
      LDR      r1, =TIM5_PSC_VALUE
      STR      r1, [r0]
      /* Установить значение сравнения */
      LDR      r0, =(TIM5 + TIM_ARR)
      LDR      r1, =TIM5_ARR_VALUE
      STR      r1, [r0]
      /* Настроить ШИМ-сигнал канала Ch2, в т. ч. */
      /* выбрать режим PWM1*/
      LDR      r0, = (TIM5 + TIM_CCMR1)
      LDR      r1, [r0]
      ORR      r1, (0b110 << OC1M)
      STR      r1, [r0]
      /* установить скважность ШИМ-сигнала */
      LDR      r0, =(TIM5 + TIM_CCR1)
      LDR      r1, =PWM_DUTY_CYCLE
      STR      r1, [r0]
      /* разрешить ШИМ-сигнал на пине PA0 */
      setBit   (TIM5 + TIM_CCER),CC1E
      /* Разрешить прерывание таймера локально */
      setBit   (TIM5 + TIM_DIER), UIE
      /* Разрешить прерывание таймера глобально */
      setBit   ISER1, TIM5_INTERRUPT_BIT
      /* Включить счётчик таймера */
      setBit   (TIM5 + TIM_CR1), CEN
      /* Возврат из функции */
      POP      {PC}
.end

Как видите, чтобы частота мигания жёлтого светодиода была видна для невооружённого глаза, в обработчике прерывания, по аналогии с AVR-8, реализовано её деление на 20, в результате чего период сигнала на пине PA1 составит 25. 5 мс х 20×2 = 1.02 секунды плюс пару микросекунд на двойное исполнение инструкций функции yellowLedToggle.

Содержимое файла main.S будет следующим:

.include "stm32f401.h"
.include "macro.h"
  .data

  .text	
    .org 0
      /* Указать на вершину стека */
      .word RAMEND
    .org Reset_vector
      .word main + 1
    .org TIM5_vector
      .word TIM5_Handler + 1

    .global main
    main:
      /* Настроить выводы управления светодиодами */
      BL  ledsInit
      /* Настроить таймер */
      BL  timerInit
    main_loop:
      /* Перейти к метке main_loop */
      B   main_loop
.end

Для полноты, подключим к проекту АЦП микроконтроллера и реализуем зависимость скважности ШИМ-сигнала на пине PA0 от уровня аналогового напряжения на входе PA4.

АЦП STM32F401 детально описан в Разделе «Analog-to-digital converter (ADC)» на странице 213 Reference manual.

На рисунке 9 представлены те регистры и их биты, запись в которые обеспечивает настройки АЦП в объёме, достаточном для реализации проекта lightControl.

Рисунок 9. Некоторые регистры модуля АЦП МК STM32F401.

Добавим в шаблонный хидер-файл недостающие макроопределения для модуля АЦП.

stm32f401.h

/* Адреса и номера битов модуля АЦП */
ADC1 = 0×40012000
    ADC_SR = 0×00
      EOC = 1
    ADC_CR1 = 0×04
      RES = 24
      EOCIE = 5
    ADC_CR2 = 0×08
      ADON = 0
      SWSTART = 30
    ADC_SQR3 = 0×34
      SQ1 = 0
    ADC_DR = 0×4C

И пропишем в файлах adc функции adcInit иниициализации АЦП и adcStartConversion запуска разового измерения, а также обработчик прерывания АЦП по завершению измерения.

В функции adcInit:
1. Переведём пин PA4 в аналоговый режим.

2. Включим тактирование модуля АЦП.

3. Выберем канал IN4 для измерения уровня аналогового напряжения, записав комбинацию 100 в группу битов SQ1 регистра SQR3.

4. Установим разрешение АЦП в 8 бит посредством комбинации 10 группы битов RES регистра CR1. Поскольку ранее мы определили для таймера TIM5 максимальное значение счёта так же восьми-битным (255), то выбор указанного разрешения для АЦП позволит напрямую, без использования пропорций, копировать значение регистра данных DR АЦП в регистр CCR1 таймера.

5. Разрешим локально, установив в 1 бит EOCIE регистра CR1, и глобально через регистр ISER0 контроллера NVIC прерывание АЦП по завершению измерения.

6. Запишем единицу в бит ADON регистра CR2, включив тем самым АЦП.

Запуск измерения в функции adcStartConversion обеспечивается записью 1 в бит SWSTART регистра CR2.

В обработчике прерывания достаточно сбросить бит EOC регистра SR, а затем скопировать значение регистра данных DR АЦП в регистр CCR1 таймера.
adc.h

.include "stm32f401.h"
.include "macro.h"

.global	ADC1_Handler, adcInit, adcStartConversion

adc.S

.include "adc.h"
  .text 	
    ADC1_Handler:
      PUSH     {LR}
      /* Очистить флаг прерывания */
      clearBit (ADC1 +  ADC_SR), EOC
      /* Считать значение регистра данных АЦП */
      LDR      r0, =(ADC1 + ADC_DR)
      LDR      r1, [r0]
      /* и записать в регистр CCR1 таймера */
      LDR      r0, =(TIM5 + TIM_CCR1)
      STR      r1, [r0]
      /* Возврат из обработчика */
      POP      {PC}

    adcInit:
      PUSH     {LR}
      /* Разрешить тактирование GPIOA */
      setBit   (RCC + AHB1ENR), GPIOAEN
      /* и перевести пин PA4 в аналоговый режим */
      LDR      r0, =(GPIOA + MODER)
      LDR      r1, [r0]
      LDR      r2, =(0b11 << MODER_4)
      EOR      r1, r2
      STR      r1, [r0]
      /* Включить тактирование ADC1 */
      setBit   (RCC + APB2ENR), ADC1EN
      /* Выбрать канал ADC1_IN4 */
      LDR      r0, =(ADC1 + ADC_SQR3)
      LDR      r1, [r0]
      LDR      r2, =(0b100 << SQ1)
      EOR      r1, r2
      STR      r1, [r0]
      /* Установить разрешение 8 бит */
      LDR      r0, =(ADC1 + ADC_CR1)
      LDR      r1, [r0]
      LDR      r2, =(0b10 << RES)
      EOR      r1, r2
      STR      r1, [r0]
      /* Разрешить прерывание АЦП локально */
      setBit   (ADC1 + ADC_CR1), EOCIE
      /* Разрешить прерывание АЦП глобально */
      setBit   ISER0, ADC1_INTERRUPT_BIT
      /* Включить АЦП */
      setBit   (ADC1 + ADC_CR2), ADON
      /* Возврат из функции */
      POP      {PC}

    adcStartConversion:
      PUSH     {LR}
      setBit   (ADC1 + ADC_CR2), SWSTART
      POP      {PC}
. end

Остаётся лишь добавить в обработчик прерывания таймера (сразу после очистки флага прерывания) вызов функции запуска измерения АЦП:

/* Запустить измерение АЦП */
BL adcStartConversion

Внести соответствующие дополнения в main.S

.include "stm32f401.h"
.include "macro.h"
  .data

  .text
    .org 0
      /* Указать на вершину стека */
      .word  RAMEND		
    .org Reset_vector
      .word  main + 1
    .org ADC1_vector
      .word  ADC1_Handler + 1
    .org TIM5_vector
      .word  TIM5_Handler + 1

    .global main
    main:
      /* Настроить выводы управления светодиодами */
      BL  ledsInit
      /* Настроить АЦП */
      BL  adcInit
      /* Настроить таймер */
      BL  timerInit
    main_loop:
      /* Перейти к метке main_loop */
      B   main_loop
.end

И наблюдать результат работы проекта.

Видео 3. Результат работы проекта lightControl для МК STM32F401.

В архиве вы найдёте весь приведённый в данной главе код.

Учитывая, что nRF52832 — не просто МК, а SoC (System on Chip), предназначенный, прежде всего, для создания устройств IoT, состав его периферии достаточно специфичен. Тем не менее, стандартные модули, необходимые для осуществления проекта lightControl, также присутствуют.

Вся информация о модуле GPIO представлена в одноимённом разделе на странице 111 Product Specification.

Регистры порта ввода-вывода nRF52832, ответственные за настройку участвующих в проекте выводов, а также их расшифровка для вывода P0.02 (для остальных выводов всё — то же самое) представлены на Рисунке 10.

Рисунок 10. Некоторые регистры порта-ввода-вывода МК nRF52832.

Коротко о назначении регистров из Рисунка 10 и их битов.

1. Соответствующий бит регистра DIR определяет направление работы того или иного пина. В частности, запись 0/1 во второй или C-бит настраивает вывод P0.02 как вход/выход, соответственно.

2. Биты регистра OUT обуславливают состояние пина, настроенного как выход.

3. Регистр IN хранит текущее состояние выводов P0.01P0.31.

4. Биты регистра PIN_CNF[N] ответственны за следующие настройки пина N:
• 0-й (или А) бит DIR дублирует для отдельного пина функцию одноимённого регистра,
• 1-й (или B) бит INPUT отключает/подключает буфер для хранения текущего состояния пина, настроенного как вход,
• группа битов С (2—3) PULL позволяет подтянуть пин, настроенный как вход, к питанию или земле через внутренний резистор сопротивлением до 16 кОм,
• группа битов D (8—10) DRIVE определяют значение максимально возможного для данного пина тока,
• биты группы E (16—17) SENSE используется при реализации внешнего прерывания и определяют уровень сигнала на пине, настроенного как вход, при котором генерируется прерывание.

Внесём макроопределения вышеупомянутых регистров и битов из в nrf52832.h:

/* Адреса и номера битов регистров модуля GPIO */
GPIO = 0×50000000
  GPIO_OUT = 0×504
  GPIO_IN = 0×510
  GPIO_DIR = 0×514
  GPIO_PIN_CNF_2 = 0×708
  GPIO_PIN_CNF_25 = 0×764
  GPIO_PIN_CNF_24 = 0×760
    PIN_CNF_DIR = 0
    PIN_CNF_INPUT = 1
    PIN_CNF_PULL = 2
    PIN_CNF_DRIVE = 8
    PIN_CNF_SENSE = 16

Поскольку nRF52832 и STM32F401 созданы на базе одного и того же ядра, глядя на примеры, приведённые выше для последнего и используя те же конструкции, вы, уверен, сможете самостоятельно создать код настройки пинов порта ввода-вывода, установки их в 0/1 или чтения их текущего состояния для nRF52832.

Поэтому, сразу перейдём к оформлению в файлах led функций настройки пинов P0.2425 и управления жёлтым светодиодом.
led.h

.include "nRF52832.h"
.include "macro. h"

YELLOW_LED_PIN     = 24
YELLOW_LED_PIN_CNF = GPIO_PIN_CNF_24
GREEN_LED_PIN      = 25
GREEN_LED_PIN_CNF  = GPIO_PIN_CNF_25

.global	ledsInit, yellowLedOn, yellowLedOff, yellowLedToggle

led.S

.include "led.h"
  .text
    ledsInit:
      PUSH     {r0-r1, LR}
      /* Настроить вывод управления жёлтым светодиодом как выход */
      LDR      r0, =(GPIO + YELLOW_LED_PIN_CNF)
      LDR      r1, =(1 << PIN_CNF_DIR)
      STR      r1, [r0]
      /* Настроить вывод управления зелёным светодиодом как выход */
      LDR      r0, =(GPIO + GREEN_LED_PIN_CNF)
      LDR      r1, =(1 << PIN_CNF_DIR)
      STR      r1, [r0]
      /* Возврат из функции */
      POP      {r0-r1, PC}

    yellowLedOn:
      PUSH     {r0-r1, LR}
      /* Включить жёлтый светодиод */
      setBit   (GPIO + GPIO_OUT), YELLOW_LED_PIN
      /* Возврат из функции */
      POP      {r0-r1, PC}

    yellowLedOff:
      PUSH     {r0-r1, LR}
      /* Выключить жёлтый светодиод */
      clearBit (GPIO + GPIO_OUT), YELLOW_LED_PIN
      /* Возврат из функции */
      POP      {r0-r1, PC}

    yellowLedToggle:
      PUSH     {r0-r1, LR}
      /* Изменить состояние жёлтого светодиода на противоположное */
      LDR      r0, =(GPIO + GPIO_OUT)
      LDR      r1, [r0]
      EOR      r1, (1 << YELLOW_LED_PIN)
      STR      r1, [r0]
      /* Возврат из функции */
      POP      {r0-r1, PC}
. end

Тогда main.S с программой blink для жёлтого светодиода будет выглядеть следующим образом:

.include "nrf52832.h"
.include "macro.h"

  .data

  .text
    .org 0
      /* Указать на вершину стека */
      .word  RAMEND
    .org Reset_vector
      .word  main + 1

    .global main
    main:
      /* Настроить выводы управления светодиодами */
      BL  ledsInit
    main_loop:
      BL  yellowLedToggle
      B   main_loop
.end

Функция yellowLedToggle, как вы наверняка заметили, состоит из абсолютно тех же инструкций, что и для STM32F401, на исполнение которых требуется 15 тиков тактового сигнала процессора.
Вспомним из предыдущей части статьи дефолтную частоту FCPU тактирования процессора nRF52832 и рассчитаем полу-период сигнала на выводе управления жёлтым светодиодом: (1 / 64 МГц) х 15 = 234 наносекунды.

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

Подробная информация об этом модуле изложены в Разделе «TIMER — Timer/counter» на странице 234 Product Specification.

Всего в состав nRF52832 входит пять таймеров TIMER0TIMER4, смещения адресов и расшифровку некоторых регистров которых вы можете увидеть на Рисунке 11.

Рисунок 11. Некоторые регистры таймеров МК nRF52832.

Настройка и работа — совершенно одинаковые для всех таймеров и отличает их лишь количество регистров сравнения CC, как видно из Таблицы 43 на Рисунке 11.
С учётом этого, остановимся на TIMER2 тем более, что в предыдущей части статьи именно для него мы внесли в шаблонный хидер-файл данные, связанные с прерыванием таймера.

Прежде, чем вникать в детали, внесём данные из Рисунка 11 в nrf52832.h:

/* Адреса и номера битов регистров модуля таймеров */
TIMER2 = 0×4000A000
  TIM_TASKS_START = 0×000
  TIM_TASKS_STOP = 0×004
  TIM_EVENTS_COMPARE_0 = 0×140
  TIM_INTENSET = 0×304
  TIM_INTENCLR = 0×308
    TIM_COMPARE_0 = 16
  TIM_MODE = 0×504
  TIM_BITMODE = 0×508
  TIM_PRESCALER = 0×510
  TIM_CC_0 = 0×540

Как уже говорилось в прошлый раз, дефолтная частота тактирования таймера составляет 16 Мгц. При необходимости её можно делить по формуле

FTIMER = 16 МГц / (2PRESCALER),

записав в регистр PRESCALER соответствующее число.

Выберем в рамках нашего проекта значение PRESCALER равное 3, что даст деление на 8 и уменьшит FTIMER до 2 МГц. Следовательно период одного тика таймера составит 500 нс.

Каждый из пяти таймеров поддерживает два режима работы, выбор между которыми определяется комбинаций двух младших битов регистра MODE:
• Комбинация 00 — режим Timer, в котором счётчик таймера считает до максимального своего значения, т. е. переполнения, сбрасываясь затем в ноль.
• Комбинация 01 — режим Counter для подсчёта количества запущенных задач TASKS_COUNT, которые могут инициироваться из любого места программы: к примеру, в обработчике внешнего прерывания с целью подсчёта общего количества нажатий кнопки.
• Комбинация 10 — тот же режим Counter, но в энергосберегающем варианте.

Нас устроит режим Timer и, поскольку он является дефолтным, оформление его в коде не понадобится.

Максимальное значение счётчика таймера обусловлено его разрядностью, задаваемой, в свою очередь, значением битов 0—1 регистра BITMODE. Разрядность счётчика по сбросу/подаче питания, как видно из Рисунка 11, составляет 16 бит, что вполне устраивает нас ещё и потому, что не потребует дополнительного кода.
Таким образом, время необходимое счётчику для переполнения, составит 500 нс х 65536 = 32.77 мс.

Каждый раз, когда счётчик таймера достигает значения, записанного в регистр сравнения CC[N], может быть сгенерировано соответствующее прерывание, если оно разрешено глобально посредством регистра ISER0 контроллера NVIC и локально установкой в 1 соответствующего бита регистра INTENSET. Запретить локально такое прерывание можно через тот же бит регистра INTENCLR.
Мы воспользуемся лишь одним сравнением — с числом, содержащимся в регистре CC[0]. Причём, в данном случае не важно, какое именно число там находится, поскольку достигать его счётчик будет в любом случае лишь один раз за проход. По сути, будет иметь место аналог прерывания по переполнению для AVR-8.
Учитывая изложенное, запись числа в CC[0] также не требует отражения в программе.

Включение процедуры счёта достигается запуском задачи TASKS_START.

В итоге, содержимое файлов timer будет иметь следующий вид.
timer.h

.include "nRF52832.h"
.include "macro.h"

TIMER2_PRESCALER_VALUE 	= 3
COUNTER_MAX             = 15

.global	TIMER2_Handler, timerInit

timer.S

.include "timer.h"
  .text
    TIMER2_Handler:
      PUSH   {LR}
      /* Сбросить флаг TIM_EVENTS_COMPARE_0 */
      LDR    r0, =(TIMER2 + TIM_EVENTS_COMPARE_0)
      LDR    r1, =0
      STR    r1, [r0]
      /* Увеличить на 1 значение r5 и сравнить с COUNTER_MAX */
      ADD    r5, 1
      CMP    r5, COUNTER_MAX
      /* Если  меньше, перейти к метке return */
      BNE    return
      /* В противном случае, бнулить r5 */
      LDR    r5, =0
      /* и изменить состояние жёлтого светодиода на противоположное */
      BL     yellowLedToggle
     return:
      /* Возврат из обработчика */
      POP    {PC}

    timerInit:
      PUSH   {LR}
      /* Установить делитель 2 ^ 3 = 8 */
      LDR    r0, =(TIMER2 + TIM_PRESCALER)
      LDR    r1, =TIMER2_PRESCALER_VALUE
      STR    r1, [r0]
      /* Разрешить прерывание TIMER2 по совпадению локально */
      setBit (TIMER2 + TIM_INTENSET), TIM_COMPARE_0
      /* Разрешить прерывание TIMER2 глобально */
      setBit ISER0, TIMER2_INTERRUPT_BIT
      /* Включить счётчик */
      LDR    r0, =(TIMER2 + TIM_TASKS_START)
      LDR    r1, = 1
      STR    r1, [r0]
      /* Возврат из функции */
      POP    {PC}
. end

Как вы помните, период переполнения счётчика таймера, а следовательно и генерации прерывания, с учётом произведённых настроек составит 32.77 мс. Такое значение выбрано, исходя из соображений частоты сэмлирования АЦП, который мы подключим к проекту позднее. Однако, для сигнала на жёлтом светодиоде это — слишком маленький период, чтобы быть заметным для глаза. Поэтому, в обработчике прерывания оформлено деление частоты на 15 посредством свободного РОН r5 и макроопределения COUNTER_MAX, что даст полу-период мигания упомянутого светодиода в 32.77 мс х 15 = 491.6 мс.

Осталось переписать код в main.S следующим образом:

.include "nrf52832.h"
.include "macro.h"
  .data

  .text
    .org 0
      /* Указать на вершину стека */
      .word RAMEND
    .org Reset_vector
      .word main + 1
    .org TIMER2_vector
      .word TIMER2_Handler + 1

    .global main
    main:
      /* Настроить выводы управления светодиодами */
      BL  ledsInit
      /* Настроить таймер */
      BL  timerInit
    main_loop:
      B   main_loop
. end

Собственно говоря, ничто не мешает нам организовать ШИМ-сигнал на выводе управления зелёным светодиодом, используя возможности таймера.
Для этого достаточно записать подходящие, не равные друг другу, числа в два регистра сравнения, к примеру CC[0] и CC[1], разрешив глобально и локально прерывания от обоих сравнений.
Далее, в обработчике необходимо выяснять, какое именно событие (EVENTS_COMPARE[0] или EVENTS_COMPARE[1]) стало причиной прерывания и, соответственно, устанавливать в 1 или сбрасывать в 0 вывод P0.25.

Однако, такой вариант содержит ряд недостатков:
а) Процессор будет раз в 32.77 мс отвлекаться от исполнения основной функции для изменения состояния зелёного светодиода.
б) Период ШИМ-сигнала будет неизменным, а сам сигнал — полу-аппаратным, поскольку изменение состояния вывода P0.25 осуществляется из программы, т. е. вручную.

Избежать указанных недостатков позволяет модуль PWM, входящий в состав nRF52832.

С деталями функционирования и настройки модуля генерации ШИМ-сигналов можно ознакомиться в Разделе «PWM — Pulse width modulation» Product Specification.

В состав nRF52832 входит три одинаковых модуля PWM0PWM2, каждый из которых располагает 4 каналами для генерации сигналов одинаковой частоты, но разной скважности, определяемой пользователем.
Таким образом, вы имеет возможность организовать аппаратный ШИМ-сигнал на 12 пинах МК, настроенных предварительно как выходы.

На Рисунке 12 представлена информация по каналу 0 модуля PWM0, включая расшифровку того минимального количества регистров, которого достаточно для управления яркостью зелёного светодиода.

Рисунок 12. Некоторые регистры модуля PWM0 МК nRF52832.

Добавим макроопределения адресов регистров из Рисунка 12 в nrf52832.h и выясним, как всё это работает.

/* Адреса и номера битов регистров модуля ШИМ */
PWM0 = 0×4001C000
  PWM_TASKS_SEQSTART_0 = 0×008
  PWM_ENABLE = 0×500
    PWM_ENABLE_ENABLE = 0
  PWM_COUNTERTOP = 0×508
  PWM_PRESCALER = 0×50C
  PWM_SEQ_0_PTR = 0×520
  PWM_SEQ_0_CNT = 0×524
  PWM_PSEL_OUT_0 = 0×560
    PWM_PSEL_OUT_PIN = 0
    PWM_PSEL_OUT_CONNECT = 31

Начнём с тактирования. Базовая частота FPWM тактирования всех модулей PWM — такая же, как и у таймеров, т. е. 16 МГц. При желании её можно делить, выбрав соответствующую комбинацию битов 0—2 регистра PRESCALER.

Основу модуля PWM составляет 16-битный счётчик, который может работать в одном из двух режимов счёта в зависимости от значения нулевого бита регистра MODE: 0 — режим Up, 1 — режим UpAndDown.
Нас устроит любой вариант, поэтому остановимся на дефолтном режиме Up, поскольку в этом случае отпадает необходимость отражать данную настройку в коде.
В указанном режиме счётчик считает до максимального своего значения, заданного пользователем посредством регистра COUNTERTOP, а затем сбрасывается в ноль.
При этом дважды инвертируется состояние вывода МК, назначенного, опять же пользователем, как канал ШИМ-сигнала:
• при достижении счётчиком значения 16-битного числа, хранимого по зарезервированному предварительно адресу SRAM,
• при обнулении счётчика.

Таким образом, период ШИМ-сигнала будет равен периоду одного тактового импульса данного модуля PWM, помноженному на значение числа в COUNTERTOP, а скважность — соотношению чисел в SRAM и COUNTERTOP.

В отношении числа, хранимого в SRAM, есть пара ограничений:
• Во-первых, оно не должно быть больше числа в COUNTERTOP, иначе вы получите скважность больше 1.
• Во-вторых, старший бит этого числа определяет инверсию ШИМ-сигнала. Чтобы случайно не изменить инверсию в проектах с переменной скважностью сигнала, лучше подстраховаться и установить максимальную длину в 15 бит для обоих чисел: в COUNTERTOP и SRAM.

В рамках нашего проекта для реализации ШИМ-сигнала на выводе управления зелёным светодиодом достаточно:

1. Настроить пин P0.25 как выход, что мы и сделали выше в функции ledsInit.

2. Выбрать пин P0.25 как канал 0 модуля PWM0, записав его номер 25 в группу битов A (0–4) регистра PSEL.OUT[0]. Кроме того, необходимо соединить между собой указанные канал и пин, сбросив в ноль 31-й бит того же регистра. Однако, поскольку по умолчанию указанный бит и без того равен нулю, в коде данное действие будет нами опущено.

3. Определиться с тактовой частотой модуля PWM0. Выберем деление на 4 до 4 МГц, записав число 2 в регистр PRESCALER, что даст период одного тика в 250 нс.

4. Установить верхнюю границу для счётчика. Чуть позже мы подключим к проекту АЦП с 14-битной разрядностью, поэтому, чтобы не заморачиваться пропорциями, запишем в регистр COUNTERTOP максимально возможное значение 16383 регистра данных АЦП. Тогда, период ШИМ-сигнала составит 250 нс х 16383 = 4.096 мс.

5. Зарезервировать в SRAM место для хранения 16-битного числа. Как вы помните, из второй части статьи, делается это посредством переменной (пусть она называется pwmSeq), объявляемой в секции данных.

6. Загрузить в pwmSeq число, определяющее скважность сигнала. Остановимся на значении 5461, что обусловит скважность 0. 33.

7. Указать на переменную pwmSeq, записав значение её адреса в регистр SEQ[0].PTR.

8. Установить количество циклов в последовательности посредством регистра SEQ[0].CNT. Подробнее об этом параметре и последовательностях в целом можете почитать в вышеуказанном раздела Product Specification, мы же запишем в указанный регистр единицу.

9. Разрешить последовательность PWM0, установив в 1 бит ENABLE одноимённого регистра.

10. Запустить генерацию ШИМ-сигнала с помощью задачи TASK_SEQSTART[0].

Оформим эти пункты в файлах pwm.

pwm.h

.include "nRF52832.h"
.include "macro.h"

PWM0_PRESCALER_VALUE = 2
PWM_COUNTERTOP_VALUE = 16383
PWM_INIT_VALUE       = 5461

.global	pwmInit

pwm.S

.include "pwm.h"
  .data
    .global pwmSeq
    pwmSeq: .word 1
  .text
    pwmInit:
      PUSH   {LR}	
      /* Выбрать пин P0. 25 как канал 0 модуля PWM0 и подключить его */
      LDR    r0, =(PWM0 + PWM_PSEL_OUT_0)
      LDR    r1, =(25 << PWM_PSEL_OUT_PIN)
      STR    r1, [r0]
      /* Установить  частоту тактирования 4 МГц */
      LDR    r0, =(PWM0 + PWM_PRESCALER)
      LDR    r1, =PWM0_PRESCALER_VALUE
      STR    r1, [r0]			
      /* Установить верхнюю границу счёта */
      LDR    r0, =(PWM0 + PWM_COUNTERTOP)
      LDR    r1, =PWM_COUNTERTOP_VALUE
      STR    r1, [r0]
      /* Загрузить в pwmSeq стартовые параметры ШИМ */
      LDR    r0, =pwmSeq
      LDR    r1, =((0 << 15) | PWM_INIT_VALUE)
      STR    r1, [r0]
      /* Указать адрес переменной pwmSeq */
      LDR    r0, =(PWM0+ PWM_SEQ_0_PTR)
      LDR    r1, =pwmSeq
      STR    r1, [r0]
      /* Установить количество циклов в pwmSeq */
      LDR    r0, =(PWM0 + PWM_SEQ_0_CNT)
      LDR    r1, =1
      STR    r1, [r0]
      /* Разрешить последовательность PWM0 */
      setBit (PWM0 + PWM_ENABLE), PWM_ENABLE_ENABLE
      /* Запустить генерацию ШИМ-сигнала */
      LDR    r0, =(PWM0 + PWM_TASKS_SEQSTART_0)
      LDR    r1, =1
      STR    r1, [r0]
      /* Возврат из функции */
      POP    {PC}
. end

И добавим в main.S инструкцию вызова функции pwnInit:

.include "nrf52832.h"
.include "macro.h"
  .data

  .text
    .org 0
      /* Указать на вершину стека */
      .word RAMEND
    .org Reset_vector
      .word main + 1
    .org TIMER2_vector
      .word TIMER2_Handler + 1

    .global main
    main:
      /* Настроить выводы управления светодиодами */
      BL  ledsInit
      /* Настроить модуль PWM */
      BL  pwmInit
      /* Настроить таймер */
      BL  timerInit
    main_loop:
      B   main_loop
.end

Для наглядности, увяжем скважность ШИМ-сигнала с уровнем аналогового напряжения на пине P0.02.

В состав периферии nRF52832 входит 8-канальный АЦП, подробное информацию о котором вы найдёте в Разделе «SAADC» на странице 357 Product Specification.

Альтернативным назначением пина P0.2 является аналоговый вход AIN0, в связи с чем на Рисунке 13 приведена информация именно по этому каналу в объёме, достаточном для завершения проекта lightControl.

Рисунок 13. Некоторые регистры модуля АЦП МК nRF52832.

Выясним назначение отдельных регистров модуля АЦП и их битов.

1. Регистры INTENSET и INTENCLR, как следует из их названия, ответственны за локальные разрешение и запрет, соответственно, прерываний АЦП, набор которых достаточно широк. Для наших целей вполне подойдёт прерывание по событию RESULTDONE, инициируемому после записи результата измерения в SRAM, поэтому в коде установим в 1 третий бит D регистра INTENSET.

2. Установка в 1 нулевого бита ENABLE одноимённого регистра включает АЦП.

3. Регистры CH[N].PSELP и CH[N].PSELN ответственны за подключение положительного и отрицательного, соответственно, полюсов канала АЦП к тому или иному выводу МК.
Дело в том, что для аналого-цифрового преобразователя nRF52832 предусмотрено два режима работы, выбор между которыми осуществляется битами MODE регистра CH[N].CONFIG:
• Single ended, когда отрицательный полюс подключён к земле, а положительный — к одному из пинов AIN.
• Differential, когда оба полюса подключены к двум разным пинам AIN.

В случае, если выбран режим Differential, посредством битов регистров PSELP/PSELN и назначаются выводы подключения обоих полюсов АЦП.
Для нашего проекта подходит режим Single ended, поэтому оставим дефолтное нулевое значение для регистра PSELN, что обусловит состояние Not connected для отрицательного полюса. Положительный же полюс АЦП подключим к пину P0.02 (AIN0), записав число 1 в биты 0—4 регистра PSELP.

4. С помощью регистра CH[0].CONFIG осуществим следующие настройки для канала AIN0:
• Откажемся от подключения внутреннего резистора оставив дефолтное значение битов RESN.
• Остановимся на усилении ¼, записав число 2 в биты GAIN. Тогда, согласно формулы из пункта 37.8 «Reference» вышеуказанного раздела Product Specification, мы получим диапазон измерений от 0 В до напряжения питания микроконтроллера VDD, при условии выбора последнего в качестве источника опорного напряжения.
• Выберем упоминавшийся выше дефолтный режим Single ended, оставив без изменения значения битов MODE.

5. Регистр RESOLUTION позволяет выбрать разрядность АЦП. Запишем число 3 в биты VAL, установив тем самым разрядность в 14 бит.

6. В регистр RESULT.PTR записывается адрес в SRAM, куда помещается результат измерения. По аналогии с модулем PWM мы объявим в секции данных файла adc.S переменную adcValue, адрес которой и запишем в указанный регистр.

7. Регистр RESULT.MAXCNT определяет размер буфера для хранения результатов измерения. Нас вполне устроит число 1.

После осуществления вышеуказанных настроек останется лишь разрешить прерывание АЦП глобально, а обработчике прерывания скопировать число из переменной adcValue в переменную pwmSeq и перезапустить последовательность PWM0.

Внесём в шаблонный хидер-файл необходимые макроопределения.

/* Адреса и номера битов регистров модуля АЦП */
SAADC = 0×40007000
  SAADC_TASKS_START = 0×000
  SAADC_TASKS_SAMPLE = 0×004
  SAADC_EVENTS_STARTED = 0×100
  SAADC_EVENTS_RESULTDONE = 0×10C
  SAADC_INTENSET = 0×304
  SAADC_INTENCLR = 0×308
    SAADC_RESULTDONE = 3
  SAADC_ENABLE = 0×500
      SAADC_ENABLE_ENABLE = 0
  SAADC_CH_0_PSELP = 0×510
  SAADC_CH_0_PSELN = 0×514
      AIN0 = 1
  SAADC_CH_0_CONFIG = 0×518
      GAIN = 8
      REFSEL = 12
  SAADC_RESOLUTION = 0×5F0
  SAADC_RESULT_PTR = 0×62C
  SAADC_RESULT_MAXCNT = 0×630

И оформим всё изложенное выше в файлах adc.
adc.h

.include "nRF52832.h"
.include "macro.h"

SAADC_MAXCNT_VALUE     = 1
SAADC_RESOLUTION_VALUE = 3
SAADS_VALUE_LIMIT      = 16383

.global	SAADC_Handler, adcInit, adcStartConversion

adc.S

.include "adc.h"
  .data	
    .global adcValue
    adcValue: .word 0
  .text
    SAADC_Handler:
      PUSH   {LR}
      /* Сбросить флаг EVENTS_RESULTDONE */
      LDR    r0, =(SAADC + SAADC_EVENTS_RESULTDONE)
      LDR    r1, =0
      STR    r1, [r0]
      /* Скопировать значение  adcValue в pwmSeq */
      LDR    r0, =adcValue
      LDR    r1, [r0]
      LDR    r0, =SAADS_VALUE_LIMIT
      AND    r1, r0
      LDR    r0, =pwmSeq
      STR    r1, [r0]
      /* Перезапустить последовательность PWM0 */
      LDR    r0, =(PWM0 + PWM_TASKS_SEQSTART_0)
      LDR    r1, =1
      STR    r1, [r0]
      /* Возврат из обработчика */
      POP    {PC}

    adcInit:
      PUSH   {LR}
      /* Положительный полюс соединить с пином AIN0(P0. 02) */
      LDR    r0, =(SAADC + SAADC_CH_0_PSELP)
      LDR    r1, =AIN0
      STR    r1, [r0]
      /* Разрешение АЦП - 14 бит */
      LDR    r0, =(SAADC + SAADC_RESOLUTION)
      LDR    r1, =SAADC_RESOLUTION_VALUE
      STR    r1, [r0]
      /* Усиление сигнала АЦП - 1/4 */
      LDR    r0, =(SAADC + SAADC_CH_0_CONFIG)
      LDR    r1, =(1 << REFSEL) | (2 << GAIN)
      STR    r1, [r0]
      /* Указать адрес буфера для хранения результатов измерения */
      LDR    r0, =(SAADC | SAADC_RESULT_PTR)
      LDR    r1, =adcValue
      STR    r1, [r0]
      /* Установить размер буфера для хранения результатов измерения */
      LDR    r0, =(SAADC + SAADC_RESULT_MAXCNT)
      LDR    r1, =SAADC_MAXCNT_VALUE
      STR    r1, [r0]
      /* Разрешить локально прерывание АЦП по завершению измерения */
      setBit (SAADC + SAADC_INTENSET), SAADC_RESULTDONE
      /* Разрешить прерывание АЦП глобально */
      setBit ISER0, SAADC_INTERRUPT_BIT
      /* Включить АЦП */
      setBit (SAADC + SAADC_ENABLE), SAADC_ENABLE_ENABLE
      /* Возврат из функции */
      POP    {PC}

    adcStartConversion:	
      PUSH   {LR}
      /* Начать измерение */
      LDR    r0, =(SAADC + SAADC_TASKS_START)
      LDR    r1, =1
      STR    r1, [r0]
     eventsStarted:
      LDR    r0, =(SAADC + SAADC_EVENTS_STARTED)
      LDR    r1, [r0]
      CMP    r1, 0
      BEQ    eventsStarted
      LDR    r0, =(SAADC + SAADC_TASKS_SAMPLE)
      LDR    r1, =1
      STR    r1, [r0]
      /* Возврат из функции */
      POP    {PC}
. end

Обратите внимание на следующие моменты:

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

б) Запуск измерения АЦП в функции adcStartConversion реализуется в два этапа: сначала запускается задача TASKS_START и ожидается событие EVENTS_STARTED и лишь затем запускается задача сэмплирования TASKS_SAMPLE.

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

TIMER2_Handler:
  PUSH {LR}
  /* Сбросить флаг TIM_EVENTS_COMPARE_0 */
  LDR r0, =(TIMER2 + TIM_EVENTS_COMPARE_0)
  LDR r1, =0
  STR r1, [r0]
  /* Запустить измерение АЦП */
  BL adcStartConversion
  /* Увеличить на 1 значение r5 и сравнить с COUNTER_MAX */
  ADD r5, 1
  CMP r5, COUNTER_MAX
  /* Если меньше, перейти к метке return */
  BNE return
  /* В противном случае, обнулить r5 */
  LDR r5, =0
  /* и изменить состояние жёлтого светодиода на противоположное */
  BL yellowLedToggle
 return:
  /* Возврат из обработчика */
  POP {PC}

Для полного завершения проекта необходимо внести дополнения в main. S.

.include "nrf52832.h"
.include "macro.h"
  .data

  .text	
    .org 0
      /* Указать на вершину стека */
      stackPointerInit
    .org Reset_vector
      .word main + 1
    .org SAADC_vector
      .word SAADC_Handler + 1
    .org TIMER2_vector
      .word TIMER2_Handler + 1

    .global main
    main:
      /* Настроить выводы управления светодиодами */
      BL  ledsInit
      /* Настроить модуль PWM */
      BL  pwmInit
      /* Настроить АЦП */
      BL  adcInit
      /* Настроить таймер */
      BL  timerInit
    main_loop:
      /* Перейти к метке main_loop */
      B   main_loop
.end

И можно полюбоваться на результаты своих трудов.

Видео 4. Результат работы проекта lightControl для МК nRF52832.

Весь код настоящей главы, разнесённый по четырём папкам (blink, blinkTimer, blinkTimerPWM и lightControl), выложен в архив.
🎁code.zip  206.76 Kb ⇣ 24

Спасибо за внимание! ?
Продолжение следует.

 

Программируемый таймер — RadioRadar

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

Отсчёт выдержки начинают с нажатия на кнопку «Старт», при этом её нельзя удерживать нажатой более 2 с. Длительности выдержки и паузы могут быть заданы в пределах от 1 до 255 единиц отсчёта времени (секунд или минут). Минимальный отсчитываемый интервал времени равен 1 с, максимальный — 4 ч 15 мин. Число циклов выдержка/пауза может быть задано от 0 (однократная выдержка) до 255 либо неограниченным.

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

Для удобства таймер оснащён световой и звуковой сигнализацией. Питать его можно от сетевого адаптера с выходным постоянным напряжением 15…20 В или от аккумуляторной батареи напряжением 12 В.

Схема таймера изображена на рис. 1. Его основа — микроконтроллер ATmega8L-8PU [1, 2]. Источник тактового сигнала — внутренний RC-генератор микроконтроллера частотой 1 МГц. Для повышения точности отсчёта интервалов времени применено тактирование внутреннего таймера-счётчика T/C2 микроконтроллера от встроенного генератора с внешним кварцевым резонатором ZQ1 частотой 32768 Гц. При включённом предварительном делителе частоты на 128 восьмиразрядный таймер T/C2 переполняется каждую секунду и генерирует запрос прерывания.

Рис. 1. Схема таймера

 

Исполнительным устройством таймера служит реле K1, которым управляет сигнал с вывода PC5 микроконтроллера через ключ на транзисторе VT1.

Контакты реле выведены на зажимную колодку XT 1 и могут управлять внешними устройствами, в том числе питающимися от сети ~230 В.

Звуковые сигналы подаёт электромагнитный излучатель звука HA1 (со встроенным звуковым генератором), которым управляет сигнал с выхода PC4 микроконтроллера.

Напряжение питания +5 В подано на микроконтроллер с выхода интегрального стабилизатора DA2, на вход которого напряжение + 12 В поступает либо с гнез-даXS1 через интегральный стабилизатор DA1, либо от аккумуляторной батареи, подключённой к контактам 2 и 1 зажимной колодки XT1. Источник питания выбирают перестановкой перемычки S1.

Светодиод HL1 зелёного свечения сигнализирует о наличии питающего напряжения. По характеру свечения светодиода HL2 красного цвета видно, в каком состоянии находится таймер. Во время отсчёта выдержки программа включает светодиод HL2 на 1,5 с и выключает его на 0,25 с, во время отсчёта паузы — наоборот. По завершении рабочего цикла светодиод HL2 гаснет.

DIP-выключатели SA1-SA8 предназначены для задания длительности выдержки, длительности паузы и числа циклов выдержка-пауза. Такими же выключателями SA9-SA12 выбирают, согласно таблице, задаваемый параметр. В этой таблице указаны также адреса ячеек EEPROM микроконтроллера, хранящих соответствующие параметры.

Таблица

Состояние выключателя

Параметр

Ячейка EEPROM

SA9

SA10

SA11

SA12

3

3

X

X

Запрещённая комбинация

Р

3

X

X

Длительность выдержки

001

3

Р

X

X

Длительность паузы

002

р

Р

X

X

Число циклов

003

X

X

3

X

Счёт времени в секундах

004

X

X

р

X

Счёт времени в минутах

X

X

X

3

Огр. циклический режим

005

X

X

X

Р

Непр. циклический режим

3 — замкнутое (ON), Р — разомкнутое (OFF), X — произвольное

 

Параметры набирают на DIP-выключателях SA1-SA8 в двоичном формате, что доставляет некоторые неудобства. Для перевода десятичных чисел в двоичные можно воспользоваться «Калькулятором» Windows. Чтобы записать набранную на DIP-выключателях информацию в EEPROM, достаточно нажать на кнопку SB1. Стартует таймер при нажатии на кнопку SB2.

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

Диоды VD1, VD3 защищают прибор от ошибочной полярности подключения источников питания, диод VD2 — защита от ЭДС самоиндукции, возникающей при коммутации обмотки реле.

Звуковая сигнализация таймера работает следующим образом. При удержании кнопки SB2 нажатой менее 2 с раздаётся короткий (0,1 с) звуковой сигнал, означающий, что отсчёт времени начался. Если используется ограниченный циклический режим, то сразу по завершении заданного числа циклов прозвучит сигнал длительностью 2 с. Если кнопку SB2 удерживать нажатой более 2 с, прозвучат пять сигналов длительностью 0,1 с, после чего таймер вернётся в исходное состояние.

Об успешной записи установленного параметра в EEPROM сигнализируют два звуковых сигнала длительностью по 0,1 с. Если произошла ошибка (например, ошибочно задана нулевая продолжительность выдержки, чего быть не должно), прозвучит сигнал длительностью 1 с. Услышав его, следует проверить положение DIP-выключателей SA1-SA8.

Таймер собран на плате из фольгированного стеклотекстолита толщиной 1,5 мм размерами 84×55 мм, чертёж которой показан на рис. 2. Транзистор 2SC945 можно заменить на КТ3102 с любым буквенным индексом или другой c аналогичными параметрами. Светодиоды можно использовать любые повышенной яркости, свечение которых хорошо заметно уже при токе 2…3 мА. Реле K1 — JZC-22F, но можно использовать и другое с катушкой на 12 В, подходящее по коммутируемому напряжению и току и по размерам.

Рис. 2. Чертёж печатной платы таймера

 

Рис. 3. Плата в сборе

 

Коды из прилагаемого к статье файла CL-TIMER.hex должны быть загружены в программную память микроконтроллера. Разряды его конфигурации SUT0, SUT1, CKSEL1, CKSEL2, CKSEL3, BODEN, BODLEVEL должны быть запрограммированы (установлены в состояние 0). Последние два из перечисленных разрядов включают в микроконтроллере внутренний детектор провалов напряжения питания и устанавливают порог его срабатывания равным 4 В. Это предотвращает сбои записи в EEPROM при пониженном напряжении питания.

Перед первым запуском таймера в EEPROM микроконтроллера из файла CL-TIMER. eep записывают следующие параметры: длительность выдержки — 10 с, длительность паузы — 10 с, число циклов — 3, режим работы — ограниченный циклический. В дальнейшем их можно будет изменить.

Задавая параметры, следует учитывать, что рабочий цикл таймера — сумма длительности выдержки и длительности паузы. Число циклов может быть и нулевым. Это означает, что реле K1 однократно включится на заданное время выдержки. Чтобы включить реле N раз, нужно задать N-1 рабочих циклов. Вот несколько примеров установки параметров.

Пример 1. Длительность выдержки — 10 с, длительность паузы — 8 с, режим работы — ограниченный циклический, число циклов — 5.

Начните с перевода всех чисел в двоичную систему счисления:

1010 = 000010102,

810 = 00 00 1 0002,

510 = 000001012.

Затем включите таймер, замкните выключатели SA11 (единица времени — секунда) и SA12 (ограниченный циклический режим), а также разомкните выключатель SA9 и замкните SA10 (длительность выдержки). Выключателями SA1 -SA8 наберите код 00001010 — SA2 и SA4 разомкнуты, все остальные замкнуты. Нажмите на кнопку SB1. Должны прозвучать два коротких сигнала.

 

Далее переведите выключатели SA9 и SA10 соответственно в замкнутое и разомкнутое положение (длительность паузы). Наберите на выключателях SA1-SA8 код 00001000 (SA4 разомкнут, остальные замкнуты) и вновь нажмите на кнопку SB1. Два коротких сигнала должны прозвучать ещё раз.

 

Теперь установите выключатели SA9 и SA10 в разомкнутое положение (число циклов). Наберите на выключателях SA1-SA8 код 00000101- выключатели SA1 и SA3 разомкнуты, остальные замкнуты. Ещё раз нажмите на кнопку SB1 и прослушайте два коротких сигнала. Выключите и вновь включите питание. Таймер готов к работе с новыми значениями параметров.

Пример 2. Длительность выдержки — 4 с, длительность паузы — 3 с, непрерывный циклический режим. 410 = 000001002, 310 = 000000112.

Включите таймер. Замкните выключатель SA11 и разомкните SA12 (непрерывный циклический режим). Разомкните SA9 и замкните SA10 (длительность выдержки). На выключателях SA1 -SA8 наберите код 00000100 — SA3 разомкнут, остальные замкнуты. Нажмите на кнопку SB1, прослушайте два коротких сигнала.

Далее замкните выключатель SA9 и разомкните SA10 (длительность паузы). Наберите на выключателях SA1-SA8 код 00000011 — SA1 и SA2 разомкнуты, остальные замкнуты. Нажмите на кнопку SB1. Должны прозвучать два коротких сигнала. Выключите и вновь включите питание. Таймер готов к работе с новыми значениями параметров.

Пример 3. Выдержка — 20 мин, однократно. 2010 = 000101002.

Включив таймер, разомкните выключатель SA11 (единица отсчёта времени — минута) и замкните SA12 (ограниченный циклический режим), разомкните SA9 и замкните SA10 (длительность выдержки). На выключателях SA1 -SA8 наберите код 00010100 — SA3 и SA5 разомкнуты, остальные замкнуты. Нажмите на кнопку SB1. Должны прозвучать два коротких сигнала.

Разомкните выключатели SA9 и SA10 (число циклов). Наберите на выключателях SA1-SA8 код 00000000 (все выключатели замкнуты). Нажмите на кнопку SA1 и прослушайте два коротких сигнала. Выключите и вновь включите питание. Таймер готов к работе с новыми значениями параметров.

Программа микроконтроллера имеется здесь.

Литература

1. Евстифеев А. В. Микроконтроллеры AVR семейств Tiny и Mega фирмы ATMEL. — М.: Додэка-XXI, 2008.

2. ATMega8, ATMega8L 8-bit AVR with 8K Bytes In-System Programmable Flash. — URL: http://ww1 .microchip.com/downloads/en/ DeviceDoc/Atmel-2486-8 — b it — AVR -microcontroller-ATmega8_L_datasheet.pdf (10.05.2018).

Автор: В. Геронда, г. Свердловск Луганской обл., Украина

avr%20atmega8%20timers спецификация и примечания по применению

MFG и тип ПДФ Теги документов
на 89×51

Резюме: MCS51 MCS-51 MCS51 AT89C51 tccr1a RXB8 LDI 001 ICCAVR ctc1 038s
Текст: Нет доступного текста файла


Оригинал PDF MCS51 224 МГц АТ90С1200/2313 AT89C1051/2051 В 90С4414/8515 AT89C51/52 at89x51 MCS51 МКС-51 MCS51 AT89C51 tccr1a RXB8 ЛДИ 001 ИККАВР ctc1 038с org/Product»>
2011 — Atmel AVR1926: XMEGA-B1 Xplained Руководство по началу работы

Резюме: Замечания по применению Пример кода на ассемблере avr adc avr studio 5 avr JTAGICE ATxmega usb A08-0735 atmel 0735 AVR1912 Светодиодный проект с использованием avr
Текст: Нет доступного текста файла


Оригинал PDF АВР1926: ATxmega128B1 439А-АВР-09/11 Atmel AVR1926: Руководство по началу работы с XMEGA-B1 Xplained Примечания по применению пример кода на ассемблере avr adc авр студия 5 авр JTAGICE ATxmega usb А08-0735 атмел 0735 АВР1912 Светодиодный проект с использованием avr
ЖУРНАЛ ПРИЛОЖЕНИЙ ATMEL

Реферат: Микроконтроллер AVR 8515 8515DEF AVR Studio AVR Studio 4 Руководство по приложениям atmel номер 3 AT90S8515a AT90S8515 Микроконтроллер avr AT90S8515AVR
Текст: Нет доступного текста файла


Оригинал PDF AT90S8515 DECR16TempR16 3АТ90С8515 F11LDI F11LDIOUTTemp0xFFR16 R160xFFTempR16 F11DDRB0xFF01DD F110xFFPORTBECPORTB0xFFPINB F11Temp0xFEPINB0xFF ЖУРНАЛ ПРИЛОЖЕНИЙ ATMEL Микроконтроллер AVR 8515. 8515DEF АВР Студия АВР Студия 4 руководство по приложениям atmel номер 3 AT90S8515a авр микроконтроллер АТ90С8515АВР
2008 — ЭВК1100

Резюме: Atmel EVK1100 EVK1101 ATMEL 626 AVR один MICTOR38 AVR 626 UC3A0512 ATMEL+626 DIP204
Текст: Нет доступного текста файла


Оригинал PDF ЭВК1100 32103С ЭВК1100 Атмел EVK1100 ЭВК1101 АТМЭЛ 626 АВР один МИКТОР38 АВР 626 UC3A0512 АТМЕЛ+626 ДИП204
2013 — ATxmega128A4U

Реферат: At90scr ATA6289 atmega644rf ATMEGA328 atxmega64a3 ATMEGA328 atxmega8 ata5831 ATXMEGA64B3
Текст: Нет доступного текста файла


Оригинал PDF 8/32-бит ATxmega128A4U At90scr ATA6289 атмега644рф ATMEGA328 atxmega64a3 ATMEGA328 atxmega8 ата5831 ATXMEGA64B3 org/Product»>
2003 — Атмел

Резюме: h5042-DL KMT-1603 IQD32.768KHZ AVR LCD J406 CR2450 3V 600MAH AVR микропроцессор jtag разъем AVR миниатюрная бабочка atmel
Текст: Нет доступного текста файла


Оригинал PDF
2003 — h5042-DL

Резюме: KMT-1603 U562246 IQD32 светозависимый резистор stk502 atmel BZX399-C1V8 SKRH Термистор NTC 5 кОм Датчик света LDR на аппаратной основе
Текст: Нет доступного текста файла


Оригинал PDF 4271Б h5042-DL КМТ-1603 U562246 32 иракских динара светочувствительный резистор stk502 атмел БЗС399-К1В8 СКРХ Термистор NTC 5кОм Датчик света LDR на аппаратной основе
2001 — Атмел 028

Аннотация: atmel avr isp «atmel studio» isp AVR ISP AVR ATmega32 atmel isp atmel atmega8 программирование isp CONNECTOR AT90С2313 АТ90С8515
Текст: Нет доступного текста файла


Оригинал PDF 10-контактный 492А-09/01/15М атмел 028 атмел авр исп «Атмел Студио» исп. Интернет-провайдер AVR АВР ATmega32 атмел провайдер программирование атмел атмега8 РАЗЪЕМ ИСП AT90S2313 AT90S8515
2010 — хмега АЦП

Аннотация: цифровой мультиметр XMEGA D avr XMEGA dma пример AVR1324 avr dac adc AVR1300 Atmel AVR XMEGA dma ADC15 ADC12
Текст: Нет доступного текста файла


Оригинал PDF AVR1324: 12-битный 378А-АВР-10/11 хмега АЦП ХМЕГА Д цифровой мультиметр авр XMEGA пример прямого доступа к памяти АВР1324 авр цап ацп АВР1300 Atmel AVR XMEGA DMA АЦП15 АЦП12
2006 — авр проекты

Резюме: нет абстрактного текста
Текст: Нет доступного текста файла


Оригинал PDF 32-битный АВР32 8/32-бит авр проекты
2001 — Атмел Мега 8

Аннотация: на меге 8 микроконтроллер atmeg16 avr spi ATICE30 AVR МИКРОКОНТРОЛЛЕР ATMEGA161 AVR биты регистра avr на меге 8 atmega163
Текст: Нет доступного текста файла


Оригинал PDF ICE30 ICE30: ICE30 ATmega161 Атмел Мега 8 микроконтроллер мега 8 atmeg16 авр спи ATICE30 МИКРОКОНТРОЛЛЕР AVR Биты регистра AVR авр на мега 8 атмега163 org/Product»>
2002 — АВР 200

Реферат: AVR Studio 5 AVR МИКРОКОНТРОЛЛЕР ATICE200 Внутрисхемный эмулятор эмулятор eeprom со свободной схемой AVR Studio Конфигурация выводов AVR AVR 8-БИТНЫЕ МИКРОКОНТРОЛЛЕРЫ atmel ice
Текст: Нет доступного текста файла


Оригинал PDF 1662Б 02.06.0M АТИС200. АВР 200 авр студия 5 МИКРОКОНТРОЛЛЕР AVR ATICE200 Внутрисхемный эмулятор бесплатный эмулятор eeprom АВР Студия конфигурация контактов avr 8-БИТНЫЕ МИКРОКОНТРОЛЛЕРЫ AVR атмел айс
2004 — внутрисистемный программатор для AT89S51

Реферат: atmel avr isp serial isp atmel at89s51 AVR ISP atmel atmega8 программирование avr atmega8535 AT89S51 ИНТЕРФЕЙС ПРОГРАММИРОВАНИЯ схема флеш программатора для AT89s52 isp РАЗЪЕМ AVR Studio
Текст: Нет доступного текста файла


Оригинал PDF 10-контактный AT90S8515 ATmega169 AT86RF401 АТ90С1200 AT90S8535 ATmega323 AT90S2313 ATmega32 AT90S2323 Внутрисистемный программатор для AT89S51 атмел авр исп серийный номер ip atmel at89s51 Интернет-провайдер AVR программирование атмел атмега8 авр atmega8535 ИНТЕРФЕЙС ПРОГРАММИРОВАНИЯ AT89S51 Схема флеш программатора для AT89s52 РАЗЪЕМ ИСП АВР Студия org/Product»>
DS1820 ASM пример

Реферат: codevision ds1307 avr avrasm32 ds1302 asm avr ds1302 asm Mega163 MEGA103 ds1820 avr 90S8535
Текст: Нет доступного текста файла


Оригинал PDF 28Подсказки 29Ограничения PCF8563Филипс PCF8583Даллас ДС1302 ДС1307 ДС1820/ДС1822 Пример ASM DS1820 кодовое видение ds1307 авр авразм32 ds1302 asm авр дс1302 асм Мега163 МЕГА103 ds1820 авр 90S8535
2010 — hdg104

Резюме: интерфейс tft ipod touch 2 bluetooth с AVR 32-bit AVR UC3 datasheet AT32UC3C1512 AT32UC3C сенсорный экран avr TFT LCD AVR AT32U AT32UC3B1512
Текст: Нет доступного текста файла


Оригинал PDF 32-битный hdg104 айпод тач 2 интерфейс bluetooth с AVR Техническое описание 32-битного AVR UC3 AT32UC3C1512 AT32UC3C сенсорный экран авр TFT LCD AVR АТ32У AT32UC3B1512 org/Product»>
2001 — блок-схема AVR

Аннотация: код verilog для 4-битного множителя testbench codevision 8-битный множитель VERILOG Реализация AVR с помощью verilog код verilog для реализации программирования des avr в микроконтроллере c микроконтроллер avr с использованием vhdl 8-битный множитель с использованием кода verilog
Текст: Нет доступного текста файла


Оригинал PDF АТ94К АТ94К doc2329 01.11.хМ Блок-схема АРН код Verilog для тестового стенда с 4-битным множителем кодовое видение 8-битный множитель VERILOG Реализация AVR от Verilog код verilog для реализации des avr программирование на c авр микроконтроллер микроконтроллер с использованием vhdl 8-битный множитель с использованием кода Verilog
2003 — Руководство пользователя AVR ICE 200

Реферат: СХЕМА AVR ГЕНЕРАТОР бесплатно СХЕМА AVR ГЕНЕРАТОР atadapem04 принципиальная схема SMD паяльная станция AVR 8515 микроконтроллер AT90EM04 ICE200 AT90E AVR 200
Текст: Нет доступного текста файла


Оригинал PDF 1413D Руководство пользователя AVR ICE 200 СХЕМА АВР ГЕНЕРАТОРА бесплатная СХЕМА AVR ГЕНЕРАТОРА atadapem04 принципиальная схема паяльной станции SMD Микроконтроллер AVR 8515. В 90EM04 ICE200 AT90E АВР 200
Микроконтроллер AVR 8515

Краткое описание микроконтроллера AVR 8515. AVR Studio 4. avr.
Текст: Нет доступного текста файла


Оригинал PDF
2005 — h5042-DL

Резюме: PC123 pin out stk502 atmel KMT-1603 jtag разъем AVR miniature J406 STK500 чертеж Kingstate 1603 светозависимый резистор LDR C401 диод
Текст: Нет доступного текста файла


Оригинал PDF 4271С h5042-DL Распиновка PC123 stk502 атмел КМТ-1603 jtag разъем AVR миниатюрный J406 Чертеж STK500 Кингстейт 1603 Светозависимый резистор LDR Диод С401
2011 — флип Atmel

Резюме: нет абстрактного текста
Текст: Нет доступного текста файла


Оригинал PDF AT90USB162 Атмел флип org/Product»>
дракон

Резюме: avr dragon avrdragon SCKT3100A3 avr dragon отладка 20-контактный разъем заголовка 28-40-контактный разъем jtag10, 40-контактный разъем JTAG atmega128
Текст: Нет доступного текста файла


Оригинал PDF hh90C1 Дракон авр дракон аврдрагон SCKT3100A3 отладка дракона avr 20-контактный разъем 28-40-контактный jtag10 Заголовок, 40-контактный РАЗЪЕМ JTAG atmega128
2013 — UC3A3256S

Резюме: UC3C0512CRevC UC256L4U UC3A0128 UC3C2512 UC3C0512 UC3B1128 UC3L032 uc3a0256 UC3C1512
Текст: Нет доступного текста файла


Оригинал PDF 32-битный 32 бит 8/32-бит АВР32, UC3A3256S UC3C0512CRevC UC256L4U UC3A0128 UC3C2512 UC3C0512 UC3B1128 UC3L032 uc3a0256 UC3C1512
2002 — авр проекты

Реферат: библиотека stk500 AVR Studio техническая библиотека AVR STK500 2510B-AVR-10 Atmel EEPROM заказ
Текст: Нет доступного текста файла


Оригинал PDF 98/ХР/ME/2000 2510Б-АВР-10/02/15М СТК500 авр проекты библиотека stk500 АВР Студия техническая библиотека АВР СТК500 2510Б-АВР-10 Заказ Atmel EEPROM org/Product»>
2001 — ДСА00359816

Аннотация: 32-битный загружаемый счетчик AT94K
Текст: Нет доступного текста файла


Оригинал PDF АТ94К АТ94К doc2326 01.11.хМ DSA00359816 32-битный загружаемый счетчик
2010 — AVR32907: Руководство по началу работы с AT32UC3C-EK

Реферат: Примечания по применению AT32UC3C AT32UC3C-EK avr dragon Блок-схема AVR atmel 442 AT32UC3C0512C AVR32907 проекты avr
Текст: Нет доступного текста файла


Оригинал PDF AVR32907: AT32UC3C-ЕК 32-битный 32-битный AT32UC3C0512C 2137А-АВР-10/10 AVR32907: Руководство по началу работы с AT32UC3C-EK Примечания по применению AT32UC3C авр дракон атмел 442 Блок-схема АРН AVR32907 авр проекты

Предыдущий 1 2 3 … 23 24 25 Next

Таймер на atmega8 с часовым модулем.

Программирование микроконтроллеров AVR

Этот таймер предназначен для установки времени экспозиции от 5 секунд до 100 минут. На его выходе стоит достаточно мощное электромагнитное реле, позволяющее коммутировать ток до 30А при напряжении 12В и ток до 10А при напряжении 220В. Благодаря использованию электромагнитного реле таймер может управлять не только отопительными или осветительными приборами, но и электронными устройствами, критичными к форме питающего переменного напряжения. Трансформаторный блок питания в сочетании с реле обеспечивает полную гальваническую развязку электронной схемы таймера от сети.

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

Таймер управляется кнопками S1, S2, S3, S4. Нажатие кнопки S1 включает нагрузку и запускает таймер. Чтобы установить время, в течение которого должна работать нагрузка, нажмите S4. Две самые значащие цифры (минуты) будут мигать на дисплее. Теперь кнопками S2 и S3 можно установить значение минут. Затем нужно снова нажать S4. Теперь будут мигать младшие значащие цифры, а секунды можно будет установить с помощью кнопок S2 и S3. Чтобы сохранить настройки, снова нажмите S4. Теперь индикатор покажет установленную выдержку. Чтобы запустить таймер, нажмите S1. Нагрузка включается, и показания индикатора начинают уменьшаться. Как только установленное время истечет, на индикаторе появится надпись «ВЫКЛ», и нагрузка отключится электромагнитным реле. Чтобы повторить, дважды нажмите кнопку S1. Первое нажатие изменит «OFF» на установленное время, а второе запустит таймер. Управление реле на клемме 23 D1. Включение является логической единицей. Ключ на VT5 и VT6 управляет электромагнитным реле К1. Такие реле используются в схемах автомобильной сигнализации. Они могут коммутировать как постоянный ток (12В), так и переменный ток (220В), так как имеют хорошую изоляцию.

Блок питания выполнен на маломощном трансформаторе. Так как вторичная обмотка трансформатора отводится от середины (12-0-12), то выпрямитель выполнен не по мостовой, а по двухполупериодной схеме на двух диодах VD2 и VD3. Если трансформатор имеет обмотку 12В без отвода, то нужен выпрямительный мост. Реле питается напрямую с выхода выпрямителя, а остальная схема через регулятор напряжения А1 5В.

При прошивке нужно настроить на работу с внутренним генератором 8МГц.

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

Транзисторы КТ315 можно заменить на КТ3102 или любые аналоги. Транзистор КТ815 можно заменить на КТ817, КТ604. Диод КД521 — практически любой аналог. Диоды в выпрямителе КД209 — любые выпрямительные диоды на постоянный ток не менее 150 мА. Интегральный стабилизатор 7805 можно заменить на любой 5-вольтовый, например, КР142ЕН5А. Или сделать стабилизатор по параметрической схеме на двух транзисторах и стабилитроне 5В. Что касается показателей, то об этом было сказано выше. Это могут быть любые семисегментные индикаторы с общим анодом (катодом).

Архив к статье «Таймер на Atmega8 и светодиодные индикаторы»
Описание: Файлы прошивки
Размер файла: 5,58 КБ Количество загрузок: 4 319

Этот таймер предназначен для установки времени экспозиции от 5 секунд до 100 минут. На его выходе стоит достаточно мощное электромагнитное реле, позволяющее коммутировать ток до 30А при напряжении 12В и ток до 10А при напряжении 220В. Благодаря использованию электромагнитного реле таймер может управлять не только отопительными или осветительными приборами, но и электронными устройствами, критичными к форме питающего переменного напряжения. Трансформаторный блок питания в сочетании с реле обеспечивает полную гальваническую развязку электронной схемы таймера от сети.

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

Таймер управляется кнопками S1, S2, S3, S4. Нажатие кнопки S1 включает нагрузку и запускает таймер. Чтобы установить время, в течение которого должна работать нагрузка, нажмите S4. Две самые значащие цифры (минуты) будут мигать на дисплее. Теперь кнопками S2 и S3 можно установить значение минут. Затем нужно снова нажать S4. Теперь будут мигать младшие значащие цифры, а секунды можно будет установить с помощью кнопок S2 и S3. Чтобы сохранить настройки, снова нажмите S4. Теперь индикатор покажет установленную выдержку. Чтобы запустить таймер, нажмите S1. Нагрузка включается, и показания индикатора начинают уменьшаться. Как только установленное время истечет, на индикаторе появится надпись «ВЫКЛ», и нагрузка отключится электромагнитным реле. Чтобы повторить, дважды нажмите кнопку S1. Первое нажатие изменит «OFF» на установленное время, а второе запустит таймер. Управление реле на клемме 23 D1. Включение является логической единицей. Ключ на VT5 и VT6 управляет электромагнитным реле К1. Такие реле используются в схемах автомобильной сигнализации. Они могут коммутировать как постоянный ток (12В), так и переменный ток (220В), так как имеют хорошую изоляцию.

Блок питания выполнен на маломощном трансформаторе. Так как вторичная обмотка трансформатора отводится от середины (12-0-12), то выпрямитель выполнен не по мостовой, а по двухполупериодной схеме на двух диодах VD2 и VD3. Если трансформатор имеет обмотку 12В без отвода, то нужен выпрямительный мост. Реле питается напрямую с выхода выпрямителя, а остальная схема через регулятор напряжения А1 5В.

При прошивке нужно настроить на работу с внутренним генератором 8МГц.

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

Транзисторы КТ315 можно заменить на КТ3102 или любые аналоги. Транзистор КТ815 можно заменить на КТ817, КТ604. Диод КД521 — практически любой аналог. Диоды в выпрямителе КД209 — любые выпрямительные диоды на постоянный ток не менее 150 мА. Интегральный стабилизатор 7805 можно заменить на любой 5-вольтовый, например, КР142ЕН5А. Или сделать стабилизатор по параметрической схеме на двух транзисторах и стабилитроне 5В. Что касается показателей, то об этом было сказано выше. Это могут быть любые семисегментные индикаторы с общим анодом (катодом).

Архив к статье «Таймер на Atmega8 и светодиодные индикаторы»
Описание: Файлы прошивки
Размер файла: 5,58 КБ Количество загрузок: 4 319

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

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

Из этих соображений сделал программу-таймер, отвечающую следующим параметрам:

— компактный дизайн и простая схемотехника;

— оперативное кнопочное управление;

— при управлении кнопками, дублирование действий на ЖКИ;

— установка времени с точностью до секунды;

— диапазон счета от 1 секунды до 24 часов;

— запуск функции, пауза;

— функция сброса обратного отсчета и установки значений времени;

— при достижении значения 00. 00.00 включается исполнительное устройство;

В этом проекте реализованы все поставленные задачи.

Описание режимов работы таймера

После включения таймера можно установить нужное нам время. Назначение кнопок видно на схеме. После установки нажимаем кнопку СТАРТ — начинается обратный отсчет. Максимальное время воздействия — 23.59.59.

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

Как только время доходит до 00.00.00, включается светодиод (на данный момент это имитация включения исполнительного устройства, а можно просто бибикать генератором).

Если нажать кнопку СТАРТ-ПАУЗА во время работы таймера, таймер остановит отсчет, двойное нажатие кнопки СТАРТ-пауза возобновит остановленный обратный отсчет.

Для отключения нагрузки после включения нужно нажать RESET, данные таймера будут установлены 00.00.01 — нагрузка выключена. Или установить новый период обратного отсчета и дважды нажать кнопку СТАРТ-пауза.

Отображение символов на ЖК-дисплее ! > означает, что нагрузка отключена (PD3), и при однократном нажатии кнопки СТАРТ установленное время начнет обратный отсчет.

Внешний кварц 8 МГц, для точности счета.

Для программатора в PonyProg и CodeVisionAVR нужно снять все галочки с настроек тактового генератора.

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

Внимание! Что касается битов FUSE. Это основной источник ошибок, приводящих к блокировке контроллера.

— CKSEL3… 0 НЕ должен быть запрограммирован.

В PonyProg и CodeVisionAVR они стоят так:

LCD должен быть на базе контроллера HD4480

16×1, для него есть прошивка V-1 в архиве

прошивка V

8×1, архив -2.

Схему можно проверить в протеусе.

Если при моделировании проекта в proteus кириллические символы отображаются на ЖКИ некорректно, то распаковать библиотеку для правильного отображения кириллических символов на ЖКИ LCDrus . zip (приложен в архиве проекта) в папку models proteus.

Этот прибор пригодится любому человеку, которому приходится постоянно готовить, и надежно защитит вашу кухню от чрезмерного количества дыма. Это устройство, как следует из названия, представляет собой кухонный таймер. Он предназначен для подсчета интервалов при приготовлении различных блюд. Таймер имеет несколько кнопок, с помощью которых можно легко установить время от 1 минуты до 99 часов. Обратный отсчет начинается автоматически через 3 секунды после установки времени. Благодаря громкому зуммеру вы обязательно услышите, когда блюдо будет готово. Устройство собрано на базе микроконтроллера ATMega8.

Описание конструкции

Сердцем устройства является микроконтроллер U1 (ATMega8) с кварцевым резонатором Х1 (16 МГц) и обвязкой из двух конденсаторов С1 (22 пФ) и С2 (22 пФ). Стабилизатор U2 (7805) с обвязкой из конденсаторов С3 (100 мкФ) и С4 (47 мкФ) стабилизирует напряжение питания 5В, необходимое для корректной работы микроконтроллера и сопутствующих компонентов. На разъем Zas подается напряжение 7-12В. Если у вас блок питания с напряжением 5-6В, можно исключить из схемы стабилизатор напряжения. Зуммер B1 и аноды дисплея W1 управляются транзисторами Т1 — Т5 (BC556), с обвязкой резисторов R1 — R8 (3,3 кОм), R17 (3,3 кОм) и R18 (3,3 кОм). Резисторы R9- R16 (330 Ом) ограничивают ток через сегменты дисплея. Разъем Prog и один контакт R используются для подключения программатора. Клавиатура таймера подключается к разъему Sw.

Производство

Изображение печатной платы устройства находится в архиве в конце статьи. Установка деталей начинается с пайки двух перемычек. Затем устанавливаются все резисторы и другие элементы по порядку от меньшего к большему. Quartz X1 должен быть «низким» — он помещается под дисплеем — иначе он туда просто не влезет. Зуммер B1 можно припаять к плате как показано на фото, но позже выяснилось, что после закрытия корпуса звук слишком тихий (несмотря на просверленные отверстия в корпусе). Зуммер лучше приклеить к одной из сторон корпуса (как показано на последнем фото) и припаять проводом к плате. Клавиатура состоит из 5 незапирающихся кнопок 12х12мм прямо на передней части корпуса, так что их толкатели находятся чуть выше поверхности корпуса. Для этого устройства хорошо использовать зарядное устройство для телефона в качестве источника питания из-за его легкого веса и размера.

Список радиоэлементов
Обозначение Тип Номинал Количество Записка Магазин Мой блокнот
У1 MK AVR, 8-битный

ATmega8A-AU

1 В блокнот
У2 Линейный регулятор

LM7805CT

1 В блокнот
Т1-Т5 Биполярный транзистор

BC556

5 В блокнот
С1, С2 Конденсатор 22 пФ 1 В блокнот
С3 100 мкФ 1 В блокнот
С4 Электролитический конденсатор 47 мкФ 1 В блокнот
Р1-Р8, Р17, Р18 Резистор 3,3 кОм 10 В блокнот
Р9-Р16 Резистор 330 Ом 8 В блокнот
W1 7-сегментный индикатор AF-05643FG-B 1 Или с аналогичной распиновкой В блокнот
В1 Пьезодинамик с генератором 1

На нашем сайте, посвященном различным электронным самоделкам, уже несколько раз публиковались схемы. Конечно, они уступают современным промышленным аналогам, где есть дисплей, программирование и другие сервисные функции. И вот пришло время разместить такую ​​схему, которая будет на равных конкурировать с лучшими фирменными разработками. Цифровой таймер используется для управления работой электроприборов по запрограммированному расписанию. Этот программируемый таймер основан на микроконтроллере PIC16F628A , который можно запрограммировать на включение и выключение подключенного к нему электроприбора по расписанию, который управляется через реле. Таймер позволяет вручную установить время включения и выключения. Максимальный временной интервал, который можно настроить для включения и выключения, составляет 99 часов 59 минут. Проект рассчитан на использование ЖК-дисплея 16×2 и 4 кнопок.

Здесь 5-вольтовое реле управляется транзистором PN2222, который, в свою очередь, управляется RB3 PIC16F628A. Цифровые входы 4 кнопок считываются с помощью портов ввода-вывода RA2, RA3, RA4 и RB0. Стандартный ЖК-дисплей 16 x 2 символа используется для отображения состояния устройства, программы, меню и времени. ЖК-дисплей работает в 4-битном режиме, поэтому для работы необходимо всего 6 выводов ввода-вывода PIC16F628A. Пьезоэлектрический зуммер издает звуковой сигнал, когда таймер запускается и останавливается. Он также издает звуковой сигнал при включении или выключении устройства. Напряжение питания для схемы поступает от стабилизатора LM7805. На его вход подается 9V от сетевого адаптера. Светодиодная подсветка улучшает читаемость ЖК-дисплея в условиях низкой освещенности.

Таймер получает команды от 4 кнопок. Их функции следующие:

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

Выбор : позволяет выбрать параметры включения и выключения, а также числа часов и минут. Выбранная цифра увеличивается нажатием кнопки ON/OFF.

Ввод : Когда выбрано соответствующее время, нажатие этой кнопки завершит настройку.

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

Программируемый контроллер «день-ночь» на базе ATmega8

Недорогой, простой в сборке, мощный светодиодный контроллер со встроенным программируемым таймером.

Этот проект публикуется с разрешения автора и загружается через наш Upload Project

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

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

Основным компонентом этого программируемого фонаря является маломощный CMOS-микроконтроллер ATmega8. Основной причиной выбора этого микроконтроллера является его более низкая стоимость и более высокая доступность. За исключением двух вышеупомянутых причин, этот микроконтроллер также поставляется с богатым набором периферийных устройств, включая 23 GPIO, 3 независимых таймера, двухпроводной последовательный интерфейс, EEPROM и т. д.

Помимо микроконтроллера ATmega8, эта система использует часы реального времени DS1307 для поддерживать системное время. Как и ATmega8, DS1307 также является очень популярным RTC на рынке.

Рис. 1: Передняя сторона программируемого контроллера освещения

Этот контроллер предназначен для работы с источником питания 24 В постоянного тока. Основная причина выбора 24 В заключается в том, что большинство светодиодных модулей средней мощности на рынке предназначены для работы с этим напряжением. Во время моих поисков все светодиодные модули средней мощности, которые я нашел, рассчитаны на работу в диапазоне 20–28 В. Из этих светодиодных модулей большинство модулей рассчитаны на входное напряжение 24 В.

Для этой схемы рекомендуется использовать портативный импульсный источник питания 24 В 1,5 А. За исключением каскада драйвера светодиода, все остальные части этого светового контроллера рассчитаны на работу с напряжением 5 В. Преобразователь постоянного тока MC34063 используется для подачи 5 В на эти компоненты.

Рис. 2: Задняя сторона программируемого контроллера освещения

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

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

Конструкция

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

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

Рис. 3: Все компоненты этой платы спаяны с помощью обычного паяльника.

После изготовления печатной платы можно приступать к впаиванию в нее компонентов. Сначала попробуйте припаять микросхемы SMD. После пайки всех трех микросхем начните с небольших компонентов SMD, таких как резисторы, конденсаторы и транзисторы SOT-23. Я настоятельно рекомендую установить семисегментный дисплей, базу с разъемом постоянного тока, зажимы для батареи и клеммную колодку на последних этапах пайки.

Загрузка микропрограммы в систему

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

Эта система предназначена для работы со стандартным 6-контактным интерфейсом AVR ISP, и для этого интерфейса доступно множество программаторов.

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

Рис. 4: Заголовок ISP для подключения программаторов.

В процессе загрузки прошивки обратите особое внимание на настройки фьюза ATMega8. Чтобы получить ожидаемые результаты, low-fuse байта должны быть установлены в 0xEF, а high-fuse байта должны быть установлены в 0xD9. Дополнительные сведения см. в документации проекта на GitHub.

При использовании ISP обязательно отключите источник питания 24 В от системы.

Светодиодный модуль

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

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

Рис. 5: Частично собранный светодиодный модуль.

При длительном освещении светодиодом его температура повышается до 70°C – 80°C. По этой причине обязательно предпримите необходимые действия, чтобы изолировать проводную линию светодиода от температуры. Чтобы преодолеть это, в прототипе я пропустил этот провод через термостойкую базальтовую втулку 9.0834 .

Использование контроллера освещения

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

Системное меню состоит из 4 опций, таких как « SYS », « ON », « OFF » и «». Вы можете изменить значение, связанное с каждой опцией, нажав кнопку « MODE ». В Системное меню нажимайте кнопки « ВВЕРХ » или « ВНИЗ » для навигации по доступным режимам.

Рис. 6: Контроллер освещения в режиме ожидания.

В режиме « SYS » пользователь может изменить системное время. Аналогичным образом в режимах « ON » и « OFF » пользователь может установить время включения и выключения света соответственно.

Для выхода из системного меню нажмите кнопку « MODE » в режиме «».

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

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

Файлы проекта и исходный код

Проект контроллера освещения, описанный в этой статье, является сертифицированным открытым аппаратным проектом. Все файлы дизайна и исходные файлы прошивки этого проекта доступны для загрузки на GitHub.

Скомпилированная прошивка и Gerber-файлы платы также доступны для загрузки на странице выпуска GitHub.

Весь контент этого проекта распространяется на условиях следующей лицензии:

  • Аппаратная лицензия: CC BY 4.0
  • Лицензия на программное обеспечение: MIT
  • Лицензия на документацию: CC BY 4.0

Eggtimer с ATmega8

Eggtimer с ATmega8 Дорожка: Главная => AVR-RU => Приложения => Eggtimer m8 Diese Seite на немецком языке:
Приложения AVR

Eggtimer с ATmega8
Оборудование, монтаж, применение и программное обеспечение для 8-минутного таймера для яиц

  1. Свойства
  2. Оборудование
  3. Крепление
  4. Программное обеспечение
  5. Выводы
Это описывает таймер, который работает с восемью красными/зелеными дуэтными светодиодами. Может быть используется для управления процессом приготовления яиц. Этот таймер для яиц имеет восемь красных/зеленых светодиодов, максимум два из которых активны, чтобы сэкономить время. батарея. Первый отображает последнюю полную минуту красным цветом, второй текущая минута зеленым цветом с возрастающей долей красного. Когда минута закончилась, он воспроизводит программируемую мелодию на динамике.

Как всегда, я предоставляю следующие файлы, которые принадлежат этому проекту:

  • Электронные таблицы Libre-Office-Calc с расчетами,
  • Файл Libre-Office-Draw со схемой оборудования,
  • Файл Libre-Office-Draw с печатной платой,
  • Файл Libre-Office-Draw с блок-схемами.
Схема (здесь в формате Libre-Office) показывает восемь двойных красно-зеленых светодиодов, подключенных к портам B и D ATmega8. Два портбита управляют одним светодиодом. Резисторы R L ограничить ток светодиода до менее 10 мА. Их значения зависят от рабочего напряжения, таблица обеспечивает значения для большинства возможных рабочих напряжений.

При запуске и по прошествии каждой полной минуты воспроизводится мелодия. Это делается через динамик, подключенный к порту PC0 через электролитический конденсатор.

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

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

Все светодиоды должны быть установлены в одном направлении, более длинный светодиод должен быть прикреплен к нижнему отверстию.

Компоновка односторонняя, с установкой только одной перемычки. Единственная маленькая линия внутри штекера ISP6 немного рискованно, но с моим лазерным принтером, моим УФ-светодиодом, с разбавленным гидроксидом натрия и хлоридом железа-III все работало нормально.

Тем, кто не хочет программировать ATmega8 внутри печатной платы, не нужен штекер ISP6.

При рабочем напряжении 3 В таймеру необходимы следующие токи питания:

Phase State Number of LEDs Spreaker Current (mA)
Start First LED green, increasing red share 1 7.4
One Зеленый светодиод, воспроизведение тона 1 X 18
Running One LED red, next LED green with increasing red share 2 13.3
Ninth minute Ony tone playing X 8
Спящий режим Конец, готовность, выкл.

Загрузка программного обеспечения

Исходный код в формате ассемблера доступен здесь, исходный код в браузере нажмите здесь.

Сборка работает с любым современным ассемблером AVR.

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

Детали программного обеспечения

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

На рисунке показаны восемь светодиодов, прикрепленных к двум портам. На комбинации шон бит соответствующие светодиоды становятся красными или зелеными. Строка Hex показывает битовую комбинацию для записи в порты B и D.

Чтобы предыдущий светодиод также стал красным, светодиоды с номера 2 должны добавить свои предыдущее красное состояние. Это приводит к шестнадцатеричным кодам для PB и PD. Когда светодиод 5 активен в состоянии PWM, комбинации PB и PD не равны нулю.

Здесь показаны комбинации светодиодов с 1 по 3 и 8, расположенные в SRAM.

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

Во время работы пара регистров Y указывает на текущий активный светодиод. Когда минута заканчивается, четыре добавляются к Y. Программное обеспечение использует способность AVR для временно добавить смещение к Y при доступе к байтам +1, +2 и +3 текущая комбинация (ldd r,Y+n): при запуске фазы ШИМ записываются Y и Y+1 на порты B и D, при сравнении PWM в эти порты записываются совпадения Y+2 и Y+3.

Контроль времени

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

Если этот внутренний цикл PWM завершен, он повторяется 14 раз.

После этого выбирается следующее значение совпадения сравнения и все внутренние и внешние Циклы ШИМ повторяются. Если значение совпадения сравнения, наконец, достигает 256, минута кончено.

Для достижения этого времени таймер TC2 работает как CTC со значением совпадения сравнения, равным 60. Это обеспечивает идеальную частоту повторения: 1 000 000 / 1 / 61 / 256 = 64,037 Гц. Это означает 15,616 мс на внешний цикл ШИМ. 256 внешних циклов ШИМ с увеличением сравнение значений соответствия дает почти четыре секунды. Повторение внутреннего цикла ШИМ теоретически должно быть 15, чтобы достичь одной минуты. Практически я слишком долго измерял раз, поэтому вместо этого я уменьшил это значение повторения до 14. Обратите внимание, что на других операционных напряжения внутренний RC имеет разные частоты, поэтому либо увеличивайте, либо уменьшайте это значение.

Когда ATmega8 запускает таймер/счетчик TC2: значение совпадения сравнения равно настроен на 60, счетчик повторений в X установлен на 14, а таймер приведен в режим CTC с предварительным масштабированием 1. Все, что последует, выполняется в ISR. этого таймера. ,

Значение совпадения сравнения 60 означает, что все действия ISR должны быть завершено в течение этого времени, чтобы гарантировать, что ни одно прерывание не будет пропущено. В рамках исходный код, а также на блок-схемах перечислены необходимые такты. Нет необходимости тактировать ATmega8 с повышенной частотой для достижения это время.

Блок-схема синхронизации с OC2-ISR

Это полная процедура TC2-OC. Начинается со сравнения регистра rPwmCnt со значением совпадения сравнения в регистре rPwmCmp . Если оба равны, светодиод переключается на зеленый.

Затем увеличивается значение счетчика ШИМ в регистре rPwmCnt . Если это не дает ноль, ISR возвращается из прерывания.

Если rPwmCnt достигает нуля, счетчик повторений ШИМ в X уменьшается. Если это не достигает нуля, следующий внутренний цикл PWM запускается с помощью переключение светодиода на красный (перезапуск с помощью Y и Y+1).

Если счетчик повторений ШИМ достигает нуля, он перезапускается с 14 и значение совпадения сравнения в регистре rPwmCmp увеличено. Опять следующий цикл начинается с красного светодиода.

Если регистр совпадения сравнения X достигает нуля, указатель Y на SRAM увеличивается к четырем. Если это не достигает конца таблицы SRAM, следующая мелодия запускается (см. OC1A-ISR), и цикл снова начинается с красного светодиода, на который Y теперь указывает.

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

Воспроизведение мелодий

Воспроизведение мелодий использует 16-битный таймер TC1 в режиме CTC. ISR переключает выходной контакт динамика PC0 высокий и низкий.

Чтобы отрегулировать скорость делителя TC1 для определенного тона, программа удерживает таблица гаммы констант за меткой gamut: . Вот гамма частоты от 27 Гц до 18,794 кГц, более 9 1/2 октавы, присваиваются константам. Для камерного тона A4 сравните соответствие указано значение 1136 для сравнения A. Это дает 1 000 000 / 1 / 1 136 / 2 = 440,14 Гц. 2 следует из того факта, что полная волна состоит из двух циклов, одного высокого и одного низкого.

Примечательны два «тона»:

  1. cPs : Этот «тон» представляет собой паузу, результирующая частота генерируется, но вывод на вывод динамика подавляется установкой бит bQuiet в регистре флагов rFlag . Этот «тон» распознается по отпечатку пальца LSB, потому что это только один с этим LSB (см. таблицу для этой проверки).
  2. cEnd : Этот «тон» выключает воспроизведение музыки. Следующий мелодия воспроизводится, когда минута закончилась. Указатель Z указывает на следующий тон после cEnd. cEnd также распознается по младшему биту, потому что никакой другой тон имеет этот LSB (см. таблицу).
Потому что каждому тону нужно определенное количество волн, чтобы достичь одинаковой продолжительности. (называется частотой), эти длительности даны для всех тонов как константы с n за меткой Продолжительность: . Таким образом, для A4 сравните значение совпадения cA4 связан с длительностью nA4 . Все эти продолжительности выводятся из четырех секунд (музыканты знают почему). Полные заметки разделены на четыре, половинные ноты на 8, четверти на 16 и шестнадцатые на 32. Путем умножения те длительности с тремя перед делением на восемь также могут быть длительности 3/8 запрограммировано. Убедитесь, что ваше деление не достигает нуля, потому что это дает очень длинный, а не очень короткий тон.

Другое ограничение касается максимальной частоты, которая может быть сгенерирована. Как вы можете см. блок-схему, для OC1A-ISR требуется максимум 40 тактовых циклов. Что ограничит максимальную генерируемую частоту до cX8 тонов. К счастью половина прерываний просто переворачивает выходной контакт и требует всего 18 циклов. На все повторения волны нужно всего 21 цикл, так что времени много осталось для выполнения OC2-ISR и OC1A-ISR. Есть только ОДИН случай где нужны 40, значит 14 (cE9), 16 (cC9) или даже 17 кГц (cD9) может хорошо генерироваться. Я никогда не пробовал cE9, поэтому не могу гарантировать, что он работает. И: мой динамик не воспроизводит тона выше 12 Гц, поэтому проверить не могу Это.

С помощью этих констант мелодии могут быть закодированы в таблицы во флэш-памяти. Вот Интернационал в виде таблицы:

Международный:
  ; Фёлькер
  .dw ch4,nh4*3/16, cPs,nPs/16, cA3,nA3/16, cPs,nPs/16
  ; hört die Sign nale
  .dw cG2,nG2/2, cPs,nPs/16, cD2,nG2*3/8, cPs,nPs/16, ch2,nh2/8, cPs,nPs/16, cE2,nE2/2, cPs,nPs/ 16, cC2,nC2/4,cPs,nPs/8
  ; Auf zum letz-ten Gefecht!
  .dw cA3,nA3*3/16, cPs,nPs/16, cG2,nG2/16, cPs,nPs/16, cF2,nF2/2, cPs,nPs/16, cE2,nE2*3/8, cPs, НП/16, CD2, nD2/8, КП, НП/16, CD2, nD2/2, КП, НП/8
  ; Die International
  .dw cD2,nD2/4, cPs,nPs/16, ch4,nh4*3/8, cPs,nPs/16, ch4,nh4/8, cPs,nPs/16, cA3,nh4/4, cPs,nPs/ 16, cD2,nD2/4, cPs,nPs/16, cG2,nG2/2, cF2,nF2*3/8,cPs,nPs/8
  ; er-kämpft das Menschen-recht.
  .dw cF2,nF2*3/16, cPs,nPs/16, cE2,nE2/8, cPs,nPs/16, cD2,nD2/8, cPs,nPs/16, cE2,nE2/4, cPs,nPs/ 16, cA3,nA3/4, cPs,nPs/16, cA3,nA3/2
  .dw cEnd

 
Каждая нота состоит из двух слов: первое cXn определяет высоту тона, второй — длительность тона nXn в пакетах по четыре секунды. Если следующий тон должен быть разделен паузой, используйте cPs и nPs, чтобы определить длительность паузы. С помощью инструментов можно закодировать любую мелодию.

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

cEnd закрывает вывод мелодии. Он перезапускается каждую минуту.

Справа вы видите полный алгоритм вывода тона.

Видео таймер для яиц

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

Ссылка на видеофайл ogg и в файл MP4.

© 2021 http://www.avr-asm-tutorial.net

Введение в таймеры AVR » maxEmbedded

Опубликовано Mayank 22 июня 2011 г. в Atmel AVR, Начало работы, Микроконтроллеры | 139комментарии

Таймеры используются повсеместно. Без таймеров вы бы никуда не попали! Диапазон таймеров варьируется от нескольких микросекунд (например, тактов процессора) до многих часов (например, лекционных занятий  ), и AVR подходит для всего диапазона! AVR может похвастаться очень точным таймером, с точностью до микросекунд! Эта особенность делает их пригодными для использования в качестве таймеров. Посмотрим, как.

Вы каждый день сталкиваетесь с таймерами. Самый простой пример висит на стене или может быть привязан к запястью. Можно сказать, что они обладают уникальным свойством измерять время. Все в этом мире синхронизировано со временем. Вы просыпаетесь, скажем, в 6 часов; вы работаете каждый день по 8 часов; вам нужно пить воду каждые 4 часа и т. д. Но концепция таймеров не ограничивается вашими повседневными делами. Каждый электронный компонент работает на временной основе. Эта временная база помогает синхронизировать всю работу. Без базы времени вы бы не имели ни малейшего представления о , когда , чтобы сделать конкретную вещь.

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

AVR может похвастаться очень точным, точным и надежным таймером. Он предлагает множество функций, что делает его обширной темой. В этом уроке мы обсудим основные концепции таймеров AVR. В этом уроке мы не будем иметь дело с каким-либо кодом, только с концепциями. Процедура генерации таймеров и их кодов будет рассмотрена в следующих постах. 916 = 65536 шагов от 0 до 65535. Из-за этой особенности таймеры также известны как счетчики . Что происходит, когда они достигают своего МАКСИМАЛЬНОГО значения? Программа перестает выполняться? Что ж, ответ довольно прост. Он возвращается к своему начальному значению, равному нулю. Мы говорим, что таймер/счетчик переполняет .

В ATMEGA32 у нас есть три разных типа таймеров:

  • TIMER0 — 8-битный таймер
  • ТАЙМЕР1 — 16-битный таймер
  • ТАЙМЕР2 – 8-битный таймер

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

Помимо обычной работы, эти три таймера могут работать в нормальном режиме, режиме CTC или режиме PWM. Мы обсудим их один за другим.

Основные понятия

С детства нам знакома следующая формула:

Теперь предположим, что нам нужно мигать светодиодом каждые 10 мс. Это означает, что его частота составляет 1/10 мс = 100 Гц. Теперь предположим, что у нас есть внешний кварц XTAL 4 МГц. Следовательно, тактовая частота процессора составляет 4 МГц. Теперь, как я уже сказал, таймер считает от 0 до TOP. Для 8-битного таймера он считает от 0 до 255, тогда как для 16-битного таймера он считает от 0 до 65535. После этого происходит переполнение. Это значение изменяется при каждом тактовом импульсе.

Предположим, что значение таймера сейчас равно нулю. Чтобы перейти от 0 к 1, требуется один тактовый импульс. Чтобы перейти от 1 к 2, требуется еще один тактовый импульс. Чтобы перейти от 2 к 3, требуется еще один тактовый импульс. И так далее. Для F_CPU = 4 МГц период времени T = 1/4M = 0,00025 мс. Таким образом, для каждого перехода (от 0 до 1, от 1 до 2 и т. д.) требуется всего 0,00025 мс!

Теперь, как сказано выше, нам нужна задержка 10 мс. Это может быть очень короткая задержка, но для микроконтроллера с разрешением 0,00025 мс это довольно большая задержка! Чтобы получить представление о , сколько времени потребуется , давайте рассчитаем счетчик таймера по следующей формуле:

Замените Требуемая задержка = 10 мс и Период времени = 0,00025 мс , и вы получите Счетчик таймера = 39996 919199

0 . Вы можете себе это представить? Часы уже прошли 39999 раз, чтобы дать задержку всего 10 мс!

Теперь, чтобы добиться этого, мы точно не можем использовать 8-битный таймер (так как он имеет верхний предел 255, после чего он переполняется). Следовательно, мы используем 16-битный таймер (который может считать до 65535) для достижения этой задержки.

Предварительный делитель

Предполагая, что F_CPU = 4 МГц и 16-битный таймер (MAX = 65535), и подставляя в приведенную выше формулу, мы можем получить максимальную задержку 16,384 мс. А что, если нам нужна большая задержка, скажем, 20 мс? Мы застряли?!

Что ж, надеюсь, здесь есть решение. Предположим, если мы уменьшим F_CPU с 4 МГц до 0,5 МГц (т.е. 500 кГц), то период времени увеличится до 1/500k = 0,002 мс. Теперь , если заменить Требуемая задержка = 20 мс и Clock Time Period = 0,002 мс , мы получаем Timer Count = 9999 . Как видим, этого легко добиться с помощью 16-битного таймера. На этой частоте может быть достигнута максимальная задержка 131,072 мс.

Теперь вопрос как мы на самом деле уменьшаем частоту? Этот метод частотного деления называется предварительным масштабированием . Мы не уменьшаем фактический F_CPU. Фактический F_CPU остается прежним (в данном случае на частоте 4 МГц). В общем, мы извлекает из него частоту для запуска таймера. Таким образом, при этом мы делим частоту и используем ее. В AVR есть возможность сделать это, установив некоторые биты, которые мы обсудим позже.

Но не думайте, что вы можете свободно использовать прескалер. Это дорого обходится. Существует компромисс между разрешением и длительностью . Как вы, должно быть, видели выше, общая продолжительность измерения увеличилась с 16,384 мс до 131,072 мс. Как и разрешение. Разрешение также увеличилось с 0,00025 мс до 0,002 мс (технически разрешение фактически уменьшилось). Это означает, что каждый тик будет занимать 0,002 мс. Итак, в чем проблема? Проблема в том, что точность снизилась. Раньше вы могли точно измерить продолжительность, например 0,1125 мс (0,1125/0,00025 = 450), но теперь вы не можете (0,1125/0,002 = 56,25). Новый таймер может измерять 0,112 мс, а затем 0,114 мс. Никакого другого значения между ними.

Выбор предделителей

Давайте рассмотрим пример. Нам нужна задержка 184 мс (я выбрал любое случайное число). У нас F_CPU = 4 МГц. AVR предлагает нам на выбор следующие значения прескалера: 8, 64, 256 и 1024. Значение прескалера 8 означает, что эффективная тактовая частота будет F_CPU/8. Теперь, подставляя каждое из этих значений в приведенную выше формулу, мы получаем разные значения значения таймера. Результаты суммированы следующим образом:

Выбор прескалера

Теперь из этих четырех прескалеров нельзя использовать 8, так как значение таймера превышает предел 65535. Кроме того, поскольку таймер всегда принимает целые значения, мы не можем выбрать 1024 в качестве счетчик таймера представляет собой десятичную цифру. Следовательно, мы видим, что допустимы значения предварительного масштабирования 64 и 256. Но из этих двух мы выбираем 64, так как это дает нам большее разрешение. Мы можем выбрать 256, если нам нужен таймер на большую продолжительность в другом месте.

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

Мы обсудим, как это реализовать, в следующих постах.

Прерывания

Что ж, это относится не только к таймерам, но я решил обсудить его, поскольку он используется в самых разных местах. Позвольте мне объяснить это, используя аналогию. Скажи, что сейчас ты читаешь мой пост. Настало время ужина, и твоя мама (только если ты живешь с мамой ;)) зовет тебя на ужин. Что вы делаете (если она становится слишком жуткой)? Вы сохраняете свою работу и отвечаете на звонок мамы, затем возвращаетесь и продолжаете чтение. Это пример прерывания.

В большинстве микроконтроллеров есть нечто, называемое прерыванием. Это прерывание может быть запущено всякий раз, когда выполняются определенные условия. Теперь всякий раз, когда срабатывает прерывание, AVR останавливает и сохраняет выполнение основной процедуры, обрабатывает вызов прерывания (путем выполнения специальной процедуры, называемой процедурой обслуживания прерывания, ISR) и после завершения работы возвращается к основную процедуру и продолжает ее выполнение.

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

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

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

Так что берите RSS-каналы или подписывайтесь на мой блог, чтобы быть в курсе! И не забудьте опубликовать свой ответ внизу!

Так:

Нравится Загрузка…

atmega8 — Лаборатория оборудования с открытым исходным кодом EasyEDA

Описание

дизайн чертеж

схематическая диаграмма

( 1 / )

печатная плата

( 1 / )

Пустой

ID Имя Обозначение След Количество
1 Р1, Р2, Р3, Р4, Р5, Р6, Р7, Р8, Р9, Р10, Р11, Р12, Р13 ОСЕВОЙ-0,3 13
2 RC1602A-GHW-ESX ДС1 RC1602A 1
3 КОНДЕНСАТОР С1,С2,С3,С4,С5,С6,С7,С8,С9,С10 С1206 10
4 БГС50А Д1, Д2 СОТ-143 2
5 БЗТ52К10 Д3, Д4 СОД123 2
6 предохранительRXEF300 Ф1, Ф2 Ф-3. 00А-60В 2
7 Точка заземления шасси У1, У2, У3, У4, У5, У6 ШАССИ_ЗЕМЛЯ 6
8 транзисторов: 2N3906 К3, К4 ТО-92_MOLDED_NARROW 2
9 Кварц У7 КВАРЦ 1
10 АККУМУЛЯТОР БАТ1 АККУМУЛЯТОР 1
11 диод Д5, Д6, Д10 ДО-41 3
12 БУЗЕР В1 БУЗЕР 1
13 ЛМ7805КТ У8 ТО-220(ТО-220-3) 1
14 Л1 ОСЕВАЯ-0,4 1
15 Кнопка СБ1, СБ2, СБ3, СБ4, СБ5, СБ6 КНОПКА DIP-1-2,54 мм 6
16 atmega8_dip 1 ATMEGA8_DIP 1
17 НРП-03К К1 НРП-03К 1

Развернуть

Приложения к проекту

Участники проекта

0

1

Собрать в альбом

Загрузка.

Добавить комментарий

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