timer/counter0 (посчитать внешние прерывания)

Обсуждаем контроллеры компании Atmel.
Ответить
Аватара пользователя
zhavnerko
Встал на лапы
Сообщения: 112
Зарегистрирован: Вс фев 24, 2013 19:02:22

timer/counter0 (посчитать внешние прерывания)

Сообщение zhavnerko »

Здравствуйте! Хочу повесить на двигатель магнит, рядом датчик Холла, допустим, он будет выдавать 0 в момент прохождения магнита. Нарыл кучу материала, вроде понимаю, что надо настроить 8-битный таймер T0, чтобы он считал импульсы, а по переполнению он мне выведет их количество. Потом я по формуле переведу в oб/мин и будет мне счастье. Но, я не понимаю как это настроить в CodeVisionAVR! Пожалуйста, ткните носом в какой-нибудь подробный мануал или объясните на пальцах, как правильно выставить счетчик, как считать прерывания до переполнения и куда после переполнения сохраняется это количество? И да, например, при частоте 8 Мгц и 8-битном счетчике где 256 тиков будет 31250 переполнений, то есть одно переполнение за 0.000032 сек? Буду очень благодарен за помощь, ведь осталось совсем немного...
phenomen
Потрогал лапой паяльник
Сообщения: 310
Зарегистрирован: Пт дек 17, 2010 14:41:25

Re: timer/counter0 (посчитать внешние прерывания)

Сообщение phenomen »

Какой МК?
zhavnerko писал(а):он считал импульсы, а по переполнению он мне выведет их количество.
Всегда 256 :)) (или иная константа в режиме прерывания при совпадении)
zhavnerko писал(а):при частоте 8 Мгц и 8-битном счетчике где 256 тиков будет 31250 переполнений
Правильно. Но нужно учитывать еще и делитель!!! 31250 / делитель.
Кратко о таймерах.
А Вашем случае нужно считать импульсы и время. Т.е. Одним таймером время (с прерыванием) а другим (но счетчиком, или же таймер с внешним источником тактирования - датчиком Холла).
В прерывании запомнили значение счетчика в переменную, обнулили (важно обнулить как можно скорее после запоминания!), поделили значение на время срабатывания прерывания (константа) и готово :)
Аватара пользователя
zhavnerko
Встал на лапы
Сообщения: 112
Зарегистрирован: Вс фев 24, 2013 19:02:22

Re: timer/counter0 (посчитать внешние прерывания)

Сообщение zhavnerko »

phenomen писал(а):Какой МК?


ATmega8. В ней вроде только один 8-битный и 16-битный. Кстати, ведь можно и без делителя юзать 8-битный таймер? А как будут синхронизированны 2 счетчика, один из которых считает импульсы, а другой 256 тиков..Хотя, толку в их синхронизации, правда? И еще, прерывание означает, что МК уходит в обработку основного кода? Или нет? Таймер может и считать тики и внешние прерывания, или только одно из двух? Я не так давно изучаю МК, про регистры пока знаю слабо, очень выручает CodeVisionAVR. Не могли бы вы привести пример на нем, как в конфигурации выставить эти счетчики?
phenomen
Потрогал лапой паяльник
Сообщения: 310
Зарегистрирован: Пт дек 17, 2010 14:41:25

Re: timer/counter0 (посчитать внешние прерывания)

Сообщение phenomen »

Вкладка 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 с плагином для АВР-ок
Позже более подробно.
Аватара пользователя
zhavnerko
Встал на лапы
Сообщения: 112
Зарегистрирован: Вс фев 24, 2013 19:02:22

Re: timer/counter0 (посчитать внешние прерывания)

Сообщение zhavnerko »

Спасибо вам, завтра я попробую написать код и проверить в протеусе. Согласен, что это полезная программа, когда пытался присосать ds18b20 и 8 символьный ЖКИ к МК, эта программа мне очень помогла)
Аватара пользователя
menzoda
Вымогатель припоя
Сообщения: 535
Зарегистрирован: Вт авг 28, 2012 22:21:33

Re: timer/counter0 (посчитать внешние прерывания)

Сообщение menzoda »

А какая скорость двигателя? А то, может, лучше не количество импульсов считать, а временной интервал между импульсами?
Аватара пользователя
zhavnerko
Встал на лапы
Сообщения: 112
Зарегистрирован: Вс фев 24, 2013 19:02:22

Re: timer/counter0 (посчитать внешние прерывания)

Сообщение zhavnerko »

menzoda писал(а):не количество импульсов считать, а временной интервал между импульсами?


Я читал и про это решение, но т.к. я слабо знаком с продвинутыми возможностями AVR, я не знаю как и просто посчитать кол-во импульсов. Алгоритм понимаю, а вот воплотить в код пока не получается. Пока есть голый энтузиазм и логически работающий мозг, который неустанно бороздит просторы интернета в поисках крупиц информации) Скорость вращения порядка 5000-7000 об/мин, BLDC мотор.
Аватара пользователя
menzoda
Вымогатель припоя
Сообщения: 535
Зарегистрирован: Вт авг 28, 2012 22:21:33

