Пятница, 29.11.2024, 22:22
| RSS
[SEARCH_TITLE]
[SEARCH_FORM]
[ Новые сообщения · Участники · Правила форума · Поиск · RSS ]
  • Страница 1 из 1
  • 1
Архив - только для чтения
16-bit Timer/Counter1
DimsanДата: Суббота, 12.09.2009, 21:17 | Сообщение # 1








Язык программированя: Си, Делфи
Зарегистрирован 18.06.2009
Группа: Опытные
Сообщений: 252
Город: Херсон
Статус: Offline
Всем привет. Разбираюсь с этим таймером в меге8. В нём 2 регистра: OCR1A и OCR1B. Настроил прерывание по совпадению значения таймера с этими регистрами. В прерыванию обнуляю регистр TCNT1.
Если значения OCR1A и OCR1B одинаковые, то всё нормально. Если значения разные, то 1 прерывание не происходит. Причину я знаю: срабатывает 1 прерывание, выполняется и обнуляет счётчик. Таким образом счётчик никогда не достигнет значения для 2 прерывания.
И собственно вопрос: Как правильно одновременно использовать два прерывания по совпадению?
 
DoniakДата: Суббота, 12.09.2009, 21:39 | Сообщение # 2








Язык программированя: CodeVisionAVR Си
Зарегистрирован 19.03.2008
Группа: Модераторы
Сообщений: 584
Город: Кривой Рог (Днепропетровск)
Статус: Offline
А что собственно мешает его обнулять во втором прерывание ?
Там должно ити два разных вектора прерываний на эти два регистра.
 
DimsanДата: Воскресенье, 13.09.2009, 15:41 | Сообщение # 3








Язык программированя: Си, Делфи
Зарегистрирован 18.06.2009
Группа: Опытные
Сообщений: 252
Город: Херсон
Статус: Offline
Допустим OCR1A=900; OCR1B=2000.
Когда TCNT1 становится равным 900, обрабатывается прерывание, в котором TCNT1 обнуляется[u].
Как TCNT1 сможет достигнуть значения 2000, если при 900 он обнуляется?
Или я не то обнуляю?

Добавлено (13.09.2009, 15:41)
---------------------------------------------
Ау, народ, что никто с таймерами нормально работать не умеет?? sad

 
DoniakДата: Воскресенье, 13.09.2009, 17:15 | Сообщение # 4








Язык программированя: CodeVisionAVR Си
Зарегистрирован 19.03.2008
Группа: Модераторы
Сообщений: 584
Город: Кривой Рог (Днепропетровск)
Статус: Offline
скинь кусок кода
 
DimsanДата: Воскресенье, 13.09.2009, 17:32 | Сообщение # 5








Язык программированя: Си, Делфи
Зарегистрирован 18.06.2009
Группа: Опытные
Сообщений: 252
Город: Херсон
Статус: Offline
Code
//Clock frequency     : 8,000000 MHz

#include <mega8.h>   

// Timer 1 output compare A interrupt service routine
interrupt [TIM1_COMPA] void timer1_compa_isr(void)
{
// Place your code here
PORTB.1^=1;
TCNT1=0;
}

// Timer 1 output compare B interrupt service routine
interrupt [TIM1_COMPB] void timer1_compb_isr(void)
{
// Place your code here
PORTB.2^=1;    
//TCNT1=0;
}

void main(void)
{
// Declare your local variables here

// Input/Output Ports initialization
// Port B initialization
// Func7=In Func6=In Func5=In Func4=In Func3=In Func2=Out Func1=In Func0=Out  
// State7=T State6=T State5=T State4=T State3=T State2=0 State1=T State0=0  
PORTB=0x00;
DDRB=0x07;

// Timer/Counter 1 initialization
// Clock source: System Clock
// Clock value: 1000,000 kHz
// Mode: Normal top=FFFFh
// OC1A output: Discon.
// OC1B output: Discon.
// Noise Canceler: Off
// Input Capture on Falling Edge
// Timer 1 Overflow Interrupt: Off
// Input Capture Interrupt: Off
// Compare A Match Interrupt: On
// Compare B Match Interrupt: On
TCCR1A=0x00;
TCCR1B=0x02;
TCNT1H=0x00;
TCNT1L=0x00;
ICR1H=0x00;
ICR1L=0x00;
OCR1A=500;
OCR1B=2500;

// Timer(s)/Counter(s) Interrupt(s) initialization
TIMSK=0x18;

// Global enable interrupts
#asm("sei")

while (1) { };
}
 
DoniakДата: Воскресенье, 13.09.2009, 18:14 | Сообщение # 6








Язык программированя: CodeVisionAVR Си
Зарегистрирован 19.03.2008
Группа: Модераторы
Сообщений: 584
Город: Кривой Рог (Днепропетровск)
Статус: Offline
Quote
OCR1A=500;
OCR1B=2500;

У тебя такая запись в эти регистры всегда работала?
К ним нужно обращаться от так :
Code
OCR1AH=1;
OCR1AL=244 ;
OCR1BH=9;
OCR1BL=196;

