РадиоКот :: Таймеры и каунтеры. Бегущий огонек v2.0
Например TDA7294

РадиоКот >Обучалка >Микроконтроллеры и ПЛИС >Микроконтроллеры AVR - пишем, компилируем, прошиваем... >

Теги статьи: Добавить тег

Таймеры и каунтеры. Бегущий огонек v2.0

Автор:
Опубликовано 01.01.1970

Сегодня мы поговорим о таких вещах как таймеры. А их, между прочим, в 90s2313 - аж целых два!

Что такое таймер? Это - счетчик импульсов!
Мы можем в любой момент из программы причитать текущее состояние таймера (сколько импульсов он сосчитал), или записать в него новое значение. Также, таймер может генерировать прерывания по каким-то важным событиям в своей жизни. О прерываниях мы только что говорили…

В нашем контроллере 2 таймера, которые называются Timer-Counter 0 и Timer-Counter 1

.

Как мы помним, у Timer-Counter 1 есть прерывание компаратора.
Что такое компаратор? Аглицкое слово compare слышали? Что означает? Правильно: to compare - сравнивать. Короче говоря, компаратор - это "сравниватель". В данном случае, он сравнивает значение таймера с некоторым числом, и как только они сравняются - тут же дает запрос на прерывание.

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

Архитектура таймера

Ну, пошли вникать в архитектуру таймер-каунтера 1.

Для этого я бы с удовольствием отправил вас на страничку 27 даташита по AT90s2313.
Ну а тем лентяям, которые остались здесь, придется снова растолковывать все на пальцах…

Все начинается с предделителя. По-ихнему, он зовется prescaler (предмасштабатор).
Для чего оно надо? Для того, чтобы выбирать частоту, подаваемую на тактовый вход таймеров. Смотрим схему:

Предделитель (prescaler) контроллера AT90s2313

Это схема прескалеров для обоих таймеров.
Вверху длинный прямоугольник - это собственно предделитель, который формирует из тактовой частоты контроллера "CK" четыре кратных частоты:

CK/8
CK/64
CK/256
CK/1024

Две трапеции внизу - енто мультиплексоры (переключатели). В зависимости от подаваемого адреса (CS10..CS12 и CS00..CS02), на выход мультиплексора подается сигнал с одного их восьми входов:

0. (000) Нет тактовой частоты (по умолчанию)
1. (001) CK (не деленная тактовая частота контроллера)
2. (010) CK/8
3. (011) CK/64
4. (100) CK/256
5. (101) CK/1024
6. (110) Инвертированный сигнал с внешней ноги (T0 или T1)
7. (111) Неинвертированный сигнал с внешней ноги.

Чтобы выбрать для таймера необходимый источник тактового сигнала, необходимо записать его адрес в соответствующие биты регистров TCCR0 - для 0-го таймера, и TCCR1B - для 1-го таймера. Какие биты - смотрим на рисунки :)

Регистры TCCR0 и TCCR1B

Далее перенесемся на страничку 30 и взглянем ясными очами на схему 1-го таймера:

Структурная схема Timer/Counter 1

Страшно? Конечно страшно! Стока всего!!!

Коротенько перечислим все, что мы видим:

TIMSK - Timer Interrupt MaSK register - регистр маски прерываний
TIFR - Timer Interrupt Flag Register - регистр флагов прерываний
TCCR1A - Timer/Counter1 Control Register A - контрольный регистр 1-го таймера А
TCCR1B - Timer/Counter1 Control Register B - контрольный регистр 1-го таймера B
Control logic - управляющая логика
ICR1 - timer/counter1 Input Capture Register1 - входной регистр защелки 1-го таймера
TCNT1 - Timer/CouNTer1 - собственно, регистр состояния таймера
16-bit Comparator - компаратор
OCR1A - timer/counter Output1 Compare Register A - выходной регистр компаратора A

Из всей этой веселой компании нас интересуют следующие товарищи:

TIMSK - он определяет, какие прерывания таймера мы будем использовать
TCCR1B - регистр управления 1-м таймером
TCNT0 - собственно, регистр состояния таймера. Его мы будем обнулять по прерыванию компаратора
OCR1A - в него загружается число, с которым сравнивает компаратор

Вот так выглядит регистр TIMSK:

Регистр TIMSK

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

7 - TOIE1 - Timer/Counter1 Overflow Interrupt Enable - разрешение прерывания по переполнению 1-го таймера

6 - OCIE1A - Timer/Counter1 Output Compare Match Interrupt Enable - разрешение прерывания компаратора 1-го таймера

5,4 - ни за что не отвечают (зарезервированы)

3 - TICIE1 - Timer/Counter1 Input Capture Interrupt Enable - разрешение прерывания защелки 1-го таймера

2 - зарезервирован

1 - TOIE0 - Timer/Counter0 Overflow Interrupt Enable - разрешение прерывания по переполнению 0-го таймера

0 - зарезервирован

Нам нужно только прерывание компаратора, а остальные идут лесом. Поэтому, мы смело устанавливаем в TIMSK 6-й бит, а остальные оставляем равными 0.
Итак:

TIMSK = 0b01000000

Теперь определимся с регистром TCCR1B. Вот он:

Регистр TCCR1B

Не хочу подробно распекаться о значении каждого бита, это и так разжевано в даташите. Нас сейчас интересуют только 3 младших бита (CS10…CS12). С ними мы уже знакомы - они определяют источник тактового сигнала…

