Histereza aTtiny13

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!
Awatar użytkownika
Ertew
Użytkownik
Posty: 1414
Rejestracja: 03 lip 2005, 10:36
Lokalizacja: Leszno
Kontakt:

Post autor: Ertew » 03 kwie 2013, 16:53

Oto szkic sumowania. Mam nadzieję że trochę ci rozjaśni jak można uzyskać średnią wartość (tablicy) przy każdym pomiarze.

Kod: Zaznacz cały

int tablica [5];
int nr=0;
;int wynik;
	for (;;)
	{
		if (++nr>7) { nr=0; }
		tablica[nr]=adc;
		
		wynik=0;
		for(uint8_t i=0;i<8;i++)
		{  
			wynik += tablica[i];

		}
	wynik = wynik >> 3; //dzielenie przez 8
	}

Druga opcja to dla każdego pomiaru sumujesz wartość zmierzoną z poprzednim wynikiem, następnie to co uzyskałeś dzielisz przez dwa. W ten sposób dostaniesz sumę kilkudziesięciu ostatnich pomiarów kolejno z wagami 1/2, 1/4, 1/8, ...

Awatar użytkownika
Dawid2379
Użytkownik
Posty: 318
Rejestracja: 17 lis 2010, 19:42
Lokalizacja: Kluczbork
Kontakt:

Post autor: Dawid2379 » 03 kwie 2013, 19:17

Wcześniej kombinowałem z tym dodawaniem ale nic nie działa. Kompiluje sie bez błędów.

Kod: Zaznacz cały

 int tab[5];
   int wynik=0;
   int suma;
while(1);
{
     for(uint8_t i=0;i<5;i++)
	   {
					   ADCSRA |= (1<<ADSC);         //ADSC: uruchomienie pojedynczej konwersji  
                       while(ADCSRA & (1<<ADSC));   //czeka na zakończenie konwersji  
	                      {
					
                              tab[i] = ADC;
                                  if(i==5)

                          {   wynik = wynik + tab[0];
                              wynik = wynik + tab[1]; 
                              wynik = wynik + tab[2];
                              wynik = wynik + tab[3]; 
                              wynik = wynik + tab[4];
                              suma = wynik/5;         } 

                          }                           
       }
    
  if(suma<200)                    //jeśli wynik ADC mniejszy od 500 gasi obydwie diody  
  {  
    PORTB  &=~ (1<<LED1);  
    PORTB  &=~ (1<<LED2);  
  }  
  else if(suma<900)               //jeśli 1000>ADC>=800 LED1 świeci 
       
  { 
    PORTB  |= (1<<LED1);  
    PORTB  &=~ (1<<LED2);  
  }  
  else                            //jeśli ADC>=1000 świecą obydwie diody    
                {  
    PORTB  |= (1<<LED1);  
    PORTB  |= (1<<LED2);  
  }  
    }  
   } 

Awatar użytkownika
dondu
Użytkownik
Posty: 211
Rejestracja: 24 maja 2005, 9:47
Lokalizacja: Śląskie
Kontakt:

Post autor: dondu » 03 kwie 2013, 23:21

Tutaj widzę chyba przyzwyczajenie z BASCOM, czyli jedno działanie w jednej linii kodu:

Kod: Zaznacz cały

wynik = wynik + tab[0]; 
wynik = wynik + tab[1]; 
wynik = wynik + tab[2]; 
wynik = wynik + tab[3]; 
wynik = wynik + tab[4]; 
suma = wynik/5;   
W C możesz zrobić to w jednej linii tak jak zapisujesz wzór matematyczny:

Kod: Zaznacz cały

wynik = (tab[0] + tab[1] + tab[2] + tab[3] + tab[4]) / 5 ;
Nazwę zmiennej zmieniłem na wynik, ponieważ Twoje nazwy suma i wynik, nie odpowiadają faktycznej ich zawartości.

Skopiuj sobie ten program poniżej, uruchom go w kompilatorze CManiak online i zobacz wynik w zakładce Terminal:

