logo elektroda
logo elektroda
X
logo elektroda
REKLAMA
REKLAMA
Adblock/uBlockOrigin/AdGuard mogą powodować znikanie niektórych postów z powodu nowej reguły.

[Atmega16][C]obsługa rs 232 i dwóch przerwań zewnętrznych

pablos249 19 Lis 2010 00:30 2193 10
REKLAMA
  • #1 8761851
    pablos249
    Poziom 11  
    witam chcę zrobić obrotomierz na atmega16 mający dwa wejścia, oraz kontaktujący się z PC przez rs232. Impulsy z przerwań są zliczane przez czas 1 sekundy a następnie przemnażane przez 60 i wyświetlane na wyświetlaczu. Po wyświetleniu chce je przesłać do PC, gdy to robię wartość pomiaru jest zaniżana na lcd oraz PC. Np z włączonym przesyłaniem prędkość wynosi 1770 obr/min(118 imp/sec), natomiast bez wynosi ona 1830 (122imp/sec) - na jeden obrót przypadają 4 impulsy. Z czego to wynika? jak wyeliminować stratę tych impulsów?
    
    
    void USART_init(unsigned int myubrr)
    {
        /* Ustala prędkość transmisji */
        UBRRH = (unsigned char)(myubrr>>8);
        UBRRL = (unsigned char)myubrr;
    
        /* Włącza nadajnika */
        UCSRB = (1<<TXEN);
      
        /* Format ramki: 8 bitów danych, 1 bit stopu, brak bitu parzystości */
        UCSRC = (1<<URSEL)|(3<<UCSZ0); 
    }
    /* Wysyła znak do portu szeregowego */
    static int USART_Transmit(char c, FILE *stream)
    {
        while(!(UCSRA & (1<<UDRE)));
        UDR = c;
    
        return 0;
    }
    /* Tworzy strumienia danych o nazwie 'mystdout' połączony
        z funkcją 'USART_Transmit' */
    static FILE mystdout = FDEV_SETUP_STREAM(USART_Transmit, NULL, _FDEV_SETUP_WRITE);
    
    


    w main mam
    
     USART_init(MYUBRR);
    
      stdout = &mystdout;
    

    kod nie jest napisany przezemnie i pochodzi z kursu
    Link
  • REKLAMA
  • #2 8762205
    tmf
    VIP Zasłużony dla elektroda
    Wynika to ze tego, że część odpowiedzialną za zliczanie impulsów masz zrobioną źle. Szkoda tylko, że nie pokazałeś jak je zliczasz. Zapewne nie korzystasz z ICP, ani w jakikolwiek sposób z hardware, podłączając impulsy np. pod wejście CLK timera?
  • REKLAMA
  • #3 8762299
    pablos249
    Poziom 11  
    zliczanie impulsów w przerwaniach :
    
    ISR(INT0_vect)
    { 
        licznik0++;
    	ttlek0=1;
    	
    	if(x1)
    	{	wynik=flaga0;
    		
    		x1=0;
    		
    		flaga0=0;
    	}
    	x1=1;
    } 
    
    ISR(INT1_vect)
    {
    	licznik1++;
    	ttlek1=1;
    	if(x2)
    	{	wynik1=flaga1;
    		
    		x2=0;
    		
    		flaga1=0;
    	}
    	x2=1;
    } 
    


    przerwanie od timera
    
    SIGNAL (SIG_OVERFLOW0)
    {
    
    	  g++; 
    	  k++; 
    
        TCNT0 = 0x70;
    	flaga0++;
    	flaga1++;
    }
    
  • REKLAMA
  • #4 8762371
    tmf
    VIP Zasłużony dla elektroda
    I to ma coś wyjaśnić? Co to za zmienne? Twoja obsługa INT0 i INT1 jest bez sensu - zobacz co się będzie działo ze zmiennymi x2 i x1. Tak jak pisałem, albo wykorzystaj wejścia ICP timera, albo jeśli masz dwa wolne timery wykorzystaj ich wejścia CLK, podając na nie zliczane impulsy. Jeśli już musisz na INT to zrobić, to w tym przerwaniu tylko zwiększaj licznik (pamiętasz, że ma być volatile?). Jego zerowanie i odczyt zrób tylko w przerwaniu timera. BTW, nie mieszaj ISR i SIGNAL.
  • #5 8762412
    pablos249
    Poziom 11  
    funkcje mam zdefiniowane w pliku obrotomierz.c, w pliku obrotomierz.h mam zadeklarowane zmienne, są one volatile
    
    void kan0(void)
    {
    	if(g>=1600)
    	{	
    		
    		g=0;
    		if(licznik0==0|licznik0>1000)
    		{   
    				wynik=0;
    		}
    		if(wynik)
    		{
    			wynik=(96000/(kanal[0].Ti))/wynik;
    			
    		}
    	
    		licznik0=licznik0*60/(kanal[0].Ti);
    	
    		if(kanal[0].stan==1)
    		{
    			if(licznik0<1000)
    			{
    				write_k0(wynik);
    				
    			}else
    			{
    				write_k0(licznik0);
                                    printf("k %d obr \n", licznik0);
    			}
    		}
    		licznik0=0;
    		
    ////////////////////////////////////////////////////		
    	
    		if(licznik1==0|licznik1>1000)
    		{  
    				wynik1=0;
    		}
    		if(wynik1)
    		{
    			wynik1=(96000/(kanal[1].Ti))/wynik1;
    		
    		}
    	
    		licznik1=licznik1*60/(kanal[1].Ti);
    	
    		if(kanal[1].stan==1)
    		{
    			if(licznik1<1000)
    			{
    				write_k1(wynik1);
    			}else
    			{
    				write_k1(licznik1);
    			}
    		} 
    		licznik1=0;
    	}
    }
    

    w main znajduje się pętla
    
    while(1)
     {
    	kan0(); 
     }
    

    przerwania
    
     MCUCR = ((1<<ISC01)|(0<<ISC00))|((1<<ISC11)|(0<<ISC10))|((1<<ISC2));  // określenie co liczymy, zbocza czy stany  
    
     GICR = _BV(INT0) | _BV(INT1) | _BV(INT2) ;   // odblokowanie int 1 i int 2 i int 3
    } 

    jest to funkcja odpowiedzialna za zliczanie impulsów. Obrotomierz ma 2 metody pomiaru, do 1000 obr/min liczy czas pomiędzy impulsami, powyżej 1000 obr/min zlicza impulsy w ciągu sekundy i mnoży przez 60.
  • #6 8762431
    Karol966
    Poziom 31  
    Mhy, zastanawiam się co tu kolega tworzy ;)

    Zrobiłem niedawno licznik impulsów (dalej miernik prędkości obrotowej). Kod jest maleńki. Do samych testów użyłem licznika impulsów zewnętrznych oraz jednego timera. Timer ma przerwanie co 50ms. W jego przerwaniu inkrementowana jest zmienna, gdy jej wartość zrówna się z 20 przerwaniami to odczytuje stan licznika i go zeruję. Mam dokładny odczyt ilości impulsów na sekundę. Później już tylko matematyka a co do wysłania tych danych teraz przez RS - spokojnie procesor zdąży to zrobić przed następnym odczytem licznika.
  • REKLAMA
  • #7 8762450
    pablos249
    Poziom 11  
    tak ale przy małych prędkościach i małych ilościach impulsów na obrót odczyt mało dokładny, wtedy dokładniej jest zmierzyć czas pomiędzy impulsami.
  • #8 8762487
    Karol966
    Poziom 31  
    Przy enkoderze 100 imp/ obrót wcale nie jest niedokładny pomiar ;)
    Przykład: mamy silnik, który obraca się z prędkością 300/obr minutę => 5obr/ sekundę = 500 impulsów - czy to jest mało, aby była kiepska dokładność? Dla wyższych prędkości aby nie "zgubić" znacznej ilości impulsów spowodowanej przepełnieniem licznika trzeba dorobić obsługę przerwania od jego przepełnienia i tak inkrementować zmienną a dalej to oczywiście prosta matematyka.
  • #9 8762504
    pablos249
    Poziom 11  
    tak ale chciałbym aby miernik był uniwersalny i działał zarówno dla 1 imp/obr jak i dla enkoderów. Ale faktycznie kod muszę zoptymalizować. Mógłby kolega udostępnić część kodu o której wspominał?
  • #10 8762581
    gaskoin
    Poziom 38  
    Wystarczy zerknąć do DS i sprawdzić jak poustawiać rejestry. To jest kilka linijek kodu

    - uruchomienie timera w tryb input capture + przerwanie overflow od niego
    - uruchomienie innego timera w trybie ctc liczącego np 20ms.
    - obsługa przerwania w którym ustawiamy flagę (tego z ctc), że trzeba obliczyć szybkość
    - w przerwaniu overflow inkrementujemy jakąś zmienną
    - w mainie sprawdzamy stan flagi, jeżeli ustawiona to (dla podanego przykładu 100 działek na obrót) szybkość = (TCNT + ilość_przepełnień*65536)/100*50. Podana liczba jest oczywiście dla 16bit licznika i 20ms. W obsłudze flagi oczywiście trzeba ją skasować i wyzerować zmienne oraz liczniki. Zero filozofii :)
  • #11 8762595
    pablos249
    Poziom 11  
    tzn że kod który podałem wyżej jest zły? do puki nie używam transmisji rs232 wszystko śmiga jak ta lala. Włączam timer gdy przepełni się on 1600 razy mam 1 sekundę, w przerwaniach zewnętrznych zliczam ilość przerwań oraz czas między nimi.
    Mam problem jedynie podczas transmisji. Jest jeszcze jedne problem mianowicie Jeżeli w main wpisze coś takiego
    
    while(1)
     {
       kan0();
       printf("dzialam");
     } 

    to po 50 wpisaniach działam zaczyny wypisywać głupoty.
REKLAMA