Skocz do zawartości

atmega8+ADC+PWM brak odczytu ADC


mateusz140m

Pomocna odpowiedź

Witam serdecznie bo to moj 1 post na forum.

AVcc zwarte z Vcc Aref do amsy przez 100nF

PB1 - dioda sterowana PWM

PC0 - wejscie z czujnika CNY70

Układ ma sciemniac i rozjasniac diode w zaleznosci od napiecia podawanego na ADC0. Woltomierz poakzuje 1.5-3.5V

Całosc zasilana z 3 baterii AA ("paluszkow") Sam PWM działa. W połaczeniu z ADC juz nie.

Moge posic mążdzejszych Kolegów o rade?

Pozdrawiam

#include <avr/io.h>               // dost�p do rejestr�w
#include <avr/interrupt.h>        // funkcje sei(), cli()definicje SIGNAL, INTERRUPT
#include <stdlib.h> 
#define cbi(sfr, bit)   (_SFR_BYTE(sfr) &= ~_BV(bit)) 
#define sbi(sfr, bit)   (_SFR_BYTE(sfr) |= _BV(bit)) 
#define tbi(PORT,BIT)   PORT^=_BV(BIT)  

/********** ZMIENNE***********/
volatile uint16_t pwmA=0x09; 
volatile int counter=0; 
volatile uint16_t x; 
/*************PWM************/

void PWM_Init (void) 
{ 
sbi(DDRB,PB1);// OC1A jako wyjscie
	sbi(DDRB,PB2); // OC1B jako wyjscie
TCCR1A = (1 << COM1A1)|(1 << COM1A0) | (1 << COM1B1)|(1 << COM1B0)|(1 << WGM11);  // COM1A1 COM1A1 wypelnij +
TCCR1B = (1<<WGM13) | (1<<WGM12) | (0<<CS12) |(0<<CS11)|(1<<CS10); //wgm11:3 f \ast pwm CS10:3 prescale 1024
ICR1H = 0x03; 
ICR1L = 0xFF ; 
OCR1A =pwmA; 
TIMSK |= (1 << TOIE1); //emable timer1 interrupt
	sei(); 
} 

SIGNAL (SIG_OVERFLOW1) 
{ 

OCR1A = pwmA; 
} 


/*************ADC************/

void ADC_Init (void) 
{ 

sei(); 
ADMUX=(0<<REFS1)|(1<<REFS0); //ustawienie Vref=AVcc
ADMUX=(0<<MUX3)|(0<<MUX2)|(0<<MUX1)|(0<<MUX0); //ADC0
ADCSRA=(1<<ADEN)|(1<<ADIE)|(1<<ADFR)|(1<<ADSC)|(0<<ADPS2)|(1<<ADPS1)|(1<<ADPS0); 
//ADEN w�aczenie przztwoernika
//ADIE interrupt enable
//ADFR free running
//ADPS preskaler 8
//ADSCrozpoczecie konwersji
} 

SIGNAL (SIG_ADC) 
{ 
counter++; 
if(counter==50) 
{ 
	pwmA=(ADCL|(ADCH<<8)); 
	counter=0; 
} 


} 


int main (void) 
{ 


PWM_Init(); 	//inicjalizajca PWM
ADC_Init(); 

for(;;)  
{ 

} 
return (0);      
} 


Link do komentarza
Share on other sites

Ja zawsze podłączam tak:

Aref-----------------+
Avcc----+------------O
       C            |
       C 100uH      |
       C           ---
       C           ---  100nF
Vcc-----+            |
Gnd------------------+

Aref połączone z Avcc i to przez 100uH do Vcc i 100nF do masy, jeśli tak zrobisz, i nadal nie będzie działało to coś namieszałeś w programie. Aha, no i musisz dać stabilizowane 5V.

Link do komentarza
Share on other sites

ADMUX=(0<<REFS1)|(1<<REFS0); //ustawienie Vref=AVcc 
ADMUX=(0<<MUX3)|(0<<MUX2)|(0<<MUX1)|(0<<MUX0); //ADC0

bład leżał tutaj poniewaz rejestr jest adresowany bajtowo nie bitowo i 2 linijka kasowała poprzednie ustawienia

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

Nie, może być zmieniane w dowolnym momencie. OCR1A (podobnie jak inne rejestry licznika, szczegóły w dokumentacji) jest buforowane i automatycznie przeładowywane jest przez procesor w momencie przeładowania licznika.

Wywal to przerwanie i zmieniaj wypełnienie w losowych momentach, a zobaczysz że "gliczy" nie będzie 😉

Link do komentarza
Share on other sites

