Тахометр и спидометр на Atmega8l

Вопросы настройки, программирования, прошивки микроконтроллеров и микросхем программируемой логики
Закрыто
Родился
Сообщения: 12
Зарегистрирован: Пт июл 02, 2010 22:05:38

Сообщение GMVM »

Здравствуйте! Нашел дома мегу8 и решил сделать тахометр и спидометр для скутера с отображением на LCD.Набросал программку и сделал модель в протеусе.Датчик с колеса подключается к INT0,с коленвала к INT1.
Но столкнулся с проблемой:при работе 2 датчиков одновременно устройство выдает не корректные данные(показания постоянно то увеличиваются, то уменьшаются). Пока программа не отображает частоту(т.к в принципе устройство представляет собой частотомер с 2-мя каналами), а кол-во тактов счетчика.

Помогите разобраться.

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

#include <mega8.h>

// Alphanumeric LCD Module functions
#asm
   .equ __lcd_port=0x18 ;PORTB
#endasm
//#define TimeRefresh 1000000

#include <lcd.h>
#include <stdio.h>
#include <delay.h>

unsigned long r;
volatile unsigned long f,TC0_ovf,TC1_ovf,TC2_ovf,Time1,counter=0,TimeRefresh=100000,i,g;
char buffer[20];
unsigned long tmp; 

//char CDI[30][30] ;

// External Interrupt 0 service routine
interrupt [EXT_INT0] void ext_int0_isr(void)
{
  if(TCCR0==0x0)       //если тс\0 выключен
  {
    TCNT0=0;            //обнуляем счетчик
    TC0_ovf=0;         // и счетчик переполнений
    TCCR0=0x04 ;     // включаем счетчик    
    i=0;
  } 
  else
  {
    TCCR0=0x0;           //  выключаем счетчик
    Time1=TCNT0;       // считываем показания    
    GICR=0x0;         //отключаем прерывания INT  
    i=1;
  }

}


interrupt [EXT_INT1] void ext_int1_isr(void)
{
  
  if(TCCR2==0x0)       //если тс\0 выключен
  {
    TCNT2=0;            //обнуляем счетчик
    TC2_ovf=0;         // и счетчик переполнений
    TCCR2=0x07;        // включаем счетчик  
    g=0;
  } 
  else
  {
    TCCR2=0x0;          //  выключаем счетчик
    f=TCNT2;          // считываем показания
    GICR=0x0;        // отключаем прерывания INT  
    g=1;
  }
 

}


interrupt [TIM0_OVF] void timer0_ovf_isr(void)
{
  TC0_ovf++;   //счетчик переполнений
}


interrupt [TIM1_OVF] void timer1_ovf_isr(void)
{
  TC1_ovf++ ;
}



interrupt [TIM1_COMPA] void timer1_compa_isr(void)
{
  OCR1A+=TimeRefresh;
  if(counter==0)
  {
    GICR=0x80;   //разрешаем прерывания int1
    counter=1;
  }
  else  
  {
    GICR=0x40;       //разрешаем прерывания int0
    counter=0;
  } 
}
// Timer1 output compare B interrupt service routine
interrupt [TIM1_COMPB] void timer1_compb_isr(void)
{
// Place your code here

}

// Timer2 overflow interrupt service routine
interrupt [TIM2_OVF] void timer2_ovf_isr(void)
{
  TC2_ovf++;
}

// 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: System Clock
// Clock value: 31,250 kHz
TCCR0=0x0;
//TCCR0=0x04;
TCNT0=0x00;

// 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
// Timer1 Overflow Interrupt: On
// 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;
OCR1AH=0x00;
OCR1AL=0x00;
OCR1BH=0x00;
OCR1BL=0x00;

// Timer/Counter 2 initialization
// Clock source: System Clock
// Clock value: 7,813 kHz
// Mode: Normal top=FFh
// OC2 output: Disconnected
ASSR=0x00;
TCCR2=0x0;
//TCCR2=0x07;
TCNT2=0x00;
OCR2=0x00;

// External Interrupt(s) initialization
// INT0: On
// INT0 Mode: Rising Edge
// INT1: On
// INT1 Mode: Rising Edge
GICR=0x0;
MCUCR=0x0F;
GIFR=0x0;

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