Kod: Zaznacz cały

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

int main(void)
{
	//symulujemy pomiary z ADC za pomocą tablicy ADC[]
	int ADC[] = {876, 850, 867, 900, 945};

   int tab[5]; 
   int wynik = 0; 
   int i;

	for(i=0;i<5;i++) 
	{ 
		//zakomentowane, bo to CManiak, a nie mikrokontroler
		//ADCSRA |= (1<<ADSC);       
		//while(ADCSRA & (1<<ADSC)); 
		//{ 
			tab[i] = ADC[i]; //odczytaj kolejny pomiar z ADC
		//}                            
	} 

  	//oblicz wynik
	wynik =  (tab[0] + tab[1] + tab[2] + tab[3] + tab[4]) / 5 ; 

	//pokaż wynik
	printf ("wynik = %d \n", wynik);



	if(wynik<200)     //jeśli wynik ADC mniejszy od 500 gasi obydwie diody  
	{  
		//PORTB  &=~ (1<<LED1);  
		//PORTB  &=~ (1<<LED2);  
		printf ("wynik<200");
	}  
	else if(wynik<900)      //jeśli 1000>ADC>=800 LED1 świeci 
	{ 
		//PORTB  |= (1<<LED1);  
		//PORTB  &=~ (1<<LED2);  
		printf ("wynik<900");
	}  
	else                  //jeśli ADC>=1000 świecą obydwie diody    
	{  
		//PORTB  |= (1<<LED1);  
		//PORTB  |= (1<<LED2);  
		printf ("wynik>=900");
	}  
  
  return 0;  //zakończ wykonywanie programu
}
Powinieneś otrzymać:
wynik = 887
wynik<900

Awatar użytkownika
Dawid2379
Użytkownik
Posty: 318
Rejestracja: 17 lis 2010, 19:42
Lokalizacja: Kluczbork
Kontakt:

Post autor: Dawid2379 » 04 kwie 2013, 14:21

No taki wynik otrzymałem. wszystko CManiak fajnie policzył. Więc wrzuciłem program do AVRStudio zaprogramowałem procka i dalej nic.

Kod: Zaznacz cały

#include <stdio.h> 
 #include <avr/io.h>  
  
#define LED1 PB1    //definicja LED1 (do którego pinu podłączony LED1)  
#define LED2 PB2    //definicja LED2 (do którego pinu podłączony LED2)    
#define ADCIN PB4   //definicja ADCIN (wejście ADC)  
  

   
int main(void) 
{    //Uruchomienie ADC, wewnętrzne napiecie odniesienia, tryb pojedynczej konwersji, preskaler 128, wejście PIN5, wynik do prawej  
  ADCSRA =   (1<<ADEN) //ADEN: ADC Enable (uruchomienie przetwornika)  
  
   //ADPS2:0: ustawienie preskalera, preskaler= 64  
   |(1<<ADPS1)  
   |(1<<ADPS2);  
      
  
  ADMUX  =  (1<<REFS0) //REFS1:0: Reference Selection Bits    
         //Internal 2.56V Voltage Reference with external capacitor at AREF pin  
    |(1<<MUX1); //Input Channel Selections (ADC5 - Pin 5 )  
  
  //ustawienie wejśc/wyjść  
  DDRB  |= (1<<LED1) | (1<<LED2); //Ustawienie pinów sterujących diodami jako wyjścia     
  DDRB &=~ (1<<ADCIN);            //Ustawienie Wejścia ADC  
        

   int tab[5]; 
   int wynik = 0; 
   int i; 

    for(i=0;i<5;i++) 
    { 
        
        ADCSRA |= (1<<ADSC);        
        while(ADCSRA & (1<<ADSC)); 
        { 
            tab[i] = ADC; //odczytaj kolejny pomiar z ADC 
        }                            
    } 

      //oblicz wynik 
    wynik =  (tab[0] + tab[1] + tab[2] + tab[3] + tab[4]) / 5 ; 

   



    if(wynik<200)     //jeśli wynik ADC mniejszy od 500 gasi obydwie diody  
    {  
        //PORTB  &=~ (1<<LED1);  
        //PORTB  &=~ (1<<LED2);  
         
    }  
    else if(wynik<900)      //jeśli 1000>ADC>=800 LED1 świeci 
    { 
        //PORTB  |= (1<<LED1);  
        //PORTB  &=~ (1<<LED2);  
      
    }  
    else                  //jeśli ADC>=1000 świecą obydwie diody    
    {  
        //PORTB  |= (1<<LED1);  
        //PORTB  |= (1<<LED2);  
        
    }  
} 