пока нечо такого необычного не нашол, ну кроме того что я бы перенёс обнуление счётчика в прерывание компаратора B поскольку оно происходит вторым и тоже записал более правильно :)
 
DimsanДата: Воскресенье, 13.09.2009, 19:04 | Сообщение # 7








Язык программированя: Си, Делфи
Зарегистрирован 18.06.2009
Группа: Опытные
Сообщений: 252
Город: Херсон
Статус: Offline
Такое обращение к регистрам работает без проблем.
Если перенести обнуление в другой обрабочик, то вообще получается одинаковая задержка.

Я ж это переделал сервы. И вот теперь надо ими рулить. Поставил на робота, и получается так, что одна должна вращаться вперёд, а другая назад. Может подругому как-то подойти к этому вопросу?
Думаю воспользоваться 8-битным таймером...

 
DeepBlackДата: Воскресенье, 13.09.2009, 22:18 | Сообщение # 8








Язык программированя: C/C++
Зарегистрирован 27.07.2009
Группа: Модераторы
Сообщений: 189
Город: Зеленоград
Статус: Offline
Dimsan, я тоже сталкивался с подобной проблемой. Пришел к выводу, что надо либо использовать еще и другой таймер, либо что-то придумать. И вот что придумал: Мне нужно было формировать задержки в мкс для 2 "потребителей" . Я запустил таймер так, чтобы он переполнялся через каждую мкс, по прерыванию от таймера происходило следующее. Я увеличивал на 1 целочисленную переменную count. Значения промежутков я записал в переменные p1, p2 и делал примерно следующее;

Code
count++;
if (count%p1==0) ....;  // действие по сравнению с А
if (count%p2==0) ....;  // действие по сравнению с В
if (count==10000) count=0;

Добавлено (13.09.2009, 22:18)
---------------------------------------------
Возможно, алгоритм не самый совершенный, но он позволяет мне генерировать задержки разной длины в количестве, превышающем число таймеров контроллера)

 
DimsanДата: Воскресенье, 13.09.2009, 22:20 | Сообщение # 9








Язык программированя: Си, Делфи
Зарегистрирован 18.06.2009
Группа: Опытные
Сообщений: 252
Город: Херсон
Статус: Offline
Т.е. использовать прерывание по переполнению? Тогда надо использовать 8-битный таймер. Верно?
При 16-битном таймере и частоте 8МГц минимальная задержка 8192 мкс получается.
 
DeepBlackДата: Воскресенье, 13.09.2009, 22:30 | Сообщение # 10








Язык программированя: C/C++
Зарегистрирован 27.07.2009
Группа: Модераторы
Сообщений: 189
Город: Зеленоград
Статус: Offline
16 разрядный таймер может дать либо наибольшую задержку (в чем и заключается его преимущество) либо задержку по совпадению с каким-либо значением. Подбираешь это значение (для 8 или если потребуется 16 разрядного таймера) и записываешь его в регистр совпадения. Далее включаешь таймер в режим прерывания по совпадению А. и все.
Идея в том, чтобы не париться с двумя регистрами совпадения, а воспользоваться одним. Но важно правильно задать режим работы таймера. Если понадобится помощь с конфигурированием таймера и выбором значения переполнения, могу объяснить.
 
DimsanДата: Воскресенье, 13.09.2009, 22:53 | Сообщение # 11








Язык программированя: Си, Делфи
Зарегистрирован 18.06.2009
Группа: Опытные
Сообщений: 252
Город: Херсон
Статус: Offline
Что-то пока не сильно понятно.
DeepBlack, Я ЛС тебе написал.


Сообщение отредактировал Dimsan - Воскресенье, 13.09.2009, 22:53
 
DeepBlackДата: Понедельник, 14.09.2009, 01:32 | Сообщение # 12








Язык программированя: C/C++
Зарегистрирован 27.07.2009
Группа: Модераторы
Сообщений: 189
Город: Зеленоград
Статус: Offline
Способ, который я придумал, заключается в том, что создается "виртуальный" таймер, который тактируется вполне реальным аппаратным таймером МК (нужно это для формирования одновременно нескольких длинных промежутков). В начале я задаю единицу отсчета "виртуального" таймера. Мне оказалось удобнее взять ее примерно равной 1мс (на практике получилось 1.024 мс). Каждый тик моего таймера формируется при помощи обычного таймера в режиме совпадения. В регистр совпадения я заношу такую константу, что время, за которое таймер до нее досчитает и обнулится, будет равно тику ВТ. С каждым вызовом прерывания от РТ (досчитал до константы) содержимое ВТ (например, целочисленная переменная count) будет увеличиваться на 1. На этом этапе уже есть таймер с тиком, равным 1мс (что уже немало в отношении больших задержек). Теперь надо сделать так, чтобы по нашему виртуальному таймеру вызывались через равные промежутки времени некоторые процессы. предположим, процесс_1 должен вызываться через каждые 30 мс, а процесс_2 должен вызываться каждые 52 мс. По прерыванию от реального таймера (по совпадению) переменная count будет увеличиваться на единицу, и через каждые 30 тиков она будет становиться кратной числу 30 (так мы получили вызов процесса_1 каждые 30 мс, т.к. каждый тик равен 1 мс ), а каждые 52 тика будет происходить вызов процесса_2. Значения интервалов времени, я обычно заношу в переменные, а кратность содержимого счетчика проверить просто: логическое выражение (count%p1==0) - то есть остаток деления содержимого счетчика на величину промежутка. Таким образом, можно обрабатывать количество процессов, превышающее количество счетчиков в микроконтроллере. Единственное, возможен конфликт, когда кратность выполнится для нескольких процессов одновременно, но такое у меня бывает редко, и немного подумав, от этого тоже можно избавиться (например, при p1=p2 для второго процесса сделать небольшой сдвиг ((count%p2)-20)==0) тут можно много чего придумать)