// Analog Comparator initialization
// Analog Comparator: Off
// Analog Comparator Input Capture by Timer/Counter 1: Off
ACSR=0x80;
SFIOR=0x00;

// LCD module initialization
lcd_init(20);

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

while (1)
      { 
       if(g!=0)
       {
        r=(unsigned long)f+(unsigned long)TC2_ovf*0x100;    // находим суммарное значение счетчика    (unsigned long)f+
       } 
       if(i!=0)
       { 
        
         tmp=(unsigned long)Time1+(unsigned long)TC0_ovf*0x100;        //(unsigned long)Time1+
            //tmp=(unsigned long)1000000/(2*tmp);
            //r=(unsigned long)1000000/r;                        // вычисляем частоту
        } 
        sprintf(buffer,"HZ %u  %u",r,tmp);                          // записываем ее в буфер
        lcd_clear();
        lcd_gotoxy(1,0);
        lcd_puts(buffer);
        delay_ms(300);
         f=0;
        //access=0;
        // Time2=0;
        

      };
}
Реклама
Друг Кота
Аватара пользователя
Сообщения: 5839
Зарегистрирован: Ср ноя 11, 2009 17:19:30
Откуда: Воронеж

Сообщение >TEHb< »

Вероятно прерывания накладываются друг на друга, что и приводит к погрешностям. Можно попробовать считать аппатно таймерами, а через определённые промежутки времени считывать что он там насчитал и обнулять. Тоесть не измерять время между сигналами, а считать их количество в определённый промежуток времени. В меге вроде 3 таймера, так что должно получится.
Усложнять просто. Упрощать сложно.
Контактная информация:
Реклама
Вымогатель припоя
Аватара пользователя
Сообщения: 672
Зарегистрирован: Вт окт 27, 2009 22:39:19
Откуда: Москва

Сообщение md5sum »

Поддержу >TEHb<
Настраивай таймер на режим NORMAL, частота меги 8МГц, таймер без делителя (то-же 8 Мгц).
Указываем три 16 разрядных счетчика: tim_ovf, speed, taxom
в прерывании переполнения таймера просто увеличиваем tim_ovf, a по int0 и int1 соответственно speed и taxom.

В основном цикле проверяем tim_ovf и если его значение достигло 15625, то значения speed и taxom умножаем на 2 и на индикатор. После чего все три счетчика обнуляем.

Останется придумать как ввести "инерционность" показаний, а то последние цифры будут скакать.
В авто спидометр и тахометр на изменения скорости и оборотов реагируют плавно с инерцией.
— Не говорите мне что делать и я не скажу куда Вам идти...
Родился
Сообщения: 12
Зарегистрирован: Пт июл 02, 2010 22:05:38

Сообщение GMVM »

А нельзя вычислять частоту импульсов с каждого входа отдельно, через определенные промежутки времени? Таким образом в некоторых интервалах прерывания от int-ов будут запрещены и можно будет обрабатывать код в mаin. Такой принцип я хотел организовать у себя в программе....но увы не заработало.
md5sum писал(а):если его значение достигло 15625
Если я правильно понял частота будет обновляться каждые 128 секунд? Если делать выборку меньше, то погрешность будет больше?
Просто на базе тахометра я хочу сделать зажигание с определенным графиком зависимости приращения угла опережения от частоты. Этот график будет забит в массив, откуда будут браться значения опережения. Т.е значение тахометра мне нужно получать точно.

Если я буду вычислять частоту способом с тремя счетчиками хватит ресурсов для обработки массива?
Реклама
Эиком - электронные компоненты и радиодетали
Вымогатель припоя
Аватара пользователя
Сообщения: 672
Зарегистрирован: Вт окт 27, 2009 22:39:19
Откуда: Москва

Сообщение md5sum »

Я про 8-ми битный таймер писал.....
8000000/256=31250 раз в секунду переполняется таймер
значение 15625 будет достигаться два раза в секунду.

"значение тахометра мне нужно получать точно" с какой периодичностью?

Можно оставить измерение скорости как я писал, а тахометр подать на ICP1, timer1 настроить в capture mode и по input capture interrupt в регистре ICR1 получать мгновенный период оборота коленвала :)
— Не говорите мне что делать и я не скажу куда Вам идти...
Реклама
Родился
Сообщения: 12
Зарегистрирован: Пт июл 02, 2010 22:05:38

