Elektroda.pl
Elektroda.pl
X
Please add exception to AdBlock for elektroda.pl.
If you watch the ads, you support portal and users.

[atmega32][c]multipleksowanie wyświetlacza LED

margas4542 20 Dec 2010 14:08 6672 15
  • #1
    margas4542
    Level 16  
    Witam. Potrzebny mi jest zegar wyświetlający równocześnie czas lokalny i czas Greenwich. W tym celu próbuje napisać odpowiedni program ale mam problem z multipleksowaniem dwu wyświetlaczy LED każdy po sześć cyfr. Podstawą czasu jest PCF8583 a że mam akurat atmegę32 więc ją wykorzystam. Od razu mówię że orłem w programowaniu nie jestem i bardzo możliwe że źle się do tego zabrałem. Poszukiwania podobnego programu który mógłby posłużyć za pomoc dydaktyczna to istna mordęga bo w C znaleźć cokolwiek nie jest łatwo a jak jest to kod jest wyższych lotów w 70% dla mnie nie zrozumiały z powodu zastosowania funkcji których nie rozumie. Segmenty podłączone do portu A, pierwsze 6-cyfr podłączyłem do portu B a drugie 6-cyfr do C, klawisze na porcie D. Wkleję cały kod aby było to w pełni dla was czytelne;
    Code:

    #include <stdint.h>
    #include <avr/io.h>
    #include <stdlib.h>
    #include <avr/interrupt.h>
    //********** funcje obsługi TWI dla PCF
    #define zegar 0xA2             // A0 PCF do plusa
    #define ACK 1
    #define NOACK 0
    static void TWI_start(void)
    {
       TWCR = (1<<TWINT)|(1<<TWSTA)|(1<<TWEN);
       while (!(TWCR & (1<<TWINT)));
    }
    static void TWI_stop(void)
    {
       TWCR = (1<<TWINT)|(1<<TWEN)|(1<<TWSTO);
    }
    void TWI_write(unsigned char scalak,unsigned char adres_rejestru,unsigned char liczba)
    {
       TWCR =(1 << TWINT) | (1 << TWSTA) | ( 1 << TWEN );
       while(!(TWCR & (1 << TWINT)));   
       TWDR =scalak;   
       TWCR =(1 << TWINT) | (1 << TWEN);
       while(!(TWCR & (1 << TWINT)));   
       TWDR =adres_rejestru;   
       TWCR =(1 << TWINT) | (1 << TWEN);
       while(!(TWCR & (1 << TWINT)));   
       TWDR =liczba;   
       TWCR =(1 << TWINT) | (1 << TWEN);
       while(!(TWCR & (1 << TWINT)));   
       TWCR =(1 << TWINT) | (1<<TWEN) | (1<<TWSTO);
    }
    unsigned char TWI_read(unsigned char scalak,unsigned char adres_rejestru)
    {
       unsigned char odczyt;        
       TWCR =(1 << TWINT) | (1 << TWSTA) | ( 1 << TWEN );
       while(!(TWCR & (1 << TWINT)));   
       TWDR =scalak;   
       TWCR =(1 << TWINT) | (1 << TWEN);
       while(!(TWCR & (1 << TWINT)));   
       TWDR =adres_rejestru;   
       TWCR =(1 << TWINT) | (1 << TWEN);
       while(!(TWCR & (1 << TWINT)));   
       TWCR =(1 << TWINT) | (1 << TWSTA) | ( 1 << TWEN );
       while(!(TWCR & (1 << TWINT)));   
       TWDR =scalak | 0x01;   
       TWCR =(1 << TWINT) | (1 << TWEN);
       while(!(TWCR & (1 << TWINT)));   
       TWCR =(1 << TWINT) | (1 << TWEN);
       while(!(TWCR & (1 << TWINT)));   
       odczyt =TWDR;   
       TWCR =(1 << TWINT) | (1<<TWEN) | (1<<TWSTO);
       return(odczyt);
    }
    //********** funkcje konwersji   
    char dec_na_bcd(char num)   {return ((num/10 * 16) + (num % 10));}   //set = dec_na_bcd(set);      
    char bcd_na_dec(char num)   {return ((num/16 * 10) + (num % 16));}   //set = bcd_na_dec(set);
    //********** definicje zmiennych dla zegara
    unsigned char PCF,PCF_dzien,dec_dzien,m,d,y;
    unsigned char buffer_t[16];
    unsigned char PCF_dmc,PCF_jmc,PCF_ddni,PCF_jdni;                  //odczyt daty
    unsigned char PCF_dg,PCF_jg,PCF_dm,PCF_jm,PCF_ds,PCF_js,PCF_sds,PCF_sjs;//odczyt czasu
    unsigned char dec_dg,dec_jg,dec_dm,dec_jm,dec_ds,dec_js;   //czas bierzący po konwersji na dec
    //********** definicje zmiennych dla menu
    unsigned char led_display;
    unsigned char set_g1,set_m1,set_g2,set_m2,set_g3,set_m3,set_g4,set_m4;
    //********** define segmentów wyświetlacza
    unsigned char nr_wysw;
    const unsigned char cyfra[10]={
                0x3F,/*cyfra 0        A         */
                0x06,/*cyfra 1       ---      */
                0x5B,/*cyfra 2      |   |      */
                0x4F,/*cyfra 3     F |   | B      */
                0x66,/*cyfra 4      |   |      */
                0x6D,/*cyfra 5     G  ---      */
                0x7D,/*cyfra 6      |   |      */
                0x07,/*cyfra 7     E |   | C      */
                0x7F,/*cyfra 8      |   |      */
                0x6F,/*cyfra 9       ---      */
                0x80 /*kropka        D     #   H   */
                };
    //***** MAIN
    int main()
    {
     DDRA=0xFF;      //wyjścia - segmenty wyświetlaczy wyś_1 oraz wyś_2
     DDRB=0xFF;      //wyjścia - cyfry wyświetlacza czasu lokalnego
     DDRC=0xFC;      //wyjścia - cyfry wyświetlacza daty oraz wejścia - SCL SDA układu PCF8583
     PORTC=0x03;   //pull-up - dla SCL SDA INT1 układu PCF8583
     DDRD=0x00;      //wejścia - klawiatura
     PORTD=0xF8;   //pull-up - klawiatura
    //----- TWI init
     TWSR=0x00;      
     TWBR=0x48; //preskaler I2C - ((F_CPU / 100000UL - 16) - inicjacja przerwań
     sei();   
    //-----pętla główna progranu
     while(1)
     {   //odczyt czasu i daty
          //-----
       PCF=TWI_read(zegar,0x06);   PCF_dmc=PCF >>4;   PCF_jmc=PCF & 0x0F;
       PCF=TWI_read(zegar,0x05);   PCF_ddni=PCF >>4;   PCF_jdni=PCF & 0x0F;
        PCF=TWI_read(zegar,0x04);   PCF_dg=PCF >>4;      PCF_jg=PCF & 0x0F;
        PCF=TWI_read(zegar,0x03);   PCF_dm=PCF >>4;      PCF_jm=PCF & 0x0F;
        PCF=TWI_read(zegar,0x02);   PCF_ds=PCF >>4;      PCF_js=PCF & 0x0F;
       //-----
       dec_dg=bcd_na_dec(PCF_dg);   dec_jg=bcd_na_dec(PCF_jg);//konwersja na dec godziny
       dec_dm=bcd_na_dec(PCF_dm);   dec_jm=bcd_na_dec(PCF_jm);//konwersja na dec minuty

       for( nr_wysw=0; nr_wysw<12; nr_wysw++)
       {
          switch(nr_wysw)
          {
             case 0://wyśw_1_dziesiątki godzin
             PORTB &=~ _BV(1);
             cyfra[dec_dg];
             PORTB |= _BV(1);
             break;
             case 1://wyśw_1_jednostki godzin
             PORTB &=~ _BV(2);
             cyfra[dec_jg];
             PORTB |= _BV(2);
             break;
             case 2://wyśw_1_dziesiątki minut
             PORTB &=~ _BV(3);
             cyfra[dec_dm];
             PORTB |= _BV(3);
             break;
             case 3://wyśw_1_jednostk minut
             PORTB &=~ _BV(4);
             cyfra[dec_jm];
             PORTB |= _BV(4);
             break;
             case 5://wyśw_1_dziesiątki sekund
             PORTB &=~ _BV(5);
             cyfra[dec_ds];
             PORTB |= _BV(5);
             break;
             case 6://wyśw_1_jednostk sekund
             PORTB &=~ _BV(6);
             cyfra[dec_js];
             PORTB |= _BV(6);
             break;
             //----------------czas 2 GMT
             case 7://wyśw_2_dziesiątki godzin
             PORTC &=~ _BV(1);
             cyfra[dec_dg];
             PORTC |= _BV(1);
             break;
             case 8://wyśw_2_jednostki godzin
             PORTC &=~ _BV(2);
             cyfra[dec_jg];
             PORTC |= _BV(2);
             break;
             case 9://wyśw_2_dziesiątki minut
             PORTC &=~ _BV(3);
             cyfra[dec_dm];
             PORTC |= _BV(3);
             break;
             case 10://wyśw_2_jednostk minut
             PORTC &=~ _BV(4);
             cyfra[dec_jm];
             PORTC |= _BV(4);
             break;
             case 11://wyśw_2_dziesiątki sekund
             PORTC &=~ _BV(5);
             cyfra[dec_ds];
             PORTC |= _BV(5);
             break;
             case 12://wyśw_2_jednostk sekund
             PORTC &=~ _BV(6);
             cyfra[dec_js];
             PORTC |= _BV(6);
             break;
             default:
             break;
          }
       }
    }
    return;      
    }
    //********** END MAIN **********
  • #2
    tmf
    Moderator of Microcontroller designs
    Źle do tego podchodzisz. Całe multipleksowanie zrób w przerwaniu timera, który określi dokładnie jak długo wyświetlana będzie każda cyfra. Teraz to robisz w pętli idącej z max szybkością proca, w efekcie piekielnie szybko multipleksujesz LCD, co pomniejsza ich jasność, a dwa, że jak program się na czymś na chwilę zatrzyma (czyli odczycie PCF) to całość szlag trafia. Swoją drogą zamiast PCFa, nie prościej byłoby zrobić programowy RTC, albo programowo/sprzętowy, wykorzystując asynchroniczny timer?
  • #3
    margas4542
    Level 16  
    Chyba niezbyt dokładnie przeczytałeś mój post nie korzystam z LCD tylko z dwu 6-cyfrowych wyświetlaczy LED. Mam parę PCF-ów więc je wykorzystuje są stabilniejsze i dokładniejsze a o miejsce na płytce nie muszę się martwić. Fakt że multiplex będzie szedł z prędkością proca ale to nie działa i w tym kłopot.
  • #4
    mirekk36
    Level 42  
    margas4542 -->

    tmf wrote:
    , w efekcie piekielnie szybko multipleksujesz LCD, co pomniejsza ich jasność, ?


    toż w tym przypadku LCD to tylko literówka, więc nic się nie pomyliło bo z kontekstu całej wypowiedzi jasno przecież wynika że chodzi o LED a nie LCD. A uwagi zawarte w tej wypowiedzi są akurat bardzo słuszne odnośnie tego jak robisz multipleksowanie i co robisz źle.

    Co do RTC to ja akurat też wolę takie rozwiązanie niż robienie tego na procku ale jednak nie zgodzę się z tobą, że:

    margas4542 wrote:
    więc je wykorzystuje są stabilniejsze i dokładniejsze


    Bo nie chodzi o to które rozwiązanie jest dokładniejsze czy stabilniejsze - oba są porównywalne. Bardziej chodzi o rozwiązywanie sposobów podtrzymywania zasilania w czasie gdy układ jest wyłączony. Dla RTC wystarczy tylko dać maleńką bateryjkę przez jakąś diodę a w przypadku procka już trzba troszkę pokombinować na zewnątrz jak i w samym kodzie. No ale każdy zrobi tak jak lubi i co mu wygodniej.


    margas4542 wrote:
    Fakt że multiplex będzie szedł z prędkością proca ale to nie działa i w tym kłopot.


    Dlatego zrób tak jak radził kolega, i tak jak się należy to robić czyli w oparciu o jakiejś przerwanie dowolnego timera ze ściśle dobranym czasem odświeżania.
  • #5
    margas4542
    Level 16  
    Mam niewielkie a raczej bardzo małe doświadczenie z licznikami i przerwaniami i nie za bardzo wiem jak się do tego zabrać,który i jak skonfigurować licznik i gdzie użyć przerwań...
  • #6
    margas4542
    Level 16  
    Dziękuje za linki...próbowałem coś wskórać tłumacząc manualna atmegi ale teraz mogę spróbować napisać potrzebny kod....co z tego wyjdzie zobaczymy
    OK mam ustawiony Timer i funkcję switch przełączającą cyfry wyświetlacza ale w jaki sposób jest zwiększana wartość zmiennej (wysw) z przykładu [linki poprzedni post] ?..
    Code:

    //********** define segmentów wyświetlacza
    volatile uint8_t led_display;
    const unsigned char cyfra[10]={
                0xC0,/*cyfra 0        A         */
                0xF9,/*cyfra 1       ---      */
                0xA4,/*cyfra 2      |   |      */
                0xB0,/*cyfra 3     F |   | B      */
                0x99,/*cyfra 4      |   |      */
                0x92,/*cyfra 5     G  ---      */
                0x82,/*cyfra 6      |   |      */
                0xF8,/*cyfra 7     E |   | C      */
                0xC0,/*cyfra 8      |   |      */
                0x90,/*cyfra 9       ---      */
                0x7F /*kropka        D     #   H   */
                };
    //***** MAIN
    int main()
    {
     DDRA=0xFF;      //wyjścia - segmenty wyświetlaczy wyś_1 oraz wyś_2
     DDRB=0xFF;      //wyjścia - cyfry wyświetlacza czasu lokalnego
     DDRC=0xFC;      //wyjścia - cyfry wyświetlacza daty oraz wejścia - SCL SDA układu PCF8583
     PORTC=0x03;   //pull-up - dla SCL SDA INT1 układu PCF8583
     DDRD=0x00;      //wejścia - klawiatura
     PORTD=0xF8;   //pull-up - klawiatura
    //----- TWI init
     TWSR=0x00;      
     TWBR=0x48; //preskaler I2C - ((F_CPU / 100000UL - 16) - inicjacja przerwań
    //-----
     TCCR1B |= (1 << WGM12);    //ustawia timer1 w tryb CTC
     OCR1A = 160000;          //ustawia wartość pożądaną na 100Hz dla preskalera 1
     TCCR1B |= (1 << CS10);    //ustawia timer z preskalerem Fcpu/1
     TIMSK |= (1 << OCIE1A);    //zezwolenie na przerwania dla CTC
     sei();                //zezwolenie globalne na przerwania
    //-----pętla główna progranu
     while(1)
     {   //odczyt czasu i daty
       PCF=TWI_read(zegar,0x06);   PCF_dmc=PCF >>4;   PCF_jmc=PCF & 0x0F;
       PCF=TWI_read(zegar,0x05);   PCF_ddni=PCF >>4;   PCF_jdni=PCF & 0x0F;
        PCF=TWI_read(zegar,0x04);   PCF_dg=PCF >>4;      PCF_jg=PCF & 0x0F;
        PCF=TWI_read(zegar,0x03);   PCF_dm=PCF >>4;      PCF_jm=PCF & 0x0F;
        PCF=TWI_read(zegar,0x02);   PCF_ds=PCF >>4;      PCF_js=PCF & 0x0F;
       //-----
       dec_dg=bcd_na_dec(PCF_dg);   dec_jg=bcd_na_dec(PCF_jg);//konwersja na dec godziny
       dec_dm=bcd_na_dec(PCF_dm);   dec_jm=bcd_na_dec(PCF_jm);//konwersja na dec minuty
        {
       //
       }
    }
    return 0;
    }

    ISR(TIMER1_COMPA_vect)
    {
       switch(led_display)
       {
          case 0://wyśw_1_dziesiątki godzin
          PORTB &=~ 0x00;
          cyfra[dec_dg];
          _delay_ms(500);
          PORTB |= 0x00;
          break;
          case 1://wyśw_1_jednostki godzin
          PORTB &=~ 0x01;
          cyfra[dec_jg];
          _delay_ms(500);
          PORTB |= 0x01;
          break;
          case 2://wyśw_1_dziesiątki minut
          PORTB &=~ 0x02;
          cyfra[dec_dm];
          _delay_ms(500);
          PORTB |= 0x02;
          break;
          case 3://wyśw_1_jednostk minut
          PORTB &=~ 0x04;
          cyfra[dec_jm];
          _delay_ms(500);
          PORTB |= 0x04;
          break;
          case 4:
          PORTB &=~ 0x08;
          PORTA=0xC0;
          _delay_ms(500);
          PORTB |= 0x08;
          break;
       }
    }
    //********** END MAIN **********
  • #7
    MichalXY
    Level 11  
    W obsłudze przerwania zupełnie niepotrzebnie dałeś opóźnienie _delay_ms(500)

    A jeśli chodzi o zwiększanie wartości zmiennej led_display to przed instrukcją break;
    zwiększasz wartość zmiennej przez instrukcję
    Code:
    led_display++;
    (ta instrukcja to inkramentacja czyli zwiekszanie zmiennej o 1).
    Dopiero gdy jesteś w ostatniej instrukcji case np case 3 to do zmiennej led_display przypisujesz wartość 0 aby nastąpił "skok" do pierwszego case'a.




    Mam tylko jeszcze jedną uwagę - o co chodzi w tym fragmencie kodu:
    Code:

    case 4:
          PORTB &=~ 0x08;
          PORTA=0xC0;
          _delay_ms(500);
          PORTB |= 0x08;
          break;


    Moim skromnym zdaniem ten fragment jest niepotrzebny.

    Pozdrawiam i życzę wesołych świąt! :)
  • #8
    margas4542
    Level 16  
    Wiem to pokłosie prób...OK zmieniłem zgodnie z sugestią i dalej mam NIC...a właściwie zmiany tylko na ostatniej cyfrze jak gdyby kolejne cyfry nakładały się na siebie....ale co robi teraz timer bo chyba nic ?......
    Code:

    //********** define segmentów wyświetlacza
    volatile uint8_t led_display;
    const unsigned char cyfra[]={
                0xC0,/*cyfra 0        A         */
                0xF9,/*cyfra 1       ---      */
                0xA4,/*cyfra 2      |   |      */
                0xB0,/*cyfra 3     F |   | B      */
                0x99,/*cyfra 4      |   |      */
                0x92,/*cyfra 5     G  ---      */
                0x82,/*cyfra 6      |   |      */
                0xF8,/*cyfra 7     E |   | C      */
                0xC0,/*cyfra 8      |   |      */
                0x90,/*cyfra 9       ---      */
                0x7F /*kropka        D     #   H   */
                };
    //***** MAIN
    int main()
    {
     DDRA=0xFF;      //wyjścia - segmenty wyświetlaczy wyś_1 oraz wyś_2
     DDRB=0xFF;      //wyjścia - cyfry wyświetlacza czasu lokalnego
     DDRC=0xFC;      //wyjścia - cyfry wyświetlacza daty oraz wejścia - SCL SDA układu PCF8583
     PORTC=0x03;   //pull-up - dla SCL SDA INT1 układu PCF8583
     DDRD=0x00;      //wejścia - klawiatura
     PORTD=0xF8;   //pull-up - klawiatura
    //----- TWI init
     TWSR=0x00;      
     TWBR=0x48; //preskaler I2C - ((F_CPU / 100000UL - 16)
    //-----
     TCCR1B |= (1 << WGM12);    //ustawia timer1 w tryb CTC
     OCR1A = 160000;          //ustawia wartość pożądaną na 100Hz dla preskalera 1
     TCCR1B |= (1 << CS10);    //ustawia timer z preskalerem Fcpu/1
     TIMSK |= (1 << OCIE1A);    //zezwolenie na przerwania dla CTC
     sei();                //zezwolenie globalne na przerwania
    //-----pętla główna progranu
     while(1)
     {   //odczyt czasu i daty
       PCF=TWI_read(zegar,0x06);   PCF_dmc=PCF >>4;   PCF_jmc=PCF & 0x0F;
       PCF=TWI_read(zegar,0x05);   PCF_ddni=PCF >>4;   PCF_jdni=PCF & 0x0F;
       PCF=TWI_read(zegar,0x04);   PCF_dg=PCF >>4;      PCF_jg=PCF & 0x0F;
       PCF=TWI_read(zegar,0x03);   PCF_dm=PCF >>4;      PCF_jm=PCF & 0x0F;
       PCF=TWI_read(zegar,0x02);   PCF_ds=PCF >>4;      PCF_js=PCF & 0x0F;
       //-----
       dec_dg=bcd_na_dec(PCF_dg);   dec_jg=bcd_na_dec(PCF_jg);//konwersja na dec godziny
       dec_dm=bcd_na_dec(PCF_dm);   dec_jm=bcd_na_dec(PCF_jm);//konwersja na dec minuty
       dec_ds=bcd_na_dec(PCF_ds);   dec_js=bcd_na_dec(PCF_js);//konwersja na dec minuty
    }
    return 0;
    }

    ISR(TIMER1_COMPA_vect)
    {
       switch(led_display)
       {
          case 0://wyśw_1_dziesiątki godzin
          PORTB &=~ 0x00;
          PORTA=cyfra[dec_dm];
          PORTB |= 0x00;
          led_display++;
          break;
          case 1://wyśw_1_jednostki godzin
          PORTB &=~ 0x01;
          PORTA=cyfra[dec_jm];
          PORTB |= 0x01;
          led_display++;
          break;
          case 2://wyśw_1_dziesiątki minut
          PORTB &=~ 0x02;
          PORTA=cyfra[dec_ds];
          PORTB |= 0x02;
          led_display++;
          break;
          case 3://wyśw_1_jednostk minut
          PORTB &=~ 0x04;
          PORTA=cyfra[dec_js];
          PORTB |= 0x04;
          led_display=0;
          break;
       }
    }
    //********** END MAIN **********
  • #9
    margas4542
    Level 16  
    Teraz dla ułatwienia multiplex chcę uruchomić na wyświetlaczach LED ale docelowo będą dwie lampy VFD typ IW-18 łącznie wyświetlanych 12-cyfr...czy nie ma na tym forum nikogo kto potrafi wytłumaczyć jak poprawnie powinno się zrobić taki multiplex ?..dołączam schemat;
    [img]
    [atmega32][c]multipleksowanie wyświetlacza LED[/img]
  • #10
    tmf
    Moderator of Microcontroller designs
    Raczej nie ma nikogo, komu chce się tłumaczyć takie podstawy, tym bardziej, że na googlach znajdziesz gotowce na ten temat w pare sekund. Poza tym kolega MichalXY dał ci już linki do materiałów.
    Co do twojego programu to zastanów się po co masz te wszystkie switch/case? Są niepotrzebne, od tego masz zmienną led_display, aby nią indeksować dane. Tak samo po co ją zwiększać w każdym case? Nie lepiej ją zwiększać na samym końcu tylko raz? Te instrukcje PORTB &=~ 0x04; są niepotrzebne, po prostu wpisz 0 na ten port, chyba, że inne linie tego portu wykorzystujesz do czegoś innego, to wtedy je zamaskuj, ale też tylko raz. Pokaż też jak są zadeklarowane twoje zmienne dec_costam, zapewne bez volatile?
    Kolejna rzecz - po co na około czytasz czas z PCFa, skoro wyświetlasz go z dokładnością do minuty? To lepiej już czytać co minutę. A najlepiej wykorzystać do tego celu wyjście przerwania z PCFa. Kolejna rzecz to atomowość - zastanó się co będzie, jeśli w trakcie zmiany np. minut z 59 na 00 przerwanie LED wystąpi pomiędzy odczytem pierwszej i drugiej cyfry? Przez chwilę będziesz miał 50, co będzie wyglądać dziwnie. Co prawda to będzie ułamek sekundy, ale to wystarczy, żeby widzieć nieprzyjemne mignięcie.
  • #11
    margas4542
    Level 16  
    To mój drugi program ale nie robiłem jeszcze niczego na przerwaniach...każdy pisze po swojemu a nawet mając doświadczenie nawet wam trudno taki program przeanalizować....wszyscy odwołujcie się do google twierdząc że wszystkiego tam jest w bród a to nie jest tak różowo...może dla kolegi to banalne podstawy podając za przykład linki a potem te same procedury krytykuje doprowadzając do sytuacji że ta procedura multiplexu nie jest już tak banalnie prosta...
    Na razie uzyskałem wyświetlanie na 2-3-4 pozycji wyświetlacza ale tą samą cyfrę..
    Code:

    //********** funkcje konwersji   
    char dec_na_bcd(char num)   {return ((num/10 * 16) + (num % 10));}   //set = dec_na_bcd(set);     
    char bcd_na_dec(char num)   {return ((num/16 * 10) + (num % 16));}   //set = bcd_na_dec(set);
    //********** definicje zmiennych dla zegara
    unsigned char rtc_tmp;
    unsigned char rtc[12];//odczyt daty i czasu
    unsigned char dec[12];//czas po konwersji na dec
    // dziesiątki -> [0] miesięcy,[2] dni,[4] godzin,[6] minut,[8] sekund
    // jednostki  -> [1] miesięcy,[3] dni,[5] godzin,[7] minut,[9] sekund
    //********** definicje zmiennych dla menu
    unsigned char licznik_1,licznik_2,cyfra_nr;
    unsigned char set_g1,set_m1,set_g2,set_m2,set_g3,set_m3,set_g4,set_m4;
    //********** define segmentów wyświetlacza
    volatile uint8_t led_display;
    const unsigned char cyfra[10]={
                0xC0,/*cyfra 0        A             */
                0xF9,/*cyfra 1       ---            */
                0xA4,/*cyfra 2      |   |           */
                0xB0,/*cyfra 3    F |   | B         */
                0x99,/*cyfra 4      |   |           */
                0x92,/*cyfra 5    G  ---            */
                0x82,/*cyfra 6      |   |           */
                0xF8,/*cyfra 7    E |   | C         */
                0xC0,/*cyfra 8      |   |           */
                0x90,/*cyfra 9       ---            */
                0x7F /*kropka         D     #   H   */
                };
    //***** MAIN
    int main()
    {
     DDRA=0xFF;      //wyjścia - segmenty wyświetlaczy wyś_1 oraz wyś_2
     DDRB=0xFF;      //wyjścia - cyfry wyświetlacza czasu lokalnego
     DDRC=0xFC;      //wyjścia - cyfry wyświetlacza daty oraz wejścia - SCL SDA układu rtc8583
     PORTC=0x03;   //pull-up - dla SCL SDA INT1 układu rtc8583
     DDRD=0x00;      //wejścia - klawiatura
     PORTD=0xF8;   //pull-up - klawiatura
    //----- TWI init
     TWSR=0x00;     
     TWBR=0x48;               //preskaler I2C - ((F_CPU / 100000UL - 16)
     TCCR1B |= (1 << WGM12);    //ustawia timer1 w tryb CTC
     OCR1A = 160000;             //ustawia wartość pożądaną na 100Hz dla preskalera 1
     TCCR1B |= (1 << CS10);       //ustawia timer z preskalerem Fcpu/1
     TIMSK |= (1 << OCIE1A);    //zezwolenie na przerwania dla CTC
     sei();   
    //-----pętla główna progranu
     while(1)
     {
        cyfra_nr=0;
        rtc_tmp=TWI_read(zegar,0x01);   rtc[10]=rtc_tmp >>4;   rtc[11]=rtc_tmp & 0x0F;//odczyt setnych sekund
       dec[10]=bcd_na_dec(rtc[10]);   dec[11]=bcd_na_dec(rtc[11]);      //konwersja na dec setne sekundy

       if(dec[11]==0)
       {
          rtc_tmp=TWI_read(zegar,0x06);   rtc[0]=rtc_tmp >>4;   rtc[1]=rtc_tmp & 0x0F;//odczyt miesięcy
          rtc_tmp=TWI_read(zegar,0x05);   rtc[2]=rtc_tmp >>4;   rtc[2]=rtc_tmp & 0x0F;//odczyt dni
          rtc_tmp=TWI_read(zegar,0x04);   rtc[4]=rtc_tmp >>4;   rtc[5]=rtc_tmp & 0x0F;//odczyt godzin
          rtc_tmp=TWI_read(zegar,0x03);   rtc[6]=rtc_tmp >>4;   rtc[7]=rtc_tmp & 0x0F;//odczyt minut
          rtc_tmp=TWI_read(zegar,0x02);   rtc[8]=rtc_tmp >>4;   rtc[9]=rtc_tmp & 0x0F;//odczyt sekund
       //-----
          dec[0]=bcd_na_dec(rtc[0]);   dec[1]=bcd_na_dec(rtc[1]);//konwersja na dec miesięcy
          dec[2]=bcd_na_dec(rtc[2]);   dec[3]=bcd_na_dec(rtc[3]);//konwersja na dec dni
          dec[4]=bcd_na_dec(rtc[4]);   dec[5]=bcd_na_dec(rtc[5]);//konwersja na dec godziny
          dec[6]=bcd_na_dec(rtc[6]);   dec[7]=bcd_na_dec(rtc[7]);//konwersja na dec minuty
          dec[8]=bcd_na_dec(rtc[8]);   dec[9]=bcd_na_dec(rtc[9]);//konwersja na dec sekundy
       }
    }
    return;     
    }
    ISR(TIMER1_COMPA_vect)
    {
       switch(cyfra_nr)         //na razie testuje na czterech cyfrach bo tylko taki mam wyświetlacz.
                            //docelowo lampa VFD IW-18
       {
          case 0:            //wyśw_1_cyfra_1
          PORTB &=~ 0x00;
          PORTA=cyfra[dec[6]];
          PORTB |= 0x00;
          cyfra_nr++;
          break;
          case 1:            //wyśw_1_cyfra_2
          PORTB &=~ 0x01;
          PORTA=cyfra[dec[7]];
          PORTB |= 0x01;
          cyfra_nr++;
          break;
          case 2:            //wyśw_1_cyfra_3
          PORTB &=~ 0x02;
          PORTA=cyfra[dec[8]];
          PORTB |= 0x02;
          cyfra_nr++;
          break;
          case 3:            //wyśw_1_cyfra_4
          PORTB &=~ 0x04;
          PORTA=cyfra[dec[9]];
          PORTB |= 0x04;
          cyfra_nr++;
          break;
          case 4:            //wyśw_1_cyfra_5
          cyfra_nr++;
          break;
          case 5:            //wyśw_1_cyfra_6
          cyfra_nr++;
          break;
          case 6:            //wyśw_2_cyfra_1
          cyfra_nr++;
          break;
          case 7:            //wyśw_2_cyfra_2
          cyfra_nr++;
          break;
          case 8:            //wyśw_2_cyfra_3
          cyfra_nr++;
          break;
          case 9:            //wyśw_2_cyfra_4
          cyfra_nr++;
          break;
          case 10:         //wyśw_2_cyfra_5
          cyfra_nr++;
          break;
          case 11:         //wyśw_2_cyfra_6
          cyfra_nr++;
          break;

       }
    }
    //********** END MAIN **********
  • Helpful post
    #12
    MichalXY
    Level 11  
    margas4542 linki które podałem zawierają naprawdę dobre materiały na temat timerow, przerwań i multipleksowania wyświetlaczy LED.

    Poniżej jest uproszczony schemat podłączenia wyświetlaczy LED i kod programu do obsługi multipleksowania wyświetlaczy LED.

    [atmega32][c]multipleksowanie wyświetlacza LED

    Poniżej znajduje się program do obsługi multipleksowania wyświetlaczy LED ze wspólną anodą i dla częstotliwości procesora 1MHz


    Code:
    /*
    
     * multipleksowanie.c
     *
     *  Created on: 21-12-2010
     *      Author: Michał
     */

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

    int wyswietlacz;

    unsigned char cyfra[10]={
               0xC0,/*cyfra 0        A         */
               0xF9,/*cyfra 1       ---      */
               0xA4,/*cyfra 2      |   |      */
               0xB0,/*cyfra 3     F |   | B      */
               0x99,/*cyfra 4      |   |      */
               0x92,/*cyfra 5     G  ---      */
               0x82,/*cyfra 6      |   |      */
               0xF8,/*cyfra 7     E |   | C      */
               0xC0,/*cyfra 8      |   |      */
               0x90,/*cyfra 9       ---      */
               0x7F /*kropka        D     #   H   */
               };



    ISR(TIMER1_COMPA_vect)
    {
       switch(wyswietlacz)
       {
       case 0: PORTB |= _BV(3);  /* USTAWIENIE BITU 3 PORTU B wyłączenie wyswietlacza nr 4*/
               PORTB |= _BV(2);  /* USTAWIENIE BITU 2 PORTU B wyłączenie wyswietlacza nr 3*/
               PORTB |= _BV(1);  /* USTAWIENIE BITU 1 PORTU B wyłączenie wyswietlacza nr 2*/
               PORTB &= ~_BV(0); /* WYZEROWANIE BITU 0 PORTU B włączenie wyswietlacza nr 1*/
               PORTA = cyfra[1];
               wyswietlacz++;
               break;
       case 1: PORTB |= _BV(0);  /* USTAWIENIE BITU 0 PORTU B wyłączenie wyswietlacza nr 1*/
               PORTB |= _BV(3);  /* USTAWIENIE BITU 3 PORTU B wyłączenie wyswietlacza nr 4*/
               PORTB |= _BV(2);  /* USTAWIENIE BITU 2 PORTU B wyłączenie wsywietlacza nr 3*/
               PORTB &= ~_BV(1); /* WYZEROWANIE BITU 1 PORTU B włączenie wyswietlacza nr 2*/
               PORTA = cyfra[2];
               wyswietlacz++;
               break;
       case 2: PORTB |= _BV(1);  /* USTAWIENIE BITU 1 PORTU B wyłączenie wyswietlacza nr 2*/
               PORTB |= _BV(3);  /* USTAWIENIE BITU 3 PORTU B wyłączenie wyswietlacza nr 4*/
               PORTB |= _BV(0);  /* USTAWIENIE BITU 0 PORTU B wyłączenie wsyweietlacza nr 1*/
               PORTB &= ~_BV(2); /* WYZEROWANIE BITU 2 PORTU B włączenie wyswietlacza nr 3*/
               PORTA = cyfra[3];
               wyswietlacz++;
               break;
       case 3: PORTB |= _BV(2);  /* USTAWIENIE BITU 2 PORTU B wyłączenie wyswietlacza nr 3*/
               PORTB |= _BV(1);  /* USTAWIENIE BITU 1 PORTU B wyłączenie wyswietlacza nr 2*/
               PORTB |= _BV(0);  /* USTAWIENIE BITU 0 PORTU B wyłączenie wyswietlacza nr 1*/
               PORTB &= ~_BV(3); /* WYZEROWANIE BITU 3 PORTU B włączenie wyswietlacza nr 4*/
               PORTA = cyfra[4];
               wyswietlacz=0;
               break;




       }
    }

    int main(void)
    {
       DDRB = 0xff;
       PORTB = 0xff;
       DDRA = 0xff;
       TCCR1B |= (1<<WGM12);
       TCCR1B |= (1<<CS10);
       TIMSK |= (1<<OCIE1A);
       OCR1A = 5000;
       sei();
       while(1);


    }


    Udostępniam również wsad do procesora ATMEGA32 z powyższym programem
  • #13
    margas4542
    Level 16  
    Ooo wielkie dzięki ruszyło z kopyta...teraz mogę się temu na spokojnie przyjrzeć i przeanalizować...ustawiłeś OCR1A = 5000/3200Hz ja miałem 160000/100Hz ale chodzi przy jednym i drugim parametrze tak samo czy jest jakaś różnica której nie dostrzegam ?...
    dodano; załapałem...to multiplex po modyfikacji
    Code:

    ISR(TIMER1_COMPA_vect)
    {
       switch(wyswietlacz)
       {
          case 0:
          PORTB = 0xFE;          //włączenie wyswietlacza nr 1
          PORTA = cyfra[dec[6]];
          wyswietlacz++;
          break;
          case 1:
          PORTB = 0xFD;          //włączenie wyswietlacza nr 2
          PORTA = cyfra[dec[7]];
          wyswietlacz++;
          break;
          case 2:
          PORTB = 0xFB;          //włączenie wyswietlacza nr 3
          PORTA = cyfra[dec[8]];
          wyswietlacz++;
          break;
          case 3:
          PORTB = 0xF7;          //włączenie wyswietlacza nr 4
          PORTA = cyfra[dec[9]];
          wyswietlacz=0;
          break;
       }
    }
  • Helpful post
    #14
    MichalXY
    Level 11  
    margas4542 zaopatrz się w program TimerCycles ze tej strony:
    http://www.wkretak.pl/downloads.php?cat_id=1

    Dzięki temu obliczysz liczbę cykli czyli to co zapisuję się do rejestru OCR1A.

    Jeśli chodzi o timery to po pierwsze preskaler(czyli część obwodu timera który dzieli impuls zegarowy tj. zmienia rozdzielczość timera) jest ustawiony na 1 mówi o tym ten fragment kodu:
    TCCR1B |= (1<<CS10) czyli ustawienie bitu CS10 rejestru TCCR1B.

    Timer1 (TCCR1B) zlicza tak długa aż jego wartość będzie równa wartości wpisanej do rejestru OCR1A. Gdy wartość timera i rejestru OCR1A będą równe następuje przerwanie i automatyczne wyzerowanie timera i zliczanie na nowo.
    Takie porównywanie jest umożliwione dzięki trybowi CTC timera czyli porównania wartości timera z wartością pożądaną czyli tą którą wpisujemy do rejestru OCR1A w tym wypadku i gdy wartości będą równe timer zostaję wyzerowany i zostaje w tym momencie wywołane przerwanie.
    Fragment kodu który ustawia timer w tryb CTC
    Code:
    TCCR1B |= (1<<WGM12);

    Samo przerwanie uzyskujemy dzięki ustawieniu bitu OCIE1A rejestru TIMSK
    czyli zezwolenie na przerwanie dla CTC.
    Fragment:
    Code:
    TIMSK |= (1<<OCIE1A);


    Ah, bym zapomniał, wogóle każde przerwanie jest odblokowywane poleceniem
    Code:
    sei()
    a blokowane poleceniem
    Code:
    cli()


    Odnośnie częstotliwości multipleksowania przyjmuję 50Hz odświerzania dla jednego wyświetlacza co dla 4 wyświetlaczy daje 200Hz.

    Pozdrawiam! :)
  • #15
    margas4542
    Level 16  
    Jeszcze raz dziękuje za cierpliwość i zrozumiałe wytłumaczenie tematu. Aż miło popatrzeć jak cyferki się wyświetlają...programik mam ale policzyłem na kalkulatorku i wszystko pasuje.
    A Z OKAZJI ŚWIĄT CHCIAŁBYM WSZYSTKIM FORUMOWICZOM ORAZ ADMINISTRATOROM PORTALU ELEKTRODA ŻYCZYĆ SPOKOJNYCH BEZTROSKICH ŚWIĄT, POMYŚLNOŚCI , ZDROWIA , SPEŁNIENIA MARZEŃ ORAZ UDANYCH KONSTRUKCJI I DAJĄCYCH SATYSFAKCJĘ PROGRAMÓW.
  • #16
    margas4542
    Level 16  
    Witam. Napisałem i uruchomiłem część zegarka a mianowicie odczyt z PCF-a czasu i daty oraz multipleksowanie wyświetlaczy LED ale mam kłopot z koncepcją jego programowania. Po kilku nieudanych próbach napisania sensownej obsługi klawiszy zmuszony jestem poprosić was o pomoc...to działający kod reszta w malinach :-(
    Code:

    //********** funkcje konwersji   
    char dec_na_bcd(char num)   {return ((num/10 * 16) + (num % 10));}   //set = dec_na_bcd(set);     
    char bcd_na_dec(char num)   {return ((num/16 * 10) + (num % 16));}   //set = bcd_na_dec(set);
    //********** definicje zmiennych dla zegara
    unsigned char rtc_tmp;
    unsigned char rtc[12];//odczyt daty i czasu
    unsigned char dec[12];//czas po konwersji na dec
    unsigned char gmt[12];//czas GMT
    // dziesiątki -> [0] miesięcy,[2] dni,[4] godzin,[6] minut,[8] sekund
    // jednostki  -> [1] miesięcy,[3] dni,[5] godzin,[7] minut,[9] sekund
    //********** definicje zmiennych dla menu
    unsigned char wyswietlacz,funkcja;
    unsigned char set,set_plus,set_minus;
    //********** define segmentów wyświetlacza
    const unsigned char cyfra[11]={
                0xC0,/*cyfra 0        A             */
                0xF9,/*cyfra 1       ---            */
                0xA4,/*cyfra 2      |   |           */
                0xB0,/*cyfra 3    F |   | B         */
                0x99,/*cyfra 4      |   |           */
                0x92,/*cyfra 5    G  ---            */
                0x82,/*cyfra 6      |   |           */
                0xF8,/*cyfra 7    E |   | C         */
                0xC0,/*cyfra 8      |   |           */
                0x90,/*cyfra 9       ---            */
                0x7F,/*kropka         D     #   H   */
             0xFF /*segmenty wygaszone*/
                };
    //***** MAIN
    int main()
    {
     DDRA=0xFF;         //wyjścia - segmenty wyświetlaczy wyś_1 oraz wyś_2
     DDRB=0xFF;         //wyjścia - cyfry wyświetlacza czasu lokalnego
     DDRC=0xFC;         //wyjścia - cyfry wyświetlacza daty oraz wejścia - SCL SDA układu rtc8583
     PORTC=0x03;         //pull-up - dla SCL SDA układu rtc8583
     DDRD=0x00;         //wejścia - klawiatura
     PORTD=0xF8;         //pull-up - klawiatura
    //----- TWI init
     TWSR=0x00;     
     TWBR=0x48;               //preskaler I2C - ((F_CPU / 100000UL - 16)
     TCCR1B |= (1 << WGM12);   //ustawia timer1 w tryb CTC
     OCR1A = 26666;            //ustawia wartość 50Hz na segment wyświetlacza
     TCCR1B |= (1 << CS10);      //ustawia timer z preskalerem Fcpu/1
     TIMSK |= (1 << OCIE1A);   //zezwolenie na przerwania dla CTC
     sei();
    //-----pętla główna progranu
     while(1)
     {
       rtc_tmp=TWI_read(zegar,0x06);   rtc[0]=rtc_tmp >>4;   rtc[1]=rtc_tmp & 0x0F;//odczyt miesięcy
       rtc_tmp=TWI_read(zegar,0x05);   rtc[2]=rtc_tmp >>4;   rtc[2]=rtc_tmp & 0x0F;//odczyt dni
       rtc_tmp=TWI_read(zegar,0x04);   rtc[4]=rtc_tmp >>4;   rtc[5]=rtc_tmp & 0x0F;//odczyt godzin
       rtc_tmp=TWI_read(zegar,0x03);   rtc[6]=rtc_tmp >>4;   rtc[7]=rtc_tmp & 0x0F;//odczyt minut
       rtc_tmp=TWI_read(zegar,0x02);   rtc[8]=rtc_tmp >>4;   rtc[9]=rtc_tmp & 0x0F;//odczyt sekund
       //-----
       dec[0]=bcd_na_dec(rtc[0]);   dec[1]=bcd_na_dec(rtc[1]);//konwersja na dec miesięcy
       dec[2]=bcd_na_dec(rtc[2]);   dec[3]=bcd_na_dec(rtc[3]);//konwersja na dec dni
       dec[4]=bcd_na_dec(rtc[4]);   dec[5]=bcd_na_dec(rtc[5]);//konwersja na dec godziny
       dec[6]=bcd_na_dec(rtc[6]);   dec[7]=bcd_na_dec(rtc[7]);//konwersja na dec minuty
       dec[8]=bcd_na_dec(rtc[8]);   dec[9]=bcd_na_dec(rtc[9]);//konwersja na dec sekundy
    }
    return;     
    }
    ISR(TIMER1_COMPA_vect)
    {
       switch(wyswietlacz)//docelowo dwie lampy VFD IW-12
       {
          case 0://------cyfra nr 1 port PB2
          PORTC=0xFC;
          PORTB = 0xFB;    PORTA = cyfra[dec[4]];   wyswietlacz++;
          break;
          case 1://------cyfra nr 2 port PB3
          PORTB = 0xF7;    PORTA = cyfra[dec[5]];   wyswietlacz++;
          break;
          case 2://------cyfra nr 3 port PB4
          PORTB = 0xEF;    PORTA = cyfra[dec[6]];   wyswietlacz++;
          break;
          case 3://------cyfra nr 4 port PB5
          PORTB = 0xDF;    PORTA = cyfra[dec[7]];   wyswietlacz++;
          break;
          case 4://------cyfra nr 5 port PB6
          PORTB = 0xBF;    PORTA = cyfra[dec[8]];   wyswietlacz++;
          break;
          case 5://------cyfra nr 6 port PB7
          PORTB = 0x7F;    PORTA = cyfra[dec[9]];   wyswietlacz++;
          break;//----------------------------------------------
          case 6://------cyfra nr 7 port PC2
          PORTB=0xFF;
          PORTC = 0xFB;    PORTA = cyfra[dec[2]];   wyswietlacz++;
          break;
          case 7://------cyfra nr 8 port PC3
          PORTC = 0xF7;    PORTA = cyfra[dec[3]];   wyswietlacz++;
          break;
          case 8://------cyfra nr 9 port PC4
          PORTC = 0xEF;    PORTA = cyfra[dec[0]];   wyswietlacz++;
          break;
          case 9://------cyfra nr 10 port PC5
          PORTC = 0xDF;    PORTA = cyfra[dec[1]];   wyswietlacz++;
          break;
          case 10://------cyfra nr 11 port PC6
          PORTC = 0xBF;   PORTA = 0xF9;   wyswietlacz++;
          break;
          case 11://-----cyfra nr 12 port PC
          PORTC = 0x7F;   PORTA = 0xF9;   wyswietlacz=0;
          break;
       }
    }