из всего выше написанного получаю кусок кода для обработки 2х процессов, с использованием прерывания по совпадению таймера МК (у Atmega8 T1 или Т2, т.к. только у них есть прерывание по совпадению)

Code

// объявляем переменные  
int count = 0; // счетчик
int p1 = 30;   // промежуток 1
int p2 = 52;   // промежуток 2

// далее из обработчика прерывания
count++;
if (count%p1==0) ....;  // действие через промежуток 1
if (count%p2==0) ....;  // через промежуток 2
if (count==10000) count=0;
 
DeepBlackДата: Понедельник, 14.09.2009, 02:04 | Сообщение # 13








Язык программированя: C/C++
Зарегистрирован 27.07.2009
Группа: Модераторы
Сообщений: 189
Город: Зеленоград
Статус: Offline
В качестве примера опишу реализацию такого виртуального счетчика с дискретностью 1мс. На сколько я понял, кварц имеется на 8 МГц. В данном примере я буду использовать Timer 1, хотя в данном случае можно вполне обойтись и timer 2.

1. Для начала выбираем коэффициент деления для t1. Пусть частота тактирования будет равна 125.000 КГц

рассчитаем для этого все параметры.
Т = 1/125000 = 0,000008с --- период 1 тика аппаратного таймера.
А = 1,0мс / T = 125 (в шестнадцатеричном виде это 7D) --- столько тиков аппаратного таймера потребуется для 1 тика виртуального. Это число нам и потребуется для регистра совпадения.

2. Теперь выбираем режим работы таймера. Нас интересует сброс по совпадению с заданным значением.
3. Прерывание. Выбираем прерывание по совпадению с А.
4. Собственно задаем число А. Число А задается в шестнадцатеричном виде. У нас 7D

Получилось примерно следующее:

Чуть позже приведу код программы, которая будет управлять двумя светодиодами (каждый из них будет включаться на малое время через определенные промежутки (свой промежуток для каждого светодиода)

[P.S] При работе с большими задержками не пользуйтесь сторожевым таймером..........

Прикрепления: 1551445.jpg (37.3 Kb)
 
DimsanДата: Понедельник, 14.09.2009, 18:15 | Сообщение # 14








Язык программированя: Си, Делфи
Зарегистрирован 18.06.2009
Группа: Опытные
Сообщений: 252
Город: Херсон
Статус: Offline
Спасибо большое. В принципе так и думал решить проблему, только для тактирования ВТ использовать прерывание по переполнению РТ.
Немного не согласен с таким вариантом:
Code
// объявляем переменные   
int count = 0; // счетчик
int p1 = 30;   // промежуток 1
int p2 = 52;   // промежуток 2

// далее из обработчика прерывания
count++;
if (count%p1==0) ....;  // действие через промежуток 1
if (count%p2==0) ....;  // через промежуток 2
if (count==10000) count=0;

т.к. при обнулении счётчика может увеличиться задержка.
Пусть в строке: if (count==10000) count=0; count сравнивается с 100. При p1=30 действие должно происходить каждые 30 тактов: 30, 60, 90, 120. Но ВТ обнуляется при счётчике равном 100. Таким образом увеличивается задержка на 10 тактов таймера.
Если count сравнивать с 500, то будет лучше, но всё равно.
Конечно чем больше, тем лучше, но бесконечность нельзя использовать))

Чтобы исправить этот момент можно для каждого действия делать свой таймер и при совпадении с нужной величиной обнулять его.

 
DeepBlackДата: Понедельник, 14.09.2009, 18:23 | Сообщение # 15








Язык программированя: C/C++
Зарегистрирован 27.07.2009
Группа: Модераторы
Сообщений: 189
Город: Зеленоград
Статус: Offline
Ну, в общем-то, да. Немного не точно получается. Но если взять достаточно большое число (например, максимальное допустимое значение переменной типа int), то количество равных промежутков будет много больше чем количество промежутков не равных нужному значению. Как правило, при управлении двигателями и в других задачах, не требующих большой точности, эта погрешность не значительна. Зато способ позволяет решить множество проблем при помощи всего одного таймера))
 
  • Страница 1 из 1
  • 1
Поиск: