Skocz do zawartości

[C] Atmega8 ADC - przerwanie - LM35


regrom

Pomocna odpowiedź

Witam! 😉

W ramach nauki programowania mikrokontrolerów chce zrobić coś ala panel PC do sterowania wentylatorów.

SCHEMAT

Docelowo:

- pomiar temperatury - 3 czujniki lm 35

- wysterowanie 3 wentylatorów PWM

- sterowanie automatyczne i ręczne(przyciski)

- wyświetlanie LED albo LCD

Mam pewien problem z przerwaniem od ADC, funkcja pomiar() nie chce mi działać w tym przerwaniu, jeśli nie ma przerwania i ADC pracuje w normalnym trybie, wszystko jest, wtedy funkcje pomiar() wywoluje w main.

Jednak chciałbym to wrzucić do przerwania bo po to ono jest.

Mam już jedno przerwanie do multipleksowania LED od timera.

W czym problem?

KOD:

volatile double temp1,tempw,tobl; // do zapisu temperatury z ADC

void Inicjalizacja(void)   //funkcja inicjalizacji przetwornika ADC
{
// Wybranie wewnętrznego żródła napięcia odniesienia      
      sbi(ADMUX,REFS0);  //wew 2,56 z C do GND                     
       sbi(ADMUX,REFS1);                                  
// Zezwolenie na konwersję   
        sbi(ADCSRA,ADEN);    // wlaczenie ADC                  
     sbi(ADCSRA,ADIE);     // przerwanie
      sbi(ADCSRA,ADFR);    //free run
// Wybranie częstotliwości dla taktowania przetwornika  (125 kHz)
     sbi(ADCSRA,ADPS1);                                        
      sbi(ADCSRA,ADPS2); // 8Mhz / 64   = 125 kHz                       
}

W MAIN OCZYWISCIE INICJALIZACJA + sei();

double pomiar(void)  //funkcja dokonująca pomiaru
{   
//wybor pinu     //ADC3
sbi(ADMUX,MUX0);
sbi(ADMUX,MUX1);
//sbi(ADMUX,MUX2);
//sbi(ADMUX,MUX2);

int i;
  temp1=0;
  for(i=0;i<10;i++)                  //petla pomiarowa
     {
       sbi(ADCSRA,ADSC);  // Rozpoczęcie przetwarzania                 

        while(bit_is_set(ADCSRA,ADSC))   // Oczekiwanie na zakończenie przetwarzania
        {};   
     temp1+=(double)ADCW;          // Zapisanie  wyniku konwersji do zmiennej
     }
  temp1=temp1/10;    //wyliczenie sredniej wartosci   

  return temp1;
}

PRZERWANIE

ISR(ADC_vect)   //przerwanie od ADC
{   
tempw=pomiar();
}

W WHILE(1) MAM przkeonwertowanie wartości na wyświetlacz

tempw=tempw*k;
tempw=tempw+17;
y=tempw/100;  //dzesiatki
x=(int)tempw%100/10; //jednosci
Link do komentarza
Share on other sites

Jeżeli rzeczywiście wykorzystujesz w przerwaniu funkcję pomiar() to mieszasz

dwie metody pomiarowe - z wykorzystaniem przerwania i bez przerwania.

Czy aby na pewno rozumiesz działanie przerwania ADC, kiedy ono jest generowane

Pojedyncza funkcja pomiar() dokonuje serii 10 pomiarów w trybie pracy bez przerwania,
zamiast tego "zawiesza" działanie programu na czas pomiaru.

Takie rozwiązanie o ile nie jest uzasadnione z jakiegoś powodu

to jest nieefektywne bo przez czas pomiaru procesor jest "zatrzymany".

Jeżeli chcesz dokonywać pomiarów w przerwaniach musisz zmienić algorytm działania.

Jest na to kilka sposobów.

jeżeli chcesz korzystać z przerwania i dokonywać pomiaru z kilku wejść ADC

(o ile dobrze rozumię to w Twoim przypadku z 3)

Możesz to zrobić w ten sposób (jest to oczywiście przykład):

- poza funkcją inicjalizacja() napisać funkcję start

- w funkcji start jako parametr wejściowy podać adres wejścia ADC,
zadaniem tej funkcji będzie ustawienie pracy jako free, wybranie wejścia ADC i uruchomienie przetwornika

- zadeklarować dwie zmienne globalne pomiar i flaga (lub adres)

- w przerwaniu:

1 zadeklarować zmienną wewnętrzną ale z parametrem static służącą do zliczania żądanej liczby sampli,
2 zapisywać sample w zmiennej pomiar

3 przy ostatnim samplu wyłączyć przetwornik i ustawić zmienną flaga

- w pętli głównej dać warunki sprawdzające zmienną flaga

(np. 0x01, 0x02, 0x4)

w przypadku pierwszego czujnika flaga przyjmuje wartość 0x01

wtedy w pętli głównej zostanie spełniony warunek od tej wartości flagi

Wtedy wartość zmiennej pomiar zostanie przypisana do jakiejś zmiennej w funkcji main np. o nazwie czujnik1, wyzerować zmienną flaga, wywołać funkcję start z adresem ADC czujnika drugiego,
- dla pozostałych wejść analogiczne warunki.

