timer/counter0 (посчитать внешние прерывания)
timer/counter0 (посчитать внешние прерывания)
Здравствуйте! Хочу повесить на двигатель магнит, рядом датчик Холла, допустим, он будет выдавать 0 в момент прохождения магнита. Нарыл кучу материала, вроде понимаю, что надо настроить 8-битный таймер T0, чтобы он считал импульсы, а по переполнению он мне выведет их количество. Потом я по формуле переведу в oб/мин и будет мне счастье. Но, я не понимаю как это настроить в CodeVisionAVR! Пожалуйста, ткните носом в какой-нибудь подробный мануал или объясните на пальцах, как правильно выставить счетчик, как считать прерывания до переполнения и куда после переполнения сохраняется это количество? И да, например, при частоте 8 Мгц и 8-битном счетчике где 256 тиков будет 31250 переполнений, то есть одно переполнение за 0.000032 сек? Буду очень благодарен за помощь, ведь осталось совсем немного...
Re: timer/counter0 (посчитать внешние прерывания)
Какой МК?
(или иная константа в режиме прерывания при совпадении)
Кратко о таймерах.
А Вашем случае нужно считать импульсы и время. Т.е. Одним таймером время (с прерыванием) а другим (но счетчиком, или же таймер с внешним источником тактирования - датчиком Холла).
В прерывании запомнили значение счетчика в переменную, обнулили (важно обнулить как можно скорее после запоминания!), поделили значение на время срабатывания прерывания (константа) и готово
Всегда 256zhavnerko писал(а):он считал импульсы, а по переполнению он мне выведет их количество.
Правильно. Но нужно учитывать еще и делитель!!! 31250 / делитель.zhavnerko писал(а):при частоте 8 Мгц и 8-битном счетчике где 256 тиков будет 31250 переполнений
Кратко о таймерах.
А Вашем случае нужно считать импульсы и время. Т.е. Одним таймером время (с прерыванием) а другим (но счетчиком, или же таймер с внешним источником тактирования - датчиком Холла).
В прерывании запомнили значение счетчика в переменную, обнулили (важно обнулить как можно скорее после запоминания!), поделили значение на время срабатывания прерывания (константа) и готово
Re: timer/counter0 (посчитать внешние прерывания)
phenomen писал(а):Какой МК?
ATmega8. В ней вроде только один 8-битный и 16-битный. Кстати, ведь можно и без делителя юзать 8-битный таймер? А как будут синхронизированны 2 счетчика, один из которых считает импульсы, а другой 256 тиков..Хотя, толку в их синхронизации, правда? И еще, прерывание означает, что МК уходит в обработку основного кода? Или нет? Таймер может и считать тики и внешние прерывания, или только одно из двух? Я не так давно изучаю МК, про регистры пока знаю слабо, очень выручает CodeVisionAVR. Не могли бы вы привести пример на нем, как в конфигурации выставить эти счетчики?
Re: timer/counter0 (посчитать внешние прерывания)
Вкладка Chip: Atmega8, 8Mhz
Вкладка Timers/Timer0: Clock source - T0 Falling edge (Пин PD4) - будет считать импульсы по спаду
кладка Timers/Timer1: Clock source - system clock, Clock value - что душе угодно (возьму 125,000kHz), mode CTC top = ICR1 (таймер будет считать до этого числа), interrupt on - timer1 overflow, input capture - 61A8 (25 000)
Итого: 125000/25000 = 5 прерываний в секунду (каждые 200 мс).
максимальная частота будет 0.2/255 = 1020Гц.
Скачайте протеус - ООООчень полезная программа. Симулирует АВР-ки на ура.
На будущее: Избавтесь CodeVisiona и его генератора! Можно использовать только для учебных целей, но не более!
Мне лично хороший Eclipse с плагином для АВР-ок
Позже более подробно.
Вкладка Timers/Timer0: Clock source - T0 Falling edge (Пин PD4) - будет считать импульсы по спаду
кладка Timers/Timer1: Clock source - system clock, Clock value - что душе угодно (возьму 125,000kHz), mode CTC top = ICR1 (таймер будет считать до этого числа), interrupt on - timer1 overflow, input capture - 61A8 (25 000)
Итого: 125000/25000 = 5 прерываний в секунду (каждые 200 мс).
максимальная частота будет 0.2/255 = 1020Гц.
Скачайте протеус - ООООчень полезная программа. Симулирует АВР-ки на ура.
На будущее: Избавтесь CodeVisiona и его генератора! Можно использовать только для учебных целей, но не более!
Мне лично хороший Eclipse с плагином для АВР-ок
Позже более подробно.
Re: timer/counter0 (посчитать внешние прерывания)
Спасибо вам, завтра я попробую написать код и проверить в протеусе. Согласен, что это полезная программа, когда пытался присосать ds18b20 и 8 символьный ЖКИ к МК, эта программа мне очень помогла)
Re: timer/counter0 (посчитать внешние прерывания)
А какая скорость двигателя? А то, может, лучше не количество импульсов считать, а временной интервал между импульсами?
Re: timer/counter0 (посчитать внешние прерывания)
menzoda писал(а):не количество импульсов считать, а временной интервал между импульсами?
Я читал и про это решение, но т.к. я слабо знаком с продвинутыми возможностями AVR, я не знаю как и просто посчитать кол-во импульсов. Алгоритм понимаю, а вот воплотить в код пока не получается. Пока есть голый энтузиазм и логически работающий мозг, который неустанно бороздит просторы интернета в поисках крупиц информации) Скорость вращения порядка 5000-7000 об/мин, BLDC мотор.
Re: timer/counter0 (посчитать внешние прерывания)
5000 оборотов это нормально, вот если меньше пары тысяч (как по мне), тогда лучше интервалы считать а не количество.
Так для общего развития, как это делать. Не имел дела с АВР, но думаю, что там есть такая функция таймера, как захват событий (capture). То есть таймер настраивается, запускается и считает себе. Так же настраивается на какое событие он будет реагировать - низкий уровень, высокий, спад, фронт. Когда происходит событие, текущее значение таймера запоминается в специальном регистре и вызывается прерывание. В самом первом прерывании ты просто сохраняешь куда-нибудь это значение, во всех последующих ты также сохраняешь это значение, но перед этим вычисляешь разницу с предыдущим, вот тебе и период. Там конечно есть всякие тонкости, к примеру, с переполнением таймера, но в принципе несложно. Если таймер не поддерживает эту функцию, то можно сделать тоже самое "объединив" его с внешним прерыванием.
Кстати, думаю неплохо будет немного сгладить твои измерения скорости, чтобы не так сильно скакало. Я вот пропускаю через простой ФНЧ:
Чем больше N - тем сильнее сглаживает. Чтобы деление можно было заменить побитовым сдвигом выбираем N равным степени двойки, ну а с умножением любой контроллер справится.
Так для общего развития, как это делать. Не имел дела с АВР, но думаю, что там есть такая функция таймера, как захват событий (capture). То есть таймер настраивается, запускается и считает себе. Так же настраивается на какое событие он будет реагировать - низкий уровень, высокий, спад, фронт. Когда происходит событие, текущее значение таймера запоминается в специальном регистре и вызывается прерывание. В самом первом прерывании ты просто сохраняешь куда-нибудь это значение, во всех последующих ты также сохраняешь это значение, но перед этим вычисляешь разницу с предыдущим, вот тебе и период. Там конечно есть всякие тонкости, к примеру, с переполнением таймера, но в принципе несложно. Если таймер не поддерживает эту функцию, то можно сделать тоже самое "объединив" его с внешним прерыванием.
Кстати, думаю неплохо будет немного сгладить твои измерения скорости, чтобы не так сильно скакало. Я вот пропускаю через простой ФНЧ:
Код: Выделить всё
<скорость> = (<скорость> * (N - 1) + <только что измеренная скорость>) / NЧем больше N - тем сильнее сглаживает. Чтобы деление можно было заменить побитовым сдвигом выбираем N равным степени двойки, ну а с умножением любой контроллер справится.
Re: timer/counter0 (посчитать внешние прерывания)
zhavnerko писал(а):Скорость вращения порядка 5000-7000 об/мин, BLDC мотор.
125000 меняем на 1000000 (умножили на 8 - 8160Гц)
25000 меняем на 20000 (умножили на 1,25 - 10200Гц)
1000000/20000 = 50 прерываний в сек., каждые 20мс.
А далее как писал menzoda
Re: timer/counter0 (посчитать внешние прерывания)
Что нужно установить в начальном конфигураторе CodeVisionAVR, чтобы каждые 500мс например, было прерывание таймера/счетчика T1?
- ChipKiller
- Сверлит текстолит когтями
- Сообщения: 1163
- Зарегистрирован: Ср янв 05, 2011 16:25:15
Re: timer/counter0 (посчитать внешние прерывания)
лучше отвыкать от конфигуратора и привыкать к datasheet_у
Код: Выделить всё
#define xtal 8000000
#define PULSE 2000
#define CLK_DIV 1
.......
interrupt[TIM1_OVF] void Timer1_ovf(void){ // прерывание по переполнению
TCNT1=0x10000-(xtal/CLK_DIV/PULSE);
...... Re: timer/counter0 (посчитать внешние прерывания)
ChipKiller писал(а):лучше отвыкать от конфигуратора и привыкать к datasheet_у
Полностью с вами согласен! Но к сожалению, я не так давно работаю с МК. Пока мне проще с ним..Посмотрите, а что тут не так? По сути, тут должно каждые 200 мс происходить переполнение и вычисляться кол-во импульсов на ногу за 1 сек. Но не работает в протеусе.
Код: Выделить всё
#include <mega8.h>
#include <delay.h>
#include <stdio.h> // для переноса данных в буфер
#include <string.h> // строковую
// Alphanumeric LCD functions
#include <alcd.h>
#define TIME_MULTIPLIER 5
char lcd_buffer[33];
int oboroty, poslednie_oboroty;
// Timer1 overflow interrupt service routine
interrupt [TIM1_OVF] void timer1_ovf_isr(void)
{
// Place your code here
oboroty = TCNT0;
TCNT0 = 0; //обнуляем таймер0
}
// Declare your global variables here
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=In Func1=In Func0=In
// State7=T State6=T State5=T State4=T State3=T State2=T State1=T State0=T
PORTB=0x00;
DDRB=0x00;
// Port C initialization
// Func6=In Func5=In Func4=In Func3=In Func2=In Func1=In Func0=In
// State6=T State5=T State4=T State3=T State2=T State1=T State0=T
PORTC=0x00;
DDRC=0x00;
// Port D initialization
// Func7=In Func6=In Func5=In Func4=In Func3=In Func2=In Func1=In Func0=In
// State7=T State6=T State5=T State4=T State3=T State2=T State1=T State0=T
PORTD=0x00;
DDRD=0x00;
// Timer/Counter 0 initialization
// Clock source: T0 pin Falling Edge
TCCR0=0x06;
TCNT0=0x00;
// Timer/Counter 1 initialization
// Clock source: System Clock
// Clock value: 125,000 kHz
// Mode: CTC top=ICR1
// OC1A output: Discon.
// OC1B output: Discon.
// Noise Canceler: Off
// Input Capture on Falling Edge
// Timer1 Overflow Interrupt: On
// Input Capture Interrupt: Off
// Compare A Match Interrupt: Off
// Compare B Match Interrupt: Off
TCCR1A=0x00;
TCCR1B=0x1B;
TCNT1H=0x00;
TCNT1L=0x00;
ICR1H=0x61;
ICR1L=0xA8;
OCR1AH=0x00;
OCR1AL=0x00;
OCR1BH=0x00;
OCR1BL=0x00;
// Timer/Counter 2 initialization
// Clock source: System Clock
// Clock value: Timer2 Stopped
// Mode: Normal top=0xFF
// OC2 output: Disconnected
ASSR=0x00;
TCCR2=0x00;
TCNT2=0x00;
OCR2=0x00;
// External Interrupt(s) initialization
// INT0: Off
// INT1: Off
MCUCR=0x00;
// Timer(s)/Counter(s) Interrupt(s) initialization
TIMSK=0x20;
// USART initialization
// USART disabled
UCSRB=0x00;
// Analog Comparator initialization
// Analog Comparator: Off
// Analog Comparator Input Capture by Timer/Counter 1: Off
ACSR=0x80;
SFIOR=0x00;
// ADC initialization
// ADC disabled
ADCSRA=0x00;
// SPI initialization
// SPI disabled
SPCR=0x00;
// TWI initialization
// TWI disabled
TWCR=0x00;
// Alphanumeric LCD initialization
// Connections are specified in the
// Project|Configure|C Compiler|Libraries|Alphanumeric LCD menu:
// RS - PORTB Bit 0
// RD - PORTB Bit 1
// EN - PORTB Bit 2
// D4 - PORTB Bit 3
// D5 - PORTB Bit 4
// D6 - PORTB Bit 5
// D7 - PORTB Bit 6
// Characters/line: 8
lcd_init(8);
// Global enable interrupts
#asm("sei")
while (1)
{
// Place your code here
lcd_clear();
if(oboroty > 0)//ждем следующего прерывания
{
poslednie_oboroty = oboroty * 5;//запоминаем
lcd_gotoxy(1,0);
lcd_puts("ПРЕРЫВАНИЕ!!!");
oboroty = 0;//обнуляем (флаг про который я говорил)
//дальше делаем все что необходимо
}
lcd_gotoxy(0,0);
sprintf(lcd_buffer,"%u Hz",poslednie_oboroty);//передаем в буфер
//sprintf(lcd_buffer,"0x%02X%02X", TCNT1H, TCNT1L);
lcd_puts(lcd_buffer); //выводим
delay_ms(100);
poslednie_oboroty=0;
}
}- ChipKiller
- Сверлит текстолит когтями
- Сообщения: 1163
- Зарегистрирован: Ср янв 05, 2011 16:25:15
Re: timer/counter0 (посчитать внешние прерывания)
Для определения кол-ва импульсов используют режим Input Capture, а что там нагенерил конфигуратор, понятно только ему...что тут не так? По сути, тут должно каждые 200 мс происходить переполнение и вычисляться кол-во импульсов на ногу за 1 сек. Но не работает в протеусе.
Re: timer/counter0 (посчитать внешние прерывания)
Моя ошибка -
будет вызван только после переполнения таймера (TCNT1 = 0xFFFF + 1)
Постановка задачи:
нам нужен счетчик - эту функцию выполняет timer/counter0
нам нужен отсчет времени - эту функцию выполняет timer/counter1
нам нужно обрабатывать событие (прерывание) каждые 200мс, потому заводим таймер с прескаллером 64 (при 8Мгц - 125 000 Гц),
верх - ICR1 = 0x61A8, собственно прерывание - compare A match - OCR1A (comp a.) = 0x61A8.
Итого:
Проект протеуса и скрин прикладываю.
В добавок таблица прерываний меги 8
Код: Выделить всё
interrupt [TIM1_OVF] void timer1_ovf_isr(void)будет вызван только после переполнения таймера (TCNT1 = 0xFFFF + 1)
Постановка задачи:
нам нужен счетчик - эту функцию выполняет timer/counter0
нам нужен отсчет времени - эту функцию выполняет timer/counter1
нам нужно обрабатывать событие (прерывание) каждые 200мс, потому заводим таймер с прескаллером 64 (при 8Мгц - 125 000 Гц),
верх - ICR1 = 0x61A8, собственно прерывание - compare A match - OCR1A (comp a.) = 0x61A8.
Итого:
Код: Выделить всё
#include <mega8.h>
#define TIME_MULTIPLIER 5
int oboroty, poslednie_oboroty;
int counter = 0;
// Timer1 output compare A interrupt service routine
interrupt [TIM1_COMPA] void timer1_compa_isr(void)
{
oboroty = TCNT0;
TCNT0 = 0; //обнуляем таймер0
}
// Declare your global variables here
void main(void)
{
PORTB=0x00;
DDRB=0xFF;
PORTC=0x00;
DDRC=0xFF;
PORTD=0x00;
DDRD=0x00;
TCCR0=0x06;
TCNT0=0x00;
TCCR1A=0x00;
TCCR1B=0x1B;
TCNT1H=0x00;
TCNT1L=0x00;
ICR1H=0x61;
ICR1L=0xA8;
OCR1AH=0x61;
OCR1AL=0xA8;
OCR1BH=0x00;
OCR1BL=0x00;
ASSR=0x00;
TCCR2=0x00;
TCNT2=0x00;
OCR2=0x00;
MCUCR=0x00;
TIMSK=0x10;
ACSR=0x80;
SFIOR=0x00;
#asm("sei")
while (1)
{
if(oboroty > 0)//ждем следующего прерывания
{
PORTC.1=~PORTC.1;
poslednie_oboroty += oboroty * TIME_MULTIPLIER;//запоминаем
oboroty = 0;//обнуляем (флаг про который я говорил)
counter++;
}
if(counter == 4)//среднее из 4-х
{
PORTC.0=~PORTC.0;
counter = 0;
//если прерывание каждые 200мс, то обновление экрана каждые 800мс
poslednie_oboroty >>= 2;//делим на 4
PORTB = poslednie_oboroty;
poslednie_oboroty=0;
}
};
}
Проект протеуса и скрин прикладываю.
В добавок таблица прерываний меги 8
Код: Выделить всё
1 0x000(1) RESET External Pin, Power-on Reset, Brown-out Reset, and Watchdog Reset - СТАРТ
2 0x001 INT0 External Interrupt Request 0 - Прерывание с ноги PD2
3 0x002 INT1 External Interrupt Request 1 - Прерывание с ноги PD3
4 0x003 TIMER2 COMP Timer/Counter2 Compare Match - таймер2 по совпадению с OCR2
5 0x004 TIMER2 OVF Timer/Counter2 Overflow - таймер2 по переполнению
6 0x005 TIMER1 CAPT Timer/Counter1 Capture Event - таймер1 при внешнему управлению, при наращивании
7 0x006 TIMER1 COMPA Timer/Counter1 Compare Match A - таймер1 по совпадению с OCR1A
8 0x007 TIMER1 COMPB Timer/Counter1 Compare Match B - таймер1 по совпадению с OCR1B
9 0x008 TIMER1 OVF Timer/Counter1 Overflow - таймер1 по переполнению
10 0x009 TIMER0 OVF Timer/Counter0 Overflow - таймер1 по переполнению
11 0x00A SPI, STC Serial Transfer Complete - по завершению передачи байта? по SPI
12 0x00B USART, RXC USART, Rx Complete - по завершению приема байта по USART
13 0x00C USART, UDRE USART Data Register Empty
14 0x00D USART, TXC USART, Tx Complete - по завершению передачи байта по USART
15 0x00E ADC ADC Conversion Complete - годовность данных АЦП
16 0x00F EE_RDY EEPROM Ready - EEPROM сделал чтение/запись
17 0x010 ANA_COMP Analog Comparator - компаратор
18 0x011 TWI Two-wire Serial Interface - хз
19 0x012 SPM_RDY Store Program Memory Ready - хзRe: timer/counter0 (посчитать внешние прерывания)
В продолжение удивительной истории: Все отлично работает в протеусе, но к сожалению, не работает в железе. Плата рабочая, ибо просто текст выводит.
Код: Выделить всё
/*****************************************************
This program was produced by the
CodeWizardAVR V2.05.3 Standard
Automatic Program Generator
© Copyright 1998-2011 Pavel Haiduc, HP InfoTech s.r.l.
http://www.hpinfotech.com
Project :
Version :
Date : 21.05.2013
Author : Dotaxe
Company : home
Comments:
Chip type : ATmega8
Program type : Application
AVR Core Clock frequency: 8,000000 MHz
Memory model : Small
External RAM size : 0
Data Stack size : 256
*****************************************************/
#include <mega8.h>
// Alphanumeric LCD functions
#include <alcd.h>
#include <delay.h>
#include <stdio.h>
#include <string.h>
char lcd_buffer[33];
#define TIME_MULTIPLIER 50
int oboroty, poslednie_oboroty;
int counter = 0;
// Timer1 output compare A interrupt service routine
interrupt [TIM1_COMPA] void timer1_compa_isr(void)
{
// Place your code here
oboroty = TCNT0;
TCNT0 = 0; //обнуляем таймер0
}
// Declare your global variables here
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=In Func1=In Func0=In
// State7=T State6=T State5=T State4=T State3=T State2=T State1=T State0=T
PORTB=0x00;
DDRB=0x00;
// Port C initialization
// Func6=In Func5=In Func4=In Func3=In Func2=In Func1=In Func0=In
// State6=T State5=T State4=T State3=T State2=T State1=T State0=T
PORTC=0x00;
DDRC=0x00;
// Port D initialization
// Func7=In Func6=In Func5=In Func4=In Func3=In Func2=In Func1=In Func0=In
// State7=T State6=T State5=T State4=T State3=T State2=T State1=T State0=T
PORTD=0x00;
DDRD=0x00;
// Timer/Counter 0 initialization
// Clock source: T0 pin Falling Edge
TCCR0=0x06;
TCNT0=0x00;
// Timer/Counter 1 initialization
// Clock source: System Clock
// Clock value: 125,000 kHz
// Mode: CTC top=ICR1
// OC1A output: Discon.
// OC1B output: Discon.
// Noise Canceler: Off
// Input Capture on Falling Edge
// Timer1 Overflow Interrupt: Off
// Input Capture Interrupt: Off
// Compare A Match Interrupt: On
// Compare B Match Interrupt: Off
TCCR1A=0x00;
TCCR1B=0x1A;
TCNT1H=0x00;
TCNT1L=0x00;
ICR1H=0x4E;
ICR1L=0x20;
OCR1AH=0x4E;
OCR1AL=0x20;
OCR1BH=0x00;
OCR1BL=0x00;
// Timer/Counter 2 initialization
// Clock source: System Clock
// Clock value: Timer2 Stopped
// Mode: Normal top=0xFF
// OC2 output: Disconnected
ASSR=0x00;
TCCR2=0x00;
TCNT2=0x00;
OCR2=0x00;
// External Interrupt(s) initialization
// INT0: Off
// INT1: Off
MCUCR=0x00;
// Timer(s)/Counter(s) Interrupt(s) initialization
TIMSK=0x10;
// USART initialization
// USART disabled
UCSRB=0x00;
// Analog Comparator initialization
// Analog Comparator: Off
// Analog Comparator Input Capture by Timer/Counter 1: Off
ACSR=0x80;
SFIOR=0x00;
// ADC initialization
// ADC disabled
ADCSRA=0x00;
// SPI initialization
// SPI disabled
SPCR=0x00;
// TWI initialization
// TWI disabled
TWCR=0x00;
// Alphanumeric LCD initialization
// Connections are specified in the
// Project|Configure|C Compiler|Libraries|Alphanumeric LCD menu:
// RS - PORTB Bit 0
// RD - PORTB Bit 1
// EN - PORTB Bit 2
// D4 - PORTB Bit 3
// D5 - PORTB Bit 4
// D6 - PORTB Bit 5
// D7 - PORTB Bit 6
// Characters/line: 8
lcd_init(8);
// Global enable interrupts
#asm("sei")
while (1)
{
// Place your code here
if(oboroty > 0)//ждем следующего прерывания
{
poslednie_oboroty += oboroty * TIME_MULTIPLIER;//запоминаем
oboroty = 0;//обнуляем (флаг про который я говорил)
counter++;
}
if(counter == 4)//среднее из 4-х
{
counter = 0;
//если прерывание каждые 200мс, то обновление экрана каждые 800мс
poslednie_oboroty >>= 2;//делим на 4
lcd_clear();
lcd_gotoxy(0,0);
sprintf(lcd_buffer,"%u",poslednie_oboroty);
lcd_puts(lcd_buffer);
lcd_gotoxy(0,1);
lcd_putsf("RPM");
poslednie_oboroty=0;
}
}
}
Re: timer/counter0 (посчитать внешние прерывания)
В теории у тебя прерывание выполняется раз в 20мс, берешь 4 отсчета, т.е. по идее текст должен обновляться каждые 80мс. Может контроллер дисплея не успевает?