Введение
Следующий режим работы таймера Т0 - это режим CTC (Clear Timer on Compare) или "сброс при совпадении". Таймер в этом режиме работает следующим образом.
При инициализации таймера мы очищаем счетный регистр TCNT0, а в регистр сравнения OCR0 загружаем число. Затем устанавливаем режим СТС и требуемый коэффициент предделителя.
Таймер начинает работу. По каждому импульсу тактового сигнала счетный регистр увеличивает свое значение на единицу. Когда значение счетного регистра совпадет с регистром сравнения, установится флаг OCF0, счетный регистр сбросится и счет продолжится с нуля. Если прерывания по совпадению разрешены, то после установки флага OCF0 запустится подпрограмма обработки. С регистром сравнения при этом ничего не произойдет, он свое значение не изменит. Диаграмма ниже поясняет работу таймера Т0 в режиме СТС.
Таймер Т0 в режиме СТС
Практический пример. Нужно генерировать меандр с частотой 70 кГц на выводе PB1. Микроконтроллер ATmega16, тактовая частота 16 МГц.
Самый простой способ генерировать меандр, состоит в инвертировании состояния вывода микроконтроллера в прерывании таймера.
Меандр меняет свое состояние два раза за период. Сначала переключается в 1, затем сбрасывается в 0. Поэтому, чтобы генерировать сигнал с частотой 70 кГц, прерывания нужно вызывать в два раза чаще, то есть с частотой 140 кГц.
Вычисляем требуемый период прерываний.
T = 1/F = 1/140000 = 7.143 мкс
Какую тактовую частоту задать таймеру, чтобы он мог отмерять интервал в 7.143 мкс? Если бы один такт таймера составлял ~0.1 мкс, это бы подошло. 71 такт даст 7.1 мкс.
Подберем коэффициент предделителя. У нас пять вариантов 1, 8, 64, 256, 1024. При 1 мы получим тактовый сигнал с периодом 0.0625 мкс.
Tt0 = 1/(Fcpu/k) = 1/(16000000/1) = 0.0625 мкс
При таком периоде для формирования интервала в 7.143 мкс нам понадобятся 7.143/0.0625 = 114 тактов. Значит такой коэффициент подойдет. И точность хорошая и разрядности счетного регистра хватает.
114 - это количество тактов, которые должен отсчитать таймер Т0. В регистр сравнения же нужно загрузить на один такт меньше, то есть 113. Счет ведь начинается с нулевого такта.
Как видите, для режима CTC рассчитать значение регистра сравнения еще проще, чем для режима Normal:
-вычисляем период одного такта таймера Tt0 = k/Fcpu,
- вычисляем требуемое количество тактов для заданного интервала n = t/Tto - 1
Для оценки точности получаемого сигнала, можно проделать обратную процедуру.
F = 1/((OCR0 + 1) * Tt0) = 1/((OCR0 + 1) * (1/(Fcpu/k)) = Fcpu/((OCR0 + 1) * k)
F = 16000000/114 = 140351 Гц
Весь код для нашей задачи будет выглядеть примерно так. (Код приведен для IAR`a. Для других компиляторов нужно изменить заголовочные файлы и обработчик прерывания.)
#include <ioavr.h>
#include <stdint.h>
#include <intrinsics.h>
#define TEST_PIN 1
int main( void )
{
/*инициализация таймера*/
TCCR0 = 0;
TCCR0 = (0<<COM01)|(0<<COM00)|(1<<WGM01)|(0<<WGM00);
TCNT0 = 0;
OCR0 = 113;
TIFR = (1<<OCIE0);
TIMSK = (1<<OCIE0);
TCCR0 |= (0<<CS02)|(0<<CS01)|(1<<CS00);
/*настройка вывода*/
DDRC |= (1<<TEST_PIN);
__enable_interrupt();
while(1);
return 0;
}
#pragma vector = TIMER0_COMP_vect
__interrupt void TIM0_Ctc(void)
{
/*инверсия логического уровня*/
PORTC ^= (1<<TEST_PIN);
}
Управление выводом OC0
Управление выводом OC0 осуществляется в режиме СТС точно так же, как и в режиме Normal. Вывод OC0 меняет состояние при совпадении значения счетного регистра и регистра сравнения. Варианты управления задаются разрядами COM01 и COM00 регистра TCCR0.
Рассмотренную выше программу можно переделать для генерации меандра на выводе OC0. При этом прерывание можно не использовать.
#include <ioavr.h>
#include <stdint.h>
#include <intrinsics.h>
int main( void )
{
/*инициализация таймера Т0*/
TCCR0 = 0;
TCCR0 = (0<<COM01)|(1<<COM00)|(1<<WGM01)|(0<<WGM00);
TCNT0 = 0;
OCR0 = 113;
TIMSK = 0;
TCCR0 |= (0<<CS02)|(0<<CS01)|(1<<CS00);
/*настройка выводов*/
DDRB |= (1<<PB3);
while(1);
return 0;
}
Заключение
Благодаря автоматическому сбросу счетного регистра, таймер в режиме СТС точнее отмеряет временные интервалы. Единственная загвоздка - в некоторых микроконтроллерах таймеры-счетчики Т0 не имеют такого режима.
Comments
Code:
/*инициализация таймера Т0*/
...
TCCR0 = (0<<COM01)|(1<<COM00)|(1<<WGM01)|(0<<WGM00);
...
/code]
вы выставили биты в регистре через присвоение (=),
а в этом месте
TCCR0 |= (0<<CS02)|(0<<CS01)|(1<<CS00);
вы выставили биты в этом же регистре, но уже через (|=) ?
почему не так
Code:
TCCR0 = (0<<CS02)|(0<<CS01)|(1<<CS00);
?я читал в даташыте. там все есть.
/*инициализация таймера CTC ATmega8*/ TCCR2 = 0b00001101 ; // foc0 WGM00 com01 com00 wgm01 cs02 cs01 cs00 ; TCNT2 = 0;
OCR2=250; TIFR |= (1<<1) ; // TOV0);
TIMSK |= (1<<OCIE2); //TOIE0);
//TCCR0 |= (1<<CS02)|(0<<CS01)|(1<<CS00); /*инициализация остальной периферии*/
DDRD |= (1<<PD0); sei();
И вообще не понимаю что здесь происходит
PORTC ^= (1
что значит, что вчесто TEST_PIN будет 1
RSS feed for comments to this post