Forum: Mikrocontroller und Digitale Elektronik Static int in Interruptroutine - bin ich doof?


von Alex V. (bastel_alex) Benutzerseite


Lesenswert?

Hallo,
ich habe eben für eine kleine Bastelei ein altes Projekt etwas 
zusammengekürzt und umgeschrieben. Ganz einfach: Wenn eine Klangschwelle 
überschritten wird, soll ein Vibro angehen.

code:
1
/*************************************************************************
2
3
   Hardware
4
   
5
   prozessor:   ATtiny13A
6
   clock:      9.6Mhz internal RC oscillator
7
8
   PIN1   RESET            
9
   PIN2   PORTB3/ADC3      
10
   PIN3   PORTB4/ADC2      Vibro Out
11
   PIN4   GND
12
13
   PIN5   PORTB0/OC10      
14
   PIN6   PORTB1       
15
   PIN7   PORTB2/ADC1      Mic in
16
   PIN8   VCC
17
18
*************************************************************************/
19
#include <avr/io.h>
20
#include <avr/interrupt.h>
21
#include <stdlib.h>
22
#include <util/delay.h> 
23
24
// sampling frequency
25
26
#define F_CPU 9.6E6 
27
28
29
static int test = 0;
30
static int toggle = 0;
31
32
33
34
35
void vibro_on()
36
{
37
  PORTB |= (1<<PINB4);
38
}
39
40
void vibro_off()
41
{
42
  PORTB &= ~(1<<PINB4);
43
}
44
45
/*************************************************************************
46
47
   adc interrupt
48
49
*************************************************************************/
50
51
SIGNAL (TIM0_OVF_vect)
52
{
53
   uint16_t x;
54
55
   // read ADC
56
   x =   ADCL;
57
   x += (ADCH << 8);
58
   x=x>>2; // ADC result with 8 bit resolution
59
60
  if (x>50)
61
  {
62
    test=1;
63
  }  
64
  
65
  
66
67
}
68
/*************************************************************************
69
70
   main
71
72
*************************************************************************/
73
int main(void)
74
{
75
  //************** init Timer ************************************
76
77
   TCCR0B=(1<<CS01) | (1<<CS00) ; // Prescaling = 64, 
78
79
   // interrupt mask register: enable timer1 overflow 
80
   TIMSK0 |= (1 << TOIE0);
81
   sei();
82
83
   // Ports
84
   DDRB=(1<<PINB4); // Pin 3 as output ( PB4, vibro)   
85
   
86
87
   // ADC_Clk=F_CPU/128 ==> fs~75kHz, free runing mode
88
   ADCSRA = (1<<ADEN) | (1<<ADATE) |(1<<ADPS2) | (1<<ADPS1) | (1<<ADPS0);
89
90
   // VCC as reference and ADC1 as input
91
   ADMUX=(0<<REFS0) | (0<<MUX1) | (1<<MUX0);
92
93
   //timer0 overflow as adc trigger
94
   ADCSRB |= (1 << ADTS2 );
95
96
   // clear interrupt flag and start first conversion
97
    ADCSRA |= (1 << ADIF) | (1<<ADSC);
98
  
99
  while(1)
100
   {
101
        
102
      if (test==1)
103
      {
104
        vibro_on();
105
        
106
      }        
107
    
108
   }
109
}

Das Problem (pease help): die Bedingung test==1 wird nie true. Der Code 
ansonsten funktioniert -  wenn ich in der Interrupt-Routine in der 
if(x>50) - Bedingung vibro_on() setze, dann läufts.
Bin ich blöd? Ja. Woran liegts?

von Karl H. (kbuchegg)


Lesenswert?

test muss volatile sein

FAQ: Was hat es mit volatile auf sich

von Michael F. (startrekmichi)


Lesenswert?

änder mal folgendes:
1
static volatile int test = 0;

