Sibprompost.ru

Стройка и ремонт
0 просмотров
Рейтинг статьи
1 звезда2 звезды3 звезды4 звезды5 звезд
Загрузка...

Прерывание счетчика для avr

Прерывание счетчика для avr

Если посмотреть таблицу прерываний для ATmega8 из предыдущего поста, то видно, что почти половина из них, это прерывания для работы с таймерами/cчетчиками. Также для их конфигурации отведена значительная часть регистров:

Также для их конфигурации отведена значительная часть регистров:

Чтобы в этом не увязнуть, я попробую, для начала, сделать что-нибудь простое, например Blink на таймере.

В ATmega8 три таймера, два 8-битных и один 16-битный. Я буду использовать нулевой Timer0, на который отведено одно прерывание по переполнению: TIMER0 OVR.

Счётчик таймера может тактироваться с частотой кварца, или в пропорциях 1:8 , 1:64, 1:256, 1:1024 к частоте кварца. Пропорция называется prescaler и выставляется в регистре TCCR0 (Timer/Counter Control Register) установкой соответствующих битов:


    Теперь считаем:
  • кварц 16МГц за секунду отрабатывает 16*1024*1024 тиков(На самом деле 16*1000*1000. И вместо числа 64 предложенного ниже, лучше использовать 61. ПРИМЕЧАНИЕ от 30.10.17);
  • если выставим пропорцию 1:1024, то счётчик будет индексироваться 16*1024 раз в секунду;
  • т.е. счётчик 8-битный то, его переполнение будет происходить каждые 256 индексаций, т.е. (16*1024)/256 = 64 раз в секунду;
  • т.е. чтобы моргать светодиодом один раз в секунду, в обработчик прерывания нам нужно поставить счётчик, который при достижения числа 64, будет сбрасываться и инвертировать порт со светодиодом.

Текст программы у меня получился таким:

Прошивка уместилась в 122 байта. Здесь сначала, счётчик myTimer объявляем как регистровую переменную. При наличии 32 регистров РОН, проблем с их нехваткой не должно быть. Для прерывания в файле interrupt.h определён макрос ISR, в качестве параметра следует указывать используемый вектор. С регистром TCCRO вроде уже разобрались, а регистр TIMSK отвечает за включённые таймеры.

Смотрим ассемблерный вариант программы сгенерированный avr-gcc:

Как видно на этот раз основную массу кода занимает подпрограмма обработки девятого прерывания, т.е. TIMER0_OVF, причём подпрограмму имеет характерную обёртку из PUSH POP команд сохраняющих используемые регистры. Однако, R1 и R0 вроде как не используются однако, тоже сохраняются, это т.н. fixed registers. Читаем официальные комментарии по этому поводу: https://gcc.gnu.org/wiki/avr-gcc

Fixed Registers Fixed Registers are registers that won’t be allocated by GCC’s register allocator. Registers R0 and R1 are fixed and used implicitly while printing out assembler instructions: R0is used as scratch register that need not to be restored after its usage. It must be saved and restored in interrupt service routine’s (ISR) prologue and epilogue. In inline assembler you can use __tmp_reg__ for the scratch register. R1always contains zero. During an insn the content might be destroyed, e.g. by a MUL instruction that uses R0/R1 as implicit output register. If an insn destroys R1, the insn must restore R1 to zero afterwards. This register must be saved in ISR prologues and must then be set to zero because R1 might contain values other than zero. The ISR epilogue restores the value. In inline assembler you can use __zero_reg__ for the zero register. Tthe T flag in the status register (SREG) is used in the same way like the temporary scratch register R0.

и вольный перевод найденый здесь:

