trójkąt zamiast sinusa na wyjś

Feb 16, 2013 15 Replies

Eksperymentuję właśnie z generowaniem przebiegu sinusoidalnego za pomocą Atmegi8. Oparłem się na drugim przykładzie z tej strony:



formatting link
Przebieg ma być dialtonem o częstotliwości około 425 Hz.



Wzór przebiegu znajduje się w tabeli, wypełnionej w następujący sposób:



for (i=0; i<256; i++) sinus_buffer[i]=128+126*sin(6.26/256*1);



PWM generowany jest za pomocą TIMER1 ustawionego na Fast PWM (tryb 14). Funkcja włączająca generowanie przebiegu wygląda następująco:



void StartDialtone (void) { TCCR1A |= _BV(COM1A1) | _BV(WGM11); TCCR1B |= _BV(WGM13) | _BV(WGM12) | _BV(CS10); ICR1 = 255; OCR1A = 0; sinus_ind = 0; TCNT0 = 182; TCCR0 |= _BV(CS00); TIMSK |= _BV(TOIE0); }



Z kolei wyłączająca wygląda następująco:



void StopDialtone (void) { TCCR0 &= ~_BV(CS00); TIMSK &= ~_BV(TOIE0); TCCR1A &= ~(_BV(COM1A1) | _BV(WGM11)); TCCR1B &= ~(_BV(WGM13) | _BV(WGM12) | _BV(CS10)); ICR1 = 0; PORTB &= ~_BV(1); }



Zmiana współczynnika wypełnienia odbywa się w przerwaniu, po przepełnieniu TIMER0:



ISR (TIMER0_OVF_vect) { OCR1A = sinus_buffer[sinus_ind]; if (sinus_ind < 255) sinus_ind++; else sinus_ind=0; TCNT0 = 182; }



Za wyjściem PB1/OC1A znajduje się filtr dolnoprzepustowy RC złożony z rezystora 330 omów oraz kondensatora 1uF do masy. Za filtrem nie mam jednak sinusoidy, a coś przypominające trójkąt:



formatting link
Gdzie mogłem popełnić błąd? Problem będzie leżał raczej po stronie softu czy sprzętu?


Ok, niemal godzinie patrzenia w kod dostrzegłem błąd - nie wiedzieć czemu w funkcji wypełniającej tabelę z wzorem sinusoidy nazwę zmiennej zastąpiła jedynka...

Użytkownik "Atlantis" snipped-for-privacy@wp.pl napisał w wiadomości news:kfo8i2$v93$ snipped-for-privacy@portraits.wsisiz.edu.pl...

W sieciach tf widzialem gorsze przebiegi... Z czystym sumieniem możesz dać taki sygnał, chyba, że chcesz dopiąć swego i mieć sinus, lub obawiasz się, że filterkiem tniesz coś znacznie gorszego.

W dniu 2013-02-16 17:41, Anerys pisze:

Jak napisałem - wszystkiemu winny był błąd w instrukcji wypełniającej tabelę wzorem sinusoidy.

Teraz przebieg na oscyloskopie jest o wiele ładniejszy - sinus o lekko postrzępionych brzegach (widoczne przy mocno zmniejszonej podstawie czasu na oscyloskopie).

Zastanawiam się jeszcze tylko nad sposobem, w jaki mam to podać na słuchawkę. Rozumiem, że nie powinienem tego wyjścia za filtrem obciążać bezpośrednio słuchawką 150 omów? Jak powinien wyglądać odpowiedni interfejs?

Atlantis wrote: [..]

Filtr dolnoprzepustowy (wygładzi przebieg) i wtórnik emiterowy.

Dnia Sat, 16 Feb 2013 16:27:53 +0100, Atlantis napisał(a):

Niezaleznie od bledu - cos tu chyba nie tak. Skad takie strome zbocza ? Filtr ma stala 0.33ms

J.

W dniu 2013-02-16 17:52, AlexY pisze:

Filtr oczywiście już jest, złożony z jednego rezystora i jednego kondensatora. Mam rozumieć, że powinienem dać jeszcze drugi taki zestaw?

Jak wygląda kwestia dobierania elementów wtórnika do konkretnych warunków pracy? Szczególnie chodzi mi o rezystor w emiterze i dzielnik w obwodzie bazy. Co decyduje o ich wartościach?

Jeżeli filtrowanie jest niewystarczające to wartości elementów trzeba zmienić lub użyć innego filtra.

Ja tam dobieram wartości przypadkowe, jeśli to nie ma być generator wzorcowy do kalibracji to poeksperymentuj z wartościami 1k i 10k, a w emiterze coś w okolicy obciążenia.

