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

8051 - Jak napisać funkcję DELAY z użyciem Timera?

kolgreen 29 Sty 2008 20:31 4446 8
REKLAMA
  • #1 4749652
    kolgreen
    Poziom 16  
    Posty: 269
    Pomógł: 11
    Ocena: 240
    Witam serdecznie!
    Uczę sie programować uC w C.
    Mam napisana funkcje Delay:
    
    void Delay(WORD czas)
               {
    		   BYTE timerS;
               timerS = czas;
               for (timerS=0;timerS<czas;timerS++) {}
    		   }
    

    Ma ona robic opóźnienie. Jednak zależy ono
    od tego jakim kwarcem taktujemy uC...
    Pytanie ile taktów potrzeba dla wykonania się jednej pętli?

    I najważniejsze pytanie...
    Szukam przykładu, jak będzie wyglądała taka funkcja,
    ale dla obsługi z Timera?!

    Z góry dziękuję za odpowiedzi ;)
  • REKLAMA
  • #2 4751371
    al777
    Poziom 27  
    Posty: 646
    Pomógł: 105
    Ocena: 83
    kolgreen napisał:
    ... ile taktów potrzeba dla wykonania się jednej pętli?

    To zależy, jak twój kompilator "przetłumaczy" tę funkcję na kod maszynowy procesora (jakich instrukcji użyje). Od razu uprzedzę, że czas wykonania takiej pętli może być inny dla różnych ustawionych stopni optymalizowania kodu (w opcjach kompilatora).
    Ja poszukuję takich informacji w następujący sposób :
    - zmuszam kompilator żeby wygenerował mi plik pośredni w asemblerze (większość z nich ma taką opcję, chociaż różnie się ona nazywa)
    - odnajduję fragment asemblerowy odpowiadający mojej funkcji
    - liczę czas procedury znając czasy wykonania poszczególnych instrukcji asemblera
    Większość instrukcji asemblerowych dla '51 wykonuje się w jednym lub dwu cyklach maszynowych (wyjątkiem są operacje mnożenia i dzielenia) - po szczegóły trzeba zajrzeć do noty aplikacyjnej danego procesora.
    W "klasycznych" procesorach '51 jeden cykl maszynowy to 12 taktów zegara.
    Stąd mamy w miarę dokładnie czas procedury.

    Można to samo trochę łatwiej - np. kompilator KEIL po włączeniu debuggera wyświetla licznik cykli maszynowych, wystarczy sobie zanotować jego stan przed wejściem do funkcji i po wyjściu z niej - i mamy wszystko. To samo można uzyskać ładując plik skompilowany do jakiegoś programowego symulatora procesorów rodziny '51.
    kolgreen napisał:

    Szukam przykładu, jak będzie wyglądała taka funkcja,
    ale dla obsługi z Timera?!

    Zobacz dowolny przykład procedury przerwania obsługującej timer. W tym przypadku oczywiście metoda działania się zmienia. W miejscu gdzie chcesz mieć opóźnienie (w programie) ustawiasz sobie licznik (dowolna zmienna - niestety musi być globalna) i uruchamiasz timer, a następnie w pętli czekasz aż licznik się wyzeruje. W procedurze obsługi timera dekrementujesz licznik, a gdy dojdzie do zera - wyłączasz timer. Oczywiście to tylko jedna z metod - zależy co będziesz chciał uzyskać.

    Mam nadzieję że pomogłem.
  • REKLAMA
  • #3 4753380
    kolgreen
    Poziom 16  
    Posty: 269
    Pomógł: 11
    Ocena: 240
    al777 zgadzam rozumiem wszystko i sie zgadzam. Wszystko sie wydaje zrozumiałe ale w asm.

    
    1: /*****************************************************************
    2: Timer0 powoduje mruganie diody LED dokładnie co 1sek.
    3: Program powoduje mruganie diody LED w ten sposób, Ŝe dioda LED świeci
    4: przez 1 sek i jest zgaszona przez 1 sekundę.
    5: Źródłem przerwań jest układ Timer0. Powoduje on przerwanie co
    6: 0.005 sek. Aby odliczyć czas 1 sek podprogram musi być wywołany
    7: 200 razy. Podprogram ten takŜe generuje krótkie dźwięki
    8: wydawane przez Buzzer z wbudowanym generatorem.
    9: *****************************************************************/
    10:
    11: #include "51xD2.h" // zbiór definiujący rejestry procesora
    12: #define Buzzer P3_5 // definicja adresu bitu Buzzer
    13: #define LED P1_0 // dodatkowy zespół diod
    14: #define TH0RELOAD 0xd8 // 0xd8f0 = -10000 dla Fosc=24MHz
    15: #define TL0RELOAD 0xf0 // ...
    16: #define SECOND 200 //1sek to 200 przerwań od Timer0
    17:
    18: //----------------------------------------------------------------
    19: void main(void) {
    20: TH0 = TH0RELOAD;
    21: TL0 = TL0RELOAD;
    22: TMOD = TMOD | 0x01; // tryb dla Timer0: mode1
    23: TR0 = 1; // start Timer0
    24: ET0 = 1; // zgoda na przerwania od Timer0
    25: EA = 1; // zezwolenie na przerwania
    26:
    27: while(1) {}; // program główny nie robi nic
    28: }
    29: //podprogram przerwania ------------------------------------------
    30: void T0_int(void) interrupt 1 using 1 {
    31: static BYTE count=0;
    32: TH0 = TH0RELOAD; // reload Timer0 registers
    33: TL0 = TL0RELOAD;
    34: Buzzer=1; // Buzer sygnał o długości 1/200sek
    35: if (++count == SECOND) { // odliczanie liczby przerwań do 200
    36: Buzzer=0; // uaktywnij Buzzer
    37: count = 0; // zeruj licznik odliczający czas jednej sek
    38: LED=!LED; // mruganie diody LED
    39: }
    40: }
    


    Taki kod znalazlem...
    tylko jakim cudem T0_int() wywoluje się skoro w main() nie ma nic o niej..

    czy jesli zrobię tak:

    
    #define Sbit(x, y, z)       sbit x = y^z
    #define Sfr(x, y)	    sfr x = y
    
    #define TH0RELOAD 0xfc // 0xfc18 = -1000 dla Fosc=12MHz
    #define TL0RELOAD 0x18 
    #define SECOND 1000 // 1 sek wymaga 1000 przerwan od Timer0
    
    typedef unsigned int  WORD;
    typedef unsigned char BYTE;
    
    //-------funkcje czasu---------------
    
    Sfr (TL0 , 0x8A); 
    Sbit (ET0  , 0xA8, 1);
    Sfr (TH0 , 0x8C);
    Sfr (TMOD , 0x89); 
    Sbit (TR0 , 0x88, 4);
    Sbit (EA   , 0xA8, 7);
    
    void timer0_init(void) 
    {
    
    TH0 = TH0RELOAD; // zaladowanie czasu odliczania
    TL0 = TL0RELOAD;
    TMOD = TMOD | 0x01; // tryb 1 ukladu Timer0
    TR0 = 1; // Timer0 start
    ET0 = 1; // odblokowanie przerwan od Timer0
    }
    
    void Delay(WORD czas)
               {
    		   BYTE i;
               ET0 = 0;
               ET0 = 1;
    		   for (i=0; i<czas; i++) {}
    
    		   }
    
    //------glowna-----------------
    void main (void)
    {
    timer0_init(); // przygotowanie ukladu Timer0
    
    while (1)
          {
    
    
    	  cos tam.....
    	  Delay(wartosc); 
              cos tam.....
    }
    


    w funkcji main() wykonuje operacje i daje opóźnienie o długości "wartość"
    Niby się to kompiluje.... ale....
  • REKLAMA
  • #4 4753590
    markosik20
    Poziom 33  
    Posty: 2261
    Pomógł: 208
    Ocena: 147
    kolgreen napisał:
    Wszystko sie wydaje zrozumiałe ale w asm.


    A co to za różnica? :wink:.
    Zasada jest tak:
    Jeżeli potrzebujesz zatrzymać program w jakimś miejscu na określony czas to w sekcji main konfigurujesz timer do pracy a później w funkcji Delay(); ustawisz odpowiednio licznik, uruchamiasz timer i czekasz na przepełnienie (sprawdzasz flagę). Jeżeli czas będzie za mały to wykonujesz to samo określoną ilośc razy.

    Jeżeli chcesz tylko odmierzyć jakiś czas a musisz kod wykonywac dalej to odpalasz timer w funkcji przerwania, w obsłudze przerwania ustawiasz flagę że czas minął i w programie odpowiednio reagujesz. I tutaj również jeżeli pojedyncze przerwanie jest za krótkie to trzeba obliczyć ile przerwań jest potrzebne i odpowiednio za każdym przerwaniem zmniejszać zmienną z ilością tych przerwań.

    Liczenie ilości cykli jakie kompilator "stworzy"może być zgubne bo nie wiadomo jak to będzie wyglądać póżniej przy coraz wiekszym kodzie.
    No chyba że w petli for opóźnienia zrobisz wstawkę ASM i kompilator już jej nie ominie.

    Przykładów opóźnień jest tyle na elce że w prawie co piątym poście się pojawia :wink:.
  • #5 4764090
    [GD]-Zami
    Poziom 14  
    Posty: 62
    Pomógł: 4
    Ocena: 1
    plik ASM:

    public delay

    DELAY: MOV 32H,#09H ;Odczekaj 0.6 sekundy
    D2: MOV 34H,#0FFH
    D3: MOV 36H,#0FFH
    DJNZ 36H,$
    DJNZ 34H, D3
    DJNZ 32H, D2
    RET

    w pliku def.h extern void delay(void)

    w pliku głównym tylko #include <def.h>

    Przykład dla 12 MHz kwarc + AT89c51Ed2 sprawdzony doświadczalnie :D o ile sie gdzie nie pomyliłem ;)
  • REKLAMA
  • #6 4764587
    jarekgol
    Poziom 40  
    Posty: 5156
    Pomógł: 644
    Ocena: 1142
    C na 51 - profanacja ;)
  • #7 4801540
    Michal19881
    Użytkownik obserwowany
    Posty: 1015
    Pomógł: 57
    Ocena: 62
    Na moje to będzie tak:

    CZEKAJ1:
    MOV R0,#255
    CZEKAJ2:
    MOV R1#255
    CZEKAJ3:
    MOV R2,#255
    CZEKANIE:
    DJNZ R2,CZEKAJ3
    DJNZ R1,CZEKAJ2
    DJNZ R0,CZEKAJ1
    RET

    Procedura na opóźnienie (delay), zamiast 255 można wpisać cokolwiek innego byleby nie przekroczyć zakresu rejestru.
  • #8 4813466
    jarekgol
    Poziom 40  
    Posty: 5156
    Pomógł: 644
    Ocena: 1142
    Cytat:

    CZEKAJ3:
    MOV R2,#255
    CZEKANIE:
    DJNZ R2,CZEKAJ3

    obawiam się że to będzie chodzić w nieskończoność.
  • #9 4813626
    Michal19881
    Użytkownik obserwowany
    Posty: 1015
    Pomógł: 57
    Ocena: 62
    jarekgol napisał:
    Cytat:

    CZEKAJ3:
    MOV R2,#255
    CZEKANIE:
    DJNZ R2,CZEKAJ3

    obawiam się że to będzie chodzić w nieskończoność.


    Rzeczywiście, wrzuciłem to do jagody i była nieskończona pętla, jednak trochę nad tym posiedziałem i wyszło coś takiego:

    	 LJMP START
    	 ORG 100H
    START:
    	 ...
    	 LCALL CZEKAJ
    	 ...
    	 LJMP START
    
    CZEKAJ:
    	 MOV R0,#255
    	 MOV R1,#255
    	 MOV R2,#255
    ODLICZAJ:
    	 DJNZ R2,ODLICZAJ
    ODLICZAJ1:
    	 DJNZ R1,ODLICZAJ1
    ODLICZAJ2:
    	 DJNZ R0,ODLICZAJ2
    	 RET


    W symulacji w jagodzie sprawuje sie świetnie.
    Pozdrawiam.