Fixed registers (r0, r1), фиксированные регистры. Никогда не выделяются gcc для локальных данных, но часто используются для определённых целей: r0 — временный регистр, может использоваться любым кодом на C (за исключением обработчиков прерываний, которые сохраняют значение этого регистра). Обычно используется для запоминания чего-нибудь в пределах кусков кода ассемблера. r1 — подразумевается, что всегда 0 в любом коде C. Может использоваться для запоминания чего-нибудь в пределах кусков кода ассемблера, однако после использования должен очищаться (clr r1). Это относится к любому использованию инструкций [f]mul[s[u]], которые возвращают результат в паре регистров r1:r0. Обработчики прерываний сохраняют и очищают r1 на входе, и восстанавливают r1 на выходе (в том случае, если r1 не ноль).

Видим, что в таблице прерываний появился rjmp на подпрограмму, обработчик RESET уже разбирали, c push/pop командами разобрались. Main не представляет интереса, там только присваивание констант, зато декримент в обработчике нашего прерывания реализован, на первый взгляд мутно.

Читайте так же:
Счетчик хотлог как установить

Три первых оператора можно было заменить одним dec R28; Но если вспомнить Си там различается предекримент и постдекримент. И здесь как видно реализация постдекримента.

Итоговый исходник на ассемблере:

82 байта против 122 на Си, выглядит неплохо.

Прерывание счетчика для avr

Описание работы таймера/счётчика 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-ый такт процессора.

Читайте так же:
Сброс счетчика чернил canon ip2600

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).

Timer/Counter for AVR для начинающих

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

За основу мы возьмем очень популярную среди разработчиков устройств на МК книгу, автором которой является А.В. Евстифеев. По ссылкам в конце статьи Вы сможете найти проект в AVRStudio 6 и проект в Proteus 7. В этой статье мы разберем работу 8-ми битного Т/С Т2, который входит в состав Т/С МК Atmega8.

Итак, что же такое Таймер/Счетчик? Т/С — это один из модулей МК AVR с помощью которого можно отмерять определенные промежутки времени, организовать ШИМ и многие другие задачи. В зависимости от модели МК, количество Т/С может составлять 4 и более. Пример тому — МК Atmega640х, 1280х/1281х, 2560х/2561х, которые содержат на своем борту 6 Т/С: два 8-ми битных и четыре 16-ти битных. МК Atmega8 содержит в себе три Т/С: Т0 и Т2 с разрядностью 8 бит, Т1 с разрядностью 16 бит.

Давайте подробнее рассмотрим Т/С Т2 микроконтроллера Atmega8.

Этот таймер может работать в нескольких режимах: Normal, Phase correct PWM, CTC (сброс при совпадении), Fast PWM. Подробнее о каждом режиме Вы можете прочитать в книге.

Данный Т/С состоит из регистра управления, счетного регистра, регистра сравнения, регистра состояния асинхронного режима. Структурная схема Т2 приведена на рис.1

Рассмотрим в теории как же работает данный модуль. Чтобы для начала Вам было понятнее, мы не будем рассматривать все лишние примочки таймера и рассмотрим самый обычный его режим — NORMAL. Для себя определим что МК тактируется от внутреннего RC-генератора с частотой 1МГц и таймер настроен на работу в режиме NORMAL.

Тактовые импульсы поступают на вход clkio и попадают в предделитель таймера. Предделитель может быть настроен, по Вашим потребностям, на прямой проход тактовых импульсов или делить входящие импульсы, пропуская только их определенную часть. Поделить входящие импульсы можно на /8, /64, /256, /1024. Так как у нас ТС может работать в асинхронном режиме, то при включении его в этот режим количество предделителей существенно вырастает, но мы их рассматривать пока не будем. С предделителя тактовые импульсы поступают в блок управления и уже с него попадают в счетный регистр. Счетный регистр в свою очередь совершает инкремент на каждый входящий импульс. Счетный регистр Т2 8-ми битный, поэтому он может считать только до 255. Когда наступает переполнение счетного регистра, он сбрасывается в 0 и в этом же такте начинает считать заново. Так же в момент переполнения счетного регистра устанавливается флаг TOV2 (флаг прерывания по переполнению) регистра TIFR.

