Forum: Mikrocontroller und Digitale Elektronik delay() mit interrupt funktioniert einfach nicht


von M. M. (blackcow)


Lesenswert?

Hallo,

ich will meinen Atmega 328 programmieren. Nur leider funktioniert meine 
delay() Funktion nicht. Ich weiß nach 3 Stunden einfach nicht mehr 
weiter und denke schön langsam ich bin zu blöd zum Programmieren. Wäre 
jemand so nett, sich meinen Code mal anzusehen? Der Fehler müsste doch 
in dem delay() drin sein?! Ich poste trotzdem mal den ganzen Code. 
PORTD,(1<<RESERVE) ist mein Debug-Pin, an dem ich mit dem Oszi hänge.
1
///preprocessing
2
  #include <avr/io.h>
3
  #include <avr/interrupt.h>
4
5
///define port pins
6
  //PortB
7
  #define LCD_CS1 PIN0
8
  #define LCD_CS2 PIN1
9
  #define LCD_WRITE PIN2
10
  #define SIPO_STR PIN3
11
  #define SIPO_CLK PIN4
12
  #define PISO_CLK PIN4
13
  #define SIPO_DS PIN5
14
  #define PISO_Q7 PIN5
15
  #define XTAL1 PIN6
16
  #define XTAL2 PIN7
17
  //PortC
18
  #define BatNotLoad PIN0
19
  #define Uline PIN1
20
  #define LED_3 PIN2
21
  #define LED_2 PIN3
22
  #define ResADC PIN4
23
  #define UBat PIN5
24
  //PC6 only ISP! >>reserve
25
  //PortD
26
  #define SIM_TxD PIN0 //avr input
27
  #define SIM_RxD PIN1 //avr output
28
  #define RESERVE PIN2
29
  #define SIM_ON PIN3
30
  #define SIM_Status PIN4
31
  #define LED_1 PIN5
32
  #define PISO_PL PIN6
33
  #define LCD_Data PIN7
34
35
///define clock variables
36
  struct time{
37
    uint16_t _100us;
38
    uint8_t _s;
39
    uint8_t _min;
40
    uint8_t _h;
41
    uint8_t _date;
42
    uint8_t _month;
43
    uint8_t _year;
44
  };
45
  struct time systime;
46
47
//update system clock              ########## datum-monat, schaltjahr
48
ISR(TIMER2_OVF_vect){
49
  ++systime._100us;
50
  if(systime._100us>=10000){
51
    systime._100us=0;
52
    ++systime._s;
53
    if(systime._s>=60){
54
      systime._s=0;
55
      ++systime._min;
56
      if(systime._min>=60){
57
        systime._min=0;
58
        ++systime._h;
59
        if(systime._h>=24){
60
          systime._h=0;
61
          ++systime._date;
62
        }
63
      }
64
    }
65
  }
66
}
67
68
void delay(uint16_t time){
69
  uint16_t tflag;
70
  tflag = systime._100us;
71
  while((systime._100us-tflag)<time);
72
  return;
73
}
74
75
int main(void)
76
{
77
  ///Reset
78
  ///configure IOs (0=in; 1=out)
79
    DDRB = (1<<LCD_CS1)|(1<<LCD_CS2)|(1<<LCD_WRITE)|(1<<SIPO_STR)|(1<<SIPO_CLK)|(1<<SIPO_DS);
80
    DDRC = (1<<BatNotLoad)|(1<<LED_3)|(1<<LED_2)|(1<<ResADC);
81
    DDRD = (1<<SIM_RxD)|(1<<SIM_ON)|(1<<LED_1)|(1<<LCD_Data)|(1<<RESERVE);
82
  ///preset IOs (in: 1=pull up)
83
    PORTB = 0;
84
    PORTC = (1<<BatNotLoad);
85
    PORTD = (1<<RESERVE);//;//(1<<SIM_ON);
86
  ///initialize clock
87
    systime._100us=0;
88
    systime._s=0;
89
  ///configure Timer2 (8-bit) system clock
90
    //TOV on OC2A >> WGM22 WGM21 WGM20
91
    TCCR2A = (1<<WGM20)|(1<<WGM21);
92
    //system tick = 100u >> 2000clk/8=250 >> CS21
93
    TCCR2B = (1<<WGM22)|(1<<CS21);
94
    OCR2A = 249;
95
    //TOV enable
96
    TIMSK2 = (1<<TOIE2);
97
  ///load settings              ##########
98
    
99
  ///enable interrupts  
100
    sei();
101
  
102
    while(1)//main
103
    {
104
    uint8_t test = 5;
105
    //testout(test);
106
    for(int i=0; i<100; ++i){
107
      PORTD &= ~(1<<RESERVE);
108
      PORTD |= (1<<RESERVE);
109
    }
110
    delay(1);
111
    for(int i=0; i<100; ++i){
112
      PORTD &= ~(1<<RESERVE);
113
      PORTD |= (1<<RESERVE);
114
    }
115
    }
116
}