Jako przykład podam funkcję uruchamiającą konwersję gdzie parametr we informuje z którego wejścia funkcja ma korzystać:

void ADC_start(uint8_t we)
{
//wybor wejscia ADC;
ADMUX &= 0xF0;				//wyzerowanie MUX
ADMUX |= (we & 0x0F);

ADCX_IN_CONFIG(we);

//uruchomienie konwersji
ADCSRA |= _BV(ADSC);

} /* ADC_start */

//makro ADCX_IN_CONFIG

#define ADCX_IN_CONFIG(PIN)		\
	DDRC  &= ~_BV(PIN);		\
	PORTC &= ~_BV(PIN)

Natomiast w załączniku zamieszczam swoją bibliotekę obsługującą ADC w mega8.

W pliku .h należy wybrać odpowiednie opcje,
obsługę przerwania zamieścić w pliku .c

w pliku z funkcją main wywołać konfiga i start

ADC_lib.zip

Link do komentarza
Share on other sites

Dziękuję za zainteresowanie się problemem.

Jeśli chodzi o zrozumienie to dopiero się uczę i więcej informacji przyjmuję do wiadomości :->

Zgadza się funkcja pomiar paraliżuje mi program dlatego chce użyć przerwania.

W domyśle będą 3 czujniki, i na ich podstawie będą wysterowanie pwmy.

Do rzeczy:

Tak jak piszesz:

1) deklaruję dwie zmienne globalne:

double pomiar, flaga;

2) Funkcja start

#define ADCX_IN_CONFIG(PIN)		\
	DDRC  &= ~_BV(PIN);		\
	PORTC &= ~_BV(PIN)
void ADC_start(uint8_t we)
{
	  sbi(ADCSRA,ADFR);    // włączamy free running tutaj?
//wybor wejscia ADC;
ADMUX &= 0xF0;                //wyzerowanie MUX
ADMUX |= (we & 0x0F);

ADCX_IN_CONFIG(we);

//uruchomienie konwersji
ADCSRA |= _BV(ADSC);

} /* ADC_start */ 

Nie rozumiem tej definicji ADCX_IN_CONFIG(PIN), czemu ona służy?

Mam w main nie ustawiać tych pinów(DDRC,PORTC) jako wejścia?

I zatrzymałem się na tym co napisać w tym przerwaniu. Coś takiego?

ISR(ADC_vect)  
{   static uint8_t probka;
 for(probka=0;probka<10;probka++)
   {

pomiar=pomiar+ADCW;

   }
cbi(ADCSRA,ADSC); // zatrzymanie konwersji
Jak ustawić zmienna flaga?

}

Gdzie wywołac funckje ADC_start?

Link do komentarza
Share on other sites

A więc tak:

co do zmiennych to wystarczy zupełnie jak flaga będzie 8 bitowa,
mało tego zmienne muszą mieć wyłączoną optymalizację więc potrzebna jest dodatkowa informacja na ten temat czyli volatile, w przeciwnym wypadku preprocesor może Ci zoptymalizować te zmienne w sposób niepowołany.

volatile double pomiar;  //zmienna 2 bajtowa (16 bitowa)
volatile uint8_t flaga;  //zmienna 1 bajtowa (8 bitowa)

ADCX_IN_KONFIG nie jest funkcją tylko makrem zdefiniowanym poniżej:

#define ADCX_IN_CONFIG(PIN)        \
       DDRC  &= ~_BV(PIN);        \
       PORTC &= ~_BV(PIN) 

ustawia ono odpowiedni pin jako wejście bez podciągnięcia,
ten fragment kodu musisz wkleić gdzieś powyżej funkcji ADC_start

Mam w main nie ustawiać tych pinów(DDRC,PORTC) jako wejścia?

No właśnie nie musisz, to makro ustawia odpowiedni pin jako wejście w przed uruchomieniem przetwornika.

Funkcję ADC_start wywołujesz w pętli głównej w main kiedy chcesz wywołać pomiar z kolejnego czujnika

/Ustawianie bitów zmiennej flaga najlepiej robić za pomocą makra _BV

wygląda to tak:

flaga |= _BV(bit); //set

flaga &= ~_BV(bit); //reset

flaga ^= _BV(bit); //negacja

bitu o numerze bit czyli zamiast bit podajesz jakąś wartość z zakresu 0-7 dla zmiennej 8 bitowej.

Zazwyczaj korzystam z przetwornika 8 bitowego, większej precyzji nie potrzebowałem.

Twoje przerwanie powinno wyglądać mniej więcej tak:

ISR(ADC_vect) 
{   static uint8_t probka;

  for(probka=0;probka<10;probka++)
  {

      pomiar=pomiar+ADC;

      if(probka==9)       //dopiero po ostatnim pomiarze...
     {
          cbi(ADCSRA,ADSC); // zatrzymanie konwersji
          flaga |= _BV(0);       //ustawienie bitu zerowego w stan wysoki
     }
  }
}

w pętli głównej sprawdzasz warunek:

if(flaga & _BV(0))
{
    flaga &= ~_BV(0);    //wyzerowanie flagi
    czujnik1 = pomiar/10;

   ADC_stert(cz2);   //uruchamia pomiar z kolejnego czujnika *opcja
}

