![]() |
![]() |
|||||||||||||||
Таймеры и каунтеры. Бегущий огонек 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 процессор выходит из обработчика и возвращается в бесконечный цикл… Конечно же, основной цикл не обязательно состоит из одной команды. В нем может содержаться и что-то осмысленное и функциональное. И обычно так и бывает. Но в нашей программе от основного цикла более ничего и не требуется кроме как крутиться на одном месте и ждать прерывания… Ну все, компилируем, шьем, любуемся. Далее мы заставим огонек не только бегать слева-направо, но и выполнять другие "трюки"… <<--Вспомним пройденное----Поехали дальше-->>
|
|
|||||||||||||||
![]() |
![]() |


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