Прежде чем приступить к изучению таймера определимся с базовым понятием «частота». Простым языком, это количество повторений, в секунду. Это значит, что если вы за секунду хлопнете в ладошки 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. Значение переменной выведем на жк дисплей. Никаких хитростей, все очень просто.
Получившийся код.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | #include <mega8.h> #asm .equ __lcd_port=0x18 ;PORTB #endasm #include <lcd.h> int s = 0; // переменная для хранения секунд // Обработка прерывания по совпадению interrupt [TIM1_COMPA] void timer1_compa_isr(void) { s++; // увеличиваем переменную каждую секунду if(s>59) // обнуляем секунды после 59 { s=0; } TCNT1=0; //обнуляем таймер } void main(void) { TCCR1A=0x00; //настройка таймера TCCR1B=0x05; TCNT1=0x00; //здесь увеличиваются тики OCR1A=0x1E85; //записываем число в регистр сравнения TIMSK=0x10; //запускаем таймер lcd_init(8); #asm("sei") while (1) { lcd_gotoxy(0,0); //вывод в 0 координате X и Y lcd_putchar(s/10+0x30); //вывод десятков секунд lcd_putchar(s%10+0x30); //вывод секунд }; } |
Прикрутив еще 2 переменные можно получить часы на микроконтроллере).
Файл прошивки и протеуса
Не совсем так. Внутренний генератор не может быть меньше 1МГц. Можно поставить внешний кварц на 32768, и использовать его для тактирования только таймера2 у меги8 а дальше настроить как вы написали. У тиньки13 такой фичи нет.
Здравствуйте! У меня возник вопрос, как сделать чтобы светодиод загорался по истечению таймера? Уже 2 дня мучаюсь, не чего не получается.. Вот мой код..
#include
unsigned int sek;
interrupt [EXT_INT0] void ext_int0_isr(void)
{
//включаем таймер
TCCR1A=0x00;
TCCR1B=0x05;
}
interrupt [TIM1_COMPA] void timer1_compa_isr(void)
{
sek++; // увеличиваем переменную каждую секунду
if(sek>1)
{
PORTB.0=1;
}
TCCR1A=0x00;
TCCR1B=0x00;
TCNT1H=0x00;
TCNT1L=0x00;
}
void main(void)
{
PORTB=0x01;
DDRB=0x01;
TCCR0=0x00;
TCNT0=0x00;
TCCR1A=0x00;
TCCR1B=0x05;
TCNT1H=0x00;
TCNT1L=0x00;
ICR1H=0x00;
ICR1L=0x00;
OCR1AH=0x1E;
OCR1AL=0x85;
OCR1BH=0x00;
OCR1BL=0x00;
ASSR=0x00;
TCCR2=0x00;
TCNT2=0x00;
OCR2=0x00;
MCUCR=0x00;
TIMSK=0x10;
UCSRB=0x00;
ACSR=0x80;
SFIOR=0x00;
ADCSRA=0x00;
SPCR=0x00;
TWCR=0x00;
}
игорь
void main(void)
{
PORTB=0x01; у вас тут на порту уже единица, надо PORTB=0x00;
здрасьте!!!!!!!!! можете подсказать как включить две лампы. одна сразу включалась а вторая через 5 сек. че то ни как не получается
#include
int s=0;
interrupt [TIM1_COMPA] void timer1_compa_isr(void)
{
s++;
if (s>420)
{
s=0;
}
TCNT1=0;
}
void main(void)
{
PORTD=0x00;
DDRD=0xFF;
PORTC=0b00000001;
DDRC=0x00;
TCCR1A=0x00;
TCCR1B=0x05;
TCNT1H=0x00;
OCR1A=0x1E85;
TIMSK=0b10000000;
#asm(«sei»)
while (1)
{
if (PINC.0==1)
{
PORTD.2=1;
}
else
{
PORTD=0x00;
}
if(s>=5)
{
PORTD.3=1;
}
}
}
можете подсказать что я неправильно делал
по идее и так должно работать
Здравствуйте. Где посоветуете подробнее прочитать про регистры TCCR, TCNT, OCR, TIMSK и т.д.?
И почему в протеусе секунды неправильно идут? Засикал секундомером.
потому что протеус не real time симулятор, т.е. секунды в нем не равны реальным секундам. внизу можно счетчик посмотреть. про регистры читать в даташите.
Я в восторге от сайта, очень здорово все описано, но мне как новичку тяжело разобраться. Нужно спроектировать измеритель частоты вращения ротора двигателя. Диапазон измерения (100-1000 об/мин). Импульсный датчик вырабатывает 16 импульсов за каждый оборот. Время измерения — не более трех оборотов ротора. Напряжение питания устройства равно 12В. Напряжение сигнала с датчика равно 10В. При превышении частоты в 1000 об/мин измеритель должен издавать звуковой сигнал и мерцать контрольной лампой. Измеритель частоты также должен хранить журнал измерений, записывая каждый час показания частоты вращения ротора двигателя (усредненные за этот период). Журнал должен быть доступен через последовательный протокол.
Очень нужны ваши советы с чего начать, что лучше включить в схему, как быть с таймером.
идею можно посмотреть тут http://avr-start.ru/?p=883
Уважаемый Админ скажите пожалуста как можно по захвату взять сигнал из пульта ду сохранить в память мк. и передать его . если можно по подробнее обясните с примерами зарание спасибо
посмотрите тут http://avr-start.ru/?p=3516
Помогите пожалуйста,я не понимаю как делать,уже готов чуть ли не заплатить,у кого ни спрошу ,помочь никто не может ,говорят очень сложное задание.Сроки поджимают ,не знаю что делать,вся надежда на вас.
Нужно спроектировать измеритель частоты вращения ротора двигателя. Диапазон измерения (100-1000 об/мин). Импульсный датчик вырабатывает 16 импульсов за каждый оборот. Время измерения — не более трех оборотов ротора. Напряжение питания устройства равно 12В. Напряжение сигнала с датчика равно 10В. При превышении частоты в 1000 об/мин измеритель должен издавать звуковой сигнал и мерцать контрольной лампой. Измеритель частоты также должен хранить журнал измерений, записывая каждый час показания частоты вращения ротора двигателя (усредненные за этот период). Журнал должен быть доступен через последовательный протокол.
Александр, я Вам дал ссылку на основную идею, сделать обвязку к ней любой «желающий» может. «Вывалить» т.з. и ждать готового результата, это не ко мне.
Подскажите,пожалуйста, как сделать так, чтобы по нажатию кнопки таймер останавливался на текущем значении, а по повторному нажатию этой кнопки таймер продолжал бы свою работу с того значения, на котором остановился. Заранее спасибо.
Про таймеры можно тут глянуть http://avr-start.ru/?p=3983
подскажите пожалуйста, никак не могу разобраться, вывожу секунды на 4х разрядные семисегменты мк работает от внешнего кварца 8мгц, предделитель 1024,
OCR1AH=1E
OCR1BH=85 , при этом секунды отстают от реальных~3-4 секунды. таймер то правильно настроен, тогда в чем загвоздка может быть?
кварц не на ту частоту
как положено на 8мгц
значит фьюзы не настроены
Нужна помощь с кодом сам никак не соображу….
задача такая, назначить один вход и три выхода, на выходах иметь лог 0.
если подать на вход одиночный импульс скажем 0.5 сек, то получить на первом выходе лог 1
если подать импульс в 0.8 сек, подать лог 1 на второвы выход
если подать импульс в 1 сек, то получить лог 1 на третьем выходе.
таймеры вроде понял, но как это всё воедино сделать, знаний не хватает….
Используйте вход внешнего прерывания(int0, int1), настраиваете по нарастанию и спаду одновременно. По восходящему фронту запускаете таймер, по спадающему останавливаете и смотрите сколько оттикало.
http://avr-start.ru/?p=883 здесь про внешние прерывания
http://avr-start.ru/?p=3983 здесь про настройку таймера
http://avr-start.ru/?p=3516 отсюда можно взять готовый кусок кода
не могли бы вы написать код по управлению на одном таймере несколькими сервоприводами ??
Заранее спасибо !
нужно делать программный таймер, все зависит от задачи
Появился такой вопрос:в даташите приведена формула по рассчёту частоты таймера(в режиме сравнения), есть там такая величина clk_i/o.Собственно сам вопрос: Равна ли частота clk_i/o частоте тактирования микроконтроллера?
нет, это частота дрыганья ножки, которую вы хотите получить
не знаю правильно ли сделал,по примеру выше делал
s++; // увеличиваем переменную каждую секунду
if(s>59) // обнуляем секунды после 59
{
if(s==59){ //если секунд больче 59
m++; //добавляем мунуту
}
s=0; //секунды обнуляем
if(m>59){ //если минут больше 59
h++; //добавляем час
m=0;
}
if(h>23){
s=0;
m=0;
h=0;
}
но не могу понять как вывести переменые с часами и минутами на дисплей,если написать
lcd_putchar( h);вылетает ошибка
lcd_putchar(h/10+0x30);
lcd_putchar(h%10+0x30);
почитать можно тут http://avr-start.ru/?p=1587
вообще посмотрите внимательно свой код
if(s==59) вот это условие работать не будет
Здравствуйте. Админ подскажи пожалуйста как и куда добавить нажатие кнопки, что бы можно было добавлять минуты и часы, для установки начального времени, пытался по всякому добавлять и в основной цикл, и в прерывание по таймеру и даже делал внешнее прерывание (просто с h++ m++) без толку, не работает(
if(PINC.0==0)
{
h++;
}
и тоже самое но с минутами
спасибо, проблема решена)
Добрый день. Прошу помощи в написании программы.
Делаю сварочный аппарат для литиевых аккумуляторов 18650, и хочу его снабдить мозгами чтоб не прожигать привариваемые пластинки и сам аккум. В общем здесь есть маленькие нюансы.
Время сварки должно регулироваться с шагом в 5 мили сек, в среднем хватает 15 мили. сек, но нужна регулировка. С ней вроде как разобрался, есть переменная куда записывается значения, а они в свою очередь привязаны к определённым ножкам мк, будет стоять маленький пакетный выключатель и замыкать какую-то ножку на землю.
Вопрос: можно ли сделать, при помощи таймера, чтобы мк включал ножку на заданный промежуток времени, и потом выключал, и если можно то как?
delay_ms, 10 и 15 мили сек., сделать не может, точнее на глаз, задержка длится гораздо больше 10 мили сек.
камень тини 2313
кварц 8 мгц
Вроде как понял, что можно включить ножку,включить таймер, высчитать сколько это будет тиков таймера (15 мил сек. или другое число), записать в регистр сравнения, и по совпадению в прерывании выключить эту самую ножку и таймер.
подскажите, хоть в том направлении думаю?
в правильном
Подскажите как программно сначала запретить прерывания перед выполнением некого кода, а после его выполнения разрешить?
Примечание выше от РВ http://avr-start.ru/?p=414#comment-20922 — по существу.
Для того, чтобы программа работала правильно, необходимо изменить режим на СТС TOP=OCR1A (или просто поправить значение регистра TCCR1B=0x0D в исходном тексте) Если не изменять, то первое прерывание будет через 1 с, а последующие — через каждые 8,388… с.
АДМИН почему не работает i дошетал 25 чтоб включал и анолагично
#include
int i=0;
// Timer1 output compare A interrupt service routine
interrupt [TIM1_COMPA] void timer1_compa_isr(void)
{
i++;
if(i<59)
{
i=0;
}
{
TCNT1=0;
}
if(i==25)
{PORTB.0=1;}
}
нашел заработало,
Можно ли изменить значение переменной внутри цикла до его завершения? Столкнулся с тем, что вводя кнопками новое значение delay в цикле, приходится ждать его завершения. Для миллисекунд не критично, а для минут или часов — просто ужас
можно, например внешним прерыванием, или опросом кнопок в прерывании