von Joe S. (bubblejoe)


Lesenswert?


von Karl H. (kbuchegg)


Lesenswert?

>
1
>  struct time systime;
2
>

->
1
volatile  struct time systime;

FAQ: Was hat es mit volatile auf sich

: Bearbeitet durch User
von M. M. (blackcow)


Lesenswert?

volatile? Ich dachte es wäre C-Standard wenn ich eine Variable am 
Anfang, außerhalb der main{}-Schleife initialisiere, dass die dann 
global gültig ist. Auf jedenfall danke für die Antwort, ich werde es 
gleich mal ausprobieren.

von Stefan F. (Gast)


Lesenswert?

Bei Zugriff auf volatile Variablen werden Interrupts gesperrt, damit die 
Variable nicht während des Zugriffs verändert wird.

Das ist nötig, wenn man Variable mit mehr als 8 Bit auf einem 8 Bit µC 
verabreitet.

Du hast eventuell noch ein zweites Problem: Ich fürchte, dass deine 
Interrupt-Routine manchmal länger als 100µS dauert.

Wenn du noch mehr Hilfe brauchst, dann beschreibe das Problem, dass du 
siehst. "Funktioniert nicht" ist zu wenig Text.

von Karl H. (kbuchegg)


Lesenswert?

Stefan U. schrieb:
> Bei Zugriff auf volatile Variablen werden Interrupts gesperrt, damit die
> Variable nicht während des Zugriffs verändert wird.

Äh. Nein

INterrupts werden da an keiner Stelle gesperrt. Das ist nicht der Sinn 
von volatile

FAQ: Was hat es mit volatile auf sich

> Das ist nötig, wenn man Variable mit mehr als 8 Bit auf einem 8 Bit µC
> verabreitet.

Auch das ist im Zusammenhang mit volatile Unsinn. Du redest hier von 
einem sog. atomaren Zugriff. Den wird er zwar auch brauchen, hat aber 
nichts mit volatile zu tun.

: Bearbeitet durch User
von Bernhard S. (dl9rdw)


Lesenswert?

Hallo M. M.,

lies dir den obigen Link durch und du wirst verstehen was da abgeht.
>> FAQ: Was hat es mit volatile auf sich

Der Compiler muß wissen, dass er an dieser Variablen nicht herum zu 
optimieren hat. Und nur dann wird das was Du programmiert hast auch so 
umgesetzt ohne irgendwelche Zusammenfassungen etc. auch wenn die 
Deklaration der Variable grundsätzlich richtig ist.

Grüße

Bernhard

von Karl H. (kbuchegg)


Lesenswert?

M. M. schrieb:
> volatile? Ich dachte es wäre C-Standard wenn ich eine Variable am
> Anfang, außerhalb der main{}-Schleife initialisiere, dass die dann
> global gültig ist. Auf jedenfall danke für die Antwort, ich werde es
> gleich mal ausprobieren.

Ich habe keine Ahnung, wovon zum Henker du da eigentlich sprichst.
volatile hat absolut nichts mit dem Scope von Variablen zu tun.

von Stefan F. (Gast)


Lesenswert?

Ich sehe gerade noch einen Fehler:

Stell Dir vor, systime._100us hat gerade den Wert 9999 und du rufst dann 
delay(1) auf.

Dann ergibt sich folgende Brechnung:

Zuerst: (9999 - 9999) = 0 und das ist < 1
Dann ein Interrupt später: (0 - 9999) = -9999 und das ist immer noch <1

Du wirst in einer ENdlosschleife hängen bleiben.

Der gleiche Fehler tritt auch mit anderen delay-Werten auf.

von Karl H. (kbuchegg)


Lesenswert?

Stefan U. schrieb:

> Du hast eventuell noch ein zweites Problem: Ich fürchte, dass deine
> Interrupt-Routine manchmal länger als 100µS dauert.

Mach die Pferde nicht scheu.
Wenn ich seine Timer Innitialisierung zurück rechne, dann läuft sein µC 
mit 20Mhz. Zumindest ist der Timer so eingestellt, dass er alle 2000 CPU 
Takte einen Interrupt auslöst. 2000 CPU Takte sind mehr als genug für 
das bischen Rechnerei.