Читайте так же:
Оплата квитанции по общедомовому счетчику

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

TCNT2 — счетный регистр, о его работе мы уже говорили.

TCCR2 — регистр управления таймером.

TIMSK — регистр маски прерываний(в Atmega8 этот регистр является единственным для всех таймеров).

TIFR — регистр флагов прерываний (в Atmega8 этот регистр является единственным для всех таймеров).

А теперь о каждом подробно:

Регистр управления TCCR2. Содержимое этого регистра вы можете посмотреть на рис.2.

Биты 0-2 отвечают за тактирование таймера. Установка определенных комбинаций в этих битах настраивает предделитель данного таймера. Если все три бита сброшены — таймер выключен.

Биты 3,6 отвечают за режим работы таймера.

Биты 4,5 нужны для настройки поведения вывода ОСn (проще говоря, используются при настройке ШИМ)

И последний бит этого регистра — бит 7. С его помощью мы можем принудительно изменять состояние вывода ОСn.

Регистр маски прерываний — TIMSK. Его мы видим на рисунке №3

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

Бит 6, если в него записать единицу, разрешает прерывание по событию «Переполнение ТС Т2»

Бит 7, если в него записать еди ницу, разрешает прерывание по событию «Совпадение счетного регистра с регистром сравнения»

Регистр флагов прерываний TIFR. Его мы видим на рисунке №4

В этом регистре нас так же интересуют два последних бита: биты 6 и 7.

Бит 6 — флаг, устанавливается по событию «Переполнение ТС Т2»
Бит 7 — флаг, устанавливается по событию «Совпадение счетного регистра с регистром сравнения»

Эти биты сбрасываются автоматически при выходе из обработчика прерывания, но для надежности их можно сбрасывать самостоятельно, сбрасывая эти биты в «0».

Остальные биты регистров TIMSK и TIFR используются ТС Т0 и Т1. Как вы уже заметили, у битов этих регистров даже названия совпадают, за исключением цифры в конце названия, которая и указывает к какому таймеру данный бит применИм.

Осталось рассмотреть две несложные таблички, а именно: таблица, в которой описано управление тактовым сигналом (рис. 6), и таблица, в которой описано, как в общем настроить таймер (рис.5).

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

Вот мы и закончили с теорией, и пора приступить к практической части. Сразу оговорюсь.

Читайте так же:
Счетчик деградации масла лансер 10

ЧАСЫ, КОТОРЫЕ ПОЛУЧАТСЯ В ХОДЕ ИЗУЧЕНИЯ ДАННОЙ СТАТЬИ, НЕ ОБЛАДАЮТ ВЫСОКОЙ ТОЧНОСТЬЮ. ДАННАЯ СТАТЬЯ ОРИЕНТИРОВАННА НА ОБЩИЕ ПРИНЦИПЫ РАБОТЫ С ТАЙМЕРАМИ.

Открываем Studio 6, создаем проект и выбираем Atmega8.

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

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

Во второй строчке кода подключается библиотека с общим описанием регистров нашего МК. Так же в ней всем регистрам присвоены читабельные имена.

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

Идем дальше по листингу и доходим до функции main, в ней произведем все настройки используемой нами периферии.

На этом настройка нашего таймера закончена. Давайте подробнее рассмотрим последние три строки кода.

В первой строке мы разрешили прерывания по событию «Переполнение таймерасчетчика Т2»

Далее мы запустили наш таймер с предделителем, равным 1024

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

Остается добавить обработчик прерывания и код наших часов реального времени.

В коде, который находится в обработчике прерывания, нет ничего сложного и нового для Вас. Внимание обратим только на переменную takt и волшебную цифру «4». Откуда взялась эта цифра? Давайте рассмотрим подробно этот момент.

Мы знаем, что наш МК работает от внутреннего генератора с частотой 1МГц, таймер тактируется с предделителем 1024, считать наш таймер может до 255. Зная эти параметры мы можем посчитать сколько переполнений он совершит за 1 секунду

