Forum: Mikrocontroller und Digitale Elektronik Timer tickt durcheinander (AVR-C)


von Hegy (Gast)


Lesenswert?

Hallo Club,

ich habe ein Problem mit einen 16-Bit Timer beim ATmega48 mit 4Mhz 
Quarz. Alle 2 bis 3 Sek. hat der Timer einen "Schluckauf", sodaß eine 
normalerweise gleichmäßig blinkende LED unregelmäßig blinkt (Periode zu 
kurz). Auch eine Periodenmessung bestätigt das.

Ersma der Code:
1
// Variablendefinition, Bits und Flags
2
union Bitfield
3
{
4
  unsigned int allbits;
5
  struct
6
  {
7
    unsigned char sleep:1;                 // 1 = sleep()-Fkt. aktiv
8
    [weitere 15 Variablen, Typ char]
9
  };
10
};
11
12
static volatile union Bitfield flag;
13
14
static volatile unsigned int waittime;
15
16
17
// 16-Bit Timer Compare Match mit quasi Autoreload, wird alle 0.5 ms aufgerufen
18
ISR(SIG_OUTPUT_COMPARE1A)
19
{
20
  if(flag.sleep) // wird in sleep()-Fkt. (rück)gesetzt
21
    waittime++;
22
}
23
24
25
// 16-Bit Timer1, 0.5 ms Wartezeit
26
void sleep(unsigned int wait)
27
{  
28
  waittime = 0;
29
  flag.sleep = 1;
30
  
31
  do
32
  {
33
    asm volatile("NOP");
34
  }while(waittime < wait);
35
36
  flag.sleep = 0;
37
  //TCNT1 = 0;
38
}
39
40
41
int main(void)
42
{
43
  // Initialisierung des 16-Bit Timers, Interrupt alle 0,5 ms bei 4MHz Quarz
44
  TCNT1 = 0;  // Timer Counter Register initialisieren
45
  TCCR1B = (1<<WGM12) | (1<<CS10);  // Prescaler = 1, CTC Modus
46
  OCR1A  = 1999;    // Compare Match Register, gemessene Zeit: 0,5ms @4MHz
47
  TIFR1  = 1<<ICF1;
48
  TIMSK1 = 1<<OCIE1A; // Interruptfreigabe bei Compare Match
49
50
  flag.allbits = 0;
51
  
52
  wdt_disable();
53
54
  // zur Vollständigkeit halber
55
  UART_init(UART_BAUD_SELECT(4800, F_CPU));
56
  TWI_SlaveInit(SLAVE_ADR);
57
58
  sei();
59
  
60
  while(1)
61
  {
62
    TWI_Start();
63
    
64
    UCSR0B = 0; // disable UART
65
    UCSR0C = 0;
66
    DDRD = 0xFF;
67
    PORTD &= 0xFD;
68
    PORTC = 0xF7;
69
    while(1)
70
    {
71
      sleep(400);
72
      PORTD |= 0x02; // PORTD1 auf 1
73
      PORTC = 0xFF;
74
      sleep(400);
75
      PORTD &= 0xFD; // PORTD1 für 200ms auf 0
76
      PORTC = 0xF7;
77
    }
78
  }
79
}

Diese Proggi sollte nichts weiters machen als eine LED an PORTC blinken 
zu lassen, gleichzeitig wird auch PORTD1 geändert, an dem ich gemessen 
habe.

Was mir bei der Fehlersuche aufgefallen ist, daß die Periodendauer sich 
sehr stark ändert, wenn ich OCR1A um 1 ändere. Hier mal die Meßwerte.
Deutlich zu sehen an den OCR1A-Werten von 1998 bis 2000, abweichende 
Zeilen habe ich mal mit nem '?' markiert.
1
/*
2
           ist      soll
3
 OCR1A |  t(ms)  |  t(ms)
4
-------+---------+-------
5
 1000    200,235    200
6
 1400    229,796 ?  280
7
 1500    300,228    300
8
 1998    327,861 ?  399
9
 1999    400,023    400
10
 2000    238,189 ?  400
11
 2999    492,015 ?  600
12
 3000    600,211    600
13
*/

Woher kommt das?
Ich zum messen in den Code nur den OCR1A-Wert geändert und gemessen. 
Irgendwas ist da mit der sleep()-Geschichte in der inneren 
while(1)-Schleife faul, meine ich, aber finde nicht raus, was es ist.

Wenn ich in der sleep()-Fkt. die auskommentierte Zeile TCNT0=0 
aktiviere, ist der "Schluckauf" weg, aber in die Tabelle oben ändert 
sich nichts an den Meßwerten.