Awatar użytkownika
dondu
Użytkownik
Posty: 211
Rejestracja: 24 maja 2005, 9:47
Lokalizacja: Śląskie
Kontakt:

Post autor: dondu » 06 kwie 2013, 21:31

Poradziłeś już sobie?

Awatar użytkownika
Dawid2379
Użytkownik
Posty: 318
Rejestracja: 17 lis 2010, 19:42
Lokalizacja: Kluczbork
Kontakt:

Post autor: Dawid2379 » 07 kwie 2013, 8:59

Heh, no nie zastanawiam się co przekręciłem albo czego brakuje. Na pewno pod "wynik" w powyższym kodzie nie wpisuje się żadna wartość bo ledy w ogóle nie świeca. Czy tak zdefiniowane zmienne są widoczne w całym programie?

Awatar użytkownika
mr_x
Użytkownik
Posty: 385
Rejestracja: 12 gru 2010, 19:05
Lokalizacja: /bin/bash
Kontakt:

Post autor: mr_x » 07 kwie 2013, 18:57

Masz tam tylko pętlę główną, więc to i tak bez znaczenia (w tym przypadku), jak je zdefiniowałeś.

Awatar użytkownika
Dawid2379
Użytkownik
Posty: 318
Rejestracja: 17 lis 2010, 19:42
Lokalizacja: Kluczbork
Kontakt:

Post autor: Dawid2379 » 10 kwie 2013, 16:19

Nie poradziłem sobie z tym kodem. Juz z ciekawości wyciagnełem Mege16, podłaczyłem wszysko jak na blogu, bez dławika bo nie posiadam, myślałem że może tiny13 jest wadliwy. Efekt taki sam - miganie diod. Podpowie może ktoś co w tym kodzie jest nie tak? Prawie się poddałem:)

Awatar użytkownika
joon
Użytkownik
Posty: 2076
Rejestracja: 30 cze 2007, 22:56
Lokalizacja: Kraków, Przemyśl, Warszawa
Kontakt:

Post autor: joon » 11 kwie 2013, 18:11

Zobacz ten kod, na mojej medze działa.

Kod: Zaznacz cały

/*
 * main.c
 *  Created on: 11-04-2013
 *       Autor: joon
 *       ATMega32 16MHz
 *		 Eclipse galileo
 */
#include <stdio.h>
#include <avr/io.h>
#include <util/delay.h>

#define LED1 PB1    //definicja LED1 (do którego pinu podłączony LED1)
#define LED2 PB2    //definicja LED2 (do którego pinu podłączony LED2)

