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

[Atmega8][C]Problem z multiplekowniem wyświetlacza 7-seg.

WesolyWojtek 04 Lut 2011 19:52 2496 9
  • #1 9103630
    WesolyWojtek
    Poziom 10  
    Witam! Siedzę już drugi dzień nad multipleksem w budziku i tracę już cierpliwość.
    Podejrzewam, że problem leży w zmiennych. Jednak trudno mi go zlokalizować, bo za długo siedzę nad tym samym. Przydałoby się świeże spojrzenie na ten kod.

    Pokrótce opiszę problem. Po wstawieniu wartości odpowiadającej za wyświetlenie "--:--" do tablicy "toDisplay" nie mogę w dalszej części programu wyświetlić nic nowego. Chcę, żeby po upływie sekundy program zaktualizował godzinę. Jedyne co otrzymuje to wyłączenie co sekundę wyświetlacza na ułamek sekundy i ponowne wyświetlenie "--:--" i tak w kółko.

    Poniżej wstawiam kod. Rzucie na niego okiem.

    #include <avr/io.h>
    #include <avr/interrupt.h>
    #include <util/delay.h>
    
    #define F_CPU 2000000UL
    #define F_OSC 2000000UL
    
    /*
    Anody wyświetlacza - PORTB
    Wyswietlacz 1 - PB0
    Wyswietlacz 2 - PB1
    Wyswietlacz 3 - PB2
    Wyswietlacz 4 - PB3
    
    Katody wyswietlacza - PORTD
    Segment A - PD0
    Segment B - PD1
    Segment C - PD2
    Segment D - PD3
    Segment E - PD4
    Segment F - PD5
    Segment G - PD6
    
    Dioda alarmu - PB4
    Buzzer - PB5
    Sekundnik - PD7
    
    Przyciski - PORTC
    Przycisk A - PC0
    Przycisk B - PC1
    Przycisk C - PC2
    Przycisk D - PC3
    Przycisk E - PC4
    */
    
    #define LED_Alarm PB4
    #define LED_Sec PD7
    
    // Zmienne
    
    struct {
    	unsigned char Hours;
    	unsigned char Minutes;
    	unsigned char Seconds;
    	unsigned char LEDisOn;
    	char delta;
    } Clock;
    
    struct {
    	unsigned char isSet;
    	unsigned char isActive;
    	unsigned char Hour;
    	unsigned char Minute;
    } Alarm;
    
    volatile char toDisplay[4];
    char digit[10] = { 0x40, 0x79, 0x24, 0x30, 0x19, 0x12, 0x02, 0x78, 0x00, 0x10 };
    
    // Definicje funkcji
    
    void SecLed_Toggle(void);	// Wylacza/wylacza diody sekundnika
    void Alarm_LedOn(void);		// Wlacz diode alarmu
    void Alarm_LedOff(void);	// Wylacz diode alarmu
    void Buzzer_On(void);		// Wlacz buzzer
    void Buzzer_Off(void);		// Wylacz buzzer
    void Clock_IncHour(void);	// Zwieksz godzine zegara
    void Clock_IncMinute(void);	// Zwieksz minuty zegara
    void Alarm_IncHour(void);	// Zwieksz godzine alarmu
    void Alarm_IncMinute(void);	// Zwieksz minuty alarmu
    void Display_Update(void);	// Uakutalnia wyswietlacz
    
    /*
    			a
    		 -------
    		|		|
    	   f|		|b
    		|	g	|
    		 -------
    		|		|
    	   e|		|c
    		|		|
    		 -------
    			d
    
        gfedcba
    0 - 1000000 - 0x40
    1 - 1111001 - 0x79
    2 - 0100100 - 0x24
    3 - 0110000 - 0x30
    4 - 0011001 - 0x19
    5 - 0010010 - 0x12
    6 - 0000010 - 0x02
    7 - 1111000 - 0x78
    8 - 0000000 - 0x00
    9 - 0010000 - 0x10
    
    */
    
    unsigned char display = 0;
    ISR(TIMER1_COMPA_vect) {
    //SIGNAL(SIG_OVERFLOW1) {		// Multipleks wyswietlacza	
    	PORTD = toDisplay[display];
    	if(Alarm.isSet) { // Dodatkowo wlacza diode alarmu
    		PORTB = ~((1<<display)|(1<<LED_Alarm));
    	} else {
    		PORTB = ~(1<<display);
    	}
    	display++;
    	if(display > 3)
    		display = 0;
    	/*	
    	switch(display) {
    		case 0:
    			PORTD = toDisplay[0];
    			PORTB = ~(1<<0);
    			display++;
    			break;
    		case 1:
    			PORTD = toDisplay[1];
    			PORTB = ~(1<<1);
    			display++;
    			break;
    		case 2:
    			PORTD = toDisplay[2];
    			PORTB = ~(1<<2);
    			display++;
    			break;
    		case 3:
    			PORTD = toDisplay[3];
    			PORTB = ~(1<<3);
    			display++;
    			break;
    		case 4:
    			display = 0;
    			break;
    	}*/
    }
    
    //SIGNAL(SIG_OVERFLOW2) {					// RTC (1 przerwanie/1 sek)
    ISR(TIMER2_OVF_vect) {
    	Clock.Seconds++;					// Dodaj sekunde
    	if (Clock.Seconds == 60) {			// Jesli minelo 60 sekund
    		Clock.Minutes++;				// Dodaj minute
    		if (Clock.Minutes == 60) {			// Jesli minelo 60 minut
    			Clock.Hours++;						// Dodaj godzine
    			if (Clock.Hours == 24) {			// Jesli minely 24 godziny
    				Clock.Hours = 0;					// Zeruj godziny
    			}
    			Clock.Minutes = 0;					// Zeruj minuty
    		}
    		Clock.Seconds = 0;					// Zeruj sekundy
    	}
    	toDisplay[0] = 0x40;
    }
    
    void SecLed_Toggle(void) {
    	Clock.LEDisOn = !Clock.LEDisOn;
    }
    
    void Display_Update(void) {
    	if(Clock.LEDisOn) { // Wlacza sekundnik
    		toDisplay[0] = (~(1<<LED_Sec) & digit[Clock.Minutes / 10]);
    		toDisplay[1] = (~(1<<LED_Sec) & digit[Clock.Minutes % 10]);
    		toDisplay[2] = (~(1<<LED_Sec) & digit[Clock.Seconds / 10]);
    		toDisplay[3] = (~(1<<LED_Sec) & digit[Clock.Seconds % 10]);
    	} else {
    		toDisplay[0] = digit[Clock.Minutes / 10];
    		toDisplay[1] = digit[Clock.Minutes % 10];
    		toDisplay[2] = digit[Clock.Seconds / 10];
    		toDisplay[3] = digit[Clock.Seconds % 10];
    	}
    }
    
    int main(void) {
    	// Konfiguracja pinow
    	DDRB = 0xFF;	// Wyjscie (Digit)
    	PORTB = 0xFF;	// Ustaw stan wysoki (wylacza wyswietlacz, diode alarmu i buzzer)
    	DDRD = 0xFF;	// Wyjscie
    	PORTD = 0xFF;	// Ustaw stan wysoki (wylacza segmenty wyswietlaczy i diody sekundika)
    	DDRC = 0x00;	// Wejscie
    	PORTC = 0x1F;	// Pin PC5 bez rezystora pull-up
    	
    	TCCR1B |= (1<<CS10);   		// Ustaw timer1 z preskalerem 1
    	TCCR1B |= (1<<WGM12);   		// Ustaw timer1 w tryb CTC
    	OCR1A = 4999;   				// Do ilu liczy timer1;dla uzyskania 200Hz 
    	TIMSK |=(1<<OCIE1A);			// Wlacza przerywania Timer0
    	
    	// Timer2 - RTC
    	TIMSK &= ~((0<<TOIE2)|(0<<OCIE2));	// Wylacza przerywania Timer2
    	ASSR |= (1<<AS2);						// Tryb asynchroniczny, z oscylatorem 32,768kHz
    	TCNT2 = 0x00;							// Licznik Timer2 = 0
        TCCR2 |= ((1<<CS22)|(1<<CS20));		// Preskaler 128
    	while (ASSR & 0x07) { };					// Czekaj az Timer2 jest uaktualniony
    	TIMSK |= (1<<TOIE2);					// Wlacz przerywania Timer0
    	
    	Clock.Hours = 0;
    	Clock.Minutes = 0;
    	Clock.Seconds = 0;
    	Clock.LEDisOn = 0x01;
    	
    	Alarm.Hour = 0;
    	Alarm.Minute = 0;
    	Alarm.isSet = 0;
    	Alarm.isActive = 0;
    	
    	toDisplay[0] = 0xBF; // Wyswietla --:--
    	toDisplay[1] = 0xBF;
    	toDisplay[2] = 0xBF;
    	toDisplay[3] = 0xBF;
    	
    	sei();								// Wlacz przerywania
    	
    	unsigned char tempSeconds = 0;
    	
    	while(1) {	// Petla glowna
    		if(tempSeconds != Clock.Seconds) {
    			tempSeconds = Clock.Seconds;
    			SecLed_Toggle();
    			Display_Update();
    		}
    	}
    }


    Timer1 odpowiada za multipleks wyświetlacza. Dane do wyświetlenia pobiera z tablicy "toDisplay". Dodatkowo sprawdza czy ma zostać włączona dioda alarmu. Timer2 działa asynchronicznie z kwarcem 32,768kHz. Wykonuje standardowe instrukcje liczenia czasu. Nic zmyślnego. Funkcja "Display_Update()" 'konwertuje' czas z struktury "Clock" do tablicy "toDisplay". Dodatkowo włącza diodę sekundnika. Zamieszanie z diodami sekundnika i alarmu komplikują nieco sprawę, ale akurat ta część programu działa dobrze.
  • #2 9103816
    Andrzej__S
    Poziom 28  
    Struktura Clock powinna być volatile.
  • #3 9103997
    WesolyWojtek
    Poziom 10  
    Dzięki za zainteresowanie. Dodałem do struktury volatile. Spowodowało to tylko, że podczas przepełnienia licznika Timera2 3 pierwsze segmenty gasną, a na ostatnim pojawia się 1. Natomiast po usunięciu linii "toDisplay[0] = 0xBF;" segmenty są wyłączone, nic nie wyświetlają. Co może powodować, że zawartość tablicy "toDisplay" po przepełnieniu Timera2 powraca do wartości 0xBF? Za bardzo nie wiem co jest grane i nie mam jak tego zdebbugować.
  • #4 9104848
    Andrzej__S
    Poziom 28  
    Nie wiem, ale u mnie w symulatorze AVR Studio, po dodaniu volatile do struktury Clock, Twój kod działa prawidłowo. Na PORTB przesuwa się ładnie stan niski w kólko po młodszych czterech bitach, na PORTD pojawiają się kolejno kody poszczególnych cyfr, a sekundy i minuty odliczane są prawidłowo.
    Bez volatile w ogóle nie były wykonywane polecenia bloku warunkowego if(tempSeconds != Clock.Seconds), ponieważ ze względu na brak volatile kompilator uważał ten warunek za nigdy nie spełniony.
    Na razie nie mam pomysłu, co może być przyczyną błędnego działania, ale moim zdaniem to nie jest wina kodu.
  • #5 9105976
    Andrzej__S
    Poziom 28  
    Cytat:

    Spowodowało to tylko, że podczas przepełnienia licznika Timera2 3 pierwsze segmenty gasną, a na ostatnim pojawia się 1. Natomiast po usunięciu linii "toDisplay[0] = 0xBF;" segmenty są wyłączone, nic nie wyświetlają.

    Nie wiem, czy to sprawdzałeś, ale proponowałbym zrobić test. W miejscach, gdzie masz obecnie toDisplay[x] = 0xBF; wstaw przykładowo toDisplay[x] = 0x40; lub toDisplay[x] = 0x00;, gdzie x to kolejne numery cyfr. Napisz jaki efekt.
    Poza tym pokaż może jeszcze schemat (z uwzględnieniem części zasilającej) i ustawienia fusebitów.
  • #6 9106073
    WesolyWojtek
    Poziom 10  
    Na wszelki wypadek zamieszczę schemat. Chociaż nie widzę w nim błędu. Być może Atmega uległa uszkodzeniu.

    [Atmega8][C]Problem z multiplekowniem wyświetlacza 7-seg.
  • #7 9106519
    Andrzej__S
    Poziom 28  
    Sprawdź może jednak to, o czym pisałem w poprzednim poście. Chodzi o to, żeby sprawdzić, czy przy próbie wyświetlenia samych zer lub ósemek zaraz na początku programu (zamiast wyświetlania --:--), wyświetlacz działa prawidłowo. To sporo może wyjaśnić.
  • #8 9107113
    WesolyWojtek
    Poziom 10  
    Na początek fusebity: hFuse = 0xD9; lFuse = E1. (Nic w nich nie zmieniałem.)

    toDisplay[0] = 0x00: Wyświetlacz pozostaje wyłączony, a powinien wyświetlać 88 88.
    toDisplay[0] = 0x40: To samo co ww.
    toDisplay[0] = 0x79: Wyświetla 11 11, wyłącza się na ułamek sekundy, co sekundę. Po wyłączeniu niżej wymienionego fragmentu wyświetla prawidłowo.
    /*if(tempSeconds != Clock.Seconds) {
    tempSeconds = Clock.Seconds;
    SecLed_Toggle();
    Display_Update();
    }*/

    toDisplay[0] = 0x24: Bez ww. fragmentu wyświetla poprawnie 22 22.
    toDisplay[0] = 0x30: Bez ww. fragmentu wyświetla poprawnie 33 33.
    toDisplay[0] = 0x19;0x12;0x02: Tak jak 0x00, nic nie wyświetla.
    toDisplay[0] = 0x78: Działa poprawnie
    toDisplay[0] = 0x78: Też nie działa.

    Po wstawieniu poniższych wartości (czyli 12 37) wyświetlacz działa poprawnie.
    toDisplay[0] = 0x79;
    toDisplay[1] = 0x24;
    toDisplay[2] = 0x30;
    toDisplay[3] = 0x78;


    Natomiast gdy chciałem wyświetlić 12 34, wyświetlacz zaczął migać i wyświetlał tylko pierwsze 3 liczby (12 3).
    toDisplay[0] = 0x79;
    toDisplay[1] = 0x24;
    toDisplay[2] = 0x30;
    toDisplay[3] = 0x19;
  • Pomocny post
    #9 9107428
    Andrzej__S
    Poziom 28  
    Cytat:

    Po wstawieniu poniższych wartości (czyli 12 37) wyświetlacz działa poprawnie.
    ...
    Natomiast gdy chciałem wyświetlić 12 34, wyświetlacz zaczął migać i wyświetlał tylko pierwsze 3 liczby (12 3).

    Wszystko wskazuje na to, że problem pojawia się w momencie, kiedy musi być wyświetlany segment f wyświetlacza.
    Proponuję sprawdzić dokładnie połączenia segmentu f, bo najprawdopodobniej masz tam jakieś zwarcie, które powoduje zanik napięcia zasilającego i w efekcie reset mikrokotrolera.

    EDIT:
    Jeszcze jedna uwaga:
    Cytat:

    
    #define F_CPU 2000000UL
    


    Na początek fusebity: hFuse = 0xD9; lFuse = E1. (Nic w nich nie zmieniałem.)

    Przy takim ustawieniu fusebitów (fabrycznym), to F_CPU=1000000UL. No ale to nie jest błąd krytyczny, co najwyżej będziesz miał o połowę niższą częstotliwość multipleksowania, niż zakładałeś. Może jednak być problem w momencie, jak zaczniesz używać funkcji _delay_us() i _delay_ms(), bo zauważyłem, że dołączyłeś <util/delay.h>. Przy okazji #define F_CPU=1000000UL powinno być zdeklarowane przed #include <util/delay.h> lub ewentualnie zamiast używania #define możesz zdefiniować F_CPU w pliku makefile lub w opcjach projektu, jeśli korzystasz z AVR Studio.
  • #10 9107913
    WesolyWojtek
    Poziom 10  
    Jesteś genialny! Widać, że wiesz o co chodzi w te klocki :) Miałeś rację, ścieżka segmentu F miała zwarcie. Dokładniej, ze ścieżką resetu µC. :!: Przez to były spowodowane resety o których mówiłeś. Mikrokontroler ustawiając zero logiczne na PD5 resetował sam siebie przez to zwarcie i przez to to całe zamieszanie.

    Jestem Ci ogromnie wdzięczny.Gdybyśmy się kiedyś spotkali to masz u mnie duże piwo. :D
REKLAMA