EdW 10/2005 - kurs C - generowanie opoznien

Jeżeli masz jakieś uwagi dotyczące bieżącego lub archiwalnych numerów "Elektroniki dla wszystkich" lub zauważyłeś błędy czy nieścisłości to jest to odpowiednie miejsce aby o tym napisać.
ODPOWIEDZ
Przemek
-
Posty: 2
Rejestracja: 24 mar 2006, 8:37
Lokalizacja: Warszawa

EdW 10/2005 - kurs C - generowanie opoznien

Post autor: Przemek » 24 mar 2006, 8:59

Witam wszystkich

Jesterm tu zupełnie nowy. Przed kilkoma miesiącami zacząłem interesować się mikrokontrolerami (AVR). Początkowo uczyłem się programować w BASCOMie, a ostatnio "przerabiam" kurs w C z EdW.
W trakcie pracy z częścią 5 kursu z numeru 10/2005 EdW, zdziwiłem się, że czas opóźnienia generowany przez funkcję delay100us8(t) jest znacznie większy od oczekiwanego.

Kod: Zaznacz cały

#define delayus8(t)\
  {asm volatile( \
      "delayus8_loop%=: \n\t"\
      "nop \n\t"\
      "dec %[ticks] \n\t"\
      "brne delayus8_loop%= \n\t"\
    : :[ticks]"r"(t) );}
  // DEC - 1 cykl, BRNE 2 cykle, + 1xnop. Zegar 4MHz

void delay100us8(uint8_t t)
{
   while(t>0)
   {
      delayus8(100); 
      --t; 
   }
}
Sprawdziłem kod wygenerowany przez kompilator i okazało się, że pętla zewnętrzna (napisana w C) jest wykonywana zadaną ilość razy, ale pętla we wstawce asemblerowej tylko za pierwszym razem ma ustawiany licznik powtórzeń pętli.
W każdym następnym obiegu pętli zewnętrznej, pętla wewnętrzna jest wykonywana nie 100 razy lecz 256, gdyż w chwili startu pętli wewnętrznej rejestr r18 jest zawsze wyzerowany po poprzednim zakończeniu pętli.

Kod: Zaznacz cały

18               	.global	delay100us8
20               	delay100us8:
21               	.LFB2:
22               	.LM1:
23               	/* prologue: frame size=0 */
24               	/* prologue end (size=0) */
25               	.LM2:
26 0000 8823      		tst r24
27 0002 39F0      		breq .L6
28 0004 24E6      		ldi r18,lo8(100)
29 0006 30E0      		ldi r19,hi8(100)
30               	.L4:
31               	.LM3:
32               	/* #APP */
33               		delayus8_loop12: 
34 0008 0000      		nop 
35 000a 2A95      		dec r18 
36 000c E9F7      		brne delayus8_loop12 
37               		
38               	.LM4:
39               	/* #NOAPP */
40 000e 8150      		subi r24,lo8(-(-1))
41 0010 D9F7      		brne .L4
42               	.L6:
43 0012 0895      		ret
Nie znalazłem sposobu, by zmusić kompilator do poprawnego ustawiania licznika pętli, więc napisałem inną procedurę (zegar 8MHz):

Kod: Zaznacz cały

#define delay100us8_new()\
   {asm volatile(\
      "push r18 \n\t"\
      "ldi r18,199 \n\t"\
      "delay100us8_loop%=: \n\t"\
      "nop \n\t"\
      "dec r18 \n\t"\
      "brne delay100us8_loop%= \n\t"\
      "pop r18 \n\t"\
   ::);}
#endif

void delay100us8(uint8_t t)
{
   while(t>0)
   {
      delay100us8_new();
      --t;
   }
}
Oczywiście, mimo, że sama wstawka odmierza dokładnie 100us, to czas wykonania funkcji delay100us8 jest już obarczony błędem - dla t < 5 czas jest o ok. 2% większy od zakładanego, a dla t>10 błąd jest mniejszy od 1%.

Na marginesie: wielkie dzięki dla autora kursu - dobra robota :D

Pozdrawiam
Przemek

Awatar użytkownika
Koppel
Użytkownik
Posty: 500
Rejestracja: 24 lip 2005, 18:05
Lokalizacja: Gliwice

Post autor: Koppel » 24 mar 2006, 18:50

Ciekawa sprawa. Procedur tego typu użyłem w dość rozbudowanym programie, jednak zawsze w takich miejscach gdzie chodziło mi o odmierzenie przynajmniej zadanego czasu. Dziękuję za spostrzeżenie. Może pomóc zadeklarowanie delay100us8 jako funkcji static inline zamiast makra. Sprawdzę to w najbliższym czasie.

Cieszę się, że kurs spełnia swoje zadanie :)

PS.: Można też wykorzystać wbudowane w biblioteki delay funkcje _delay_us oraz _delay_ms. Chcę je właśnie przedstawić, jednak już teraz można znaleźć więcej informacji o nich w dokumentacji.

____________________________
Edit: 03-06-2007
Ostatnio pracowałem nad problemem i wiem już, że makro delayus8 nie jest napisane prawidłowo. Kompilator nie ma informacji, że rejestr używany do odmierzanie czasu jest tracony. Prawidłowo powinno być

Kod: Zaznacz cały

: :[ticks]"+r"(t) );} 
Informacja na ten temat pojawi się jeszcze w zakończeniu kursu.

ODPOWIEDZ