von Karl H. (kbuchegg)


Lesenswert?

Stefan U. schrieb:
> Dann ein Interrupt später: (0 - 9999) = -9999 und das ist immer noch <1

Stefan!

Lehn dich zurück!
Was sagt dir 'unsigned'?

Es sagt dir, dass das Ergebnis per Definition nicht negativ sein kann

: Bearbeitet durch User
von Stefan F. (Gast)


Lesenswert?

> Interrupts werden da an keiner Stelle gesperrt.
> Das ist nicht der Sinn von volatile.

Ich hätte drauf geschworen. Aber aufgrund deines Hinweises habe ich mal 
einen konkretes Listing überprüft und kann nun deine AUssage bestätigen.

So ein Käse, das hätte ich richtig wissen sollen.

von M. M. (blackcow)


Lesenswert?

Bernhard S. schrieb:
> Der Compiler muß wissen, dass er an dieser Variablen nicht herum zu
> optimieren hat. Und nur dann wird das was Du programmiert hast auch so
> umgesetzt ohne irgendwelche Zusammenfassungen etc. auch wenn die
> Deklaration der Variable grundsätzlich richtig ist.

Das ist ein guter Punkt. Wie läuft das in C? Wie geht man bei sowas vor? 
Ich komme halt aus der Assembler-Ecke und bin in AVR-C ein Anfänger.

Karl H. schrieb:
> Mach die Pferde nicht scheu.
> Wenn ich seine Timer Innitialisierung zurück rechne, dann läuft sein µC
> mit 20Mhz. Zumindest ist der Timer so eingestellt, dass er alle 2000 CPU
> Takte einen Interrupt auslöst. 2000 CPU Takte sind mehr als genug für
> das bischen Rechnerei.

Ja richtig 20MHz. Also im schlimmsten Fall gibt es einen Arithmetischen 
Überlauf bei der Subtraktion. Aber da das unsigned ist, gibts keine 
Endlosschleife. Die delay-Funktion ist halt nicht genau 100us genau, 
aber 100us hin oder her ist mir hier egal.

von Karl H. (kbuchegg)


Lesenswert?

Stefan U. schrieb:
>> Interrupts werden da an keiner Stelle gesperrt.
>> Das ist nicht der Sinn von volatile.
>
> Ich hätte drauf geschworen. Aber aufgrund deines Hinweises habe ich mal
> einen konkretes Listing überprüft und kann nun deine AUssage bestätigen.

volatile ist Standard-C

In Standard-C gibt es den Begriff 'Interrupt' sowie so gut wie alles 
sonstige hardware-abhängige nicht. Im C-Standard wird eine idealisierte 
Maschine vorausgesetzt. Ob die Interrupts kennt oder nicht, ob die eine 
Stack hat oder nicht, wie sie dynamischen Speicher zur Verfügung stellt 
(das was wir Heap nennen), ob es ein Terminal oder Keyboard gibt oder 
nicht, das alles und noch viel mehr interessiert im C Standard nicht.

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

M. M. schrieb:
> Bernhard S. schrieb:
>> Der Compiler muß wissen, dass er an dieser Variablen nicht herum zu
>> optimieren hat. Und nur dann wird das was Du programmiert hast auch so
>> umgesetzt ohne irgendwelche Zusammenfassungen etc. auch wenn die
>> Deklaration der Variable grundsätzlich richtig ist.
>
> Das ist ein guter Punkt. Wie läuft das in C? Wie geht man bei sowas vor?

Wie oft soll ich denn noch den Link posten, bis du endlich mal 
draufklickst und nachliest, was Sache ist

FAQ: Was hat es mit volatile auf sich

von Eric B. (beric)


Lesenswert?

Karl H. schrieb:
> Stefan U. schrieb:
>> Dann ein Interrupt später: (0 - 9999) = -9999 und das ist immer noch <1
>
> Stefan!
>
> Lehn dich zurück!
> Was sagt dir 'unsigned'?
>
> Es sagt dir, dass das Ergebnis per Definition nicht negativ sein *kann*

Nichtdestotrotz wird das so nicht funktionieren: 65536-9999 == 55537.
Und jetzt bitte weiterrechnen ;-)

: Bearbeitet durch User
von TF (Gast)


Lesenswert?

@Eric B.

Das passt schon solange das Delta nicht größer als der halbe 
Zahlenbereich ist.

Delta = NeuerWert - AlterWert
10000 =  9464     -   65000

von TF (Gast)


Lesenswert?

@Eric B.

Sorry geht natürlich nur wenn 100µs Zähler nicht gelöscht wird.

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.