Ok. A jest jakas roznica czy zmienia sie to w petli głownej czy w przerwaniu? Czy to tylko kwestia estetyczna? Moge to walnac do przedwania od ADC np. Bo niepotrzebnie ta instrukcja musi w petli głownej wykonwyac sie tak czesto skororo wystaryczy ze sie wykona po zakonczeniu konwersji ADC?

Link do komentarza
Share on other sites

Zmienne mają pewien zakres ważności, Zależny od tego gdzie się umieści definicje funkcji. Czyli jak umieścisz definicje funkcji w pętli to jest ona tylko ważna w tej pętli. Możesz też zdefiniować zmienną o tej samej nazwie, w innym miejscu programu, ale w tedy to będzie inna zmienna niż w tej pętli.

Link do komentarza
Share on other sites

hehe to wiem bo w C sie dość dobrze orientuje;p Nie wiem tylko jak to sie ma do uaktualniania rejestru OCR1A. W petli głownej uaktualnia sie w każdym cyklu maszynowym, a w przerwaniu rzadziej, co nie "zawala" tak uC. Chyba ze macie na myśli minimalizacje zasiagu zmiennnej pwmA?

Link do komentarza
Share on other sites

Przerwanie timera 1 ma względnie wysoki priorytet - siecze nieco program. Może powodować jego spowolnienie (zrzucanie danych na stos).

Uaktualnianie wartości PWM częściej niż ona może się zmienić jest bezsensowne. Dlatego Wyliczanie wartości OCR1A (swego rodzaju sterowania) może być inicjowane przez tego rodzaju przerwanie. Robienie tego częściej może prowadzić do błędów (w przypadku wyliczania sterowania na podstawie danych sensorycznych).

Innymi słowy, to nie tylko kwestia estetyczna. Zależy od zastosowania. Jeśli sterowanie dane jest jakimś wzorem (regulator, automat stanu etc.) to powinno być inicjowane przez przerwanie PWM i wyliczanie jego częściej jest błędne i prowadzi do niepoprawnego działania. To jak to zrobisz pod względem językowym nie ma znaczenia (kwestia optymalizacji kompilatora)

Skąd bierzesz dokładnie to sterowanie (wartość PWM) w pętli głównej?

Link do komentarza
Share on other sites

Zamiast stosować przerwanie zakończenia konwersji, wykorzystaj przerwanie od przepełnienia timera. W nim odczytuj wartość pomiaru i startuj następny pomiar. W czasie nim timer wykona pełny cykl, zdąży się wykonać pomiar (to raptem kilkanaście Hz).

W ten sposób nie będziesz generował nierealizowalnych sterowań, a program nie będzie niepotrzebnie posiekany przerwaniami. Co z tego że wyliczysz sterowanie 100 razy nim minie jeden okres PWMa? Preskaler od PWMa możesz zmniejszyć jak chcesz szybszej reakcji.

Link do komentarza
Share on other sites

Mam problem bardzo podobny (ale jednak trochę się różni), dlatego dopiszę w tym temacie. Walczę już jakiś czas i nie mogę sobie poradzić za żadne skarby, byłbym bardzo wdzięczny gdyby ktoś mógł mi pomóc. Przeczytałem pełno postów o ADC na ATMEGA8, artykułów itp. Napisałem program, taki tylko dla sprawdzenia działania przetwornika (na podstawie m.in. postów na tym forum i pdf-a Cezarego Klimasza "Obsługa przetwornika ADC..", korzystałem też z http://www.tkdami.net/~voytek/programy/adc/Przetwornik_AC.html).

Mój problem:

Podłączyłem CNY70 do atmegi8 (konkretnie do ADC0) przez stabilizator 7805. Poniżej prosty kod mający zapalić diodę w zależności od sygnału z czujnika. Funkcja SIGNAL w ogóle nie "załapuje". Jak wyczytałem SIGNAL(SIG_ADC) powinna uruchomić się po konwersji z przetwornika. Być może nie rozumiem jej poprawnie, bo wcześniej nawet wyczyściłem wszystko z jej wnętrza i wrzuciłem tylko instrukcję, żeby zapaliła diodę co tez sie nie stało. Dioda oczywiście podłączona do PD7, zewnętrzne napięcie odniesienia 5V.

Może zupełnie źle to rozumiem... Może ktoś pokieruje mnie na materiały jeszcze do poczytania lub wyjaśni w czym tkwi problem.. Proszę w każdym razie o wyrozumiałość.

#include <avr/io.h> 
#include <avr/interrupt.h> 
#include <util/delay.h> 
#include <stdlib.h> 
#include <avr/signal.h> 

#define LED1_ON PORTD|=(1<<7) 
#define LED1_OFF PORTD &=~(1<<7) 
#define VREF 5
#define VGRAN 3

volatile float res; 
volatile float sens1, sens2, sens3; 


//------------------------------------------------------------------------
//aktywowanie przetwornika
//------------------------------------------------------------------------

void init_adc(void) 
{ 
//ADEN - aktywowanie ADC
//ADFR - pomiar cały czas
//ADPSX - ustawienie preskalera
//ADIE - aktywowanie przerwania
//ADSC - start pomiaru

   ADCSR  = (1<<ADEN) | (1<<ADIE) | (1<<ADFR) | (1<<ADSC) | (1<<ADPS0) |  (1<<ADPS1) | (1<<ADPS2); 
   ADMUX &= ~(1<<REFS1); 
   ADMUX &= ~(1<<REFS0); 
} 

//------------------------------------------------------------------------
//ADC
//------------------------------------------------------------------------

SIGNAL (SIG_ADC) 
{     

   res=(float)(ADCL | (ADCH<<8))/1024*VREF; 
           ADMUX  = 0b00000000; 
               sens1=res; 

  if(sens1<VGRAN)     
         {             
       LED1_ON; 
     } 

  else     

        {   
           LED1_OFF; 
        } 
} 

//------------------------------------------------------------------------
//funkcja główna
//------------------------------------------------------------------------

int main(void) 
{ 

   DDRD |= (1<<7); 

       init_adc(); 
   sei(); 

       for(;;) 
       { 

       } 

  return 0; 

}

Pozdrawiam

Edit:

Zrobiłem jeszcze inny eksperyment - wszystko z wnętrza funkcji SIGNAL wrzuciłem do for'a w mainie, żeby sprawdzić czy chociażby odczyt ADC jest ok i program działa, dioda zapala się i gaśnie tak jak powinna, czyli ewidentnie coś z tym SIGNALem jest nie tak... Pomoże ktoś?

Link do komentarza
Share on other sites

Witam.

Zakładam, że w C programujecie używając kompilatora gcc i środowiska WinAVR lub AVRstudio.

Od pewnego czasu twórcy GCC odchodzą od stosowania makr SIGNAL i INTERRUPT a w zamian powstało jedno makro ISR

przykład dla przetwornika ADC dla ATmega8:

ISR(ADC_vect) 
{ 

}

Czy kompilator nie generuje Wam ostrzerzenia??

Normalnie jest generowane przerwanie typu (o ile się nie mylę) SIGNAL czyli z zablokowanym globalnym zezwoleniem na przerwania SREG I

Jeżeli chcemy wygenerować przerwanie typu INTERRUPT czyli bez blokady przerwań trzeba wywołać funkcję ISR z odpowiednim parametrem.

ISR(vector [, attributes]) 
Valid attributes are ISR_BLOCK, ISR_NOBLOCK, ISR_NAKED and
   ISR_ALIASOF(vect).

Przy czym jak pisałem wyżej domyślnym jest atrybut ISR_BLOCK

To rozwiązanie ma też inną zaletę, bo przy okazji nazwy wektorów przerwań zostały wzięte z not katalogowych odpowiednich procesorów co znacznie ułatwia szukanie odpowiedniej.

/* Interrupt vectors */

/* External Interrupt Request 0 */
#define INT0_vect			_VECTOR(1) 
#define SIG_INTERRUPT0			_VECTOR(1)

Dla przykładu przerwanie INT0 dla procesora ATmega8

Link do komentarza
Share on other sites

Pierwszy błąd to nazwa rejestru.

Masz ADCSR a powinno być ADCSRA

Co do funkcji SIGNAL, oddzieliłbym odczyt z rejestru ADC na kolejne operacje lub przez zmienną 16 bitową:

ISR(ADC_vect) 
{     
uint16_t tmp; 

   tmp = ADC; 
// jak powyzsze nie zadziala to sprobowac po kolei odczytywac mlodszy i starszy bajt

   res=(float)(tmp)/1024*VREF; 
  ADMUX = 0x00; 
               sens1=res; 

  if(sens1<VGRAN)     
         {             
       LED1_ON; 
     } 

  else     

        {   
           LED1_OFF; 
        } 
} 

Nie sprawdzałem ale powinno zadziałać, najpierw popraw nazwę rejestru

a jak to nie pomoże to sprawdź podaną przeze mnie wersję przerwania.

Szczerze mówiąc nie rozumie zastosowania linijki ADMUX w przerwaniu,
ale za pewne jest o fragment większego programu.

Link do komentarza
Share on other sites

Bądź aktywny - zaloguj się lub utwórz konto!

Tylko zarejestrowani użytkownicy mogą komentować zawartość tej strony

Utwórz konto w ~20 sekund!

Zarejestruj nowe konto, to proste!

Zarejestruj się »

Zaloguj się

Posiadasz własne konto? Użyj go!

Zaloguj się »
×
×
  • 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.