[ATMEGA8][C] Programowe powielenie częstotliwości

To forum jest dla wszystkich pasjonatów mikrokontrolerów AVR Atmela. Wymiana doświadczeń i pomoc dla początkujących w pisaniu programów zarówno w C, Asemblerze jak i BASCOM. Zapraszam znawców tematu, aby pomogli wszystkim początkującym!
ODPOWIEDZ
piroman215
-
Posty: 3
Rejestracja: 26 maja 2009, 10:40
Lokalizacja: Mielec

[ATMEGA8][C] Programowe powielenie częstotliwości

Post autor: piroman215 » 19 sty 2010, 21:13

Witam, stawiam pierwsze kroki w programowaniu procesorów i mam taki problem. Potrzebuję powielić częstotliwość pewnego sygnału prostokątnego 39x. Zakres częst. wej. od ok. 1-200Hz
Siedzę już nad tym kilka dni i nie mogę sobie z tym poradzić.
Czy da radę uzyskać taki mnożnik przy takim zakresie częst. wej.?
Stworzyłem coś takiego ale nie za bardzo to działa :(

Kod: Zaznacz cały

/***************************************************** 
Chip type           : ATmega8L 
Program type        : Application 
Clock frequency     : 8,000000 MHz 
Memory model        : Small 
External RAM size   : 0 
Data Stack size     : 256 
*****************************************************/ 

#include <mega8.h> 

unsigned int licznik=0, ovf=0; 

// External Interrupt 0 service routine 
interrupt [EXT_INT0] void ext_int0_isr(void) 
{ 

    TCCR0 = 0x00;                      
    licznik = TCNT0 + (ovf*255);    
    TCNT0 = 0;                      
    TCCR0 = 0x05;                      
    ovf = 0; 
            
   licznik = licznik/39; 
   if (licznik<1) licznik=1; 

} 

// Timer 0 overflow interrupt service routine 
interrupt [TIM0_OVF] void timer0_ovf_isr(void) 
{ 
    ovf++;    
} 

// Timer 1 overflow interrupt service routine 
interrupt [TIM1_OVF] void timer1_ovf_isr(void) 
{ 

} 

// Timer 2 overflow interrupt service routine 
interrupt [TIM2_OVF] void timer2_ovf_isr(void) 
{          
   PORTB.3 = !PORTB.3; 
   TCNT2 = 0xFF - licznik;  
} 


void main(void) 
{ 

// Input/Output Ports initialization 
// Port B initialization 
PORTB=0x00; 
DDRB=0x08; 

// Port C initialization 
PORTC=0x00; 
DDRC=0x00; 

// Port D initialization 
PORTD=0x00; 
DDRD=0x00; 

// Timer/Counter 0 initialization 
TCCR0=0x05; 
TCNT0=0x00; 

// Timer/Counter 1 initialization 
TCCR1A=0x00; 
TCCR1B=0x00; 
TCNT1H=0x00; 
TCNT1L=0x00; 
ICR1H=0x00; 
ICR1L=0x00; 
OCR1AH=0x00; 
OCR1AL=0x00; 
OCR1BH=0x00; 
OCR1BL=0x00; 

// Timer/Counter 2 initialization 
ASSR=0x00; 
TCCR2=0x07; 
TCNT2=0x00; 
OCR2=0x00; 


// External Interrupt(s) initialization 
GICR|=0x40; 
MCUCR=0x03; 
GIFR=0x40; 

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

// Analog Comparator initialization 
ACSR=0x80; 
SFIOR=0x00; 

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

while (1) 
      { 

      }; 
}
Moglibyście mi pomóc, ewentualnie wskazać błędy, naprowadzić na własciwą drogę

Awatar użytkownika
Aro
Użytkownik
Posty: 677
Rejestracja: 30 paź 2006, 18:49
Lokalizacja: Świerczyniec | Wrocław
Kontakt:

Post autor: Aro » 20 sty 2010, 22:07

Witam,
Nie przyglądałem się dokładnie temu co ty masz, ale trochę dziwne to jest. Ja bym spróbował tak:

Kod: Zaznacz cały

SIGNAL(SIG_OVERFLOW1) //timer generuje sygnał wyjściowy
{ 
   TCNT1=war;
   licz++;
}

SIGNAL(SIG_INTERRUPT0) //sygnał wejściowy
{

  if(licz>39) //sprawdza ile razy było wykonane przerwanie od timera
  {
     if(--war<1)war=0; //zmniejsz f generowanego sygnału
  }elseif(licz<39)
  {
    if(++war>65534)war=65534; //przyspiesz
  }
  licz=0;
}
Nie jestem pewien czy zadziała, w każdym razie polega to na tym, że sprawdzana jest ilośc impulsów sygnału generowanego przez timer w ciągu jednego impulsu sygnału wejściowego i odpowiednio jest korygowana częstotliwość syg. wyjściowego. Tak aż liczba impulsów wyniesie 39 na jedno przerwanie timera.

Awatar użytkownika
kayron
Użytkownik
Posty: 2088
Rejestracja: 21 wrz 2008, 12:53
Lokalizacja: Poland
Kontakt:

Post autor: kayron » 20 sty 2010, 23:28

Hm.. :?: programowy powielacz częstotliwości. Pierwsze słyszę szczerze mówiąc, bo raczej nigdy o niczym takim nie słyszałem, pomijając że raczej takie proste to nie będzie. Dla czego :?: otóż załóżmy że mamy to 100Hz i chcemy je powielić te 39x. czyli do 3900Hz. Okres dla 100Hz to 1000/100 = 10ms to akurat nie jest problem, ale dla 3900Hz otrzymujemy 1000/3900 = 0,256ms (256us), czyli tak defakto 128us bo aby generować częstotliwość o wypełnieniu 50% musimy zmieniać stan końcówki OUT 2x szybciej. Czyli musimy mieć nie tylko szybki procesor ale i timer. akurat przy 100Hz nie będzie to aż taki problem przy takim mnożniku, ale w okolicy 1KHz mogą już być spore problemy. a jak to zrobić :?: Jeden timer musi pracować jako miernik czasu trwania impulsy wejściowego, czyli określać okres przebiegu wejściowego. Na tej podstawie UC musi obliczyć czas trwania impulsu wyjściowego na podstawie mnożnika i czasu trwania impulsu wejściowego i odpowiednio sterować drugim timerem pracującym jako generator, czyli sterować jego prekselerem i komparatorem.
Reasumując, najpierw na jednym Timerze0 i 1 (właściwie 2, bo jeden musi być podstawą czasu aby pomiar trwał 1sek) zrób sobie miernik częstotliwości/okresu. Jak będziesz umiał już zmierzyć czas trwania impulsu, to na jego podstawie obliczysz czas trwania impulsów powielonego przebiegu (paradoksalnie dzieląc przez mnożnik) i ustawisz odpowiednie Timer2 jako generator. Ale dla niektórych częstotliwości będziesz musiał zaokrąglać wynik do postaci całkowitej bo do licznika nie wpiszesz np. 2,5 wiec będziesz musiał zaokrąglić do 3.
TAk mikroprocesor musi mieć 3 Timery do tego

piroman215
-
Posty: 3
Rejestracja: 26 maja 2009, 10:40
Lokalizacja: Mielec

Post autor: piroman215 » 21 sty 2010, 19:23

Aro, troche zmodyfikowałem Twój sposób i jak narazie wydaje się działać. Sprawdzę jeszcze jutro w pracy jak wygląda przebieg bo narazie zmierzyłem tylko miernikiem częstotliwość.

Kod: Zaznacz cały

interrupt [EXT_INT0] void ext_int0_isr(void)
{
    if (ovf>79)
    {
        if(--licznik<1) licznik=0;
    }
    else if(ovf<79)
    {
        if(++licznik>254) licznik=254;
    }
    ovf=0;
            
}

interrupt [TIM0_OVF] void timer0_ovf_isr(void)
{
   PORTB.3 = !PORTB.3;
   TCNT0=licznik;
   ovf++;
}
kayron, różne rzeczy się na procki pisze a ja akurat potrzebuje, że tak powiem przemnożyć częstotliwość pewnego przebiegu prostokatnego przez 39 więc pomyślałem, że można by to zrobić za pomocą właśnie avr-ka
Na pewno nie potrzeba do realizacji tego zadania aż trzech timerów bo po co :?: Przecież jak timer działa na podstawie sygnału zegarowego więc już ma podstawę czasu (chyba że się mylę :roll: )
Problem 1KHz syg. wej. też tutaj odpada bo jak pisałem wyżej zakres częst. wej. 1~200Hz
Generalnie to co napisałem wcześniej w pierwszym poście miało działać tak, że timer0 mierzy okres syg. wej. a timer2 działa jako generator ale trochę pogmatwałem :neutral:

Awatar użytkownika
kayron
Użytkownik
Posty: 2088
Rejestracja: 21 wrz 2008, 12:53
Lokalizacja: Poland
Kontakt:

Post autor: kayron » 21 sty 2010, 23:24

No tak zapomniałem że timer może działać w trybie przechwytywania, wtedy pomiar okresu da się zrobić na jednym. Tu muszę przyznać koledze racje.

Awatar użytkownika
Aro
Użytkownik
Posty: 677
Rejestracja: 30 paź 2006, 18:49
Lokalizacja: Świerczyniec | Wrocław
Kontakt:

Post autor: Aro » 22 sty 2010, 16:21

Dodam jeszcze, że jeśli chcesz przyspieszyć reakcję na zmianę częstotliwości, to powstawiaj więcej warunków czyli np:

Kod: Zaznacz cały

    if (ovf>85)
    {
        if((licznik-=2)<2) licznik=2;
    }
    else if (ovf>79)
    {
        if(--licznik<1) licznik=0;
    }
    else if(ovf<73)
    {
        if((licznik+=2)>253) licznik=253;
    }
    else if(ovf<79)
    {
        if(++licznik>254) licznik=254;
    } 
itd...
Tylko pamiętaj, im więcej warunków, tym wolniej wykonywane przerwanie i w efekcie mniejsza maksymalna częstotliwość wejściowa.

ODPOWIEDZ