Ratlos
 Hegy

von antworter (Gast)


Lesenswert?

ich würde testweise JEDEN Zugriff auf waittime mit cli() sei() 
einschliessen (nur im INT nicht)



und nebenbei

  do
  {
    asm volatile("NOP");
  }while(waittime < wait);

verkürzen zu

  while(waittime < wait);

von antworter (Gast)


Lesenswert?

ach ja.. die Schleife wird natürlich zu

while(1)
{
  cli();
  if (waittime >= wait) break;
  sei();
}
sei(); // nachdem Schleife durch "break" verlassen

von Hegy (Gast)


Lesenswert?

supi, so gehtz!

Nur warum müssen unbedingt alle Interrupts gesperrt werden bei diesem 
Vergleich? waittime wird 'nur' alle 0,5ms hochgezählt, der Vergleich in 
der do...while-Schleife ist wesentlich schneller und selbst wenn während 
des Vergleichens waittime wiederrum eins hochgezählt werden sollte, ist 
das Limit erreicht und die do...while-Schleife wird verlassen, das Bit 
flag.sleep wird auf 0 gesetzt und damit wird waittime in der ISR nicht 
mehr hochgezählt.

Wenn ich in der Schleife das cli() auskommentiere und OCR1A mit 1998 
initialisiere erhalte ich eine Periodendauer von 327ms (soll 399ms), bei 
aktiviertem cli() sind's 399,8ms, was rein rechnerisch auch past.

Nur den Sinn von cli() an dieser Stelle verstehe ich nicht.

von antworter (Gast)


Lesenswert?

ich sag es mal kurz:

nichtsequenzieller/überschneidender Zugriff auf eine 16Bit-Variable auf 
einer 8Bit-Architektur

(die lange Version mußt Du Dir ergrübeln)

von Hegy (Gast)


Lesenswert?

Nee du, ergrübel mir nix, ist schon klar, auf einem 8-Bitter mit 16 Bit 
rumzukacheln ist schon etwas Hallas (=Aufwand), schlimmer wirds bei 
Fließkommazahlen. Wer es mal in plain Assembler gemacht hat.....

Oder steckt da noch mehr hinter?


Mal die Assembler Listings zum Vergleich dazu.
1
    neue Version           Buggy-Version
2
3
    movw    r18, r24       movw    r18, r24
4
    sts     0x0113, r1     sts     0x0113, r1
5
    sts     0x0112, r1     sts     0x0112, r1
6
    lds     r24, 0x0102    lds     r24, 0x0102 
7
    ori     r24, 0x02      ori     r24, 0x02
8
    sts     0x0102, r24    sts     0x0102, r24
9
    cli                    nop
10
    lds     r24, 0x0112    lds     r24, 0x0112
11
    lds     r25, 0x0113    lds     r25, 0x0113
12
    cp      r24, r18       cp      r24, r18
13
    cpc     r25, r19       cpc     r25, r19
14
    brcc    .+4            brcs    .-16
15
    sei                    lds     r24, 0x0102
16
    rjmp    .-20           andi    r24, 0xFD
17
    sei                    sts     0x0102, r24
18
    lds     r24, 0x0102    sts     0x0085, r1
19
    andi    r24, 0xFD      sts     0x0084, r1
20
    sts     0x0102, r24    ret
21
    ret
22
23
    56 Bytes lang          54 Bytes lang
Eigentlich kein Riesenunterschied, aber der Unterschied von 
alle-0,5ms-Variable-hochzählen und der Ausführungszeit von 54/56 Bytes 
Assemblercode ohne zeitaufwendige Befehle (z.B. div, mul beim 8086) ist 
groß und immernoch ist mir nicht klar, warum genau hier der Hund 
begraben liegt.

von antworter (Gast)


Lesenswert?

"Nee du, ergrübel mir nix, ist schon klar.."

"...immernoch ist mir nicht klar, warum genau hier der Hund begraben 
liegt"


ohne worte

von Hegy (Gast)


Lesenswert?

Hättest du den ersten Satz vollständig hingeschrieben, dann würde man 
sehen, daß das eine Unterthema nichts mit dem anderen Unterthema zu tun 
hat. Zuerst ging es um 16-Bit Operationen auf 8-Bit Prozessoren und 
unten dann darum, warum eine im us Bereich dauerne Operation 
(sleep-Funktion einmalig durchlaufen) durch einen wesentlich längeren 
Vorgang (hochzählen einer Variable im 0,5ms Takt) derart behindert wird, 
daß es hier zu Fehlfunktionen kommen kann.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.