/*gdzie czujnik1 to zmienna typu double
a cz2 to numer pinu wejścia czujnika drugiego
pozostałe 2 czujniki w sposób analogiczny*/ 
Link do komentarza
Share on other sites

Zarejestruj się lub zaloguj, aby ukryć tę reklamę.
Zarejestruj się lub zaloguj, aby ukryć tę reklamę.

jlcpcb.jpg

jlcpcb.jpg

Produkcja i montaż PCB - wybierz sprawdzone PCBWay!
   • Darmowe płytki dla studentów i projektów non-profit
   • Tylko 5$ za 10 prototypów PCB w 24 godziny
   • Usługa projektowania PCB na zlecenie
   • Montaż PCB od 30$ + bezpłatna dostawa i szablony
   • Darmowe narzędzie do podglądu plików Gerber
Zobacz również » Film z fabryki PCBWay

Bardzo dziękuję za cenne wskazówki, już Ci kolego wiszę porządne piwo.

Jednak nadal nie mogę dojść o co tu biega. ADC jakoś działa ale na wyświetlaczu mam same śmieci, odczyt wariuje i nie jest zgodny z rzecziwistością, lepsze to niż brak odczytu ale do rzeczy:

Przetwornik reaguje na zmianę napięcia na LM35.

Ustawiam w ADCstart pin ADC3 czyli 0x03

Wszystko ustawiłem tak jak pisałeś.

Dodałem w kodzie wyzerowanie pomiar=0; po sprawdzeniu warunku flagi

Flaga przyjmuje dla kazdego czujnika inna wartość? To dla czego w przerwaniu jest ustawiania na BV(0)?

Tryb free running musi być włączany w funckji ADC start, czy może być w Inicjalizacja?

Cały kod poniżej, TIMER0 multipleksuje ok, jak wpiszę na sztywno wartość do zmiennej tempw przelicza mi ładnie na temperature i wyswietla to co powinno.

#define F_CPU 8000000L  // taktowenie wewnetrznym MHz
#include <avr/io.h>
#include <util/delay.h>               
#include <avr/sfr_defs.h>
#include <avr/interrupt.h>
//#include <avr/signal.h>
#include <inttypes.h>
#include <stdlib.h>
#include <math.h> 
/*----------------------DEFINICJE---------------------------------*/
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit)) // makra clear bit i set bit
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))

#define ADCX_IN_CONFIG(PIN)		\
	DDRC  &= ~_BV(PIN);		\
	PORTC &= ~_BV(PIN)

/*----------------------ZMIENNE GLOBALNE--------------------------*/
volatile int x,y,w; //x,y wyswietlacz
volatile float k,p; // przeliczniki

volatile uint8_t timer;
volatile double pomiar;  //zmienna 2 bajtowa (16 bitowa)
volatile double temp1,tempw;  // temp1 , tempw - temp na wyswietlacz
volatile uint8_t flaga;  //zmienna 1 bajtowa (8 bitowa) 


/*----------------------FUNKCJE--------------------------*/
void Inicjalizacja(void)   //funkcja inicjalizacji przetwornika ADC
{

// Wybranie wewnętrznego żródła napięcia odniesienia   	
		sbi(ADMUX,REFS0);  //wew 2,56 z C do GND	 					   
 		sbi(ADMUX,REFS1);						 					  
//		sbi(ADMUX,ADLAR);  

// Zezwolenie na konwersję   
 	    sbi(ADCSRA,ADEN);	 // wlaczenie ADC						
	sbi(ADCSRA,ADIE);     
//		sbi(ADCSRA,ADFR);    
// Wybranie częstotliwości dla taktowania przetwornika  (125kHz)
		sbi(ADCSRA,ADPS1); 												
	sbi(ADCSRA,ADPS2); // 8Mhz / 64	= 125 Khz	  					 
} 

void ADC_start(uint8_t we)
{
		 sbi(ADCSRA,ADFR);  
	 //wybor wejscia ADC;
			ADMUX &= 0xF0;		//wyzerowanie MUX
			ADMUX |= (we & 0x0F);

		//	ADCX_IN_CONFIG(we);

			//uruchomienie konwersji
		ADCSRA |= _BV(ADSC);

} 


void led7(int i)  // funkcja do zapalania kolejnych segmentow w zaleznosci od cyfry
{				  // dla kazdej cyfry kolejno ustawione odpowiednie bity na porcie D
#define A 0  // A odpowiada 0 itd...
#define B 1
#define C 2
#define D 3
#define E 4
#define F 5
#define G 6
if(i>=10&&i<100)i=i%10;
switch(i)
{
case 0: PORTD &= ~_BV(A) &~_BV(B) &~_BV(C) &~_BV(D) &~_BV(E) &~_BV(F); break; //0
case 1:PORTD &= ~_BV(B) &~_BV(C);break; //1
case 2:PORTD &= ~_BV(A) &~_BV(B) &~_BV(D) &~_BV(E) &~_BV(G);break; //2
case 3:PORTD &= ~_BV(A) &~_BV(B) &~_BV(C) &~_BV(D) &~_BV(G);break; //3
case 4:PORTD &= ~_BV(B) &~_BV(C) &~_BV(F) &~_BV(G); break;//4
case 5:PORTD &= ~_BV(A) &~_BV(C) &~_BV(D) &~_BV(F) &~_BV(G);break; //5
case 6:PORTD &= ~_BV(A) &~_BV(C) &~_BV(D) &~_BV(E) &~_BV(F) &~_BV(G);break;//6
case 7:PORTD &= ~_BV(A) &~_BV(B) &~_BV(C); break;//7
case 8:PORTD &= ~_BV(A) &~_BV(B) &~_BV(C) &~_BV(D) &~_BV(E) &~_BV(F) &~_BV(G);break; //8
case 9:PORTD &= ~_BV(A) &~_BV(B) &~_BV(C) &~_BV(D) &~_BV(F) &~_BV(G);break; //9
}
}

