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.