Użytkownik "Atlantis" napisał w wiadomości

Generalnie dokladanie kolejnych czlonow RC to nie jest najlpeszy pomysl - filtr wychodzi z tego kiepski. Bieguny nie tam gdzie trzeba. Ale moze do prostych celow wystarczy.

J.

W dniu 16.02.2013 16:27, Atlantis pisze:

Skoro wypełniasz tablice stałymi wartościami to po co do tego zaprzęgać biednego avr i męczyć go liczeniem funkcji sinus? Policz sobie to w jakimś arkuszu i wrzuć samą tablice gotowych wartości - będziesz miał znacznie krótszy kod.

W dniu 2013-02-18 21:15, Jakub Rakus pisze:

Ostatecznie tak właśnie zrobiłem, dzięki temu mogłem dać tablicę jako volatile const unsigned char. :)

Jednak przy pierwszej próbie łatwiej było dać tą linijkę kodu wypełniającego tabelę. Oczywiście zlecenie przeliczania wartości Atmedze miało tę zaletę, że hex zajmował mniej, ale mam jeszcze na tyle wolnego flasha, że nie muszę liczyć bajtów. ;)

Dlaczego?

volatile i const w jednym miejscu znaczy zazwyczaj "kompilatorze, zepsuj mi tu optymalizacje".

Póki masz miejsce we flash to takie rzeczy powinieneś pakować w progmem.

Myślę, ze raczej wynikało to z błędu w kodzie - z tego co widzę sinus miał stały argument, więc całe to "liczenie" sprowadziło się do podstawienia wartości 131. Liczenie sinusa to nie takie hop-siup bo powoduje wciągnięcie całej biblioteki zmiennoprzecinkowej - dodatkowe 4kB.

Masz eeprom na pokładzie - jak zacznie brakować to użyj go.

W dniu 2013-02-19 00:56, Michoo pisze:

Kierowałem się analogią do wyczytanej kiedyś zasady, że wszystkie zmienne globalne używane w przerwaniach powinny mieć "volatile" przy definicji. "Const" z kolei dałem na wszelki wypadek, aby zabezpieczyć się przed możliwością zmiany zawartości tabeli po jej wypełnieniu. Rozumiem, że w przypadku stałej "volatile" nie jest wskazane?

Jak rozumiem masz na myśli użycie avr/pgmspace.h i zdefiniowanie tablicy przez:

prog_char sinus_buffer[] = {wartość 1, wartość 2, .., wartość n};

oraz odczyt przez:

pgm_read_byte(&sinus_buffer[sinus_ind])

?

Rozumiem, że przy definicji takiej tabeli nie muszę stosować "volatile", nawet jeśli będę się do niej odwoływał w przerwaniu?

Tak z ciekawości: jak zachowa się program przy próbie zapisania czegoś do takiej tabeli, przechowywanej w pamięci programu?

I jeszcze jedno: czy odczytywanie wartości z tabeli przechowywanej w pamięci flash bardzo spowolni wykonywanie programu? Pytam, ponieważ odwołuję się do niej w przerwaniu, a jak wiadomo ono powinno się wykonywać jak najszybciej...

Am 20.02.13 18:20, schrieb Atlantis:

Marna to zasada bo zbytnio uogolnia.

Dokladnie o to chodzilo. Trzymanie tablicy ze stalymi w RAM to marnotrastwo pamieci ktorej Atmel ma dosc malo. Tak samo jak trzymanie tam np. wszelkich lancuchow tekstowych.

Ale po co tam mialo by byc volatile? to slowo informuje kompilator, ze zawartosc pamieci moze ulec zmianie w sposob dla kompilatora malo przewidywalny, innymi slowy kompilatorowi nie wolno optymalizowac dostepu do zmiennej.

Am 20.02.13 18:32, schrieb Atlantis:

trzy cykle CPU zamiast dwoch. Tyle co nic.

ps: nie napisales jakiego typu jest zmienna sinus_ind w twoim kodzie, zakladam ze 16-bitowa (typ int). Poniewaz twoja tablica sinusa ma 256 bajtow mozesz uzyc 8-bitowego typu i napisac "brzydki" kod:

unsigned char sinus_ind;

ISR (TIMER0_OVF_vect) { OCR1A = pgm_read_byte(&sinus_buffer[sinus_ind++])

TCNT0 = 182; }

zmienna sinus_ind "przepelni sie" sama, po wartosci 255 kolejna inkrementacja ustawi zmienna na 0. "if ... else ..." mozesz sobie odpuscic.

Tak tak, to jest brzydki styl ;)

Join the Discussion

Have something to add? Share your thoughts — no account required.

Didn't find your answer?

Ask the community — no account required