int main(void)	// główna funkcja
{
timer=200;
k=2.5;
p=8.5;

DDRB = 0b11001111;	//4,5 przyciski 
DDRC = 0x00;   //0,1,2 przeciski
DDRD = 0xFF;   // wyjscia dla segmentu
PORTD = 0xFF; // ustawienie 1   
PORTB = 0xFF; 
PORTC = 0b00000111; // podciagniecie przyciskow do VCC

//TIMER 0 MULTIPLEKSOWANIE
TCCR0 |= (1 << CS00); 
TCCR0 |= (1 << CS02); 
TIMSK |= (1 << TOIE0);

 Inicjalizacja();
 sei();

while (1)
{

ADC_start(0x03);

if(flaga & _BV(0))
{
    flaga &= ~_BV(0);    //wyzerowanie flagi
    temp1 = pomiar/10;
    pomiar=0;

} 


tempw=temp1;
tempw=tempw*k;
tempw=tempw+17;
y=tempw/100;  //dzesiatki
x=(int)tempw%100/10; //jednosci

}



}

ISR(ADC_vect)   //przerwanie od ADC
{   
static uint8_t probka;

  for(probka=0;probka<10;probka++)
  {

      pomiar=pomiar+ADC;

      if(probka==9)       //dopiero po ostatnim pomiarze...
     {
          cbi(ADCSRA,ADSC); // zatrzymanie konwersji
          flaga |= _BV(0);       //ustawienie bitu zerowego w stan wysoki
     }
  } 


} 


SIGNAL(SIG_OVERFLOW0)
{
TCNT0=timer;

switch (w)
{
	case 0:
			PORTD =0xFF;    // wyzerowanie portu D - zgaszenie wszystkich lini
				sbi(PORTB,0); //zgaszenie segmentu 2
		led7(x);
				cbi(PORTD,7); // zapalenie segment 1
				w++;
				break;

	case 1:		
			PORTD =0xFF;     	
				sbi(PORTD,7); ///zgaszenie segmentu 1
		led7(y);

				cbi(PORTB,0);	// zapalenie segment 2
				w=0;
				break;
}

}

Po dalszej nocnej zabawie ciekawostka, być może która naprowadzi Cię na rozwiązanie problemu bo ja tego nie ogarniam.

Temperatura na wyświetlaczu jest prawidłowa jeśli kod jest w takiej postaci, nie ma petli for w przerwaniu i usredniania:

Przerwanie wygląda tak:

ISR(ADC_vect)   //przerwanie od ADC
{   
pomiar=ADCW;
          cbi(ADCSRA,ADSC); // zatrzymanie konwersji
          flaga |= _BV(0);       //ustawienie bitu zerowego w stan wysoki

}

A w main tedy jest:

ADC_start(0x03);

if(flaga & _BV(0))
{
    flaga &= ~_BV(0);    //wyzerowanie flagi
    temp1 = pomiar;
 pomiar=0;
} 

Reszta kodu bez zmian.

Link do komentarza
Share on other sites

Widze 2 rzeczy:

- ustawiasz sbi(ADMUX,ADLAR);

czyli najstarszy bit jest na pozycji najstarszej.

Nie doczytałem o jakim procku mówimy (założyłem, że maga8, który ma 10cio bitowy przetwornik ADC).

Jeżeli mierzysz wartość 10cio bitową przesuniętą do 16 bitowej

i sumujesz ją 10 razy masz duże szanse na przepełnienie zmiennej

a co za tym idzie na błędy z tego powodu wynikłe.

Więc proponowałbym ustawić rejestr tak, aby wynik był wyrównany do prawej strony a nie lewej.

Mnie zazwyczaj wystarcza wynik 8 bitowy, wtedy przesuwam wynik do lewej strony, ale sczytuję tylko starszy bajt, młodszy pomijam.

Czy potrzebujesz uśredniać te wyniki?

- jak się nazywa rejestr wyjściowy ADC - ADC czy ADCW

bo gdzieś jest błąd - w kodzie masz ADC a w przykładzie, który działa ADCW

Racja w przerwaniu ustawiam tylko bit 0.

Ale to był tylko przykład.

Sprawę możesz rozwiązać na kilka sposobów, tak czy inaczej gdzieś trzeba zapamiętać, z którego czujnika był dokonywany pomiar.

Możesz to powiązać dodatkowo z ADC_start, uzmiennić wejście czujnika

I w if-ie sprawdzać dwa warunki

if((flaga & _BV(0)) && (czujnik=1))
{
...
}

