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.

Obsługa wyświetlacza 7-segmentowego Atmega16

plazgut 27 Sty 2012 17:16 6104 12
  • #1 27 Sty 2012 17:16
    plazgut
    Poziom 14  

    Zaprojektowałem na potrzeby projektu na studia zamek kodowy do drzwi - coś jak w blokach montują. Program ogólnie jako taki już mam, lecz nie wiem jak się zabrać za wyświetlacz. Otóż chciałbym, żeby Atmega wyrzucała na wyświetlacz (4 wyświetlacze po 7 segmentów) aktualnie wybierane cyfry. Problem polega na tym, że zawsze włączony ma być tylko jeden segment wyświetlacza. A więc po prostu segmenty mają być załączane powiedzmy z częstotliwością 100Hz dla pierwszego wyświetlacza, potem załączany jest wyświetlacz drugi i znowu przełączanie segmentów. Proszę o pomoc w napisaniu programu w C++, bo tylko ten język poznałem. Przydatne też będą jakieś linki, materiały. Poniżej schemacik jak to wygląda:
    Obsługa wyświetlacza 7-segmentowego Atmega16

    0 12
  • Pomocny post
    #2 27 Sty 2012 18:08
    skalsky5000
    Poziom 20  

    Najprościej odpalić timer i w przerwaniu wystawiać dane i przełączać segmenty przykład:

    Code:
    ISR(TIMER1_COMPA_vect)
    
    {
       switch(wysw)
       {
          case 0:
             PORTD = 0b1111110;
             PORTB=cyfra[jednosci];
             wysw++;
             break;

          case 1:
             PORTD = 0b1111101;
             PORTB = cyfra[dziesiatki];
             wysw++;
             break;
             
          case 2:
             PORTD = 0b1111011;
             PORTB = cyfra[setki];
             wysw=0;
             break;

       }
    }

    W tym przykładzie segmenty są na porcie B,tranzystory na porcie D.

    0
  • #3 27 Sty 2012 18:25
    tmf
    Moderator Mikrokontrolery Projektowanie
  • #4 27 Sty 2012 20:03
    plazgut
    Poziom 14  

    Dziękuję, samo załączanie kolejnych wyświetlaczy wiem jak zrealizować, ale dodatkowo muszę załączać kolejno segmenty wyświetlacza. Idąc za tym przykładem napisałem coś takiego:

    Kod: cpp
    Zaloguj się, aby zobaczyć kod


    Spełni to swoją funkcję?

    0
  • #5 27 Sty 2012 23:20
    skalsky5000
    Poziom 20  

    Źle.Tak jakbyś obsługiwał przerwanie w przerwaniu.Co do multiplexowania poczytaj o trybie CTC.Bardzo fajna rzecz,raz ładujesz wartość do odpowiedniego rejestru a resztą już zajmuje się sprzęt.Gdzieś wygrzebane z moich plików:

    Code:
       TCCR1B |= (1 << WGM12); // tryb CTC
    
       OCR1A = 15000;   
       TCCR1B |= (1 << CS10);
       TIMSK |= (1 << OCIE1A);
       sei();//odblokowanie przerwań
    dalsza cześć programu itp........


    gdzieś tam obsługa przerwania od timera
    ISR(TIMER1_COMPA_vect)
    {
       switch(wysw)
       {
          case 0:
             PORTD = 0b1111110;
             PORTB=cyfra[jednosci];
             wysw++;
             break;

          case 1:
             PORTD = 0b1111101;
             PORTB = cyfra[dziesiatki];
             wysw++;
             break;
             
          case 2:
             PORTD = 0b1111011;
             PORTB = cyfra[setki];
             wysw=0;
             break;

       }
    }

    0
  • #6 28 Sty 2012 00:57
    plazgut
    Poziom 14  

    No ok, doczytałem na czym polega przewaga trybu CTC nad normalnym. Ale dlaczego nie stosować przerwania w przerwaniu? Wtedy CTC zrealizowałbym na Timer1 a w tym przerwaniu zrealizował przerwanie od Timer2, bo ma wyższy priorytet.

    W twoim programie dla wyświetlacza jest ustawiany cały port, a ja nie mogę sobie na to pozwolić, gdyż dodatkowo muszę oprócz załączania każdego wyświetlacza załączać kolejno każdy jego segment. Chodzi mi o zminimalizowanie pobieranego prądu, bo cała płytka jest w SMD i stabilizator 5V jest na max 100mA. Gdybym chciał powiedzmy zapalić cyfrę 8 to wtedy poszło by na wyświetlacz ok. 80mA, czyli już za dużo.

    Czy zatem napisany powyżej przeze mnie program jest faktycznie źle?

    0
  • Pomocny post
    #7 28 Sty 2012 13:26
    tmf
    Moderator Mikrokontrolery Projektowanie

    Przecież twój kod się nawet nie powinien kompilować, więc jest faktycznie zły :)
    Jak sobie wyobrażasz zagnieżdżenie ISR? Mieszasz nowe makra (ISR) ze starymi (SIGNAL). Poza tym ATMega nie ma czegoś takiego jak priorytety przerwań. Przerwanie TIMER1_COMPA_vect nie zostanie przerwane, chyba, że jawnie odblokujesz wszystkie przerwania co zapewne doprowadzi do kolejnej katastrofy. Poza tym co ma port do prądu pobieranego przez LED? Chcesz go zmniejszyć to zwiększ rezystory ograniczające prąd - będzie świecić słabiej, ale też będzie pobierał mniej prądu.

    0
  • #8 28 Sty 2012 14:53
    plazgut
    Poziom 14  

    Jeśli chodzi o makra, no cóż... Nikt nigdy się nie pokusił, żeby o tym wspomnieć na studiach, a samemu jeszcze do tego nie doszedłem, stąd te błędy. Co do priorytetów to się nie zgodzę, bo przecież ważniejsze jest przerwanie o niższym adresie (najpierw RESET, potem INT0, INT1 itd. ) No chyba, że to co jest w książkach jest błędem.
    Jeśli chodzi o prąd to ma niestety dużo. Bo w tym wypadku prąd będzie pobierany przez wszystkie 8 ledów, a jeśli by zrobić przełączanie segmentów to świeciłby zawsze tylko jeden led. O ile Atmega mi to wytrzyma o tyle stabilizator już będzie miał problemy. Jeśli zwiększe rezystory i zwiększe częstotliwość to może się okazać, że nie będą prawie świecic... No ale jeśli nie znajde rozwiązania programowego to będę zmuszony jednak to zrobić.

    0
  • #9 28 Sty 2012 15:00
    skalsky5000
    Poziom 20  

    Za bardzo kombinujesz.Te 80mA to przy ciągłym świeceniu wszystkich segmentów.Ty multipleksując sterujesz je impulsowo i nie ma sytuacji kiedy wszystkie wyświetlacze świecą naraz.

    0
  • #10 29 Sty 2012 18:07
    plazgut
    Poziom 14  

    I znów muszę prosić o pomoc. Poniżej mój program w całości, który... nie działa. Postaram się zaraz wstawić schemat. Co jest nie tak z programem?

    Code:

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

    void init(void)
       {
          /* Konfigurowanie Buzzera */
          DDRB|=(1<<PB4);
          PORTB&=~(1<<PB4);

          /* Konfigurowanie Przekaznika */
          DDRB|=(1<<PB5);
          PORTB&=~(1<<PB5);

          /* Konfigurowanie klawiatury */
          DDRD=0b00001111;
          PORTD=0b01111111;

          /* Konfigurowanie Wyświetlaczy */
          DDRA=0xFF;
          PORTA=0;
          DDRB|=(1<<PB2)|(1<<PB3);
          PORTB&=~(1<<PB2)|~(1<<PB3);
          DDRC|=(1<<PC6)|(1<<PC7);
          PORTC&=~(1<<PC6)|~(1<<PC7);
       };

    void init_T1(void)
       {
          TCCR1B|=(1<<WGM12)|(1<<CS12); // tryb CTC i preskaler 256   
             TIMSK|=(1<<OCIE1B); //Odblokowanie przerwania Output Compare B licznika 1
          OCR1B=64911; // wartosc przeladowania dla f=100Hz
       };

    void Buzzer_Blad()
       {      
          PORTB|=(1<<PB4);
          for(int i = 0; i < 100; i++)
             {
                     _delay_ms(10);
             };
          PORTB&=~(1<<PB4);
       };

    void Buzzer_Poprawny()
       {   
          PORTB|=(1<<PB4);
          for(int i = 0; i < 300; i++)
             {
                     _delay_ms(10);
             };
          PORTB&=~(1<<PB4);
          _delay_ms(15);
          PORTB|=(1<<PB4);




          _delay_ms(5);
          PORTB&=~(1<<PB4);
          _delay_ms(5);
          PORTB|=(1<<PB4);
          _delay_ms(5);
          PORTB&=~(1<<PB4);
          _delay_ms(5);
          PORTB|=(1<<PB4);
          _delay_ms(5);
          PORTB&=~(1<<PB4);
          _delay_ms(5);
       };

    void Buzzer_Reset()
       {
          PORTB|=(1<<PB4);
          _delay_ms(25);
          PORTB&=~(1<<PB4);
          _delay_ms(5);
          PORTB|=(1<<PB4);
          _delay_ms(5);
          PORTB&=~(1<<PB4);
          _delay_ms(5);
          PORTB|=(1<<PB4);
          _delay_ms(5);
          PORTB&=~(1<<PB4);
          _delay_ms(5);
       };

    void Buzzer_Klik()
       {
          PORTB|=(1<<PB4);
          _delay_ms(15);
          PORTB&=~(1<<PB4);
       };

    volatile int wysw;

    ISR(TIMER1_COMPA_vect)
    {
       volatile char cyfra[12]={0xD0, 0xDF, 0xDB, 0x93, 0x5B, 0x5F, 0x90, 0xCE, 0xCA, 0x02, 0xDD, 0x02,}; // tablica w ktorej zapisane sa cyfry kolejno 789456123+0-
       volatile int jednosci,dziesiatki,setki,tysiace;

       switch(wysw)
       {
          case 0:
             PORTB&=~(1<<PB2);
           PORTC&=~(1<<PC7);
           PORTC&=~(1<<PC6);

             PORTB|=(1<<PB3);
             PORTA=cyfra[jednosci];
             wysw++;
             break;

          case 1:
             PORTB&=~(1<<PB3);
           PORTC&=~(1<<PC7);
           PORTC&=~(1<<PC6);

             PORTB|=(1<<PB2);
             PORTA=cyfra[dziesiatki];
             wysw++;
             break;
             
          case 2:
             PORTB&=~(1<<PB3);
           PORTB&=~(1<<PB2);
           PORTC&=~(1<<PC6);

             PORTC|=(1<<PC7);
             PORTA=cyfra[setki];
             wysw++;
             break;

         case 3:
             PORTB&=~(1<<PB3);
           PORTB&=~(1<<PB2);
           PORTC&=~(1<<PC7);

             PORTC|=(1<<PC6);
             PORTA=cyfra[tysiace];
             wysw=0;
             break;
       }
    }

    void Przekaznik_ON()
       {      
          PORTB|=(1<<PB4);
          for(int i = 0; i<300; i++)
             {
                     _delay_ms(10);
             };
          PORTB&=~(1<<PB4);
       };

    void Przekaznik_OFF()
       {
          PORTB&=~(1<<PB4);
       };

    unsigned char read_keypad(void)
    {
       unsigned char row,col,key;
       
       for(row=0b01111110,key=1; row>=0x01110111; row=(row<<1|0x1)&0x01111111)
       {
            PORTD = row;
             for(col=0x10; col< 0x80; col<<=1, key++)
            if(!(PIND & col)) return key;
       }
       return 0;
    }


    int main(void)
       {
          unsigned char i,key,err=0;

          unsigned char pass[] = {1,2,3,4};

          volatile int kod,jednosci,dziesiatki,setki,tysiace;

          init();
          init_T1();

          for(;;)
          {
             Przekaznik_OFF();

                err = 0; // Zeruje licznik bledow

                for(i=0; i<4; i++) // odczyt i porownanie z kodem 4 cyfr klucza
                  {
                     while(!(key = read_keypad())); // Oczekiwanie na wcisniecie klawisza

                     if(key == 10) // Jesli wcisnieto + to kasowanie
                      {
                         Buzzer_Reset();
                         break;
                      };

                     Buzzer_Klik(); // Sygnalizacja postepu wprowadzania kolejnych cyfr

                     while(read_keypad()); // Oczekiwanie na zwolnienie przycisku
                     _delay_ms(80); // Zatrzymanie az wygasna drgania stykow

                   kod = (10*kod)+read_keypad(); // obliczanie wprowadzanego kodu

                   jednosci = kod % 10;
                      kod = kod / 10;

                   dziesiatki = kod % 10;
                      kod = kod / 10;

                   setki = kod % 10;
                      kod = kod / 10;

                   tysiace = kod % 10;

                     if(key != pass[i]) err++; // Kazda cyfra kodu jest porownywana z pass. Jesli nie pasuje to licznik bledow err jest zwiekszany

                     if(i == 3) // Sprawdzenie czy wprowadzona cyfra jest ostatnia
                        {
                            if(!err) // Jesli wprowadzono wlasciwy klucz to otworz
                               {
                                   Przekaznik_ON();
                                Buzzer_Poprawny();
                               }
                            else   Buzzer_Blad(); // Sygnalizacja bledu    
                        }
                   }
          }
       }


    Obsługa wyświetlacza 7-segmentowego Atmega16
    I jeszcze jedno, czy w #include mogę dołączyć osobne pliki *.c ? Rozumiem, że wtedy trzeba je wstawić w " ".
    Proszę też o zmianę nazwy tematu na choćby "Projekt zamka kodowego".

    0
  • #11 29 Sty 2012 20:46
    tmf
    Moderator Mikrokontrolery Projektowanie

    plazgut napisał:
    Jeśli chodzi o makra, no cóż... Nikt nigdy się nie pokusił, żeby o tym wspomnieć na studiach, a samemu jeszcze do tego nie doszedłem, stąd te błędy. Co do priorytetów to się nie zgodzę, bo przecież ważniejsze jest przerwanie o niższym adresie (najpierw RESET, potem INT0, INT1 itd. ) No chyba, że to co jest w książkach jest błędem.
    Jeśli chodzi o prąd to ma niestety dużo. Bo w tym wypadku prąd będzie pobierany przez wszystkie 8 ledów, a jeśli by zrobić przełączanie segmentów to świeciłby zawsze tylko jeden led. O ile Atmega mi to wytrzyma o tyle stabilizator już będzie miał problemy. Jeśli zwiększe rezystory i zwiększe częstotliwość to może się okazać, że nie będą prawie świecic... No ale jeśli nie znajde rozwiązania programowego to będę zmuszony jednak to zrobić.


    Nie wiem jakie książki czytałeś, ale albo są tam bzdury, alby (co bardziej prawdopodobne) coś źle zrozumiałeś. Priorytet o którym piszesz ma znaczenie w sytuacji w której jednocześnie jest zgłoszonych kilka przerwań. W pozostałych sytuacjach, po zgłoszeniu jednego wszystkie inne są blokowane i procesor ich nie obsłuży dopóki pierwsze się nie zakończy. To można zmienić odblokowując jawnie przerwania w ISR, ale powiedzmy, że to naprawdę trzeba wiedzieć co się robi.
    Co do sterowania multipleksowego - jeśli segment sterujesz z dutycycle 1/n to średnia jego jasność (w dużym uproszczeniu) wynosi 1/n. Więc na tym nic nie zyskujesz, a wręcz tracisz. Aby to skompensować, przez diodę puszcza się spory prąd (kilkakrotnie większy od nominalnego). Więc jedyną możliwością zmieszczenia się w 100mA jest zwiększenie rezystorów i średniego prądu diody ze wszystkimi konsekwencjami.

    Dodano po 1 [minuty]:

    Co do include - oczywiście możesz includować pliki źródłowe ale to naprawdę kiepski pomysł. Różnica pomiędzy <> a "" polega na tym, gdzie kompilator szuka pliku (a raczej jaki jest priorytet wyszukiwania).

    Dodano po 3 [minuty]:

    Co do programu, napisz co jest nie tak, łatwiej będzie doradzić. Z tego co widzę to nie ma sensu deklarować jako volatile zmiennych lokalnych funkcji obsługi przerwania (zresztą kompilator pewnie generuje ci warninga?), kolejna sprawa - schemat. Nie możesz dać rezystora na wspólną anodę LED - każdy segment musi mieć własny rezystor ograniczający. Błędnie też sterujesz przekaźnikiem - nie po tej stronie jest tranzystor. Nie powinieneś też ograniczać prądu przekaźnika rezystorem - nie ma to sensu.

    0
  • #12 30 Sty 2012 02:32
    plazgut
    Poziom 14  

    1. Prawdę mówiąc tak tłumaczył prowadzący zajęcia u mnie na studiach i tak też czytałem w książkach. A dokładnie tak: wykonywany jest program główny, występuje przerwanie, uC przechodzi do obsługi przerwania, jeśli wystąpiło kolejne przerwanie o wyższym priorytecie to przechodzi do jego obsługi, po jego zakończeniu wraca do poprzedniego przerwania i je kończy a następnie wraca do programu głównego. Jeśli tak nie jest to popraw mnie a będę wdzięczny ;)

    3. Właściwie to nie mam pojęcia, bo po załadowaniu programu do Atmegi nic się nie dzieje. Nawet pobierany prąd jest ciągle na poziomie 10-20mA. W trybie debugowania wyskoczyła jakaś informacja o sprawdzeniu napięcia, ale teraz nie pamiętam dokładnie. Napiszę jak podłącze znowu.

    Ogólnie kompilator nie wywala żadnych erorów i warningów, więc teoretycznie jest ok. Co do rezystorów na LED - z założenia miał świecić tylko jeden segment wyświetlacza w danej chwili (o czym pisałem wyżej) stąd tylko jeden rezystor na wyświetlacz. Tranzystora teraz nie zmienię, ale dzięki za uwagę i było by fajnie gdybyś mi wytłumaczył dlaczego jest to błędem.
    Prąd przekaźnika jest ograniczony bo BC849 ma Ic=100mA. Sugerujesz, żeby mimo to go wywalić?

    0
  • #13 30 Sty 2012 12:21
    tmf
    Moderator Mikrokontrolery Projektowanie

    ad 1. To co piszesz jest prawdą dla niektórych procesorów, posiadających przerwania wielopoziomowe z priorytetami. AVR8 z wyjątkiem rodziny XMEGA tak nie mają. Jeśli wystąpi przerwanie to wszystkie inne są blokowane. Tylko XMEGA ma 4 poziomy piorytetów (właściwie 3 - jeden to po prostu blokada przerwań) i procedura obsługi może być przerywana przez przerwanie o wyższym priorytecie - no ale tu nie ma to nic wspólnego z samym wektorem przerwania.
    Co do przekaźnika. On sobie pobierze prąd wynikający z rezystancji cewki. Możesz go ograniczyć rezystorem, ale wtedy po pierwsze na rezystorze będzie się wydzielało sporo ciepła (I^2*R), po drugie prąd cewki może być zbyt mały do włączenia przekaźnika. Sprawdź w danych katalogowych przekaźnika jaki jest nominalną prąd cewki i jeśli za duży to go po prostu zmień, inaczej się nie da.
    Co do tranzystora - aby go włączyć potencjał B musi być wyższy niż E. Problem w tym, że przekaźnik ma obór i wystąpi na nim spadek napięcia. Jeśli jest on wyższy niż jakieś 4V to potencjał bazy będzie za niski aby włączyć tranzystor. W efekcie on nawet jeśli się włączy to prąd kolektora będzie mizerny. Stąd też emiter powinien być na masie, a kolektor do cewki przekaźnika.

    0