![]() |
![]() |
|||||||||||||||
Таймеры и каунтеры. Бегущий огонек v2.0
Автор: Сегодня мы поговорим о таких вещах как таймеры. А их, между прочим, в 90s2313 - аж целых два!
Что такое таймер? Это - счетчик импульсов! В нашем контроллере 2 таймера, которые называются Timer-Counter 0 и Timer-Counter 1 .
Как мы помним, у Timer-Counter 1 есть прерывание компаратора.
Это-то нам и нужно! Архитектура таймера Ну, пошли вникать в архитектуру таймер-каунтера 1.
Для этого я бы с удовольствием отправил вас на страничку 27 даташита по AT90s2313.
Все начинается с предделителя. По-ихнему, он зовется prescaler (предмасштабатор).
Это схема прескалеров для обоих таймеров.
CK/8 Две трапеции внизу - енто мультиплексоры (переключатели). В зависимости от подаваемого адреса (CS10..CS12 и CS00..CS02), на выход мультиплексора подается сигнал с одного их восьми входов:
0. (000) Нет тактовой частоты (по умолчанию) Чтобы выбрать для таймера необходимый источник тактового сигнала, необходимо записать его адрес в соответствующие биты регистров TCCR0 - для 0-го таймера, и TCCR1B - для 1-го таймера. Какие биты - смотрим на рисунки :)
Далее перенесемся на страничку 30 и взглянем ясными очами на схему 1-го таймера:
Страшно? Конечно страшно! Стока всего!!! Коротенько перечислим все, что мы видим:
TIMSK - Timer Interrupt MaSK register - регистр маски прерываний Из всей этой веселой компании нас интересуют следующие товарищи:
TIMSK - он определяет, какие прерывания таймера мы будем использовать Вот так выглядит регистр TIMSK:
Он общий для обоих таймеров, поэтому в нем есть биты отвечающие отвечающие за прерывания и того и другого таймера. Посмотрим, кто за что отвечает:
7 - TOIE1 - Timer/Counter1 Overflow Interrupt Enable - разрешение прерывания по переполнению 1-го таймера
Нам нужно только прерывание компаратора, а остальные идут лесом.
Поэтому, мы смело устанавливаем в TIMSK 6-й бит, а остальные оставляем равными 0. TIMSK = 0b01000000 Теперь определимся с регистром TCCR1B. Вот он:
Не хочу подробно распекаться о значении каждого бита, это и так разжевано в даташите. Нас сейчас интересуют только 3 младших бита (CS10…CS12). С ними мы уже знакомы - они определяют источник тактового сигнала…
Так. Надо определиться с тактовым сигналом! Наша задача - подобрать такую тактовую частоту таймера, чтобы он считал до 65535 немного дольше, чем 1/8 секунды (125 мс). Пойдем методом околонаучного тыка:
Сначала вычислим, за сколько досчитает до конца таймер при тактовой частоте равной частоте кварца:
Хорошо, а что если делить тактовую частоту на 8 (то есть, увеличить ее период в 8 раз): Итак, выяснилось, что нам подойдет коэффициент деления 64. Ну значит, смотрим, какой код соответствует этому коэффициенту.
Увидели: сигналу CK/64 соответствует код 011. TCCR1B = 0b00000011 Осталось только рассчитать число, которое мы загрузим в OCR1A, то есть - с которым будет сравнивать компаратор текущее состояние таймера. Оно считается очень просто.
Мы уже знаем, что тактовая частота таймера в 64 раза меньше частоты кварца. Значит ее период - в 64 раза больше: То есть, задержка в 125мс равна 19531 такту. Именно это число мы и загрузим в OCR1A. Единственное, что надо помнить: этот регистр - составной. То есть, он состоит из двух 8-битных регистров. Поэтому, сначала нужно преобразовать это число в шестнадцатеричную систему и загрузить старшие и младшие разряды в соответствующие регистры: OCR1AH и OCR1AL. 19531(10) = 4C4B(16). Итого имеем:
OCR1AH = 0x4c
Ну все! Теперь пишем прогу...
sei - global interrupt enable - глобальное разрешение прерываний
Ни одно прерывание не начнет работать, пока в тексте программы не встретится команда глобального разрешения прерываний.
.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 ;выход из обработчика
Как видите, начальные настройки заняли в программе даже больше места, чем функциональная ее часть.:)
Что мы сделали? Основной цикл - это одна команда rjmp со ссылкой на метку, стоящую на этой же команде. Получается "бесконечный цикл". Войдя в этот цикл, процессор будет выполнять его до тех пор, пока не возникнет прерывание. По прерыванию, он перейдет на начало обработчика прерываний. Текст обработчика с небольшими изменениями взят из предыдущей версии программы. Думаю, всем ясно, что в нем происходит. По команде reti процессор выходит из обработчика и возвращается в бесконечный цикл… Конечно же, основной цикл не обязательно состоит из одной команды. В нем может содержаться и что-то осмысленное и функциональное. И обычно так и бывает. Но в нашей программе от основного цикла более ничего и не требуется кроме как крутиться на одном месте и ждать прерывания… Ну все, компилируем, шьем, любуемся. Далее мы заставим огонек не только бегать слева-направо, но и выполнять другие "трюки"… <<--Вспомним пройденное----Поехали дальше-->>
|
|
|||||||||||||||
![]() |
![]() |


![]() |
||||
|
|
||||