coś w tym stylu. (w funkcji main potrzebujesz dodatkową zmienną czujnik pamiętającą z którego wejścia ADC dokonywany jest pomiar)

Przykład przepełnienia i jego braku:

jeżeli masz zmienną 10cio bitową to masz max 1023 (0x 03 FF), jak ją pomnożysz 10 razy otrzymasz 10230 (0x 27 F6) - czyli wynik mieści się w zmiennej 2 bajtowej typu double

jeżeli masz zmienną 10cio bitową dosuniętą do 16to bitowej to masz 65472 (0x FF C0),
jak ją pomnożysz 10 razy to otrzymasz 654720 (0x 09 FD 80) czyli potrzebujesz zmienną 3 bajtową bo nastąpiło w międzyczasie przepełnienie, procesor mając tylko zmienną dwubajtową odrzuci najstarszy bajt.

Link do komentarza
Share on other sites

Kurde już sam nie wiem co jest grane.

Zrobiłem przesuwanie wyniku do lewej strony. czyli ustawiam ADLAR.

Pominąłem to uśrednianie w przerwaniu, bo dodanie jekiejkolwiek funkcji w tym przerwaniu wszystko kaszani, wyniki wariują wyświetlacz świruje i nawet bez uśrednia kiedy nie na pewno przepełnienia.

Może najpierw napisze co działa i kiedy.

Mam konwersje 8 bitowa wiec, nie potrzebuje żadnych przeliczników, np dla wejście 230 mV co w przypadku LM35 odpowiada 23 °C mamy:

230*256/2560=23 - wiec wartość rejestru ADCH odpowiada temperaturze.

Zamiast double użyłem char co znacznie odchudza program.

Dodałem zmienną globalną we za pomoca ktorej bede wybieral wejście.

Początkowo w main ustawiam 0x03;

Twoja definicja ADC konfig niczego nie zmienia jak jej nie ma,ustawiam porty na stale w main.

No i muszę dodawać delay w while bo inaczej cyfra drga niewidzieć czemu, a ten delay znów mi opóźnia resztę programu.

Flaga w sumie tylko informuje nas o tym kiedy zakończyła się konwersja?

DZIAŁA KIEDY:

ZMIENNE:

volatile char x,y,w; //x,y wyswietlacz
volatile uint8_t timer;
volatile char pomiar;  //zmienna 2 bajtowa (16 bitowa)
volatile char temp1,temp2,temp3,tempw;  // temp1 , tempw - temp na wyswietlacz
volatile uint8_t flaga;  //zmienna 1 bajtowa (8 bitowa) 
volatile uint8_t we;  //zmienna 1 bajtowa (8 bitowa) 

INICJALIZACJA:

void Inicjalizacja(void)   //funkcja inicjalizacji przetwornika ADC
{

// Wybranie wewnętrznego żródła napięcia odniesienia   	
		sbi(ADMUX,REFS0);  //wew 2,56 z C do GND	 					   
 		sbi(ADMUX,REFS1);						 					  
	sbi(ADMUX,ADLAR);  

// Zezwolenie na konwersję   
 	    sbi(ADCSRA,ADEN);	 // wlaczenie ADC						
	sbi(ADCSRA,ADIE);     
//		sbi(ADCSRA,ADFR);    
// Wybranie częstotliwości dla taktowania przetwornika  (125kHz)
		sbi(ADCSRA,ADPS1); 												
	sbi(ADCSRA,ADPS2); // 8Mhz / 64	= 125 Khz	  					 
} 

ADC START:

void ADC_start(uint8_t we)
{
		 sbi(ADCSRA,ADFR);  
		ADMUX &= 0xF0;		//wyzerowanie MUX
			ADMUX |= (we & 0x0F);    //wybor wejscia ADC;
		//	ADCX_IN_CONFIG(we);
	        ADCSRA |= _BV(ADSC); //uruchomienie konwersji

} 

W WHILE(1):

ADC_start(we);


if((flaga & _BV(0))&&(we=0x03))
{
    flaga &= ~_BV(0);    //wyzerowanie flagi
    temp1 = pomiar;

} 
y=tempw/10;  //dzesiatki
x=(int)tempw%10; //jednosci 

_delay_ms(100);
}

W PRZEWANIU:

 
ISR(ADC_vect)   //przerwanie od ADC
{pomiar=ADCH;
cbi(ADCSRA,ADSC); // zatrzymanie konwersji
 flaga |= _BV(0);
}

Wszystko się chrzani kiedy chce odczytywać 3 czujniki po kolei a nie jeden:

W przerwaniu daje we++;

ADC_start(we);

if((flaga & _BV(0))&&(we=0x03))
{
     flaga &= ~_BV(0);    //wyzerowanie flagi
    temp1 = pomiar;	
} 

if((flaga & _BV(0))&&(we=0x04))
{
    flaga &= ~_BV(0);    //wyzerowanie flagi
    temp2 = pomiar;//10;
} 

if((flaga & _BV(0))&&(we=0x05))
{
    flaga &= ~_BV(0);    //wyzerowanie flagi
    temp3 = pomiar;//10;
} 

PRZERWANIE:

ISR(ADC_vect)  
{
      pomiar=ADCH;

          cbi(ADCSRA,ADSC); // zatrzymanie konwersji
	   flaga |= _BV(0);  
 we++;
 if(we==0x06){we=0x03;}

} 

EDYCJA PO DWÓCH GODZINACH WALKI Z KODEM..

Ten ADC to niezłe ustrojstwo, powyżej szkolny błąd powinno być we==0x03 we=0x03, przyrównanie w if(),

Uzyskałem zadowalający rezultat, tylko nadal musi być opóźnienie while, żeby odczyt nie latał, jak to zlikwidować, to jest nadal problem do rozwiązania...

Przyciskami wybieram sobie czujnik do wyswietlenia.

Od wartości ADC bedą sterowane PWM sprzętowe ale o tym póżniej

Teraz jest tak:

while (1)
{

ADC_start(we);


if((flaga & _BV(0))&&(we==0x03))
{

    temp1 = pomiar;
 flaga &= ~_BV(0);    //wyzerowanie flagi
 we=0x04;


} 

if((flaga & _BV(0))&&(we==0x04))
{

    temp2 = pomiar;//10;
  flaga &= ~_BV(0);    //wyzerowanie flagi
  we=0x05;


} 
if((flaga & _BV(0))&&(we==0x05))
{

    temp3 = pomiar;//10;
     flaga &= ~_BV(0);    //wyzerowanie flagi
	 we=0x03;
} 

if(LEWO){tempw=temp2;}  // przesuniecie, 3-1, 2-3, 1-2
else if(OK){tempw=temp3;}
else if(PRAWO){tempw=temp1;}
else{tempw=99;}
//tempw=temp3;y=tempw/10;  //dzesiatki
x=(int)tempw%10; //jednosci %100/10

_delay_ms(100);
}//while

a w przerwaniu:

