Elektroda.pl
Elektroda.pl
X

Wyszukiwarki naszych partnerów

Wyszukaj w ofercie 200 tys. produktów TME
Europejski lider sprzedaży techniki i elektroniki.
Proszę, dodaj wyjątek elektroda.pl do Adblock.
Dzięki temu, że oglądasz reklamy, wspierasz portal i użytkowników.

[Atmega8][C] Atmega8 i TWI na przykładzie zegara LED

p_zag 10 Cze 2009 20:28 22615 1
  • #1 10 Cze 2009 20:28
    p_zag
    Poziom 13  

    Witam wszystkich.
    Zaprojektowałem i uruchomiłem zegarek z wyświetlaczami LED(SA10-21GWA) i układem PCF8583 współpracującym z mikroprocesorem Atmega8 oraz układami PCF8574AP na szynie TWI(i2c).
    [Atmega8][C] Atmega8 i TWI na przykładzie zegara LED [Atmega8][C] Atmega8 i TWI na przykładzie zegara LED [Atmega8][C] Atmega8 i TWI na przykładzie zegara LED

    A teraz chciałbym go wam zaprezentować.
    Na początek podręcznik języka C w formie pliku pdf -> 'C.pdf' i plik -> 'atmega16mmr.pdf' ,oraz obrazki -> 'Atmega8.gif' i 'Atmega16.gif' (obie Atmegi różnią się ilością wyprowadzeń, natomiast rejestry sterujące mają takie same). Ja wykorzystuje opis 'atmega16mmr.pdf' do ustawiania rejestrów procesora Atmega8.
    [Atmega8][C] Atmega8 i TWI na przykładzie zegara LED
    [Atmega8][C] Atmega8 i TWI na przykładzie zegara LED

    A oto program 'twiAtmega8_zegarLED.c' jest to ostateczna wersja mojego w pełni działającego programu zegara LED.

    Code:


    #define F_CPU 8000000UL      // częstotliwosci zegara procesora Atmega8 (musi byc przed "#include <util/delay.h>")
    #include <avr/io.h>
    #include <util/delay.h>    // moduł ->        (C:\WinAVR-20081205\avr\include\util\delay.h)
    #include <avr/wdt.h>       // moduł -> Watchdog (C:\WinAVR-20081205\avr\include\avr\wdt.h)
    #include <avr/interrupt.h>   // moduł ->        (C:\WinAVR-20081205\avr\include\util\interrupt.h)







    Jest to zestaw modułów używanych w moim programie. I tu taka uwaga dotycząca kompilatora otóż ja używam 'WinAVR-20081205-install.exe' ponieważ ten kompilator tworzy najmniejsze pliki 'twiAtmega8.hex' około 2,690kB(około 32,8% całej pamięci procesora), natomiast 'WinAVR-20090313-install.exe' jak i również 'WinAVR-20080610-install.exe' tworzyły pliki *.hex bliskie 7kB(zajmowały prawie całą pamięć procesora) kompilując ten sam program.
    [Atmega8][C] Atmega8 i TWI na przykładzie zegara LED

    Code:


    /* Adres układu PCF8574A w zależności od podpięcia wyprowadzeń nr 1,2,3(nóżka układu PCF8574A o nazwie A0,A1,A2)
        bit 7 do 4 -> na stałe zaprogramowane przez producenta, bit 3 do 1 -> to adres, który ustawiasz, bit 0 -> to bit zapisu lub odczytu.
       01110100(R/~W) sekundy            -> adres = 0x74
       01110110(R/~W) dziesiątki sekund      -> adres = 0x76
       01111000(R/~W) minuty            -> adres = 0x78
       01111010(R/~W) dziesiątki minut      -> adres = 0x7A
       01111100(R/~W) godziny            -> adres = 0x7C
       01111110(R/~W) dziesiątki godzin      -> adres = 0x7E */
    #define sekundy 0x74
    #define dziesiątki_sekund 0x76
    #define minuty 0x78
    #define dziesiątki_minut 0x7A
    #define godziny 0x7C
    #define dziesiątki_godzin 0x7E

    //-------------------------------------------------------------------------------------------------
    /* Adres układu PCF8583 w zależności od podpięcia wyprowadzenia nr 3(nóżka układu PCF8583 o nazwie A0)
        bit 7 do 2 -> na stałe zaprogramowane przez producenta,  bit 1 -> to adres, który ustawiasz, bit 0 -> to bit zapisu lub odczytu.
       10100000(R/~W) - jeśli wyprowadzenie nr 3 jest podpięte do GND -> adres = 0xA0
       10100010(R/~W) - jeśli wyprowadzenie nr 3 jest podpięte do Vcc -> adres = 0xA2 */
    #define zegar 0xA0   

    //-------------------------------------------------------------------------------------------------



    Jest to ustalenie adresów układów PCF8574AP pod wyświetlacze LED i adresu zegara PCF8583.
    Układy PCF8574A(PCF8574AP) i PCF8574(PCF8574P) "Remote 8-bit I/O expander for I2C-bus" różnią się od siebie tylko adresowaniem (na stałe zaprogramowane przez producenta) po za tym są identyczne:
    Adres PCF8574A -> 0111 <- bit 7 do 4 -> na stałe zaprogramowane przez producenta
    Adres PCF8574 -> 0100 <- bit 7 do 4 -> na stałe zaprogramowane przez producenta
    Pozostałe bity obu układów są identyczne.
    [Atmega8][C] Atmega8 i TWI na przykładzie zegara LED
    [Atmega8][C] Atmega8 i TWI na przykładzie zegara LED
    [Atmega8][C] Atmega8 i TWI na przykładzie zegara LED

    Code:


    const unsigned char tablica_7seg[] ={
     0x88,/*cyfra 0*/
     0xEB,/*cyfra 1*/
     0x4C,/*cyfra 2*/
     0x49,/*cyfra 3*/
     0x2B,/*cyfra 4*/
     0x19,/*cyfra 5*/
     0x18,/*cyfra 6*/
     0xCB,/*cyfra 7*/
     0x08,/*cyfra 8*/
     0x09 /*cyfra 9*/
    };
    /* Segmenty: (PCF8574A,  SA10-21EWA,  nr_segmentu) -> aktywny stan niski L
                12(P7)      G         10
                11(P6)      F          9
                10(P5)      A         7
                 9(P4)      B          6
                 7(P3)       diody LED
                 6(P2)      C          4      
                 5(P1)      D          2
                 4(P0)      E          1
    */

    const unsigned char kropka =0b00001000; // dioda LED wyświetlaczy [PCF8574A nóżka7(P3)] -> kropka i dwukropek



    Są to stałe określające kształt cyfr na wyświetlaczach LED, oraz stała kropki oddzielającej sekundy od minut i dwukropka między godzinami i minutami.
    Zastosowałem wyświetlacz jedno-calowy zielony o nazwie 'SA10-21EWA'
    [Atmega8][C] Atmega8 i TWI na przykładzie zegara LED

    Code:


    // volatile -> zmienna nie optymalizowana przez kompilator (występuje w przerwaniu i programie głównym)
    volatile unsigned char czas[6] ={0,}; // zerowanie wszystkich elementów tablicy
    unsigned char czasPCF8583;
    unsigned char liczba_pwm, test_pwm;   
    unsigned char klawiatura;
    unsigned char segment =2; // wyświetlacze przy zapisie z klawiatury
    unsigned char takt0, takt1, takt2, takt3, takt4, takt5; // dla ustawiania czasu do zapisu
    unsigned char taktZapisu[3] ={0,}; // zerowanie wszystkich elementów tablicy


    /*   ---   POLA BITOWE ---
    volatile Flagi polaBitowe;          <- znienna - polaBitowe(bit lub bajt) typy volatile Flagi 
        (wywołwnie pojedyńczego bita      ->     polaBitowe.Bity.bit1 =0; itp.)
        (wywołwnie całeg bajta        ->    polaBitowe.Bajt =0;)
    */
    typedef struct   // Liczba po dwukropku oznacza ilość bitów.
     {   
      unsigned char bit0:1;   // jeden bit
      unsigned char bit1:1;   // jeden bit
      unsigned char bit2:1;   // jeden bit
      unsigned char bit3:1;   // jeden bit
      unsigned char bit4:1;   // jeden bit
      unsigned char bit5:1;   // jeden bit
      unsigned char bit6:1;   // jeden bit
      unsigned char bit7:1;   // jeden bit
     } FlagiBitow;
    typedef union
     {
      FlagiBitow Bit;      // flaga jednego z ośmiu(pojedyńczego) bitu   
      unsigned char Bajt;   // jeden bajt zawiera w sobie 8 pojedyńczych bitów
     } Flagi;
    volatile Flagi wborDoZapisu;
    volatile Flagi wborZnacznika;



    Pola bitowe -> nieraz potrzeba tylko jednego bitu żeby coś włączyć lub wyłączyć i szkoda marnować całego bajta skoro w bajcie jest od 0 do 7 bitów(osiem bitów)

    Code:


    // Prototypy funkcji  <- deklaracja funkcji, które znajdują się poniżej funkcji 'int main(void) {...}'
    void TWI_inicjacja(void);
    unsigned char TWI_odczyt(unsigned char, unsigned char);
    void TWI_zapis(unsigned char, unsigned char, unsigned char);
    void TWI_wswietlacz(unsigned char, unsigned char);
    void PWM_inicjacja(void);
    void wartoscPWM(unsigned char);
    void INT0_inicjacja(void);
    void minusCzas(unsigned char *, unsigned char *);
    void plusCzas(unsigned char *, unsigned char *);



    Prototypy funkcji, procedur -> przy takim zapisie program jest bardziej przejrzysty, ponieważ "ciała" funkcji, procedur mogą być poniżej funkcji 'int main(void)' czyli programu głównego i kompilator doskonale wie gdzie ich ma szukać.

    Code:


    //-------------------------------------------------------------------------------------------------
    // ************************************************************************************************
    //-------------------------------------------------------------------------------------------------
    //   *** PROGRAM GŁÓWNY ***   //
    int main(void)
     {
       /* Instrukcje jednokrotnego wykonania */

      //UWAGA ! Porty: PC5(SCL) PC4(SDA)  -  wejścia TWI
      //        Porty: PB2(OC1B) PB1(OC1A) -  wyjścia PWM
      //        Porty: PD0{góora} PD1{lewy} PD3{środek} PD4{prawy} PD5{dol}  -  wejścia klawiatury z pull-up'em
      //        Port:  PD2(INT0)  -  wejście sygnałowe z zegara PCF8583 z pull-up'em
      //        Port:  PB0  -  bit "test" z pull-up'em
      //        Port:  PC[0-3], PD[6-7] -> bity pomocnicze
            // PC0 -> znacznik jednokrotnego wykonania
          // PC1 -> znacznik naciśnięcia klawiszy
          // PC2 -> znacznik trybu dodać/odjąć
          // PC3 -> znacznik zapis/odczyt zegara

      //---------------------------
      // 1 -> wyjście
      // 0 -> wejście
      DDRB =0b00000111; 
      DDRC =0b00001111; 
      DDRD =0b11000000;
      // 1 -> z podciągnięciem do VCC (pull-up)
      // 0 -> bez podciągniecia do VCC
      PORTB =0b00000001;
      PORTC =0b00110000;
      PORTD =0b00111111;

      TWI_inicjacja();
      TWI_zapis(zegar,0x00,0b00000000);      // ustawienie początkowe zegara PCF8583
      PWM_inicjacja();
      liczba_pwm =TWI_odczyt(zegar,0x10);    // odczyt liczby_pwm w pamięci RAM zegara o adresie 0x10
      test_pwm =0;
      for(unsigned char i=1; i<=17; ++i)   // test poprawności liczby pamięci RAM zegara o adresie 0x10
       {
        if((i *15) ==liczba_pwm) test_pwm =liczba_pwm;      
       }
      if(test_pwm ==0) liczba_pwm =135;    // 135 okolo polowa zakresu (0 do 255)
      wartoscPWM(liczba_pwm);
      INT0_inicjacja();
      wborDoZapisu.Bajt =0;      // system bitów
      wborZnacznika.Bajt =0;   // system bitów
      wdt_reset();            // konieczny restart Watchdoga         
      wdt_enable(WDTO_250MS);   // Watchdog -> restart procesora co 0,25 sekundy
      sei();               // Pozwalamy na wykonywanie wszelkich przerwań !!!
      // -----------------------------



    Instrukcje wykonania jednokrotnego podczas startu procesora.
    Czyli ustawienie portów, start procedur(TWI,PWM,INT0,itp), zerowanie znaczników jako całych bitów(opcja ->> typedef union, patrz język C.pdf), watchog, globalne zezwolenie na przerwania procesora. Watchdog to taki niezależny układ, który w razie zawieszenia się procesora sam go po pewnym czasie(np. 250 milisekund) resetuje.

    Code:


      while(1)  /* Instrukcje wielokrotnego wykonania */
       {
    /*
    1. Tylko jeden klawisz może być aktywny (wciśnięcie kilku klawiszy na raz jest ignorowane)
    2. Podwójna funkcja klawiatury jako:
       a) regulator generatora PWM (regulacja stopnia świecenia wyświetlaczy LED)
       b) wybór segmentów wyświetlaczy LED do ustawienia czasu i zapisu do zegara
    */
    //---------------------------
    //  początek
    //  test klawisza
    //---------------------------
        klawiatura =0b00111011;
       if(bit_is_clear(PIND,PIND0)) { _delay_ms(30); klawiatura =klawiatura & 0b00111010; } //gora
        if(bit_is_clear(PIND,PIND1)) { _delay_ms(30); klawiatura =klawiatura & 0b00111001; } //lewy
       if(bit_is_clear(PIND,PIND3)) { _delay_ms(30); klawiatura =klawiatura & 0b00110011; } //srodek
       if(bit_is_clear(PIND,PIND4)) { _delay_ms(30); klawiatura =klawiatura & 0b00101011; } //prawy
       if(bit_is_clear(PIND,PIND5)) { _delay_ms(30); klawiatura =klawiatura & 0b00011011; } //dol
       if(klawiatura ==0b00111011) PORTC &=~(1<<1); // znacznik  -> znacznik zwolnienia klawisza



    Po naciśnięciu klawisza przez czas 30 milisekund program czeka na zakończenie drgań styków klawisza, nadaje mu specyficzny numer, oraz sprawdza czy klawisz naciśnięty zastał już zwolniony.

    Code:


    //---------------------------
    //  początek
    //  wybór klawisza
    //---------------------------
        if(bit_is_clear(PINC,PINC1)) // start klawiatury
         {
          switch (klawiatura)
         {
           // -*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
           case 0b00111010: if(bit_is_clear(PINC,PINC3)) { if(liczba_pwm <=240) wartoscPWM(liczba_pwm +=15); } // regulacja świecenia wyświetlaczy LED
                           if(bit_is_set(PINC,PINC3))
      /* góra */            { // DODAWANIE
                        PORTC |=(1<<2); // znacznik  -> dodac
                              PORTC |=(1<<0); // znacznik -> (zezwolenie) jedno naciśnięcie jedno wykonanie dodać/odjąć
                             } 
                            PORTC |=(1<<1); // znacznik  -> wcisnietego klawisza
                      break;
           // -*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
           case 0b00111001: if(bit_is_set(PINC,PINC3)) { if(segment >=2) segment -=1; } // segment lewy =1   
      /* lewy */            PORTC |=(1<<1); // znacznik  -> wcisnietego klawisza
                      break;
           // -*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
           case 0b00110011: if(bit_is_clear(PINC,PINC3)) //  ***ODCZYT CZASU Z ZEGARA***
                            {                            
     /* środek */             segment =2; // segment srodek =2                                         
                        PORTC |=(1<<3);  // znacznik  -> przelacanie - zapis zegara             
                       }
                      if(bit_is_set(PINC,PINC3)) //  ***ZAPIS CZASU DO ZEGARA***
                       {
                        GICR = 0b00000000;   // wyłaczamy przerwanie INT0 ->stop (INT0 = 0)
                      // ----------------------------------------------------------
                        if(wborDoZapisu.Bajt !=0)
                         {                      
                         TWI_zapis(zegar,0x00,0b10000000);                           //* 1. stop zliczania i wyzerowanie dzielnika
                         if(wborDoZapisu.Bit.bit1 ==1) TWI_zapis(zegar,0x04,taktZapisu[0]);   //* 2. wpis czasu do zegara godzin
                         if(wborDoZapisu.Bit.bit2 ==1) TWI_zapis(zegar,0x03,taktZapisu[1]);   //* 3. wpis czasu do zegara minut       
                         if(wborDoZapisu.Bit.bit3 ==1) TWI_zapis(zegar,0x02,taktZapisu[2]);   //* 4. wpis czasu do zegara sekund
                         TWI_zapis(zegar,0x01,0b00000000);                           //* 4a. zerowanie setnych i dziesiętnych sekundy
                         TWI_zapis(zegar,0x00,0b00000000);                           //* 5. start zliczania
                         wborDoZapisu.Bajt =0;                     
                         }
                      // ----------------------------------------------------------
                        PORTC &=~(1<<3); // znacznik  -> przelacanie - odczyt zegara
                              wborZnacznika.Bajt =0;
                           }
                      GICR = 0b01000000;   // wlaczamy przerwanie INT0 ->start (INT0 = 1)
                       PORTC |=(1<<1); // znacznik  -> wcisnietego klawisza
                       break;
           // -*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
           case 0b00101011: if(bit_is_set(PINC,PINC3)) { if(segment <=2) segment +=1; } // segment prawy =3                    
      /* prawy */           PORTC |=(1<<1); // znacznik  -> wcisnietego klawisza
      /* korekcja */      break;
           // -*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
           case 0b00011011: if(bit_is_clear(PINC,PINC3)) { if(liczba_pwm >=30) wartoscPWM(liczba_pwm -=15); } // regulacja świecenia wyświetlaczy LED
                           if(bit_is_set(PINC,PINC3))
      /* dół */            { // ODEJMOWANIE
                        PORTC &=~(1<<2); //znacznik  -> odjac
                              PORTC |=(1<<0); // znacznik -> (zezwolenie) jedno naciśnięcie jedno wykonanie dodać/odjąć
                             }
                            PORTC |=(1<<1); // znacznik  -> wcisnietego klawisza
                      break;
           // -*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
           default: /* klawisze poza wyborem */
                    break;
         } // END switch (klawiatura)
    //---------------------------
    //  koniec
    //  test klawisza
    //  wybór klawisza
    //---------------------------



    Tu jest cała logika (zawarta w instrukcji 'switch (klawiatura)') sterowania klawiaturą -> główne wykorzystanie znaczników pól bitowych, oraz wolnych bitów portu C (muszą być w najważniejszych miejscach pełniąc role szybkich przełączników).
    Klawisze są rozmieszczone w kształcie "krzyża" -> góra, środek, dół oraz -> lewy, środek, prawy.
    Klawisz środek zajmuje centralne miejsce i on służy do przełączania klawiatury:
    a) regulator generatora PWM (regulacja stopnia świecenia wyświetlaczy LED)
    b) wybór segmentów wyświetlaczy LED do ustawienia czasu i zapisu do czasu zegara
    Regulator generatora PWM to klawisze -> góra, dół
    Wybór segmentów wyświetlaczy LED to klawisze -> lewy, prawy
    Dodawanie lub odejmowanie cyfr wyświetlacza Led to klawisze -> góra, dół (po przełączeniu klawiszem środek).
    No i klawisz -> środek , który zajmuje centralne miejsce, a służy do przełączeń klawiatury z funkcji zapisu do zegara do funkcji normalnej pracy(ciągłego wyświetlania bieżącego czasu).

    Code:


    //---------------------------
    //  początek
    //  zapisu do zegara PCF8583
    //---------------------------     
        if(bit_is_set(PINC,PINC3))
         {
       // ***********************************
          if(segment ==1) // sekcja godz.
          {
       TWI_wswietlacz(dziesatki_minut,0xFF);   
            TWI_wswietlacz(minuty,0xFF);         
            TWI_wswietlacz(dziesatki_sekund,0xFF);
            TWI_wswietlacz(sekundy,0xFF);   
       if(wborZnacznika.Bit.bit1 ==0) // deaktywne ustawienia do zapisu min.(czas PCF8583)           
             {
              takt0 =czas[0]; // dziesiątki
              takt1 =czas[1]; // jednośći
           }
            if(bit_is_set(PINC,PINC0)) // zezwolenie -> jedno naciśnięcie jedno wykonanie dodać/odjąć
           {
            if(bit_is_set(PINC,PINC2)) { if(!((takt0 ==2) && (takt1 ==3))) plusCzas(&takt0,&takt1); }   //-----  dodawanie cyfr  -----
              if(bit_is_clear(PINC,PINC2)) minusCzas(&takt0,&takt1);                            //----- odejmowanie cyfr -----   
              wborDoZapisu.Bit.bit1 =1;
            taktZapisu[0] =(takt0<<4)|takt1;              
              PORTC &=~(1<<0);// blokada -> jedno naciśnięcie jedno wykonanie dodać/odjąć
            wborZnacznika.Bit.bit1 =1; // znacznik  -> aktywacji ustawienia do zapisu min.(czas ustawień)          
           }
            TWI_wswietlacz(dziesatki_godzin,tablica_7seg[takt0]);
            TWI_wswietlacz(godziny,tablica_7seg[takt1]);
           }
       // ***********************************
          if(segment ==2) // sekcja min.
          {
       TWI_wswietlacz(dziesatki_godzin,0xFF);   
            TWI_wswietlacz(godziny,0xFF);         
            TWI_wswietlacz(dziesatki_sekund,0xFF);   
            TWI_wswietlacz(sekundy,0xFF);         
           if(wborZnacznika.Bit.bit2 ==0) // deaktywne ustawienia do zapisu min.(czas PCF8583)           
             {
              takt2 =czas[2]; // dziesiątki
              takt3 =czas[3]; // jednośći
           }
            if(bit_is_set(PINC,PINC0)) // zezwolenie -> jedno naciśnięcie jedno wykonanie dodać/odjąć
           {
            if(bit_is_set(PINC,PINC2)) plusCzas(&takt2,&takt3);      //-----  dodawanie cyfr  -----
              if(bit_is_clear(PINC,PINC2)) minusCzas(&takt2,&takt3);   //----- odejmowanie cyfr -----   
              wborDoZapisu.Bit.bit2 =1;         
            taktZapisu[1] =(takt2<<4)|takt3;              
              PORTC &=~(1<<0);// blokada -> jedno naciśnięcie jedno wykonanie dodać/odjąć
            wborZnacznika.Bit.bit2 =1; // znacznik  -> aktywacji ustawienia do zapisu min.(czas ustawień)          
           }
            TWI_wswietlacz(dziesatki_minut,tablica_7seg[takt2]);
            TWI_wswietlacz(minuty,tablica_7seg[takt3]);
           }   
       // ***********************************
         if(segment ==3) // sekcja sek.
          {
       TWI_wswietlacz(dziesatki_godzin,0xFF);   
            TWI_wswietlacz(godziny,0xFF);         
            TWI_wswietlacz(dziesatki_minut,0xFF);   
            TWI_wswietlacz(minuty,0xFF);         
       if(wborZnacznika.Bit.bit3 ==0) // deaktywne ustawienia do zapisu min.(czas PCF8583)           
             {
              takt4 =czas[4]; // dziesiątki
              takt5 =czas[5]; // jednośći
           }
            if(bit_is_set(PINC,PINC0)) // zezwolenie -> jedno naciśnięcie jedno wykonanie dodać/odjąć
           {
            if(bit_is_set(PINC,PINC2)) plusCzas(&takt4,&takt5);   //-----  dodawanie cyfr  -----
              if(bit_is_clear(PINC,PINC2)) minusCzas(&takt4,&takt5);   //----- odejmowanie cyfr -----   
              wborDoZapisu.Bit.bit3 =1;         
            taktZapisu[2] =(takt4<<4)|takt5;              
              PORTC &=~(1<<0);// blokada -> jedno naciśnięcie jedno wykonanie dodać/odjąć
            wborZnacznika.Bit.bit3 =1; // znacznik  -> aktywacji ustawienia do zapisu min.(czas ustawień)          
           }
            TWI_wswietlacz(dziesatki_sekund,tablica_7seg[takt4]);
            TWI_wswietlacz(sekundy,tablica_7seg[takt5]);
           }            
       // ***********************************
         }
    //---------------------------
    //  koniec
    //  zapisu do zegara PCF8583      
    //---------------------------
        }// stop klawiatury



    Ustalanie cyfr na wyświetlaczach LED do zapisu w zegarze PCF8583.
    Najpierw wygaszone zostaną nieaktywne segmenty pozostawiając segmenty, które są ustawiane. Później zależnie od potrzeby albo odejmuje albo dodaje cyfry, oraz zapisuje wynik do tablicy 'taktZapisu'. Zwalnia i ustawia zależnie od potrzeby znacznika (flagi) i wyświetla wynik na wyświetlaczach LED.

    Code:



        wdt_reset();         // tu nalezy zresetować Watchdoga
       }
      wdt_disable();         // wylaczenie Watchdog
      return 0;
     }
    //-------------------------------------------------------------------------------------------------
    // ************************************************************************************************
    //-------------------------------------------------------------------------------------------------



    Tu resetujemy Watchdog'a jeżeli z jakiś powodów czas wykonywania programu się wydłuży ponad czas ustalony dla Watchdog'a wtedy Watchdog zadziała -> zresetuje procesor

    Code:



    // ************************************************************************************************
    //------------------------------------------------------------------------------------------------- 
    // ************************************************************************************************
    //   --- PRZERWANIE ---   //

    ISR(INT0_vect)   /* aktywne zbocza: opadajace i narastajace */
     {
    //-------------------------------------
      if(bit_is_clear(PINC,PINC3)) // zezwolenie na odczyt i wyswietlenie aktualnego czasy
       {
        if(bit_is_clear(PIND,PIND2))
        {
         // ------------------------------
          czasPCF8583 =TWI_odczyt(zegar,0x04);
          czas[0] =(czasPCF8583 & 0b00110000) >> 4;      // dziesatki_godzin         
          czas[1] =czasPCF8583 & 0b00001111;         // godziny
     
          czasPCF8583 =TWI_odczyt(zegar,0x03);
          czas[2] =czasPCF8583 >> 4;                // dziesatki_minut
          czas[3] =czasPCF8583 & 0b00001111;         // minuty

          czasPCF8583 =TWI_odczyt(zegar,0x02);
          czas[4] =czasPCF8583 >> 4;                // dziesatki_sekund
          czas[5] =czasPCF8583 & 0b00001111;         // sekundy

          if(czas[0] == 0x00) TWI_wswietlacz(dziesatki_godzin,0xFF);    // zero wygaszone     
           else TWI_wswietlacz(dziesatki_godzin,tablica_7seg[czas[0]]);
          TWI_wswietlacz(godziny,tablica_7seg[czas[1]] - kropka); // świeci migajacy dwukropek
          TWI_wswietlacz(dziesatki_minut,tablica_7seg[czas[2]]);
          TWI_wswietlacz(minuty,tablica_7seg[czas[3]] - kropka);  // swieci kropka
          TWI_wswietlacz(dziesatki_sekund,tablica_7seg[czas[4]]);
          TWI_wswietlacz(sekundy,tablica_7seg[czas[5]]);
         // ------------------------------
         } else TWI_wswietlacz(godziny,tablica_7seg[czas[1]]); // nie świeci migajacy dwukropek     
       }
    // ---------- test procesora ----------
      PORTB ^=(1<<0);  // takt -> 1Hz
    // ---------- test procesora ----------
     }
    // ************************************************************************************************
    //------------------------------------------------------------------------------------------------- 
    // ************************************************************************************************



    Całe przerwanie w,którym odbywa się odczyt czasu. Jest tu też umieszczony bit "test procesora" przy podłączeniu diody led do portu B0 (dioda led z portu przez opornik do plusa zasilania) powinna ona migać z częstotliwością 1Hz. Co oznacza prawidłową współpracę procesora Atmega8 i zegara PCF8583.

    Dalej już jest zestaw procedur i funkcji pracujących w tym programie.

    Code:


    //-------------------------------------------------------------------------------------------------
    //-------------------------------------------------------------------------------------------------
    //  *** FUNKCJE I PROCEDURY ***
    //-------------------------------------------------------------------------------------------------
    //-------------------------------------------------------------------------------------------------
    void TWI_inicjacja(void)
    {
      // Czestotliwosc TWI(i2c) = 100kHz (max 100kHz) -> atmega8 taktowana wewnetrznie 8MHz (RC=8MHz)
      // ------------------------------------------------
      /* TWSR = TWS7 TWS6 TWS5 TWS4 TWS3  -   TWPS1 TWPS0 */
      TWSR =0b00000000;    // Preskaler = 1  ->> TWPS1=0 TWPS0=0
      // ------------------------------------------------------
      /* TWBR = TWBR7 TWBR6 TWBR5 TWBR4 TWBR3 TWBR2 TWBR1 TWBR0 */
      TWBR =0b00100000;
      /* Rejestr odpowiedzialny za wybór współczynnika podziału dla generatora.
         Generator ten odpowiada za czestotliwosc która jest dzielona przez
         sygnał zegarowy SCL w trybie pracy Master.
         ->> TWBR musi byc wieksze od 10 dla stabilnej pracy TWI(i2c)   
     Czyli ((8MHz/100kHz)-16)/2=32  => TWBR=32 lub TWBR=0x20 lub TWBR=0b00100000 */ 
    }

    unsigned char TWI_odczyt(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);
    }

    void TWI_zapis(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);
    }

    void TWI_wswietlacz(unsigned char scalak,
                      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 =liczba;
      TWCR =(1 << TWINT) | (1 << TWEN);
      while(!(TWCR & (1 << TWINT)));
      TWCR =(1 << TWINT) | (1<<TWEN) | (1<<TWSTO);
    }

    //-------------------------------------------------------------------------------------------------
    //-------------------------------------------------------------------------------------------------
    void PWM_inicjacja(void)
    {   /* Tryb szybkie PWM(FastPWM) -> Generator 8-bitowy */
    /*  PWM na Timer1:
        Takt_Atmega8 = 8MHz, Preskaler_Atmega8 = 256, Generator 8-bitowy = 256
        Czestotliwosc_PWM = Takt_Atmega8/Preskaler_Atmega8/Generator 8-bitowy 
         czyli
        Czestotliwosc_generatoraPWM = 8000000Hz / 256 / 256    ==> okolo 122Hz */
      // ------------------------------------------------------------       
      /* TCCR1A = COM1A1 COM1A0 COM1B1 COM1B0 FOC1A FOC1B WGM11 WGM10 */
      TCCR1A = 0b11110001;
      /* Ustawia OC1A przy zrównaniu, kasuje OC1A przy TOP  -> COM1A1=1  COM1A0=1
         Kasuje OC1A przy zrównaniu,ustawia OC1A przy TOP  -> COM1A1=1  COM1A0=0
         Ustawia OC1B przy zrównaniu, kasuje OC1B przy TOP  -> COM1B1=1  COM1B0=1
         Kasuje OC1B przy zrównaniu,ustawia OC1B przy TOP  -> COM1B1=1  COM1B0=0
         Tryby PWM 8-bitowy  -> WGM11=0 WGM10=1 */
      // ----------------------------------------------------
      /* TCCR1B = ICNC1 ICES1 - WGM13 WGM12 CS12 CS11 CS10 */
      TCCR1B = 0b00001100;
      /*  Tryby PWM 8-bitowy  -> WGM13=0  WGM12=1
          Preskaler = 256  ->> CS12=1  CS11=0  CS10=0 */
    }


    void wartoscPWM(unsigned char wartosc)
    {
     /* niezależne sterowanie dwoma wyjściami generatora PWM */
      OCR1A = 255;
      OCR1B = wartosc;
      // zapis w pamieci RAM zegara PCF8583 liczby 'wartosc' pod adresem 0x10
      TWI_zapis(zegar,0x10,wartosc);    
    }

    //-------------------------------------------------------------------------------------------------
    //-------------------------------------------------------------------------------------------------
    void INT0_inicjacja(void)
    {
      // ----------------------------------------------
      /* MCUCR = SE SM2 SM1 SM0 ISC11 ISC10 ISC01 ISC00 */
      MCUCR = 0b00000001;    // ISC01=0  ISC00=1
      /* Dowolna zmiana stanu logicznego na INT0 wywola przerwanie typu ISR(INT0_vect) */
      // ------------------------------------
      /* GICUR = INT1 INT0 - - - - IVSEL IVCE */
      GICR = 0b01000000;   // wlaczamy przerwanie INT0
    }


    //-------------------------------------------------------------------------------------------------
    //-------------------------------------------------------------------------------------------------
    /* Przekazanie parametru przez wskazanie na zmienną (wskaźnik) umożliwia dokonania modyfikacji tej zmiennej wewnątrz funkcji.
      Czyli do funkcji wkładamy przez parametr jakąś wartość, tam zostaje ta wartość przerobiona
      i po zakończeniu funkcji funkcja oddaje zmienioną wartość przez ten sam parametr

     void funkcja(typ_zmiennej *wartosc1, typ_zmiennej *wartosc2)
      {
       typ_zmiennej zmienna_pomocnicza =0;
       zmienna_pomocnicza =*wartosc1;
       *wartosc1=*wartosc2;
       *wartosc2=zmienna_pomocnicza;
      } //  wywolanie funkcji np.->   funkcja(&zmienna1, &zmienna2);

     Uwaga! po wykonaniu funkcji " funkcja(&zmienna1, &zmienna2) "
            "zmienna1" i "zmienna2" beda zmodyfikowane przez tą funkcje
          ("zmienna1" i "zmienna2" mają inną wartość przed funkcją i inną wartość po wykonaniu tej funkcji)
    */

    void minusCzas(unsigned char *dziesiatki, unsigned char *jednosci)
     {
      signed char pomoc =*jednosci;
      if(!((*dziesiatki ==0) && (*jednosci ==0))) pomoc -=1;         
      if(pomoc ==-1)
       {
        pomoc =9;
        *dziesiatki -=1;
       }
      *jednosci =pomoc;
     } // wywolanie funkcji np.->   minusCzas(&wartosc1,&wartosc2);

    void plusCzas(unsigned char *dziesiatki, unsigned char *jednosci)
     {
      if(!((*dziesiatki ==5) && (*jednosci ==9))) *jednosci +=1;
      if(*jednosci ==10)
       {
        *jednosci =0;
        *dziesiatki +=1;
       }
     } // wywolanie funkcji np.->   plusCzas(&wartosc1,&wartosc2);

    /*
    //--------------------------------
     Kiedy wskażnik pokazuje już na konkretnie miejsce możemy odnieśćc się do tego obiektu na
     który on wskazuje, odczytać jego wartość, lub wpisać wartość pod wskazany adres.
     Podstawowa operacja na wskażniku jest wyłuskanie, czyli odwolanie się do obiektu
     wskazywanego przez wskażnik. Operacja ta nazywa się adresowaniem pośrednim.
     Operatorem adresowania pośredniego jest jednoargumentowy * zapisywana jako przedrostek.

     Zastosowana do wskażnika daje zawartość obiektu wskazanego przez ten wskażnik np.:

     unsigned char *wsk;            // tworzona zmienna jest zmienną wskaźnikową
     unsigned char inne_cos =0, cos =3;   // inne_cos i cos maja wartości dowolne dla zilustrowania przykładu
        wsk =&cos;                     // <-- Uwaga!!! wsk przechowuje adres zmiennej cos.
        inne_cos =*wsk;               // <-- inne_cos posiada wartość z pod adresu cos (czyli  inne_cos =3)
        *wsk =200;                     // <-- wstaw 200(wartość dowolna np.200) pod adres wskazywany przez zmienną wsk (czyli  cos =200)
    //--------------------------------
    */
    //-------------------------------------------------------------------------------------------------
    //-------------------------------------------------------------------------------------------------

    //-------------------------------------------------------------------------------------------------
    //      KONIEC      KONIEC      KONIEC      KONIEC      KONIEC      KONIEC      KONIEC      KONIEC
    //-------------------------------------------------------------------------------------------------



    Poniżej kilka plików (typu *.pdf) do poczytania - po polsku!
    Przyjemnej lektury.
    Piotr

    A oto zapakowany pliki zawierający plik c i plik hex (twiAtmega8_zegar.zip)

    6 1
  • #2 24 Lis 2009 11:17
    p_zag
    Poziom 13  

    Witam.
    Długo, bo długo nie zamieszałem schematów moich płytek, ale nikt oprócz jednego kolegi - damkar - mnie o ten zegar i schemat nie pytał.
    "Zegar" ten projektowałem w zasadzie bez sporządzania jakiegokolwiek schematu (dla mnie schematem był rozkład ścieżek na laminacie projektowanych przeze mnie płytek) - składałem z sobą kilka podstawowych schematów. Są to dwie płytki: płytka A z procesorem i płytka B z wyświetlaczami.
    UWAGA!!! wszystkie płytki są zaprojektowane tak jak wyglądają od strony miedzi, gotowe do wlutowania w nie elementów elektronicznych (ja maluje te ścieżki ręcznie - taka benedyktyńska praca) - jeżeli stosujesz metodę 'na żelazko' to musisz zrobić odbicie lustrzane.
    A więc przyjrzyjmy się płytkom widzianym od strony miedzi i elementom elektronicznym widzianym od spodu przylutowanym do tej płytki. Płytki te musiałem odpowiednio powiększyć w celu odpowiedniego zaprezentowania i dostosowania do tej prezentacji.
    [Atmega8][C] Atmega8 i TWI na przykładzie zegara LED [Atmega8][C] Atmega8 i TWI na przykładzie zegara LED
    Jest to widok elektronicznych elementów(kolor żółty), łączówek(kolor zielony), ścieżek(kolor czerwony).
    uwaga - na płytce z wyświetlaczami żółte kreski to oporniki (mój program do projektowania płytek nie posiada symboli oporników umieszczonych pionowo w płytce)
    [Atmega8][C] Atmega8 i TWI na przykładzie zegara LED [Atmega8][C] Atmega8 i TWI na przykładzie zegara LED
    Jest to widok łączówek(kolor zielony), ścieżek(kolor czerwony).
    Łączówki to kawałki drutu łączące ze sobą ścieżki.
    [Atmega8][C] Atmega8 i TWI na przykładzie zegara LED [Atmega8][C] Atmega8 i TWI na przykładzie zegara LED
    Jest to widok samych ścieżek(kolor czerwony).

    A oto schemat pierwszej płytki, jaki już po czasie sporządziłem
    [Atmega8][C] Atmega8 i TWI na przykładzie zegara LED [Atmega8][C] Atmega8 i TWI na przykładzie zegara LED
    oraz fragment z tranzystorem IRF9520 - wyjście generatora PWM
    [Atmega8][C] Atmega8 i TWI na przykładzie zegara LED

    Projektując płytki ważne jest odpowiednie rozmieszczenie elementów. Kiedyś zaprojektowałem płytkę pod układ pcf8583 no i on mi się nie wzbudzał (milczał jak zaklęty). Zaprojektowałem więc nową inną płytkę i włożyłem te same elementy z płytki nie działającej, no i układ pcf8583 ożył(bardzo stabilnie migał diodką led na wyprowadzeniu nr 7 w takt 1Hz). Bardzo ważny jest rozkład ścieżek szczególnie przy nóżkach 1 i 2 czyli kwarcu, oraz kondensator 100nF bezpośrednio na zasilaniu scalaka. Co do zegara PCF8583 to kondensator (nóżka 1 i 8 PCF8583) decyduje o spieszeniu lub spóźnianiu się zegara. I tak bez tego kondensatora mój zegar spieszył się 18 sekund na dobę. Wlutowałem więc kondensator 10pF - spóźniał mi się 2 sekundy na dobę (czyli mój cel był pomiędzy brakiem kondensatora, a kondensatorem 10pF). I tak metodą dobrań kondensatorów doszedłem do 8,2pF z odchyłką 1 do 2 sekund na tydzień!, co mnie zupełnie satysfakcjonuje. A kwarc to zwyczajnym standardowy kwarc zegarkowy.
    Tranzystor IRF9520 wybrałem taki bo ma znikomy opór(rDS(ON)= 0.600om), oraz wystarczający prąd drenu (Id około 1A przy VGS = -5V). Max prąd wszystkich jednocześnie świecących elementów sterowanych z tego tranzystora nie przekracza 0,5A. Miedzy nóżką 15, 16 mikroprocesora a bramka tranzystora IRF9520 jest opornik 100kom do masy i mikro przełącznik suwakowy.
    Obecnie mój procesor jest taktowany z zewnętrznego kwarcu 8MHz plus dwa kondensatory 22pF. Ale równie dobrze procesor może być taktowany wewnętrznie (RC=8Mhz).
    Co do wyświetlaczy LED(SA10-21GWA) a ściśle sterujących nimi układami PCF8574AP to trzeba pamiętać o nie przekraczaniu maksymalnej mocy PCF8574AP równej 0,4W i maksymalnego prądu zasilającego ten układ równego 0,1A. Dlatego rezystory: do wyświetlaczy to 98 Om, do dwukropka to 98 Om, do kropli to 300 Om.

    Adres strony z programem, w którym projektowałem płytki to
    http://www.expresspcb.com/ExpressPCBHtm/Download.htm

    0
TME logo Szukaj w ofercie
Zamknij 
Wyszukaj w ofercie 200 tys. produktów TME
TME Logo