1 000 000 1024 256 = 3,814697.

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

Почему мы делили на 256 если таймер считает только до 255? Потому что «0» это тоже число. Думаю, здесь все понятно.

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

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

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

В архиве Вы найдете проект с выводом информации на дисплей от nokia5110, проект в Proteus 7 и все нужные файлы и библиотеки для работы.

Обращаю внимание на то, что библиотека LCD_5110 для работы с дисплеем написана участником форума Kobzar и предоставлена с его разрешения.

Урок 5. Использование таймера в AVR микроконтроллерах

Прежде чем приступить к изучению таймера определимся с базовым понятием «частота». Простым языком, это количество повторений, в секунду. Это значит, что если вы за секунду хлопнете в ладошки 2 раза, то частота хлопков будет равна 2Гц. Если за 3 раза, значит 3Гц.

Каждый микроконтроллер работает на определенной частоте. Большинство инструкций выполняется за один такт, поэтому чем выше частота, тем быстрее работает микроконтроллер. Если нет источника тактирования, соответственно ничего работать не будет. На случай отсутствия внешнего источника тактирования, в большинстве микроконтроллеров имеется свой внутренний генератор. Обычно на него «с завода» настроены.

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

Читайте так же:
Что такое счетчик моу

Теперь, кое что о таймере. Таймер работает на той же частоте, что и микроконтроллер. Иногда это может быть слишком быстро, поэтому используют предделитель который уменьшает количество тиков в 8/64/256/1024… раз. Включается это все программно.

Допустим, мы выбрали предделитель 1024, частота микроконтроллера 8 МГц, значит после предделителя частота таймера станет:
8 000 000 / 1024 = 7813 Гц — это частота, на которой работает наш таймер. По простому говоря, за одну секунду таймер тикнет 7813 раз.

К количеству тиков можно привязать выполнение кода. Эта фича есть не для всех таймеров, читайте документацию на свой камень. Допустим, нам нужно, чтобы раз в 0,5 секунды выполнялся наш код. За одну секунду 7813 тиков, за пол секунды в 2 раза меньше — 3906. Это значение вносится в регистр сравнения, и с каждым тиком проверяется достаточно ли оттикало или нет, как в будильнике, только очень быстро.

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

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

Вот теперь мы готовы написать нашу программу. Поэтому создаем проект с помощью мастера проектов. Сразу прицепим LCD, мы же уже это умеем).

Переходим на вкладку Timers и тут остановимся поподробнее:

Выбираем частоту 7813 и устанавливаем галочку напротив пункта Interrupt on: Compare A Match. Таким образом мы указали, что при совпадении значения выполнять прерывание (то о чем было написано выше). Прерывание будем выполнять 1 раз в секунду, т.е. нам нужно тикнуть 7813 раз, поэтому переводим число 7813 в шестнадцатеричную систему и получим 1e85. Именно его и записываем в регистр сравнения Comp A. Регистр сравнения Comp A 16 битный, поэтому число больше 2^16=65536 мы записать не можем.

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

// Timer 1 output compare A interrupt service routine
interrupt [TIM1_COMPA] void timer1_compa_isr(void)
<

Это то самое прерывание. Именно внутри этих скобок мы можем писать тот код, который мы хотели бы выполнять через определенные промежутки времени. У нас это одна секунда. Итак логично создать переменную, которую мы будем увеличивать 1 раз в секунду, т.е. 1 раз за прерывание. Поэтому проинициализируем переменную int s =0; а в прерывании будем ее увеличивать от 0 до 59. Значение переменной выведем на жк дисплей. Никаких хитростей, все очень просто.
Получившийся код.

Прикрутив еще 2 переменные можно получить часы на микроконтроллере).
Файл прошивки и протеуса

голоса
Рейтинг статьи
Ссылка на основную публикацию
Adblock
detector