int main(void)
{
	while(1)
	{
		ADCSRA |= (1<<ADEN)|(1<<ADPS1)|(1<<ADPS0); // włączenie, preskaler
		ADMUX |= (1<<REFS0)|(0<<REFS1); //napięcie ref 5V
		ADMUX |= (0<<MUX0); //ADC PA0

		DDRB  |= (1<<LED1) | (1<<LED2);  //piny jako wyjsciowe

   int tab[5];
   int wynik = 0;
   int i;

   for(i=0;i<5;i++)
    {
        ADCSRA |= (1<<ADSC);
        while(ADCSRA & (1<<ADSC));
        tab[i] = ADCW; //odczyt z ADC
    }

      //oblicz wynik
    wynik =  (tab[0] + tab[1] + tab[2] + tab[3] + tab[4]) / 5 ;
    if(wynik<500)     //jeśli wynik ADC mniejszy od 500 gasi obydwie diody
    {
        PORTB  |= (1<<LED1);
        PORTB  |= (1<<LED2);
    }
    else if(wynik<900)      //jeśli ADC<900 LED1 świeci
    {
        PORTB  &=~ (1<<LED1);
        PORTB  |=  (1<<LED2);
    }
    else                  //jeśli ADC>=9000 świecą obydwie diody
    {
        PORTB  &=~ (1<<LED1);
        PORTB  &=~ (1<<LED2);
    }
}
}
teraz przeanalizuj swój kod i to co widzisz, podpowiem że "//" sprawiało że linia była traktowana jako komentarz.
Inne rzeczy sam wyłapiesz, wiem bo też uczę się c ;)

Awatar użytkownika
Dawid2379
Użytkownik
Posty: 318
Rejestracja: 17 lis 2010, 19:42
Lokalizacja: Kluczbork
Kontakt:

Post autor: Dawid2379 » 11 kwie 2013, 19:16

O dobrze że napisałeś o tym // ponieważ kiedys też to sprawiło że nie działał kod, ale kompletnie zapomniałem o tym. Tak wiec usunełem wszystkie komentarze, wziełem pod uwagę że ja mam mege16, podstawilem co trzeba i nic dalej nie działa może cos z programowaniem no nie wiem co jest nie tak... :(

Kod: Zaznacz cały

#include <stdio.h> 
#include <avr/io.h> 
 

    
#define LED1 PD5     
#define LED2 PD6       
int main(void)  
{ 
   while(1) 
    {          
                     
 
  DDRD  |= (1<<LED1)|(1<<LED2);
 
  ADCSRA |= (1<<ADEN)|(1<<ADPS0)|(1<<ADPS1)|(1<<ADPS2);   
  ADMUX  |= (1<<REFS1)|(1<<REFS0);
  ADMUX |= (0<<MUX0);
  
  int tab[5]; 
   int wynik = 0; 
   int i; 

   for(i=0;i<5;i++) 
    { 
     ADCSRA |= (1<<ADSC); 
        while(ADCSRA & (1<<ADSC)); 
              tab[i] = ADCW; 
    } 
	    
    wynik =  (tab[0] + tab[1] + tab[2] + tab[3] + tab[4]) / 5 ; 

    if(wynik<500)      
    { 
        PORTD  |= (1<<LED1); 
        PORTD  |= (1<<LED2); 
    } 
    else if(wynik<900)      
    { 
        PORTD  &=~ (1<<LED1); 
        PORTD  |=  (1<<LED2); 
    } 
    else                  
    { 
        PORTD  &=~ (1<<LED1); 
        PORTD  &=~ (1<<LED2); 
    } 
} 

}

Dlaczego masz w linijce ADCW? :

Kod: Zaznacz cały

 tab[i] = ADCW;

Skąd się bierze to ADMUX |= (0<<MUX0);?
Dodam że w medze16 jest jakiś dziwny bład o którym wyczytałem na forum otóz wg. noty aby ADC0 działało wcale nie należy go definiować i faktycznie tak zrobiłem i kod działał (poprzedni bez uśredniania wyniku)

Awatar użytkownika
joon
Użytkownik
Posty: 2076
Rejestracja: 30 cze 2007, 22:56
Lokalizacja: Kraków, Przemyśl, Warszawa
Kontakt:

Post autor: joon » 11 kwie 2013, 19:56

Moja wiedza raczej nie jest zbyt duża ale postaram się wytłumaczyć co i jak.
1. // - nie chodziło o usunięcie komentarzy- komentarze są naprawdę ważne, zobaczysz jak kiedyś będziesz chciał przeanalizować jeszcze raz własny program ;)
Chodziło o to np.

Kod: Zaznacz cały

  //PORTB  |= (1<<LED1);  
        //PORTB  &=~ (1<<LED2);  
