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.

[AVR][C] Multipleksacja 8 segmentowego LED - problem

mozgh 06 Sty 2009 16:48 3222 7
  • #1 06 Sty 2009 16:48
    mozgh
    Poziom 10  

    Witam.

    Napisałem kawałek kodu do multipleksacji wyświetlaczy LED.

    Zrobiłem układ z wyświetlaczem LED TOF - 5462. Wspólna anoda 4 cyfry + 2 kropki pośrodku.

    Jestem początkującym programistą C dla AVR i sam nie mogę rozgryźć problemu.

    Wyświetlacz jest podłączony do atmegi następująco:

    PORTB to wszystkie 8 segmentów wyświetlacza (A do G i DP).
    PORTD 0b11110000 to tranzystory sterujące anodami wyświetlaczy.

    Problem jest następujący.

    Przy ustawieniu preskalera na wartości 0,8 - Wyświetla pięknie 4 cyfry ale wykonywanie for'a w main() jest bardzo wolne _delay_ms(10) wykonuje się więcej jak 10-20 sek.

    Przy preskalerze na 64 cyfry się wyświetlają kod w main() wykonuje się szybciej, ale nie tak jak powinien. milisekundowy delay wykonuje się okolo sekundy.

    Przy preskalerze ustawionym na 256 kod w main() wykonuje się prawidłowo, ale za to wyświetlanie nie jest prawidłowo.
    Wyświetla się 4 cyfry, ale tylko pierwsza jest odpowiedniej jasności (T4), reszta świeci około 30-40% swojej mocy.

    Przy preskalerze na 1024 pierwsza cyfra (T4) wyświetla się jeszcze jaśniej a reszta świeci na 10-20%.

    Nie potrafię sam znaleźć błędu w tym kodzie.

    Dla testu napisałem szybko kod w języku BASKÓW :) i tam wszystko chodzi dobrze.
    Dla porównania dołączam kod w BASCOMie.

    Będę wdzięczny za sugestie do kodu w C.

    Kod w C nie działający zbyt dobrze.


    Code:

    #define F_CPU 8000000L

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

    #define LED_MATRIX   PORTB
    #define SEGMENTS   PORTD

    #define T1   _BV(7)
    #define T2   _BV(6)
    #define T3   _BV(5)
    #define T4   _BV(4)

    #define T0_INIT         100


    const unsigned char cyfry[] = { 0x3f, 0x6, 0x5b, 0x4f, 0x66, 0x6d, 0x7d,
                        0x7, 0x7f, 0x6f };

    volatile unsigned int liczba;

    //SIGNAL(SIG_OVERFLOW0) {
    //ISR(TIMER1_OVF_vect) {
    ISR(TIMER0_OVF_vect) {

    unsigned char i = 0;

    for (i=0; i<4; i++) {

    switch(i) {

       case 3:
          SEGMENTS |= T1;
          SEGMENTS |= T2;
          SEGMENTS |= T3;
          SEGMENTS |= T4;

          LED_MATRIX = ~cyfry[liczba/1000];
          SEGMENTS &= ~T4;





          _delay_us (1);   
       break;

       case 2:
          SEGMENTS |= T1;
          SEGMENTS |= T2;
          SEGMENTS |= T3;
          SEGMENTS |= T4;

          LED_MATRIX = ~cyfry[(liczba%1000)/100];
          SEGMENTS &= ~T3;

          _delay_us (1);
       
       break;

       case 1:
          SEGMENTS |= T1;
          SEGMENTS |= T2;
          SEGMENTS |= T3;
          SEGMENTS |= T4;

          LED_MATRIX = ~cyfry[(liczba%100)/10];
          SEGMENTS &= ~T2;

          _delay_us (1);
       
       break;

       case 0:
          SEGMENTS |= T1;
          SEGMENTS |= T2;
          SEGMENTS |= T3;
          SEGMENTS |= T4;

          LED_MATRIX = ~cyfry[liczba%10];
          SEGMENTS &= ~T1;

          _delay_us (1);
       break;

    /*   
       default:
          SEGMENTS = 0xff;
       break;
    */

    }

    //SEGMENTS = 0xff;

    }

    return;
    }

    void inicjalizacja(void) {

    // 8 segmentów LED podłączone do PORTB
    DDRB = 0xFF;
    PORTB = 0x00;
    // 4 tranzysotry podłączone do 0b11110000 a coś innego do 0b00000001
    DDRD = 0xF1;
    PORTD = 0x0E;

    }

    int main (void)
    {

    inicjalizacja();


    /* timer 0 */
      TIMSK = _BV(TOIE0);        // włącz obsługę przerwań T/C0
    //  TCNT0 = T0_INIT;         // wartość początkowa T/C0
    //TCCR0 = _BV(CS01)|_BV(CS02); // wyzwalanie z T0 opad. zboczem
    //TCCR0 = _BV(CS00)|_BV(CS02); // preskaler 1024
    TCCR0 = _BV(CS02); // preskaler 256

    //  TCCR0 = _BV(CS00)|_BV(CS01); // preskaler 64

    //TCCR0 = _BV(CS01); // preskaler 8
    //TCCR0 = _BV(CS00); // NO preskaler

    /* timer 1 */
    //TIMSK = _BV(TOIE1);
    //TCCR1B = _BV(CS10); // No prescaler
    //TCCR1B = _BV(CS11); // prescaler 8
    //TCCR1B = _BV(CS10)|_BV(CS10); //prescaler 64
    //TCCR1B = _BV(CS12); //prescaler 256
    //TCCR1B = _BV(CS10)|_BV(CS12); //prescaler 1024


      sei();                // włącz obsługę przerwań


    while(1) {

       for (liczba = 4444; liczba < 9999; liczba++) {
          _delay_ms (10);
       }

    }

    return(0);
    }




    Kod w BASCOM działający dobrze.

    Code:

    $regfile = "m8def.dat"
    $crystal = 8000000
    $baud = 9600

    '-- porty
    Config Portd.2 = Input
    Config Portd.3 = Input

    Set Portd.2
    Set Portd.3

    Config Portb = Output
    Config Portd.4 = Output
    Config Portd.5 = Output
    Config Portd.6 = Output
    Config Portd.7 = Output


    T4 Alias Portd.4
    T3 Alias Portd.5
    T2 Alias Portd.6
    T1 Alias Portd.7

    S1 Alias Pind.2
    S2 Alias Pind.3

    Wyswietlacz Alias Portb

    Declare Sub Rozloz

    Config 1wire = Portc.5

    '--- zmienne

    Dim A As Byte
    Dim Cyfra1 As Integer
    Dim Cyfra2 As Integer
    Dim Cyfra3 As Integer
    Dim Cyfra4 As Integer

    Dim Temp As Integer
    Dim Tempo As Integer

    Dim I As Integer

    '-- przerwania
    Config Timer0 = Timer , Prescale = 8
    'Config Timer1 = Timer , Prescale = 1                        'konfiguracja licznik�w
    Enable Interrupts
    Enable Timer0
    On Timer0 Tajmer1


    Print "start"

    Do

    For I = 1979 To 9999
       Temp = I


       Print "I = " ; I ; "temp = " ; Temp ; " C4 = " ; Cyfra4 ; " C3 = " ; Cyfra3 ; " C2 = " ; Cyfra2 ; " C1 = " ; Cyfra1

       Call Rozloz

       Waitms 10

    Next I

    Loop

    '-- rozkladanie liczby na cyfry
    Sub Rozloz

    Cyfra4 = Temp / 1000

    Temp = Temp Mod 1000

    Cyfra3 = Temp / 100

    Temp = Temp Mod 100

    Cyfra2 = Temp / 10

    Cyfra1 = Temp Mod 10

    'If Znaczek = 1 Then Cyfra4 = 14
    ''Else
    ''   Cyfra3 = Temp / 100
    ''End If

    End Sub

    '- obsluga przerwania
    Tajmer1:
    Incr A
    If A = 4 Then A = 0

    Select Case A
    Case 0:
    Wyswietlacz = 255
    Set T1
    Set T2
    Set T3
    Set T4
    '- nowy
    Wyswietlacz = Lookup(cyfra1 , Dta)

    Reset T1

    Case 1:
    Wyswietlacz = 255
    Set T1
    Set T2
    Set T3
    Set T4
    '- nowy
    'If Cyfra2 <> 0 Then Wyswietlacz = Lookup(cyfra2 , Dta)
    Wyswietlacz = Lookup(cyfra2 , Dta)

    ' - end
    Reset T2

    Case 2:
    Wyswietlacz = 255
    Set T1
    Set T2
    Set T3
    Set T4
    '- nowy
    'If Cyfra3 <> 0 Then Wyswietlacz = Lookup(cyfra3 , Dta)
    Wyswietlacz = Lookup(cyfra3 , Dta)

    ' - end
    Reset T3

    Case 3:
    Wyswietlacz = 255
    Set T1
    Set T2
    Set T3
    Set T4
    '- nowy
    'If Cyfra2 <> 0 Then Wyswietlacz = Lookup(cyfra4 , Dta)
    Wyswietlacz = Lookup(cyfra4 , Dta)

    ' - end
    Reset T4

    End Select

    Return
    '---- koniec przerwania

    Dta:
    ' .GFEDCBA
    ' 11111111
    Data &B11000000 , &B11111001 , &B10100100 , &B10110000 , &B10011001
    Data &B10010010 , &B10000010 , &B11111000 , &B10000000 , &B10010000
    Data &B10000110 , &B11001110 , &B11110001 , &B11000111
    Data &B10111111 , &B11110111
    'Data &B10000110

    0 7
  • Pomocny post
    #2 06 Sty 2009 17:05
    dawid512
    Poziom 32  

    Proponuję zrezygnować z opóźnienia w przerwaniu i pętli for również. Proponuję zrobić coś tego typu:

    Code:


    unsigned char i=0;

    ISR(TIMER0_OVF_vect) {

    PORTD=0xFF;  //wygaszenie wyswietlaczy

    if(i >= 4)
    {i=0;}

    switch(i) {

       case 3:
         
          LED_MATRIX = ~cyfry[liczba/1000];
          SEGMENTS &= ~T4;

       break;

       case 2:
         
          LED_MATRIX = ~cyfry[(liczba%1000)/100];
          SEGMENTS &= ~T3;
     
       break;

       case 1:
       

          LED_MATRIX = ~cyfry[(liczba%100)/10];
          SEGMENTS &= ~T2;
     
       break;

       case 0:
         
          LED_MATRIX = ~cyfry[liczba%10];
          SEGMENTS &= ~T1;

       break;

    }
    }


    [EDIT]
    Kod poprawiony.

    0
  • #3 06 Sty 2009 18:29
    mozgh
    Poziom 10  

    Testowałem już podobne rozwiązanie.

    Jak nie wstawiam opóźnienia to pierwsza od lewej cyfra (T4) bardzo wyraźnie mruga ale ją widać. Natomiast reszta cyfr (T3,T2,T1) miga tak szybko, że praktycznie ich nie widać.

    Aczkolwiek sprawdzę ten kawałek kodu i dam znać może zastąpienie for'a inkrementacją zmiennej "i" i optymalizacja kodu sterującego tranzystorami pomoże :).

    0
  • Pomocny post
    #4 06 Sty 2009 18:34
    dawid512
    Poziom 32  

    W podanym kodzie musisz za pewne zmienić wartość wpisywaną do timera. Po za tym w atmedze 8 timer0 jest troche inny niz np. w m16. Każde wywołanie przerwania zeruje timer. Musisz więc za każdym razem gdy wywolujesz przerwanie wpisać do timera odpowiednią wartość.

    0
  • Pomocny post
    #5 06 Sty 2009 19:59
    Dr.Vee
    VIP Zasłużony dla elektroda

    Źle podchodzisz do problemu - zmienna i (czyli numer wyświetlanej cyfry) powinna być zmienną globalną, którą w przerwaniu od timera zwiększasz, a następnie wykonujesz blok switch (i) { ... }. Oczywiście delay w przerwaniu to "bluźnierstwo" :)

    Preskaler ustaw sobie na 64, wtedy przerwanie od timera dostniesz ok. 400x na sekundę, czyli odświeżanie całego wyświetlacza masz 100x na sekundę.

    Jeśli przerwanie od timera Ci się wykonuje zbyt długo (np. dzięki użyciu dzielenia), to możesz wykonywać dzielenia poza przerwaniem i wpisywać cyfry do globalnej tablicy, z której pobierzesz je później do wyświetlenia - tak zresztą zrobiłeś w bascomie.

    Pozdrawiam,
    Dr.Vee

    0
  • Pomocny post
    #6 06 Sty 2009 20:43
    dawid512
    Poziom 32  

    Cytat:
    Źle podchodzisz do problemu - zmienna i (czyli numer wyświetlanej cyfry) powinna być zmienną globalną


    Zmienna "i" służy do wybierania który wyświetlacz ma być aktualnie włączony. Jeśli chodzi o zadeklarowanie tej zmiennej jako globalną to oczywiście masz rację.

    0
  • #7 06 Sty 2009 22:57
    zumek
    Poziom 39  

    dawid512 napisał:
    Jeśli chodzi o zadeklarowanie tej zmiennej jako globalną to oczywiście masz rację.

    Można też użyć zmiennej statycznej.
    Poza tym, masz jeszcze jeden błąd w kodzie, więc też go popraw.

    0
  • #8 07 Sty 2009 09:03
    mozgh
    Poziom 10  

    Witam ponownie.

    Dziękuje wszystkim za wszelkie sugestie związane z kodem.

    Dokonałem poprawek, według waszych podpowiedzi i wyświetlacz działa już prawidłowo.

    Nie pomyślałem wcześniej że zmienna "i" definiowana lokalnie jest złym pomysłem.

    Kawałek kodu zamieszczam poniżej.

    Mam jeszcze lekkie przebicia, niektórych linni na cyfrze T4, ale to pewnie wina elektroniki, a nie softu :).

    Jeszcze raz dziękuje.

    Code:

    volatile unsigned char i;

    ISR(TIMER0_OVF_vect) {

    i++;
    if (i>=4) {
    i=0;
    }

    SEGMENTS |= T1;
    SEGMENTS |= T2;
    SEGMENTS |= T3;
    SEGMENTS |= T4;

    switch(i) {

       case 3:
          LED_MATRIX = ~cyfry[liczba/1000];
          SEGMENTS &= ~T4;
       break;

       case 2:
          LED_MATRIX = ~cyfry[(liczba%1000)/100];
          SEGMENTS &= ~T3;
       break;

       case 1:
          LED_MATRIX = ~cyfry[(liczba%100)/10];
          SEGMENTS &= ~T2;
       break;

       case 0:
          LED_MATRIX = ~cyfry[liczba%10];
          SEGMENTS &= ~T1;
       break;

    }

    return;
    }

    0