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

8051 funkcja DELAY

kolgreen 29 Jan 2008 20:31 4278 8
Computer Controls
  • #1
    kolgreen
    Level 16  
    Witam serdecznie!
    Uczę sie programować uC w C.
    Mam napisana funkcje Delay:
    Code:

    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 ;)
  • Computer Controls
  • #2
    al777
    Level 26  
    kolgreen wrote:
    ... 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 wrote:

    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.
  • Computer Controls
  • #3
    kolgreen
    Level 16  
    al777 zgadzam rozumiem wszystko i sie zgadzam. Wszystko sie wydaje zrozumiałe ale w asm.

    Code:

    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:

    Code:

    #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....
    [/code]
  • #4
    markosik20
    Level 33  
    kolgreen wrote:
    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
    [GD]-Zami
    Level 13  
    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 ;)
  • #7
    Michal19881
    User under supervision
    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
    jarekgol
    Level 29  
    Quote:

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

    obawiam się że to będzie chodzić w nieskończoność.
  • #9
    Michal19881
    User under supervision
    jarekgol wrote:
    Quote:

    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:

    Code:
        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.