Re: timer/counter0 (посчитать внешние прерывания)

Сообщение menzoda »

5000 оборотов это нормально, вот если меньше пары тысяч (как по мне), тогда лучше интервалы считать а не количество.

Так для общего развития, как это делать. Не имел дела с АВР, но думаю, что там есть такая функция таймера, как захват событий (capture). То есть таймер настраивается, запускается и считает себе. Так же настраивается на какое событие он будет реагировать - низкий уровень, высокий, спад, фронт. Когда происходит событие, текущее значение таймера запоминается в специальном регистре и вызывается прерывание. В самом первом прерывании ты просто сохраняешь куда-нибудь это значение, во всех последующих ты также сохраняешь это значение, но перед этим вычисляешь разницу с предыдущим, вот тебе и период. Там конечно есть всякие тонкости, к примеру, с переполнением таймера, но в принципе несложно. Если таймер не поддерживает эту функцию, то можно сделать тоже самое "объединив" его с внешним прерыванием.

Кстати, думаю неплохо будет немного сгладить твои измерения скорости, чтобы не так сильно скакало. Я вот пропускаю через простой ФНЧ:

Код: Выделить всё

<скорость> =  (<скорость> * (N - 1) + <только что измеренная скорость>) / N

Чем больше N - тем сильнее сглаживает. Чтобы деление можно было заменить побитовым сдвигом выбираем N равным степени двойки, ну а с умножением любой контроллер справится.
phenomen
Потрогал лапой паяльник
Сообщения: 310
Зарегистрирован: Пт дек 17, 2010 14:41:25

Re: timer/counter0 (посчитать внешние прерывания)

Сообщение phenomen »

zhavnerko писал(а):Скорость вращения порядка 5000-7000 об/мин, BLDC мотор.

125000 меняем на 1000000 (умножили на 8 - 8160Гц)
25000 меняем на 20000 (умножили на 1,25 - 10200Гц)
1000000/20000 = 50 прерываний в сек., каждые 20мс.
А далее как писал menzoda
Аватара пользователя
zhavnerko
Встал на лапы
Сообщения: 112
Зарегистрирован: Вс фев 24, 2013 19:02:22

Re: timer/counter0 (посчитать внешние прерывания)

Сообщение zhavnerko »

Что нужно установить в начальном конфигураторе CodeVisionAVR, чтобы каждые 500мс например, было прерывание таймера/счетчика T1?
Аватара пользователя
ChipKiller
Сверлит текстолит когтями
Сообщения: 1163
Зарегистрирован: Ср янв 05, 2011 16:25:15

Re: timer/counter0 (посчитать внешние прерывания)

Сообщение ChipKiller »

лучше отвыкать от конфигуратора и привыкать к datasheet_у

Код: Выделить всё

#define  xtal 8000000
#define  PULSE 2000
#define  CLK_DIV 1
.......
interrupt[TIM1_OVF] void Timer1_ovf(void){ // прерывание по переполнению
    TCNT1=0x10000-(xtal/CLK_DIV/PULSE);
......
Аватара пользователя
zhavnerko
Встал на лапы
Сообщения: 112
Зарегистрирован: Вс фев 24, 2013 19:02:22

Re: timer/counter0 (посчитать внешние прерывания)

Сообщение zhavnerko »

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 (посчитать внешние прерывания)

Сообщение ChipKiller »

что тут не так? По сути, тут должно каждые 200 мс происходить переполнение и вычисляться кол-во импульсов на ногу за 1 сек. Но не работает в протеусе.
Для определения кол-ва импульсов используют режим Input Capture, а что там нагенерил конфигуратор, понятно только ему...
phenomen
Потрогал лапой паяльник
Сообщения: 310
Зарегистрирован: Пт дек 17, 2010 14:41:25

Re: timer/counter0 (посчитать внешние прерывания)

Сообщение phenomen »

Моя ошибка -

Код: Выделить всё

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 - хз
Вложения
123.rar
(13.12 КБ) 185 скачиваний
123.png
(56.04 КБ) 598 скачиваний
Аватара пользователя
zhavnerko
Встал на лапы
Сообщения: 112
Зарегистрирован: Вс фев 24, 2013 19:02:22

Re: timer/counter0 (посчитать внешние прерывания)

Сообщение zhavnerko »

В продолжение удивительной истории: Все отлично работает в протеусе, но к сожалению, не работает в железе. Плата рабочая, ибо просто текст выводит.

Код: Выделить всё

/*****************************************************
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;
    }
      }
}
phenomen
Потрогал лапой паяльник
Сообщения: 310
Зарегистрирован: Пт дек 17, 2010 14:41:25

Re: timer/counter0 (посчитать внешние прерывания)

Сообщение phenomen »

В теории у тебя прерывание выполняется раз в 20мс, берешь 4 отсчета, т.е. по идее текст должен обновляться каждые 80мс. Может контроллер дисплея не успевает?
Ответить

Вернуться в «AVR»