Сообщение GMVM »

Буду пробовать такой вариант.


С коленвала, я думаю, 3-4 раз в секунду делать замер. С колеса можно раз в секунду. Диапоазон частот получается 0...28 гц для
спидометра, 5....170гц для тахометра.

Если подать тахометр на ICP то придется наверное выключать прерывания по int....т.к у прерывания по int больший приоритет. Успеет посчитаться частота с int-а?
Реклама
Родился
Сообщения: 12
Зарегистрирован: Пт июл 02, 2010 22:05:38

Сообщение GMVM »

Опять возникла проблема..вожусь уже пол дня. Делаю задержку циклом. Если задержка внутри условия if в main(), все работает нормально, но если цикл в самой main() то значения на экране опять начинают "прыгать".

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

#include <mega8.h>

// Alphanumeric LCD Module functions
#asm
   .equ __lcd_port=0x18 ;PORTD
#endasm
#include <lcd.h>
#include <stdio.h>
#include <delay.h>

unsigned long r,i;
volatile unsigned long f,TC0_ovf;
char buffer[20];


 

 

// External Interrupt 1 service routine
interrupt [EXT_INT1] void ext_int1_isr(void)
{
 if(TCCR0==0x0)       //если тс\0 выключен
 {
  TCNT0=0;            //обнуляем счетчик
  TC0_ovf=0;         // и счетчик переполнений
  TCCR0=0x02;        // включаем счетчик
 } 
 else
 {
  TCCR0=0;          //  выключаем счетчик
  f=TCNT0;          // считываем показания
  }
}
// Timer 0 overflow interrupt service routine
interrupt [TIM0_OVF] void timer0_ovf_isr(void)
{
TC0_ovf++;   //счетчик переполнений
}


void main(void)
{

// 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=0xFF;

// 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: System Clock
// Clock value: 1000,000 kHz
TCCR0=0x00;
TCNT0=0x00;


// Timer/Counter 2 initialization
// Clock source: System Clock
// Clock value: Timer2 Stopped
// Mode: Normal top=FFh
// OC2 output: Disconnected
ASSR=0x00;
TCCR2=0x00;
TCNT2=0x00;
OCR2=0x00;

// External Interrupt(s) initialization
// INT0: Off
// INT1: On
// INT1 Mode: Rising Edge
GICR|=0x80;
MCUCR=0x0C;
GIFR=0x80;

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

// Analog Comparator initialization
// Analog Comparator: Off
// Analog Comparator Input Capture by Timer/Counter 1: Off
ACSR=0x80;
SFIOR=0x00;

// LCD module initialization
lcd_init(20);


#asm("SEI");

while (1)
  {
    if(f>0)
    { 
      r=1000000/((unsigned long)f+(unsigned long)TC0_ovf*0x100);    // находим суммарное значение счетчика вычисляем частоту
      sprintf(buffer,"HZ %u",r);
                                           // записываем ее в буфер
      i=0xfff;
      while(i>0)     //задержка 
      {
        i--;
      }
    f=0;
    }
    lcd_gotoxy(1,0);
    lcd_puts(buffer);
      
   
   
  }
}
Друг Кота
Аватара пользователя
Сообщения: 5839
Зарегистрирован: Ср ноя 11, 2009 17:19:30
Откуда: Воронеж

Сообщение >TEHb< »

Да ну их, эти задержки в майне, лучше на счётчик как источник включите не тактовую, а внешние импульсы и двумя таймерами аппаратно считайте.
Усложнять просто. Упрощать сложно.
Контактная информация:
Вымогатель припоя
Аватара пользователя
Сообщения: 672
Зарегистрирован: Вт окт 27, 2009 22:39:19
Откуда: Москва

Сообщение md5sum »