Podsumowanie tematu

✨ Dyskusja dotyczy implementacji funkcji opóźniającej DELAY dla mikrokontrolera 8051 z wykorzystaniem Timera zamiast pętli programowej. Wskazano, że czas wykonania pętli zależy od częstotliwości kwarcu i optymalizacji kompilatora, a dokładne obliczenie wymaga analizy kodu asemblerowego generowanego przez kompilator. Klasyczny 8051 wykonuje jeden cykl maszynowy w 12 taktów zegara. Zaproponowano podejście polegające na konfiguracji Timera do generowania przerwań co określony czas (np. 0,005 s), a następnie zliczaniu przerwań do uzyskania żądanego opóźnienia (np. 1 s). Przykłady kodu w asemblerze pokazują implementację funkcji delay z wykorzystaniem rejestrów i instrukcji DJNZ, dostosowaną do kwarcu 12 MHz i mikrokontrolera AT89C51ED2. Podkreślono, że stosowanie Timera jest bardziej precyzyjne niż pętle programowe, a w C można integrować kod asemblerowy dla stabilności opóźnień. Wskazano również, że w trybie przerwań Timer może sygnalizować upłynięcie czasu, co pozwala na dalsze wykonywanie kodu bez blokowania.
Podsumowanie wygenerowane przez AI na podstawie treści dyskusji.
REKLAMA