ISR(ADC_vect)   //przerwanie od ADC
{
     pomiar=ADCH;
       cbi(ADCSRA,ADSC); // zatrzymanie konwersji
	   flaga |= _BV(0); 
Link do komentarza
Share on other sites

no dobra po kolei tylko szybko bo jestem w pracy a mam trochę roboty.

Po pierwsze jaki masz wyświetlacz

Jeżeli korzystasz z LED to najlepiej zrobić multipleksowanie na timerze, tak dobierz czasy aby wyświetlacz miał odświeżanie mniej niż 20ms.

W ATmega8 są 3 timery z czego timer1 może sterować dwoma wyjściami PWM

z wykorzystaniem rejestrów OCR1A i OCR1B, timer2 może sterować trzecim,
timer0 może być wykorzystany sterowania wyświetlaczem LED.

Przy czym uwaga timery 0 i 1 mają ten sam preskaler

W ten sposób nie potrzebujesz żadnych opóźnień w programie.

Co do funkcji main i pętli głównej to ja bym zrobił coś takiego:

 int main(void)
{
char czujnik=0x03;

//dalsza inicjalizacja

ADC_start(0x03);   // pierwsze uruchomienie ADC bezpośrednio przed pętlą
while(FOREVER)
{
      if((flaga & _BV(0)) && (czujnik==0x03))
      {
             flaga &= ~_BV(0);
             czujnik1 = pomiar;
             czujnik=0x04;
             ADC_start(czujnik);
// nową konwersję uruchamiamy po skończeniu poprzedniej
// a nie bezpośrednio w while :!:
      }

      if((flaga & _BV(0)) && (czujnik==0x04))
      {
             flaga &= ~_BV(0);
             czujnik2 = pomiar;
             czujnik=0x05;
             ADC_start(czujnik);
      }

      if((flaga & _BV(0)) && (czujnik==0x05))
      {
             flaga &= ~_BV(0);
             czujnik3 = pomiar;
             czujnik=0x03;
             ADC_start(czujnik);
      }

      // dalsze przeliczenia, wyświetlanie i sterowanie,
      // najlepiej też zrealizowane przy pomocy flag i warunków


}
}

O ile kod w while nie będzie zbyt obszerny możesz ten program napisać bez sprawdzania flagi i bez przerwania, w takim wypadku zamiast sprawdzać flagę możesz sprawdzać bit ADIF w rejestrze ADCSRA. I ją później zerować.

Jeżeli nie uśredniasz wyników w takim razie możesz wyłączyć "FREE running mode" bo i tak co pomiar zmieniasz wejście.

Mam konwersje 8 bitowa wiec, nie potrzebuje żadnych przeliczników, np dla wejście 230 mV co w przypadku LM35 odpowiada 23 °C mamy:

230*256/2560=23 - wiec wartość rejestru ADCH odpowiada temperaturze.

To nie wygodniej podzielić od razu przez 10

230/10 =23

Wersja bez przerwania i bez flagi, w trybie single a nie free running:

 int main(void)
{
char czujnik=0x03;

//dalsza inicjalizacja

ADC_start(0x03);   // pierwsze uruchomienie ADC bezpośrednio przed pętlą
while(FOREVER)
{
      if((ADCSRA & _BV(ADIF)) && (czujnik==0x03))
      {
             ADCSRA &= ~_BV(ADIF);
             czujnik1 = ADCH;
             czujnik=0x04;
             ADC_start(czujnik);
// nową konwersję uruchamiamy po skończeniu poprzedniej
// a nie bezpośrednio w while :!:
      }

      if((ADCSRA & _BV(ADIF)) && (czujnik==0x04))
      {
             ADCSRA &= ~_BV(ADIF);
             czujnik2 = ADCH;
             czujnik=0x05;
             ADC_start(czujnik);
      }

      if((ADCSRA & _BV(ADIF)) && (czujnik==0x05))
      {
             ADCSRA &= ~_BV(ADIF);
             czujnik3 = ADCH;
             czujnik=0x03;
             ADC_start(czujnik);
      }

      // dalsze przeliczenia, wyświetlanie i sterowanie,
      // najlepiej też zrealizowane przy pomocy flag i warunków


}
}

W tym przypadku w rejestrze ADC wynik jest przetrzymywany tak długo, aż wystąpi odpowiedni warunek w pętli.

Link do komentarza
Share on other sites

Kolego nie musisz zaniedbywać swojej pracy żeby mi pomóc 😉

Skoro nie uśredniam, mogę wyłączyć ADFR ale nie robię tego, gdyż pomiar wtedy znów waruje.

Doszedłem do wniosku że opóźnienie to zło konieczne i jednak mi się przyda.Mam sekcje automatyczną i ręczną. W automatyczne pwm jest uzależniony od ADC, a w ręcznym pmw ustawia się przyciskami, a temperatura wtedy jest opcją do tylko wyświetlenia.

Więc odczytywanie musi być w while(FOREVER), ideałem by było gdyby pomiar był poza programem i wtedy tylko byś się wpisało np, temp1=pwm1, przydał by się kolejny timer, który by czytał wszystkie wejścia adc i przypisywał do zmiennych, jednak wszystkie timery są zajęte.

Timery ustawione są od samego początku tak jak mówiłeś gdyż cały czas testuje program nie tylko pod względem ADC.

Napisałeś że timer 0 i 1 ma ten sam preskaler, to znaczy że nie mogą pracować z różną częstotliwością?

Mam tak ustawione:

//TIMER 1 pwm 1 2
TCCR1A = _BV(WGM10) | _BV(COM1A1) | _BV( COM1A0) | _BV(COM1B1) | _BV(COM1B0);
TCCR1B = _BV(WGM12) | _BV(CS10);

//TIMER0 wysw
TCCR0 |= (1 << CS00); 
TCCR0 |= (1 << CS02); 
TIMSK |= (1 << TOIE0);

//TIMER2 pwm 3
TCCR2 =  _BV(COM21) | _BV(COM20)| _BV(WGM21) | _BV(WGM20) |  _BV(CS20); 

A wracając do pomiaru w sekcji auto mam tak:

	ADC_start(we);


		if((flaga & _BV(0))&&(we==0x03))
		{
		     temp1 = pomiar;
			  we=0x04;
			 flaga &= ~_BV(0);    //wyzerowanie flagi

		} 

		if((flaga & _BV(0))&&(we==0x04))
		{

		     temp2 = pomiar;//10;
			  we=0x05;
			  flaga &= ~_BV(0);    //wyzerowanie flagi

		} 

		if((flaga & _BV(0))&&(we==0x05))
		{

		     temp3 = pomiar;//10;
			 we=0x03;
			     flaga &= ~_BV(0);    //wyzerowanie flagi

		} 

Dalej w kodzie jest wyświetlanie uzależnione od zmiennej która za każdym razem jest inkrementowana co około 100ms(TO OPOZNIENIE), i są warunki że przy konkretnej wartości wyświetlane jest odpowiednia temperatura, dzięki temu wartość licznika odpowiada czasowi, jest warunek sprawdzanie przycisku przejścia do ręcznego trybu if(OK):

if(licznik==50) // okolo 5 sekundy
			{
			t1;
			_delay_ms(1000);
			c=1;				//zmienna pomocnicza do ustawienia co ma byc na wyswietlaczu

			}

			else if(licznik==100) //kolejne 5 sekund
			{
			t2;
			_delay_ms(1000);
			c=2;

			}

			else if(licznik==150)
			{
			t3;
			_delay_ms(1000);
			c=3;
			}

			else if(licznik==200)
			{
			P1;
			_delay_ms(1000);
			c=4;
			}

				else if(licznik==250)
			{
			P2;
			_delay_ms(1000);
			c=5;
			}

				else if(licznik==300)
			{
			P3;
			_delay_ms(1000);
			c=6;
			}

			else if(licznik==350){licznik=48;}

			if(c==1){tempw=temp2; }//1
			if(c==2){tempw=temp3; }//2
			if(c==3){tempw=temp1; }//3
			if(c==4){tempw=pwm1; tempw=255-tempw;tempw=tempw*100; tempw=tempw/257;}//1
			if(c==5){tempw=pwm2; tempw=255-tempw;tempw=tempw*100; tempw=tempw/257;}//2
			if(c==6){tempw=pwm3; tempw=255-tempw;tempw=tempw*100; tempw=tempw/257;}//3


			licznik++; _delay_ms(100); // wtedy licznik zwieksza sie co 1ms
			if(OK){m=1;_delay_ms(80);while(OK) {}_delay_ms(80);}

A w trybie ręcznym robię to samo praktycznie ale pomiar jest w podmenu i jest aktywowany po przycisku:

else if(d==1)
		{	
				we=0x03;
				ADC_start(we);
					if((flaga & _BV(0))&&(we==0x03))
						{
				     tempw = pomiar;
					 flaga &= ~_BV(0);    //wyzerowanie flagi
					 	} 
			//	tempw=temp1; 
				y=tempw/10;  //dzesiatki
				x=(int)tempw%10; // jendosci
				_delay_ms(100);
				if(OK){d++;timer=50;_delay_ms(80);while(OK) {}_delay_ms(80);}
		}

Jestem prawie usatysfakcjonowany, chce dodać jeszcze jeden bajer, to zamieszczę cały kod, jest w sumie moje pierwsze dziecko w C i na Atmedze, wiem że kod nie jest doskonały i jakość pisania programu ma wiele do życzenia

Program: 6204 bytes (75.7% Full) 😉

Przy tym projekcie staram się jak najwięcej nauczyć dlatego też zawitałem na tym forum.

Co do całości projektu to ala panel PC do sterowania wentylatorów,

Gdybyś chciał takie coś zrobić jak byś to rozwiązał?

Myślę jeszcze nad użyciem pamięci eprom, wtedy by był to programowalny sterownik, np. uzyskanie zadanej temperatury, ale nie mam pojęcia na razie jak sie za to zabrać.

Na dniach zamieszczę cały kod.

Link do komentarza
Share on other sites

To może przedstaw algorytm jaki chcesz zaimplementować w swoim sterowniku.

Postaramy się go jakoś zoptymalizować.

Najlepiej przedstaw go na zasadzie schematu blokowego.

Obsługa pamięci nieulotnej EEPROM nie jest trudna.

To jest tylko kilka poleceń. Przejżyj plik eeprom.h.

Zaglądałeś może do datasheeta ATmega8

Tam jest taki rozdział jak Timer/counter0 and timer/counter1 preskaler.

I jest tam napisane, że obydwa timery mają jeden preskaler.

Czyli np przerwanie t0 w trybie max będzie wywoływane z tą samą częstotliwością jak

analogiczne przerwanie t1 (o ile t1 będzie jako 8bitowy).

W przypadku powyższym, jeżeli t1 będzie 9 bitowy to częstotliwość t1 będzie o połowę mniejsza niż t0 itd.

  • Lubię! 1
Link do komentarza
Share on other sites

Przedstawię algorytm jak znajdę czas(w tym tyg jeszcze), w skrócie; sekcja autu i manual

w auto wyświetla temperatury i wartości procentowe PWMów

w manual przyciskiami chodzimy po menu, np sterujemy każdym wiatrakiem, i wybieramy temp do wyświetlenie

oto moje dzieło sekcja manual działa świetnie natomiast w sekcji auto czasem miesza mi pomiary z adc, przypisuje nie do tej zmiennej co powinien :/

ot całość

http://www.regrom.ovh.org/ostateczna.pdf

Link do komentarza
Share on other sites

Po długiej przerwie wywołanej sesją, zaliczoną, której częścią był i ten projekt zamieszczam to co udało mi się stworzyć.

Projekt nie jest doskonały, najwięcej problemów sprawiał mi ten ADC, i nadal nie mogę wyeliminować błędu mieszania się pomiarów.

Raz na jakiś czas, sprawdzałem na oscyloskopie, do jednego PWMa na chwile przypisana jest wartość z innego czujnika niż powinna.

Stawiam że jest to problem z trybem free running,

Nadal zamierzam rozwijać projekt, gdyż pozwala poznać dokładnie peryferia mikrokontrolera.

Kod nie jest optymalny, i dużo w nim do zmiany.

Najbardziej jestem zadowolony z ręcznego trybu sterowania.

Zamieszczam, cały opis, zdjęcia, kod źródłowy.

Schematic Prints.pdf

TOP2.thumb.JPG.acd4f053f9f6d7232e81b9b436a439ec.JPG

TOP1.thumb.jpg.32c4bad7376ea46c020824c4af1f09ea.jpg

100_7935.thumb.jpg.7da5867906837d7e96fd42727ac2da90.jpg

sterownik.c

System.pdf

Link do komentarza
Share on other sites

Dołącz do dyskusji, napisz odpowiedź!

Jeśli masz już konto to zaloguj się teraz, aby opublikować wiadomość jako Ty. Możesz też napisać teraz i zarejestrować się później.
Uwaga: wgrywanie zdjęć i załączników dostępne jest po zalogowaniu!

Anonim
Dołącz do dyskusji! Kliknij i zacznij pisać...

×   Wklejony jako tekst z formatowaniem.   Przywróć formatowanie

  Dozwolonych jest tylko 75 emoji.

×   Twój link będzie automatycznie osadzony.   Wyświetlać jako link

×   Twoja poprzednia zawartość została przywrócona.   Wyczyść edytor

×   Nie możesz wkleić zdjęć bezpośrednio. Prześlij lub wstaw obrazy z adresu URL.

×
×
  • Utwórz nowe...

Ważne informacje

Ta strona używa ciasteczek (cookies), dzięki którym może działać lepiej. Więcej na ten temat znajdziesz w Polityce Prywatności.