Polecenia zawarte w komentarzach nie są wykonywane
2. dzięki ADCW mamy wynik w jednej zmiennej i nie musimy stosować osobnych: ADCH oraz ADHL oraz ich "zespajania" ;)
3.

Kod: Zaznacz cały

ADMUX |= (0<<MUX0)
Wybrałem pin PA0, w pdfie są tabele z których przeczytasz pod jakimi adresami są dane kanały przetwornika [takiego zapisu nie powinno się jednak stosować, a domyślnie ustawiony jest kanał "zerowy"]
4. Ten kod u mnie działa bez problemu ;)

Jeżeli masz więcej takich pytań zapraszam na forum.atnel.pl - sam tam sporo "przesiaduję" i uwierz mi że żadne pytanie, nawet błache nie leci do kosza- bo kosza tam nie ma :D
Ostatnio zmieniony 11 kwie 2013, 21:28 przez joon, łącznie zmieniany 1 raz.

Awatar użytkownika
dondu
Użytkownik
Posty: 211
Rejestracja: 24 maja 2005, 9:47
Lokalizacja: Śląskie
Kontakt:

Post autor: dondu » 12 kwie 2013, 9:00

joon pisze:2. dzięki ADCW mamy wynik w jednej zmiennej i nie musimy stosować osobnych: ADCH oraz ADHL oraz ich "zespajania" ;)
Odczytanie tylko górnego bajtu z 16-bitowego rejestru ADC, ma sens w dwóch przypadkach:

1. gdy potrzebujemy tylko dwóch najbardziej znaczących bitów
2. gdy potrzebujemy 8 najbardziej znaczących bitów pod warunkiem, że włączymy wyrównywanie rejestru ADC do lewej.

Patrz rysunek poniżej.

W pozostałych wypadkach odczytujemy cały rejestr ADC i nie trzeba niczego składać - kompilator sam wie co ma zrobić, by odczytać prawidłowo pomiar z ADC do zmiennej typu int.

@Dawid2379
Twój problem polega właśnie na tym o czym napisałem powyżej.
Czyli odczytujesz tylko najstarsze dwa bity pomiaru, a nie masz ustawionego wyrównywania do lewej (bit ADLAR).



Aby Twój program działał prawidłowo powinieneś odczytywać cały ADC czyli:

Kod: Zaznacz cały

tab[i] = ADC; 
ponieważ Twoje zmienne są typu int i masz do nich ustawione warunki if().
Miałeś tak w poprzednim programie - dlaczego zmieniłeś?


Uporządkujmy co właściwie robisz. Bo ja się gubię na jakim właściwie mikrokontrolerze, który program uruchamiasz.

Podaj komplet danych:
- schemat,
- program
- objawy,
- fusebity.


----------------
joon pisze:Jeżeli masz więcej takich pytań zapraszam na forum.atnel.pl - sam tam sporo "przesiaduję" i uwierz mi że żadne pytanie, nawet błache nie leci do kosza- bo kosza tam nie ma
Czyżby? Temat kolegi Dawid2379 który wczoraj tam go napisał został bezpowrotnie skasowany (temat o numerze 2844). Poza tym dlaczego odbierasz Elportalowi użytkowników?
Załączniki
atmega32-adlar.gif
(14.65 KiB) Pobrany 3937 razy

Awatar użytkownika
Dawid2379
Użytkownik
Posty: 318
Rejestracja: 17 lis 2010, 19:42
Lokalizacja: Kluczbork
Kontakt:

Post autor: Dawid2379 » 12 kwie 2013, 13:17

Dla mnie i tak forum elpotralu zawsze jest na pierwszym miejscu. Po prostu szukalem odpowiedzi na atnel, faktem jest że post został usuniety chyba bardziej z powodu napisania że temat jest poruszany tutaj, a nie że nie podałem schematu itp, dla mnie to dziwne i tak każdy jest zarejestrowany gdzie tylko można, i udziela sie na wielu forach. W ogóle to mniejsza o to.....

