logo elektroda
logo elektroda
X
logo elektroda
REKLAMA
REKLAMA
Adblock/uBlockOrigin/AdGuard mogą powodować znikanie niektórych postów z powodu nowej reguły.

ATmega8 [C] - Wyświetlanie wartości na wyświetlaczu 7-segmentowym

papachili 01 Maj 2014 23:38 1893 8
REKLAMA
  • #1 13561116
    papachili
    Poziom 9  
    Witam!
    Próbuję zrobić sobie prosty metronom na avr - póki co składa się on tylko z dwóch przycisków (zwiększanie i zmniejszanie BPM) oraz wyświetlacza 7-segmentowego.
    Multipleksowanie działa, wartość początkowa wyświetla się, ale brak jakiejkolwiek reakcji na naciskanie przycisku.
    Timer1 (odpowiedzialny za multipleksowanie) jest zatrzymywany po wciśnięciu przycisku; następnie zwiększa się wartość BPM o 1; delay 50ms i z powrotem włączany jest Timer1 - ale niestety na wyświetlaczu jest dalej ta sama liczba.
    Czy ktoś mógłby mi powiedzieć co robię nie tak?
    PS. Póki co w kodzie nie ma debouncingu.

    Kod: C / C++
    Zaloguj się, aby zobaczyć kod
  • REKLAMA
  • Pomocny post
    #2 13561211
    Brutus_gsm
    Poziom 25  
    Pomijając to, że zasada działania tego programu jest dziwna i wypadałoby trochę rzeczy zmienić, to problemem w tym przypadku jest fakt, że zmienna speed nie jest typu volatile.
  • #3 13561224
    papachili
    Poziom 9  
    Brutus_gsm napisał:
    Pomijając to, że zasada działania tego programu jest dziwna i wypadałoby trochę rzeczy zmienić, to problemem w tym przypadku jest fakt, że zmienna speed nie jest typu volatile.

    Dzięki za szybką odpowiedź, jeden problem rozwiązany :)
    A czy mógłbyś mi przy okazji powiedzieć co dokładnie wypadałoby jeszcze zmienić w tym programie? Przyznam się, że ekspertem jeszcze nie jestem i dużo błędów się zdarza.
  • REKLAMA
  • Pomocny post
    #4 13561251
    Brutus_gsm
    Poziom 25  
    Nie wiem w jakim celu wyłączasz timer na czas obsługi przycisku. Procedura obsługi przerwania powinna być jak najkrótsza. Niepotrzebnie wywołujesz za każdym razem funkcję setNum(). Warto byłoby ją wyrzucić do pętli głównej i wykonywać tylko w momencie zmiany prędkości. Utwórz sobie tablicę trzech elementów, w których przechowywał będziesz kolejne cyfry. Następnie w przerwaniu wysyłaj odpowiedni element tablicy na port. Dodatkowo niepotrzebnie zerujesz PORTD. Zamiast tego powinieneś wyłączyć wszystkie wyświetlacze (anody). Zmienna digit nie musi być zmienną globalną (a nawet nie powinna). Wystarczy lokalna zmienna z modyfikatorem static.

    Tak może wyglądać przykładowy kod.

    Kod: C / C++
    Zaloguj się, aby zobaczyć kod


    Musiałbyś także przerobić funkcję setNum(), tak by przyjmowała np. zmienną speed i wpisywała do tablicy o nazwie cyfry[] odpowiednie wartości. Oczywiście jest to tylko przykład, bo każdy może to rozwiązać na swój sposób ;) W razie czego jutro mogę pomóc więcej.
  • REKLAMA
  • #5 13561637
    papachili
    Poziom 9  
    Dziękuję za cenne porady ;) Faktycznie zatrzymywanie Timera było zbędne w tym wypadku, aczkolwiek w przyszłości od zmiennej speed wyświetlanej na ekranie zależeć będzie szybkość 'pikania' buzzera i nie jestem pewien czy wtedy to będzie działać.
    Zgodnie z zaleceniem zamiast zerować PORTD spróbowałem wyłączać wszystkie digity DISP_AN_PORT |= ((1<<DIGIT1)|(1<<DIGIT2)|(1<<DIGIT3)), ale w efekcie dało to '888' na wyświetlaczu.
  • REKLAMA
  • #6 13561647
    Brutus_gsm
    Poziom 25  
    Pokaż kod, który napisałeś, będzie łatwiej skomentować.

    Dodano po 12 [sekundy]:

    Pokaż kod, który napisałeś, będzie łatwiej skomentować.
  • #7 13561703
    papachili
    Poziom 9  
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod
  • Pomocny post
    #8 13561790
    Brutus_gsm
    Poziom 25  
    Efekt 888 pojawia się dlatego, że używasz operatora OR w funkcji setNum() oraz dlatego, że źle korzystasz z operatorów logicznych (AND i OR). Zamiast wpisać nową wartość do rejestru PORTD, sumujesz ją z poprzednią wartością. Sama funkcja jeszcze mogłaby wyglądać inaczej, ponieważ nadal niepotrzebnie w procedurze obsługi przerwania jest ona wywoływana - niech wszystko dzieje się w programie głównym, a w przerwaniu tylko wpisanie odpowiedniej wartości do PORTD. Czyli najlepiej byłoby stworzyć tablicę znaków do wyświetlacza. Takie coś jak masz w funkcji setNum, tylko bez wpisywania do rejestru, same bajty. Np.
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod


    Aktualnie w twoim kodzie tablica cyfry[] przechowuje setki, dziesiątki i jedności zmiennej speed. Musisz zrobić tak, żeby przechowywała już prawidłowe wartości z tablicy znaki[]. Bo po co tracić czas w przerwaniu na przepisywanie tych rzeczy? Możesz to zrobić mniej więcej w ten sposób:
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod


    Wtedy w nieskończonej pętli while w programie głównym wystarczy wywołać tę funkcję tylko w momencie zmiany prędkości:
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    Dzięki temu w przerwaniu wystarczy jedna linijka (zamiast wywołania funkcji setNum() ):
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    Możesz tak zrobić, bo tablica ta przechowuje wartości, które trzeba wpisać do PORTD, żeby wyświetlić pożądaną wartość. Pomyśl jeszcze jak zrobić, żeby za pomocą jednej linijki i zmiennej "i" włączać odpowiedni wyświetlacz ;)

    Dodatkowo taka operacja:
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    Nie robi tego co byś chciał zrobić. Jeśli nie pamiętasz działań logicznych, to przeanalizuj jaki będzie jej efekt przy pomocy tablic prawdy.

    Zmieniłbym także nazwę funkcji clearDigit(); bo ona nie ma za zadanie skasować cyfrę z wyświetlacza, tylko ten wyświetlacz wyłączyć ;) Założenie jest takie, że przed zmianą wartości na wyświetlaczu, wyłącza się go, żeby nie pojawiły się "duszki". A wyłączenie jednego wyświetlacza jest równoważne wyłączeniu wszystkich, więc wystarczy jedna linijka. Zresztą niepotrzebnie jest to w ogóle funkcja. Tę linijkę równie dobrze można przepisać do procedury obsługi przerwania. Nie tracimy wtedy kilku taktów zegara na niepotrzebny skok.

    Cytat:
    w przyszłości od zmiennej speed wyświetlanej na ekranie zależeć będzie szybkość 'pikania' buzzera i nie jestem pewien czy wtedy to będzie działać.

    Też nie wiem, czy to będzie działać, bo nie napisałeś nic na temat obmyślonej przez ciebie zasady działania ;) Pamiętaj, że atmega8 ma więcej niż jeden sprzętowy timer. Mogą one działać niezależnie.
  • #9 13561958
    papachili
    Poziom 9  
    Wielkie dzięki za kolejną odpowiedź :)
    Kod poprawiony, program działa, "duszków" nie ma, DISP_AN_PORT |= ~(1<<DIGIT2) zastąpione
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod
    .
    Zmienna speed już nie musi być volatile

    Zamieszczam cały kod poniżej.
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod


    A wracając do buzzera to będzie działał na innym timerze, co prawda jeszcze nie wiem dokładnie jak, ale będę kombinował ;)
    Jeszcze raz dziękuję za pomoc. ;)
REKLAMA