Elektroda.pl
Elektroda.pl
X
Proszę, dodaj wyjątek www.elektroda.pl do Adblock.
Dzięki temu, że oglądasz reklamy, wspierasz portal i użytkowników.

ATtiny2313 - [avr-gcc] PWM i multipleksowanie wyświetlacza jednocześnie

sylweksylwina 19 Lut 2015 16:07 1593 15
  • #1 19 Lut 2015 16:07
    sylweksylwina
    Red. Komputery FAQ

    Witam, jako że w programowaniu jestem niemalże zupełnie zielony zwracam się do was o pomoc. Konstruuję regulator obrotów wentylatorów do PC oparty o pwm z wyświetlaniem wartości na czterech (w zasadzie tylko na trzech) multipleksowanych 7-mio segmentowych wyświetlaczach. W zasadzie to zostało mi dodanie w programie obsługi wyświetlacza i tu wydaję mi się że tu jest "pies pogrzebany".

    Otóż we wszystkich programach z obsługą wyświetlacza jakie widziałem jest skonfigurowany inaczej timer (inny prescaler - częstotliwość wyboru poszczególnych wyświetlaczy ma wynosić ok. 100Hz) co wydaje mi się będzie stało na przeszkodzie sterowania pwm (nie chcę żeby np. wentylatory wydawały z siebie pisk itp.)

    Kolejnym problemem wydaje mi się że konfigurując timer (dokładnie w miejscu gdzie ustawiam (1 << COM1A1)|(0 << COM1A0)|(1 << COM1B1)|(0 << COM1B0)) "robię obejście" domyślnej funkcji poszczególnych wyjść (włącznie z tymi pod które podłączony jest wyświetlacz.

    Napisałem taki oto program:

    Kod: c
    Zaloguj się, aby zobaczyć kod


    Segmenty wyświetlacza są podłączone w nast. sposób

    a PD0
    b PD1
    c PD2
    d PD3
    e PD4
    f PD5 (ustawione jako OC0B?)
    g PD6

    L1 PB3 (ustawione jako OC1A?)
    L2 PB2 (ustawione jako OC0A?)
    L3 PB1
    L4 PB0

    Dodatkowo przyciski pod PB5-PB7
    Dwukolorowa dioda led PA0, PA1 zielona (PA1) sygnalizuje "włączenie" a czerwona (PA0) ma migać podczas zmiany wartości.

    Wyjście PWM do sterowania jest na PB4 (OC1B)
    Z góry proszę o wyrozumiałość gdyż z pisaniem programów pod avr mam styczność od paru dni ;)

    0 15
  • #2 19 Lut 2015 19:05
    BlueDraco
    Specjalista - Mikrokontrolery

    Jaka jest częstotliwość PWM?

    Częstotliwość odświeżania wyświetlacza powinna wynosić 150..300 Hz, przy trzech cyfrach oznacza to częstotliwość przemiatania cyfr 3 razy większą, np. 500..800 Hz. Jeżeli np. częstotliwość PWM wynosi 2000 Hz, to w przerwaniu timera PWM piszesz

    if ((++ x & 3) == 0)
    // wyświetl cyfrę

    1
  • #3 20 Lut 2015 06:37
    Andrzej__S
    Poziom 28  

    sylweksylwina napisał:

    ..."robię obejście" domyślnej funkcji poszczególnych wyjść (włącznie z tymi pod które podłączony jest wyświetlacz.
    ...
    PD5 (ustawione jako OC0B?)
    PB3 (ustawione jako OC1A?)
    PB2 (ustawione jako OC0A?)
    ...


    Nie zauważyłem, żebyś coś wpisywał coś do rejestru TCCR0A, więc piny PB2(OC0A) i PD5(OC0B), będą pracować normalnie, a nie jako wyjścia PWM.
    Jeśli chodzi o PB3(OC1A) to po prostu nie wpisuj nic do bitów COM1A0 i COM1A1 w rejestrze TCCR1A, czyli:
    Kod: c
    Zaloguj się, aby zobaczyć kod

    Wtedy PB3 też będzie pracować jako normalne wyjście, a PWM będziesz miał tylko na PB4(OC1B).

    Jeśli masz ustawioną fabryczną częstotliwość taktowania procesora, to częstotliwość PWM wynikająca z kodu będzie wynosiła ok. 10kHz. Można oczywiście w przerwaniu timera 1 umieścić kod do multipleksowania wyświetlaczem (nie przeszkadza to w generowaniu prawidłowego PWM), ale taka częstotliwość multipleksu jest nieco za duża, więc musiałbyś zmieniać cyfrę co któreś (np. co 10) przerwanie. Myślę, że wygodniej byłoby użyć do multipleksowania wyświetlacza odpowiednio skonfigurowanego (z odpowiednim preskalerem) innego timera (np. Timer/Counter0), chyba że potrzebujesz go do czegoś innego.

    P.S.
    Jeszcze jedna uwaga. W związku z tym, że:
    Atmel napisał:

    The extreme values for the OCR1x Register represents special cases when generating a PWM
    waveform output in the fast PWM mode. If the OCR1x is set equal to BOTTOM (0x0000) the output
    will be a narrow spike for each TOP+1 timer clock cycle. Setting the OCR1x equal to TOP
    will result in a constant high or low output (depending on the polarity of the output set by the
    COM1x1:0 bits.)

    wpisanie do OCR1B wartości 0 nie wyłączy całkowicie PWM. Być może to nie spowoduje ruchu wentylatora, ale cały czas będzie tam PWM, w tym konkretnym przypadku będzie to wypełnienie ok. 1%. Aby wyłączyć całkowicie generowanie PWM na tym pinie trzeba skonfigurować pin PB4 w tryb normalnej pracy bitami COM1B1:0 w rejestrze TCCR1A, ustawić go jako wyjście i wpisać odpowiedni poziom (czyli w tym przypadku będzie to poziom niski).

    1
  • #4 20 Lut 2015 11:41
    sylweksylwina
    Red. Komputery FAQ

    Ok, czyli moje wątpliwości zostały rozwiane. Procesor jest taktowany domyślnie 1MHz. Mój kod wygląda teraz tak:

    Kod: c
    Zaloguj się, aby zobaczyć kod

    Dzięki za wszystkie wskazówki ;)

    Najdziwniejsze jest to że gdy wpisuję TCCR0A lub TCCR0B od razu zaznacza jako: symbol could not be resolved. A wszystko mam zdefiniowane tzn. wpisane:
    #include <avr/io.h>
    #include <util/delay.h>

    0
  • #5 20 Lut 2015 20:11
    Andrzej__S
    Poziom 28  

    Jeśli chodzi o kod, to sporo pokręciłeś.
    Przykładowo:

    sylweksylwina napisał:
    Kod: c
    Zaloguj się, aby zobaczyć kod



    Mógłbym jeszcze podać co najmniej kilka uwag z obszernymi wyjaśnieniami, ale forum to nie miejsce na kurs programowania. Proponuję poczytać o ustawianiu i zerowaniu poszczególnych bitów w rejestrach (i nie tylko o tym) np. tutaj, a na pewno wiele się wyjaśni.

    sylweksylwina napisał:
    Najdziwniejsze jest to że gdy wpisuję TCCR0A lub TCCR0B od razu zaznacza jako: symbol could not be resolved.

    Nie wiem, jakiego środowiska używasz, ale sprawdź czy wybrałeś prawidłowy typ mikrokontrolera.

    1
  • #6 21 Lut 2015 15:32
    sylweksylwina
    Red. Komputery FAQ

    Teraz powinno być dobrze:

    Kod: c
    Zaloguj się, aby zobaczyć kod


    Środowisko to eclipse luna + atmel avr toolchain. Co do błędu to wystarczyło wybrać inny mikrokontroler i z powrotem dać na ten sam.
    Co do obsługi wyświetlacza to rozumiem że w głównej pętli while po tym co wpisałem konfiguruję timer np TCCR0A i piszę dalszą część obsługi wyświetlacza?

    0
  • Pomocny post
    #7 21 Lut 2015 18:07
    Andrzej__S
    Poziom 28  

    sylweksylwina napisał:
    Teraz powinno być dobrze

    Zależy, co rozumiesz przez "dobrze".

    Jeśli chodzi o to, czy ten program będzie działał zgodnie z Twoimi założeniami, to musisz sam jakoś przetestować, bo ja nie mam czasu na szczegółowe analizy. Nie znam zbyt dobrze Eclipse i nie wiem, jakie ma możliwości symulacji, ewentualnie pozostaje skompilowanie, zaprogramowanie mikrokontrolera i przetestowanie.

    Jeśli chodzi o czytelność kodu i sposób zrealizowania założeń, to osobiście miałbym jeszcze nieco zastrzeżeń. Przykładowo, jeśli piszesz PORTD=0xFF; lub PORTB=0x00; to jest to jeszcze do przyjęcia, ale jak się ustawia tylko jeden lub kilka bitów, jak w przypadku DDRA = 0x03; lub PORTA = 0x02; to czytelniejszy jest format taki, jak w przypadku rejestrów, czyli np.:
    Kod: c
    Zaloguj się, aby zobaczyć kod

    W Twoim kodzie brakuje komentarzy, np. jeśli napiszesz tak:
    Kod: c
    Zaloguj się, aby zobaczyć kod

    to będzie wiadomo, że wiesz co chcesz zrobić i dlaczego, a osoba, która zechce Ci pomóc będzie miała ułatwione zadanie (czyli masz większą szansę na uzyskanie pomocy).

    Jak może zauważyłeś usunąłem z kodu wszelkie konstrukcje z zerem typu (0<<COM1B0) czy (0<<CS11), ponieważ one nic nie wnoszą do programu, a moim zdaniem powodują, że kod jest mało czytelny.

    Zapis liczb w formacie heksadecymalnym ma jakiś sens np. w przypadku portów wejścia/wyjścia, ale tam gdzie istotna jest wartość, jak w przypadku rejestru ICR1 lepiej jest używać zapisu ICR1=100; zamiast ICR1=0x64;, bo to bardziej "human readable" :) Na marginesie, jeśli chciałeś uzyskać cykl trwający 100 taktów zegara, należało wpisać do ICR1 wartość 99.

    Atrybut volatile dla zmiennej pwm jest zbędny. Nie jest to błąd, program będzie działał. W przypadku tego programu nie ma to większego znaczenia, bo to tylko jedna zmienna, a program jest krótki, ale generalnie nadużywanie tego atrybutu nie jest korzystne ze względu na rozmiar kodu i czas wykonywania programu, więc dobrze jest wiedzieć, kiedy trzeba go użyć, a kiedy nie.

    Strasznie się rozpisałem, więc już wystarczy, chociaż coś by się jeszcze pewnie znalazło ;) Polecam postudiować jeszcze tę stronę, do której podałem łącze w poście 5 tego wątku lub inne strony, lub może kupić jakąś książkę...

    sylweksylwina napisał:
    Co do obsługi wyświetlacza to rozumiem że w głównej pętli while po tym co wpisałem konfiguruję timer np TCCR0A i piszę dalszą część obsługi wyświetlacza?

    Zupełnie nie tak. Czas trwania pętli while nie jest powtarzalny i trudny do przewidzenia (poza tym pewnie nieco za krótki w tym przypadku), a multipleksowanie trzeba zrobić w powtarzalnych interwałach czasowych, czyli z określoną częstotliwością (tak jak kilka postów wyżej napisał kolega BlueDraco). Przed pętlą while powinieneś odpowiednio skonfigurować timer 0, włączyć przerwania od timera i przerwania globalnie, a później w procedurze obsługi tego przerwania piszesz kod obsługi multipleksowania wyświetlacza. Brzmi groźnie, ale wbrew pozorom nie jest takie trudne, tylko trzeba trochę poczytać o timer'ach i przerwaniach. Niestety to jedyny sensowny sposób jaki znam, żeby zrealizować multipleksowanie wyświetlacza.

    1
  • #8 22 Lut 2015 01:13
    sylweksylwina
    Red. Komputery FAQ

    Chodziło mi o to że teraz powinno być "prościej i czytelniej" :) Program w tej postaci po wgraniu działa, zostało się trochę podszkolić z timerów, przerwań i zabrać się za multipleksowanie wyświetlaczy.

    Offtop: Przyznam że idę na łatwiznę i czerpię fragmenty kodów stąd wychodzą różne komplikacje, później zostaje studiowanie datasheetu uC i przeglądanie rożnych stron ze wskazówkami i wszystko staje się jasne. W zasadzie robiąc na razie jeden projekt mam nadzieję że coś z tego wyniosę na przyszłość.
    Dzięki za objaśnienie podstaw i nakierowanie na "właściwą" drogę ;)

    0
  • #9 23 Lut 2015 19:10
    sylweksylwina
    Red. Komputery FAQ

    Programowanie znacząco mnie przerasta :/ Wyświetlacz już działa (wyświetla wartość wpisaną w flashNumber(), lecz nie działa zmiana wartości (wyświetlacz gaśnie podczas trzymania przycisku), oraz wentylator generuje już dosyć słyszalne częstotliwości. Próbowałem umieszczać mój kod w różnych miejscach lecz bez pozytywnego rezultatu. Byłbym wdzięczny jak by ktoś mógł mi wskazać dla czego tak się dzieje. Chciałbym też żeby przy wyświetlaniu wartości poniżej 100 nie świeciło się "0" na początku. Byłbym wdzięczny jak by ktoś mógł mi wskazać dla czego tak się dzieje.

    Kod: c
    Zaloguj się, aby zobaczyć kod

    0
  • #11 23 Lut 2015 19:58
    Andrzej__S
    Poziom 28  

    sylweksylwina napisał:
    Programowanie znacząco mnie przerasta

    Bo sądząc po kodzie mało poczytałeś o tych timer'ach i przerwaniach i chyba nawet niedokładnie przeczytałeś to, co ja napisałem.

    sylweksylwina napisał:
    wyświetlacz gaśnie podczas trzymania przycisku

    To oczywiste, skoro funkcję wyświetlającą umieściłeś w pętli głównej, w której masz np. coś takiego:
    Kod: c
    Zaloguj się, aby zobaczyć kod

    Multipleksowanie wyświetlacza miałeś zrealizować w procedurze obsługi przerwania, a Ty skonfigurowałeś timer 0, włączyłeś przerwania, a procedury obsługi nie masz wcale. Pomijam fakt, że w ten sposób uzyskasz zbyt niską częstotliwość: 1000000/64/256 ≈ 61Hz, a biorąc pod uwagę, że masz trzy cyfry, to cały wyświetlacz będzie odświeżany z częstotliwością 61/3 ≈ 20Hz. Będzie widać migotanie.

    Konfiguracja timera 1 niepotrzebnie powędrowała do pętli głównej, przez co np. w każdym wykonaniu pętli ustawiasz pwm=50 a później OCR1B = pwm, więc jak ma Ci działać regulacja.

    W sumie tę obsługę przycisków też masz jakąś dziwną.

    0
  • #12 25 Lut 2015 17:19
    sylweksylwina
    Red. Komputery FAQ

    Dziękuje że nie daliście mi gotowca, przynajmniej się czegoś nauczyłem. Migotania na wyświetlaczu nie widać, ostatnie co chciałbym zrobić to żeby na wyświetlaczu aktualizowała się na bieżąco wartość podczas wciskania przycisku zwiększ, zmniejsz. W tej formie programu jest prawie dobrze tylko że wartości zmieniają się za szybko, po dodaniu delaya jest lepiej, lecz wtedy trwa to za długo i wartości nie zmieniają się na bieżąco. Przyciski mam podpięte między Vcc a AVR i przed AVR przez rezystory 10k do masy. Mój kod wygląda tak:

    Kod: c
    Zaloguj się, aby zobaczyć kod

    0
  • Pomocny post
    #13 25 Lut 2015 19:40
    Andrzej__S
    Poziom 28  

    sylweksylwina napisał:
    Dziękuje że nie daliście mi gotowca, przynajmniej się czegoś nauczyłem.

    No to gratuluję, ale niestety musisz się jeszcze nauczyć duuużo więcej, bo nadal jest źle.

    Andrzej__S napisał:
    Multipleksowanie wyświetlacza miałeś zrealizować w procedurze obsługi przerwania

    a Ty zrobiłeś dokładnie odwrotnie, czyli przeniosłeś cały kod z funkcji main() do procedury obsługi przerwania, a multipleksowanie masz nadal w funkcji main(). To jest totalnie źle, i argument, że prawie dobrze działa nie ma żadnego znaczenia, jeśli naprawdę chcesz się czegoś nauczyć. A dlaczego to nie do przyjęcia? Poczytaj jeszcze więcej o tym jak działają przerwania i o tym, czym powinna się cechować procedura obsługi.

    Aby wyprzedzić następny potencjalny błąd: użycie funkcji wyświetlającej flashNumber() wewnątrz procedury obsługi przerwania też nie wchodzi w grę. Znajdź jakiś przykładowy kod, jak się robi multipleksowanie na przerwaniach timera, bo ta funkcja bazuje na opóźnieniach i nadaje się chyba tylko do programu, który nie robi nic więcej poza wyświetlaniem.

    No i na gotowca nie licz (przynajmniej nie ode mnie), bo forum nie jest od tego, żeby kogoś wyręczać.

    P.S.
    Trochę się obawiam, że jesteś zbyt niecierpliwy i wytyczyłeś sobie zbyt ambitne zadanie, jak na początek. Chyba za szybko chcesz osiągnąć wysoki poziom i możesz przez to jeszcze szybciej się zniechęcić.

    1
  • #14 02 Mar 2015 17:25
    sylweksylwina
    Red. Komputery FAQ

    Tak wiem że zadanie zbyt ambitne jak na kogoś z praktycznie zerową wiedzą z zakresu programowania w C. Już nawet znajomy mówił mi żebym zaczął z "Hello World!" czyli w przypadku AVR najczęściej jest to migająca dioda ;) Akurat regulator wentylatorów był mi potrzebny, gotowe rozwiązania co prawda mają wiele kanałów, jednak w związku z tym posiadają również kilka tranzystorów na których jest duża strata energii. Ja wykorzystałem tylko jeden mosfet, regulacje mam za pomocą przycisków i mam jeszcze wyświetlacz ;) Wykorzystałem do tego darmową płytkę przednią z dekodera CP Echostar DSB-717 co dodatkowo obniżyło koszty. Do programu dodałem podtrzymywanie wartości po wyłączeniu przyciskiem oraz "rozruch" wentylatorów czyli zwiększenie wypełnienia pwm podczas włączania regulatora do 15%. Zdaję sobie sprawę że program nie jest żadnym mistrzostwem świata (np. pierwsza cyfra zawsze mocniej świeci), ale program spełnia moje założenia.

    Kod: c
    Zaloguj się, aby zobaczyć kod

    0
  • Pomocny post
    #15 03 Mar 2015 14:12
    Andrzej__S
    Poziom 28  

    sylweksylwina napisał:
    Zdaję sobie sprawę że program nie jest żadnym mistrzostwem świata (np. pierwsza cyfra zawsze mocniej świeci), ale program spełnia moje założenia.


    To zależy, jakie są Twoje założenia.
    Jeśli chciałeś osiągnąć to, żeby wykonać ten jeden konkretny projekt, bo był Ci potrzebny i na tym planujesz poprzestać, to może i bitwę wygrałeś, chociaż w niezbyt dobrym stylu.

    Jeśli Twoim celem jest kontynuacja nauki programowania, to wojnę zapewne przegrasz, bo poprzestając na tym, co do tej pory napisałeś, wpajasz sobie złe nawyki, takie jak np. (już nie wiem ile razy wałkowane na tym forum) używanie funkcji _delay_ms() w obsłudze przerwania lub obsługa klawiszy właściwie pozbawiona debouncing'u (Twój program może mieć tendencje do samoczynnej zmiany PWM, szczególnie jeśli klawiatura będzie podłączona poprzez długie przewody umieszczone w pobliżu źródeł zakłóceń).

    Gdybyś planował kontynuować naukę programowania, to moja rada jest taka: zawsze dobrze jest znaleźć przykłady kodu, żeby poznać zasady i wpoić sobie dobre nawyki, ale więcej się nauczysz pisząc 10 linijek własnego kodu, niż jak doktor Frankenstein skleić z fragmentów cudzych programów jakiegoś potworka, który nigdy nie wiadomo, kiedy się wymknie spod kontroli.

    Uwierz mi, że to nie jest jakaś złośliwość z mojej strony, więc mam nadzieję, że się nie obrazisz. Chciałem po prostu coś Ci uświadomić. Pamiętam jeszcze (choć to było dawno), jak ja zaczynałem i wiem jakie błędy się wtedy popełnia :)

    W tej chwili nie mam czasu, ale gdybyś był zainteresowany, to może w wolnej chwili podpowiem Ci, jak napisać tę procedurę obsługi przerwania do multipleksowania wyświetlacza.

    0