Так. Надо определиться с тактовым сигналом!
Я так понимаю, что огонек должен пробегать примерно один раз в секунду, то есть, между переключениями светодиодов должно быть где-то 1/8с. То есть - 0,125с или 125 мс
Тактовая частота нашего контроллера (частота кварца): 10 МГц. Его период: 1/10е6 = 100 нс.
Максимальное значение таймера: 2^16 = 65535.

Наша задача - подобрать такую тактовую частоту таймера, чтобы он считал до 65535 немного дольше, чем 1/8 секунды (125 мс).

Пойдем методом околонаучного тыка:

Сначала вычислим, за сколько досчитает до конца таймер при тактовой частоте равной частоте кварца:
100 нС * 65536 = 6,6 мс - маловато будет!

Хорошо, а что если делить тактовую частоту на 8 (то есть, увеличить ее период в 8 раз):
6,6 * 8 = 52 мс - тоже маловато. А если на 64?
6,6 * 64 = 419 мс - а вот это уже больше чем 125 - стало быть подойдет :)

Итак, выяснилось, что нам подойдет коэффициент деления 64. Ну значит, смотрим, какой код соответствует этому коэффициенту.

Соответствие кодов TCCR1B тактовым сигналам

Увидели: сигналу CK/64 соответствует код 011.
Пишем его в соответствующие биты TCCR1B. Так как остальные биты этого регистра нам не интересны - они тоже идут лесом, то есть, остаются в нулях.
Итак:

TCCR1B = 0b00000011

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

Мы уже знаем, что тактовая частота таймера в 64 раза меньше частоты кварца. Значит ее период - в 64 раза больше:
100 нс * 64 = 6,4 мкс.
Нам нужно, чтобы сравнение происходило в момент времени, отстоящий от запуска таймера на 125 мс.
Считаем количество тактовых импульсов, которое пройдет за это время:
125мс / 6,4 мкс = 19531 имп.

То есть, задержка в 125мс равна 19531 такту. Именно это число мы и загрузим в OCR1A. Единственное, что надо помнить: этот регистр - составной. То есть, он состоит из двух 8-битных регистров. Поэтому, сначала нужно преобразовать это число в шестнадцатеричную систему и загрузить старшие и младшие разряды в соответствующие регистры: OCR1AH и OCR1AL.

19531(10) = 4C4B(16). Итого имеем:

OCR1AH = 0x4c
OCR1AL = 0x4B

Ну все! Теперь пишем прогу...
Ах, да! Чуть не забыл! Еще две малюсенькие командочки:

sei - global interrupt enable - глобальное разрешение прерываний
cli - global interrupt disable - глобальный запрет прерываний (по умолчанию).

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

.cseg
.org 0

rjmp Reset            ;вектора прерываний
rjmp INT_0
rjmp INT_1
rjmp Timer1_capt1
rjmp Timer1_comp1
rjmp Timer1_OVF1
rjmp Timer0_OVF0
rjmp UART_RX
rjmp UART_UDRE
rjmp UART_TX
rjmp ANA_COMP




;Reset:
INT_0:
INT_1:
Timer1_capt1:
;Timer1_comp1:
Timer1_OVF1:
Timer0_OVF0:
UART_RX:
UART_UDRE:
UART_TX:
ANA_COMP:
          reti
;****************************************************
; ИНИЦИАЛИЗАЦИЯ
;****************************************************
Reset:    ldi Temp,0b11111111   ;настройка портов
          out DDRB,Temp

          ldi Temp,0b01000000   ;разрешить прерывание компаратора
          out TIMSK,Temp

          ldi Temp,0b00000011   ;тактовый сигнал = CK/64
          out TCCR1B,Temp

          ldi Temp,0x4C         ;инициализация компаратора
          out OCR1AH,Temp
          ldi Temp,0x4B
          out OCR1AL,Temp

          ldi Temp,RamEnd       ;установка указателя стека
          out SPL,Temp

          ldi Temp1,0b00000001  ;инициализация индикатора

          ldi Temp,0            ;обнуление таймера
          out TCNT1H,Temp
          out TCNT1L,Temp

          sei                   ;разрешить прерывания


;****************************************************
; ОСНОВНОЙ ЦИКЛ
;****************************************************
Inf:      rjmp Inf              ;бесконечный цикл


;****************************************************
; ОБРАБОТЧИК ПРЕРЫВАНИЯ КОМПАРАТОРА
;****************************************************

Timer1_comp1:
         ldi Temp,0            ;обнуление таймера
         out TCNT1H,Temp
         out TCNT1L,Temp


Shift:   cpi Temp1,0b10000000  ;сравнить с крайним знач.
         breq Init             ;если равно - загрузка нач. знач.

         lsl Temp1             ;иначе - сдвиг влево
         rjmp Output           ;перейти на вывод в порт

Init:    ldi Temp1,0b00000001  ;загрузить нач. значение
Output:  out PortB,Temp1       ;вывод в порт

         reti                  ;выход из обработчика

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

Что мы сделали?
Сначала идет настройка - мы настраиваем порты, таймеры и все такое - это понятно. Разрешаем прерывания командой sei и выходим в основной цикл.

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

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

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

Ну все, компилируем, шьем, любуемся.

Далее мы заставим огонек не только бегать слева-направо, но и выполнять другие "трюки"…

<<--Вспомним пройденное----Поехали дальше-->>




Как вам эта статья?

Заработало ли это устройство у вас?

34 1 1
11 10 1