static sagt für globale Variablen, dass sie nur innerhalb dieses 
Source-Files verwendet wird.
volatile sagt, dass sich die Variable ändern kann, ohne dass der 
Compiler das bei der Analyse erkennen kann. Und von Interrupts weiß der 
nix -> er fragt die Variable in der main() wenn überhaupt nur einmal ab.

von Alex V. (bastel_alex) Benutzerseite


Lesenswert?

Dankesehr, das war natürlich die Lösung! :)
Komisch nur, dass in meinem alten Projekt aus dem ich den code genommen 
habe alles nur mit statics funktioniert hat. Da war der Compiler wohl 
aus irgendeinem Grunde schlau genug!

von Karl H. (kbuchegg)


Lesenswert?

Alex v. L. schrieb:
> Dankesehr, das war natürlich die Lösung! :)
> Komisch nur, dass in meinem alten Projekt aus dem ich den code genommen
> habe alles nur mit statics funktioniert hat. Da war der Compiler wohl
> aus irgendeinem Grunde schlau genug!

Wahrscheinlich war in deinem alten Projekt noch mehr in der Schleife. 
Lies dir den Link durch. Die Optimierung hängt logischerweise auch davon 
ab, was sonst noch so in der Schleife los ist, d.h. ob die 
Register-Optimierung überhaupt möglich ist oder ob die Register auch 
noch für etwas anderes gebraucht werden.

Es zeigt sich die alte Regel:
Nur weil etwas zu funktionieren scheint, bedeutet das noch lange nicht, 
dass es auch richtig ist.
Durch Testen kann man nur die Anwesenheit von Fehlern nachweisen, nicht 
jedoch die Abwesenheit.

von Karl H. (kbuchegg)


Lesenswert?

Im übrigen solltest du um Datentypen wie int einen Bogen machen, wenn du 
nicht wirklich 16 Bit brauchst.
Solange du mit einem Zahlenbereich 0..255 auskommst, solltest du 
unbedingt uint8_t nehmen.

In deinem

int test;

steckt nämlich noch eine 2-te Falle. Gut, bei dir in deinem Programm 
jetzt nicht, aber grundsätzlich schon. Auf einen int kann der Prozessor 
nicht atomar zugreifen (in einem Rutsch) sondern er muss das auf 2 mal 
machen. Wenn jetzt genau zwischen den beiden Zugriffen die ISR ausgelöst 
wird und innerhalb der ISR die Variable verändert wird, dann hat der 
unterbrochene Code unter Umständen ein Problem: er hat einen int 
gelesen, dessen beiden Bytes von verschiedenen Werten stammen. Und das 
kann sich dann auch so auswirken, dass in dieser Kombination Werte 
entstehen, die eigentlich ungültig sind.

Anhand des Dezimalsystems erklärt:
In deiner Variablen können nur Werte von 0 bis 100 stehen (inklusive). 
Und jetzt nehmen wir mal an, der µC müsste die Tasuender und Hunderter 
getrennt von den Zehnern und Einern einlesen (also es sind 2 
Lesezugriffe notwendig). Das Programm liest also die 1 für 100. Und noch 
ehe es den Rest lesen kann, kommt die ISR zum Zug und ändert den Wert 
auf 99. Wird danach der 2.te Lesezugriff gemacht, dann liest dein 
Hauptprogramm natürlich für Zehner und Einer die 99 aus. Die 1 aus den 
Hundertern hatte es ja schon vor der Unterbrechung gelesen. 
Zusammengenommen hat also dein Programm den Wert .... 199 aus der 
Variablen ausgelesen. Und das war nicht im Sinne des Erfinders.

Daher: Immer den kleinsten Datentyp nehmen der passt. Und wenn geht 
einen uint8_t. Denn der ist 1 Byte groß und kann in einem Rutsch aus dem 
Speicher gelesen werden. Und somit gibt es da auch kein Problem mit dem 
atomaren Zugriff. Denn 1 Byte lesen ist trivialerweise atomar.

von Alex V. (bastel_alex) Benutzerseite


Lesenswert?

Danke für die Anmerkung und deine Zeit! Ich versuche sie in Zukunft zu 
beherzigen!

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.