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

Obsługa wyświetlacza 7-segmentowego Atmega16

plazgut 27 Sty 2012 17:16 7373 12
  • #1 10456065
    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
  • Pomocny post
    #2 10456268
    skalsky5000
    Poziom 21  
    Najprościej odpalić timer i w przerwaniu wystawiać dane i przełączać segmenty przykład:
    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.
  • #4 10456845
    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: text
    Zaloguj się, aby zobaczyć kod


    Spełni to swoją funkcję?
  • #5 10457996
    skalsky5000
    Poziom 21  
    Ź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:

    	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;
    
    	}
    }
    
  • #6 10458240
    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?
  • Pomocny post
    #7 10459481
    tmf
    VIP Zasłużony dla elektroda
    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.
  • #8 10459944
    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ć.
  • #9 10459977
    skalsky5000
    Poziom 21  
    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.
  • #10 10465754
    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?
    
    #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".
  • #11 10466724
    tmf
    VIP Zasłużony dla elektroda
    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.
  • #12 10468023
    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ć?
  • #13 10468960
    tmf
    VIP Zasłużony dla elektroda
    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.
REKLAMA