GMVM писал(а):Если подать тахометр на ICP то придется наверное выключать прерывания по int....т.к у прерывания по int больший приоритет. Успеет посчитаться частота с int-а?
Ну зачем огород городить?
Прикинь на калькуляторе: максимальную частоту вращения коленвала и колеса,
и посчитай - сколько операций МК успеет сделать за это время? Его еще и в sleep загнать можно будет.
:)
В машине, к примеру, 10000 об. (это на тахометре, а реально больше 4т мало кто крутит).
8МГц тактовой разделить на 10000 оборотов, то получим, что за оборот коленвала МК выполнит 800 команд.
Вполне хватит на обработку двух прерываний и еще от безделья замучается.
— Не говорите мне что делать и я не скажу куда Вам идти...
akl
Друг Кота
Сообщения: 4466
Зарегистрирован: Пт мар 07, 2008 06:54:43
Откуда: Ижевск

Сообщение akl »

md5sum писал(а): 8МГц тактовой разделить на 10000 оборотов, то получим, что за оборот коленвала МК выполнит 800 команд.
В 30 раз больше, как минимум (как всегда, забывается размерность об/мин).

GMVM
Здесь есть темы по спидометрам и тахометрам.
http://radiokot.ru/forum/viewtopic.php?f=25&t=19240
http://radiokot.ru/forum/viewtopic.php?f=20&t=24703
http://radiokot.ru/forum/viewtopic.php?f=20&t=23688
Родился
Сообщения: 12
Зарегистрирован: Пт июл 02, 2010 22:05:38

Сообщение GMVM »

Это все ясно, что можно сделать проще.

Просто опыт в программировании мк, да и вообще в программировании всего с неделю и многое еще не понятно. И хотелось бы разобраться почему происходят скачки результатов в случае с f=0 до задержки, и почему они пропадают если f=0 после нее.
Родился
Сообщения: 12
Зарегистрирован: Пт июл 02, 2010 22:05:38

Сообщение GMVM »

>TEHb< писал(а):Да ну их, эти задержки в майне
Хм..вроде экраны не мгновенно отражают инфу и им нужно время "подумать".
Сверлит текстолит когтями
Аватара пользователя
Сообщения: 1262
Зарегистрирован: Пн дек 08, 2008 10:58:48
Откуда: Винница

Сообщение urry »

Вы не останавливаете прерывание при индикации - задержке, происходит выполнение прерывания, с таймера берется значение f, причем каждый раз разное, главное, чтобы не равнялось нулю.
Достаточно добавить лишний флаг - флаг готовности посчитанного результата.
Контактная информация:
Родился
Сообщения: 12
Зарегистрирован: Пт июл 02, 2010 22:05:38

Сообщение GMVM »

urry писал(а):Вы не останавливаете прерывание при индикации - задержке, происходит выполнение прерывания, с таймера берется значение f, причем каждый раз разное, главное, чтобы не равнялось нулю.
Но с таймера берется значение в каждом втором прерывании, и в условии f обнуляется, чтобы не было скачков от TC0_ovf которая постоянно приращается. Получается f!=0 только когда приходит второй импульс, т.е когда посчитается период.

Как сделать флаг готовности?
В прерывании при втором импульсе ставить, например flag=1, и в условии сделать if(flag==1)?
Опытный кот
Аватара пользователя
Сообщения: 703
Зарегистрирован: Вс янв 18, 2009 21:12:49

Сообщение asteroid7 »

Честно пытался разобраться в коде и понять ход мыслей. Не получилось.
Задача измерить 2 частоты по INT0 и INT1. Частота - количество импульсов за время.

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

volatile long int_spedometr, f_spedometr;
volatile long int_tahometr,  f_tahometr;

interrupt INT0
{
    int_spedometr += 1;
}

interrupt INT1
{
    int_tahometr += 1;
}

//таймер настраивается на время, например, 1 секунда
timer_owf
{
    f_spedometr = int_spedometr;
    f_tahometr  = int_tahometr;
    int_spedometr = int_tahometr = 0;
}

main
{
    //настройки прерываний и таймера
    ...
        
    while ( 1 )
    {
        long lcd_spedometr = f_spedometr ... по формуле вычислить
        long lcd_tahometr  = f_tahometr ... тоже самое
    
        lcd_out( lcd_spedometr );
        lcd_out( lcd_tahometr );
    }
}
Примерно так. Никаких флагов, ожиданий и остановок.
Задержку после вывода на дисплей можно поставить, чтобы не быстро изменялись значения.
Закрыто

Вернуться в «Микроконтроллеры и ПЛИС»