Ja dalej działam na Tiny13. Chciałem tylko sprawdzić działanie kodu na innym uC, poza tym za duzy uC do ciagłego przekładania z płytki do programatora. Więc schemat ponieżej. Nie mam części kodu odpowiedzialnej za przycisk aby mieć ciągły odczyt z ADC. Udało mi znaleść problem, zdefiniowanie predkości Tiny w kodzie - i wszystko działa... prawie
Pojawił sie inny problem, wcześniej wzrost napięcia na nóżce ADC powodował zapalanie się diody teraz opadające napiecie powoduje zapalanie sie diody?
Szczerze nawet przy średniej z dwudziestu pomiarów jak widac poniżej, diody migaja ale to raz czy dwa nie tak jak wcześniej jak na dyskotece.

Kod: Zaznacz cały

#define F_CPU 4800000UL
#include <stdio.h> 
#include <avr/io.h> 
     
#define LED1 PB1
#define LED2 PB2
#define ADCIN PB4  

int main(void)  
{ 
   while(1) 
    {          
                     
 
  DDRB  |= (1<<LED1)|(1<<LED2);
  DDRB &=~ (1<<ADCIN); 
  ADCSRA |= (1<<ADEN)|(1<<ADPS2)|(1<<ADPS1) ;   
  ADMUX  |= (1<<MUX1)|(1<<REFS0);

  
  int tab[20]; 
   int wynik = 0; 
   int i; 

   for(i=0;i<20;i++) 
    { 
     ADCSRA |= (1<<ADSC); 
        while(ADCSRA & (1<<ADSC)); 
              tab[i] = ADC; 
    } 
	    
    wynik =  (tab[0] + tab[1] + tab[2] + tab[3] + tab[4] +
               tab[5] + tab[6] + tab[7] + tab[8] + tab[9] +
			   tab[10] + tab[11] + tab[12] + tab[13] + tab[14] +
               tab[15] + tab[16] + tab[17] + tab[18] + tab[19]) / 20 ; 

    if(wynik<300)      
    { 
        PORTB  |= (1<<LED1); 
        PORTB  |= (1<<LED2); 
    } 
    else if(wynik<900)      
    { 
        PORTB  &=~ (1<<LED1); 
        PORTB  |=  (1<<LED2); 
    } 
    else                  
    { 
        PORTB  &=~ (1<<LED1); 
        PORTB  &=~ (1<<LED2); 
    } 
} 

}
Załączniki
beztytuu_106.png
beztytuu_106.png (4.02 KiB) Przejrzano 8316 razy

Awatar użytkownika
joon
Użytkownik
Posty: 2076
Rejestracja: 30 cze 2007, 22:56
Lokalizacja: Kraków, Przemyśl, Warszawa
Kontakt:

Post autor: joon » 12 kwie 2013, 14:05

U mnie diody na płycie uruchomieniowej są "odwrotnie" wiec zamieniłem

|= (1<<LED1) z &=~ (1<<LED1) to pierwsze wystawia "1" na pin, to drugie "0"

jak chcesz ograniczyć mruganie możesz za wszystkimi warunkami dać niewielkie opóźnienie a następnie wygasić obie diody- u mnie znacznie poprawiło to wyświetlanie mimo uśrednianiu z jedynie 5 pomiarów.

Awatar użytkownika
Dawid2379
Użytkownik
Posty: 318
Rejestracja: 17 lis 2010, 19:42
Lokalizacja: Kluczbork
Kontakt:

Post autor: Dawid2379 » 12 kwie 2013, 15:06

Tak tak wiem że można pozamieniac w kodzie włączanie LED, ale zastanawia mnie skąd to się wzieło że teraz malenie napięcia powoduje że do rejestru wpisywane jest coraz wieksza wartość... dziwne ale myślę że to kwestia ustawienia jakis bitów nie wiem tylko jakich, chyba że problem leże gdzie indziej.

ODPOWIEDZ