Forum: Mikrocontroller und Digitale Elektronik Zehntel springt von 4 wieder auf 2. Warum?


Announcement: there is an English version of this forum on EmbDev.net. Posts you create there will be displayed on Mikrocontroller.net and EmbDev.net.
von Jens J. (trial)


Bewertung
0 lesenswert
nicht lesenswert
Ziel ist es eine Stoppuhr mit internen Timer zu programmieren. Wie genau 
das sein muss sei mal dahin gestellt.

Selbst wenn ich das ganze ohne die Externen Interrupts laufen lasse und 
den zaehler so einstelle das die Zehntel Sekunden direkt bis 9 
hochzählen sollten, springt er nach der 4 (zehntel sek) direkt wieder 
auf 2 und das in der Endlosschleife.

Was ich mir gedacht habe:
Vllt. haut da was mit der Freigabe der Overflow Timer nicht hin. 
Ausgewählt hatte ich am Anfang einen Atmega16. Datenblatt für Atmega16 
finde ich jedoch nicht. Sondern nur für Atmega16(plus irgendwelche 
Zahlen und Buchstaben).

Geöffnet habe ich nebenher Atmel ATmega16M1/32M1/64M1 [DATASHEET].
Da wird zwischen TIMSK0 und TIMSK1 unterschieden. Ebenso ist das TCCR0 
in A und B aufgeteilt. Der Compiler erkennt aber nur TIMSK und TCCR0 
ohne A und B.

Hier noch der Code:

1
#define TAKT 1000000LU /* Controlertakt 1Mhz*/
2
3
4
#include <asf.h> // Bibliothek für STK600 (glaub ich)
5
#include <avr/interrupt.h>
6
#include <util/delay.h>
7
8
volatile char zaehler = 9; // 1 zaehler entspricht zehntel++
9
volatile char reset = 0; // für den Reset der Stoppuhr
10
volatile int ausgleich = 0; // Auslgiech für Ungenauigkeit
11
volatile char on_off = 0; // Stoppuhr zählt oder nicht
12
13
//char segment_code[16]={0xC0,0xF9,0xA4,0xB0, 0x99,0x92,0x82,0xF8,
14
            //0x80,0x90,0x88,0x83,0xC6,0XA1,0x86,0x8E};
15
            
16
char segment_code[10]={0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,
17
            0x08,0x09}; //angepasst um die Werte besser in der Simmulation zu sehen
18
            
19
ISR(INT0_vect)
20
{
21
  if ( on_off == 0)
22
  {
23
     on_off = 1;
24
  }
25
  else
26
  {
27
    on_off = 0;
28
  }
29
}        
30
31
ISR(INT1_vect)
32
{
33
  reset = 1;
34
}
35
36
ISR (TIMER0_OVF_vect)
37
{
38
  if (on_off==1)
39
  {
40
    TCNT0=0x9E;
41
    zaehler++;
42
    ausgleich++;
43
    if(ausgleich==284)
44
    {
45
      zaehler++;
46
      ausgleich=0;
47
    }
48
  }
49
}
50
51
52
int main (void)
53
{
54
  /* Insert system clock initialization code here (sysclk_init()). */
55
56
  board_init();
57
58
  /* Insert application code here, after the board has been initialized. */
59
  
60
  char zehntel = 0;
61
  char ein_sec = 0;
62
  char zehn_sec = 0;
63
  char ein_min = 0;
64
  
65
  TCCR0 |= (1<<CS02) | (1<<CS00); 
66
  TIMSK |= 0x01;
67
  
68
  GICR  |= 0xFF;
69
  MCUCR |= 0xFA;
70
  
71
  DDRA = 0xff;
72
  DDRB = 0xff;
73
  DDRD = 0x00;
74
  
75
  sei();
76
  while (1)
77
{
78
    
79
  
80
  if(zaehler>=1) //wenn >1 darf eine zehntel dazu addiert werden
81
{  zaehler--;
82
  
83
    if (zehntel == 9)
84
    {
85
      if (ein_sec == 9)
86
      {
87
        if (zehn_sec == 5)
88
        {
89
          if (ein_min == 9)
90
          {
91
            on_off=0;
92
          } 
93
          else
94
          {
95
            ein_min++;
96
            zehn_sec = 0;
97
            ein_sec = 0;
98
            zehntel = 0;
99
          }
100
        } 
101
        else
102
        {
103
          zehn_sec++;
104
          ein_sec = 0;
105
          zehntel = 0;
106
        }
107
      }
108
      else
109
      {
110
        ein_sec++;
111
        zehntel = 0;
112
      }
113
    } 
114
    else
115
    {
116
      zehntel++;
117
    }
118
} // Ende zähler
119
120
121
122
  
123
  
124
  
125
    if (reset==1)
126
    {
127
      ein_min = 0;
128
      zehn_sec = 0;
129
      ein_sec = 0;
130
      zehntel = 0;
131
      reset = 0;
132
      ausgleich=0;
133
    }
134
    
135
      
136
        PORTA = 0x01;
137
        PORTB = segment_code[zehntel];
138
        //_delay_ms(5);
139
      
140
        PORTA = 0x02;
141
        PORTB = segment_code[ein_sec];
142
        //_delay_ms(5);
143
        
144
        PORTA = 0x03;
145
        PORTB = segment_code[zehn_sec];
146
        //_delay_ms(5);
147
        
148
        PORTA = 0x04;
149
        PORTB = segment_code[ein_min];
150
        //_delay_ms(5);
151
  
152
  
153
  
154
  
155
}
156
  
157
  
158
  
159
  return 0;
160
}

--

Über dem Texteingabefeld steht eine Bedienungsanleitung. Lesen!

: Bearbeitet durch Moderator
von M.K. B. (mkbit)


Bewertung
0 lesenswert
nicht lesenswert
Bitte den Code als solchen formatieren oder als Datei anhängen. Steht 
alles vor dem Eingabefeld für den Post.

Sonst verstehe ich deine Logik nicht wirklich. Deine ISR zählt 
irgendeine Zeiteinheit, die man normalerweise nur noch mit Modulo in 
Sekunden, Minuten, ... aufteilen müsste.

Dein Zähler ist ein char, welcher signed oder unsigned sein kann. Wenn 
der überläuft könnte der Wert erstmal negativ sein. Außerdem hat der 
Wertebereich nur 256 Werte und danach fängt alles wieder von vorne an.

Wenn du jetzt einen größeren Zähler nimmst denke daran, dass die Bytes 
dann nicht mehr alle gleichzeitig gesetzt werden. Damit bekommst du in 
Main sporadisch sehr falsche Werte, wenn mitten in der Auswertung die 
ISR zuschlägt.

von Wolfgang (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Jens N. schrieb:
> Selbst wenn ich das ganze ohne die Externen Interrupts laufen lasse und
> den zaehler so einstelle das die Zehntel Sekunden direkt bis 9
> hochzählen sollten, springt er nach der 4 (zehntel sek) direkt wieder
> auf 2 und das in der Endlosschleife.

Und was sagt dein Simulator/Debugger, warum der Zähler verfrüht zurück 
springt.

Gewöhnlich macht ein µC genau das, was man ihm sagt. Wenn er etwas 
anderes tut, als man erwartet, hat man einen Denkfehler.

Isoliere deine Zählersteuerung in einer separaten Funktion und teste 
sie, bevor du Timer und Interruptsteuerung dazu nimmst.

p.s. Warum hast du deine ganzen uint8_t Variablen als char 
deklariert?
Bist du sicher, dass dein Compiler die so behandelt, wie du hoffst?

von Wolfgang (Gast)


Bewertung
0 lesenswert
nicht lesenswert
M.K. B. schrieb:
> Sonst verstehe ich deine Logik nicht wirklich. Deine ISR zählt
> irgendeine Zeiteinheit, die man normalerweise nur noch mit Modulo in
> Sekunden, Minuten, ... aufteilen müsste.

Die Modulo Funktion läuft auf eine Division hinaus.

Da der µC bei der Stop-Uhr Anwendung wahrscheinlich sowieso nur 
Langeweile hat, ist das natürlich eine schöne Methode, um ihn zu 
beschäftigen. Laufzeiteffizient ist das auf einem µC ohne HW-Division 
nicht.

Dann sollte man sich den Zählerstand vor der Rechnerei allerdings mit 
einer atomaren Operation in einen Puffer holen.

von Jens M. (schuchkleisser)


Bewertung
0 lesenswert
nicht lesenswert
Jens N. schrieb:
> Ausgewählt hatte ich am Anfang einen Atmega16. Datenblatt für Atmega16
> finde ich jedoch nicht. Sondern nur für Atmega16(plus irgendwelche
> Zahlen und Buchstaben).
>
> Geöffnet habe ich nebenher Atmel ATmega16M1/32M1/64M1 [DATASHEET].

Tja, es würde vielleicht schon mal helfen, das richtige DS zu benutzen.
Irgendeins das irgendwo Teile des richtigen Chips beinhaltet ist knapp 
daneben, also auch vorbei.

Einen Zähler der 9,9,5,9,9 zählt, schafft der Chip so gerade noch.
Wenn man ihn richtig baut, zumindest.
Und nicht so eine unübersichtliche Fehleranfällige Kropsität mit den 
verschachtelten IFs...

Zehntel++
Zehntel > 9? Dann Zehntel=0; Einer++
Einer >9? Dann Einer=0; Zehner++
Zehner >5? Dann Zehner=0; Hunderter++
usw. usf.

Wobei man evtl. Minuten, Sekunden und Hunderstel zählen sollte und für's 
Display konvertieren. Das Multiplexen würde ich auch in den Interrupt 
legen.

von MaWin (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Jens N. schrieb:
> Hier noch der Code:

Was ist das für abstruser code ?

zaehler++ und zaehler-- aber nie zaehler=0, dafür startet zaehler bei 9 
?

Wer bedient Int0 (on_off)? Ein nicht-entprellter Taster ?

von Wolfgang (Gast)


Bewertung
0 lesenswert
nicht lesenswert
MaWin schrieb:
> Was ist das für abstruser code ?

Passt zur genauso abstrusen Formatierung ...

von ... (Gast)


Bewertung
1 lesenswert
nicht lesenswert
Interrupt und Main arbeiten hier scheinbar gegeneinander.

Im INT steht "zaehler++" und in der Main "zaehler--".
Klar dass da was schief geht.

von Nop (Gast)


Bewertung
-3 lesenswert
nicht lesenswert
Code, wo Variablen und Funktionen nicht auf englisch sind, ist 
regelmäßig ein Indikator für totale Grütze.

von Jens J. (trial)


Bewertung
0 lesenswert
nicht lesenswert
> Und was sagt dein Simulator/Debugger, warum der Zähler verfrüht zurück
> springt.

Nichts, er macht es einfach.

> Gewöhnlich macht ein µC genau das, was man ihm sagt. Wenn er etwas
> anderes tut, als man erwartet, hat man einen Denkfehler.

Kann sein, den versuche ich ja zu finden.

> Isoliere deine Zählersteuerung in einer separaten Funktion und teste
> sie, bevor du Timer und Interruptsteuerung dazu nimmst.

Klasse Idee, Danke. Ist nur halt schwierig nachzu vollziehen wenn wegen 
der Optimierungen der Debugger sich nicht 100% an die Reihenfolge hält. 
Werde es trotzdem mal testen

> p.s. Warum hast du deine ganzen uint8_t Variablen als /char/
> deklariert?

was ist eine uint8_t ?

> Bist du sicher, dass dein Compiler die so behandelt, wie du hoffst?

Nein, dazu fehlt mir die Erfahrung.

von Jens J. (trial)


Bewertung
0 lesenswert
nicht lesenswert
>
> Einen Zähler der 9,9,5,9,9 zählt, schafft der Chip so gerade noch.
> Wenn man ihn richtig baut, zumindest.
> Und nicht so eine unübersichtliche Fehleranfällige Kropsität mit den
> verschachtelten IFs...
>
> Zehntel++
> Zehntel > 9? Dann Zehntel=0; Einer++
> Einer >9? Dann Einer=0; Zehner++
> Zehner >5? Dann Zehner=0; Hunderter++
> usw. usf.

Na er zählt halt die zehntel Sekunden bis 9, anstatt im nächsten 
Durchlauf auf zehn zu springen, springt er zu den Sekunden und erhöht 
diese um 1, dann stellt er die zehntel auf null.
wenn irgendwann die Sekunden auf 9 sind, geht das mit der stelle für die 
zehner Sekunden genauso weiter. Nicht für die Praxis tauglich, aber zum 
programmieren üben, ganz gut, denk ich. Will ja nicht einfach fertiges 
abtippen.

> Wobei man evtl. Minuten, Sekunden und Hunderstel zählen sollte und für's
> Display konvertieren. Das Multiplexen würde ich auch in den Interrupt
> legen.

was meinst du mit Multiplexing?

: Bearbeitet durch User
von Wolfgang (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Jens N. schrieb:
> Kann sein, den versuche ich ja zu finden.

Dann guck ihm mit Simulator/Debugger auf die Finger, wo genau und warum 
es passiert. Ein Zähler verzählt sich nicht mal eben.

von Wolfgang (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Jens N. schrieb:
> was ist eine uint8_t ?

Ein Variablentyp für ganzzahlige, vorzeichenlose Variablen mit 8 Bit
https://www.gnu.org/software/libc/manual/html_node/Integers.html

von Jens J. (trial)


Bewertung
0 lesenswert
nicht lesenswert
Nochmal als Erklärung nachgereicht:

mein Code stößt scheinbar deshalb auf Unverständnis weil Ihr das so 
nicht machen würdet. Da ich aber das programmieren und das verhalten der 
Controller verstehen will, bringt es mir wenig einfach fertige 
Musterlösungen abzutippen. Später im Beruf geht das ja auch nicht mehr, 
sonst bräuchte man einen ja nicht.

Also zur Funktion:

Der Timer Overflow wurde nachträglich eingeführt da die erste Übung 
einfach nur eine Stoppuhr sein sollte. Genauigkeit wurde erstmal nicht 
verlangt.

Vorher war es so das einfach die Ausgabe der 7-Segment anzeige ungefähr 
100ms lief und dann eine zehntel Sekunde dazu addiert wurde. Und wenn 
die zehntel "voll" waren, sprang er rüber zu den Sekunden um diese um 1 
zu erhöhen, mit anschließender Nullung der zehntel Sekunden.

Mit Timer Overflow ist es nun so das dieser Zählvorgang immer dann 
durchgeführt werden darf solange der zaehler größer null ist. Der 
Overflow erhöht den zaehler, die eigentlliche Zählschleife (die 
verschachtelten IF) verringert ihn wieder.

Char Variablen sind es deshalb weil ich ja nur max. eine 9 Brauche und 
char ja von -128 bis 127 reicht.

: Bearbeitet durch User
von Jens M. (schuchkleisser)


Bewertung
0 lesenswert
nicht lesenswert
Jens N. schrieb:
> Na er zählt halt die zehntel Sekunden bis 9, anstatt im nächsten
> Durchlauf auf zehn zu springen, springt er zu den Sekunden und erhöht
> diese um 1, dann stellt er die zehntel auf null.
> wenn irgendwann die Sekunden auf 9 sind, geht das mit der stelle für die
> zehner Sekunden genauso weiter. Nicht für die Praxis tauglich, aber zum
> programmieren üben, ganz gut, denk ich. Will ja nicht einfach fertiges
> abtippen.

Dein Lied hört sich genau so an wie meins, nur anders.
Deins hat einen Fehler. Meins tut. hm.
Du musst diese verschachtelten Ifs mal genau durchdenken...

Jens N. schrieb:
> was meinst du mit Multiplexing?

Du gibst am Ende die Daten auf ein Display, wie's aussieht.
Da wird immer auf PortA die Stelle gewählt und auf PortB die jeweilige 
Ziffer.
Das ist in der Form doof, weil nicht isochron. Im Interrupt wäre es das, 
dann wäre die Anzeige flackerfrei

von Georg G. (df2au)


Bewertung
0 lesenswert
nicht lesenswert
Jens N. schrieb:
> Optimierungen der Debugger sich nicht 100% an die Reihenfolge hält.

Das kann man dem Compiler verbieten. Wenn alles läuft, werden die 
Optimierungen wieder eingeschaltet.

von leo (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Jens N. schrieb:
> mein Code stößt scheinbar deshalb auf Unverständnis weil Ihr das so
> nicht machen würdet

... weil er extrem kompliziert, unnoetig lang und eben fehlertraechtig 
ist.
Das wiederholte Nullsetzen der Variablen sollte schon zu denken geben.
Weiter obem wurde schon eine kompakte Loesung gepostet. Vergleich mal 
die Zeilenanzahl. Du lernst nicht mehr oder besser durch langen Code - 
im Gegenteil

leo

von Yalu X. (yalu) (Moderator)


Bewertung
1 lesenswert
nicht lesenswert
Ich habe mir deinen Code mal etwas näher angeschaut und glaube, ihn
größtenteils verstanden zu haben :)

Der Zählalgorithmus

1
    if(zaehler>=1) { //wenn >1 darf eine zehntel dazu addiert werden
2
    ...
3
    } // Ende zähler

ist zwar etwas holprig programmiert, aber bis auf eine Kleinigkeit
korrekt. In der Zeile

1
      zaehler--;

wird zuerst die Variable zaehler erst vom Speicher gelesen, dann
dekrementiert und schließlich wieder in den Speicher geschrieben. Wird
zwischen dem Lesen und dem Schreiben ein Timer-Interrupt ausgelöst und
damit zaehler inkrementiert, bleibt diese Inkrementierung ohne
Wirkung, d.h. es können sporadisch Zählerinkremente verloren gehen. Das
hat aber lediglich den Effekt, dass die Uhr ein ganz winziges Bisschen
langsamer läuft als erwartet. Dieses Problem kannst du dadurch beheben,
dass du für die Dauer der obigen Anweisung die Interrupt sperrst, bspw.
so:

1
      cli();
2
      zaehler--;
3
      sei();

oder mit dem ATOMIC_BLOCK-Makro wie hier erläutert:

  https://www.nongnu.org/avr-libc/user-manual/group__util__atomic.html

Das erklärt noch lange nicht diesen von dir beschriebenen Fehler:

Jens N. schrieb:
> Selbst wenn ich das ganze ohne die Externen Interrupts laufen lasse und
> den zaehler so einstelle das die Zehntel Sekunden direkt bis 9
> hochzählen sollten, springt er nach der 4 (zehntel sek) direkt wieder
> auf 2 und das in der Endlosschleife.

Da die Ursache dafür nicht in deinem Zählalgorithmus liegt, muss
woanders etwas schief laufen. Deswegen folgende Fragen:

Ist bei diesem Test der Timer-Interrupt aktiviert?

Wie wird genau gezählt? 0,0s, 0,1s, 0,2s, 0,3s, 0,4s, 0,2s?

Und was passiert danach? Geht es weiter mit 0,3s?

Bleiben die Variablen zehntel, ein_sec, zehn_sec und ein_min
irgendwann nach einer größeren Anzahl von Schleifendurchläufen auf
festen Werten stehen? Wenn ja, wie lauten diese Werte?

Wie stellst du die Zählreihenfolge fest? Schaust du mit flinkem Auge
aufs LED-Display? Oder lässt du das Programm im Simulator/Debugger
laufen und hast einen Breakpoint gesetzt? Wenn letzteres, in welcher
Codezeile liegt dieser Breakpoint?

: Bearbeitet durch Moderator
von Jens J. (trial)


Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Yalu X. schrieb:

>
> wird zuerst die Variable zaehler erst vom Speicher gelesen, dann
> dekrementiert und schließlich wieder in den Speicher geschrieben. Wird
> zwischen dem Lesen und dem Schreiben ein Timer-Interrupt ausgelöst und
> damit zaehler inkrementiert, bleibt diese Inkrementierung ohne
> Wirkung, d.h. es können sporadisch Zählerinkremente verloren gehen. Das
> hat aber lediglich den Effekt, dass die Uhr ein ganz winziges Bisschen
> langsamer läuft als erwartet. Dieses Problem kannst du dadurch beheben,
> dass du für die Dauer der obigen Anweisung die Interrupt sperrst, bspw.
> so:
>
>
>
1
>       cli();
2
>       zaehler--;
3
>       sei();
4
>
>

Das Problem habe ich zwar nicht bedacht, doch ist es nicht so 
gravierend, da der Zählvorgang sowieso viel schneller abläuft als wieder 
ein timer Overflow eintritt. hat mir dennoch geholfen den Vorgang an 
sich zu verstehen, danke.

>

>
> Ist bei diesem Test der Timer-Interrupt aktiviert?

Ich hatte tatsächlich vergessen ihn zu deaktivieren. das ergeniss ist 
ohne allerdings das gleiche.

> Wie wird genau gezählt? 0,0s, 0,1s, 0,2s, 0,3s, 0,4s, 0,2s?
>
> Und was passiert danach? Geht es weiter mit 0,3s?
>
> Bleiben die Variablen zehntel, ein_sec, zehn_sec und /ein_min/
> irgendwann nach einer größeren Anzahl von Schleifendurchläufen auf
> festen Werten stehen? Wenn ja, wie lauten diese Werte?

ein_sec/zehn_sec/ und ein_min werden garnicht erst hochgezählt. Wenn man 
bedenkt das ja die zehntel eh nur bis 9 zählen sollten uach erstmal 
nicht überraschend.

>
> Wie stellst du die Zählreihenfolge fest? Schaust du mit flinkem Auge
> aufs LED-Display? Oder lässt du das Programm im Simulator/Debugger
> laufen und hast einen Breakpoint gesetzt? Wenn letzteres, in welcher
> Codezeile liegt dieser Breakpoint?

Ich habe zuhause keine Hardware, also nur im Simulator. Ich habe ein 
Breakpoint gesetzt und zwar in der Zeile PORTA = 0x02; Er stoppt also 
genau nachdem bzw. während an Port B die zehntel ausgegeben werden.

Habe mir mal die Mühe gemacht und einzeln durchgesteppt (Optimierungen 
abgestellt) und was abgefahrenes entdeckt:

Nachdem die zehntel die 4 erreicht haben, macht er zunächst normal 
weiter. Von PortA=0x03 springt er dann zurück zu PortA=0X02 und zeigt an 
PortB plötzlich eine 1 an obwohl kein Befehl dazu abgearbeitet wurde. 
Beim nächsten zehner=4 das gleiche wieder.
Der anfangs auf 9 gesetzte zaehler sollte ja auch irgendwann mal auf 
null kommen, tut er aber nicht.

Bevor ich das ohne ext_interrupt durchlaufen lies, hat er auch immer den 
ON_Off abgestellt. Also aus irgend einen Grund kommt er da ins stolpern.

: Bearbeitet durch User
von Stefan ⛄ F. (stefanus)


Bewertung
0 lesenswert
nicht lesenswert
Zum Durchsteppen mit dem Debugger solltest du den Optimizer 
deaktivieren. Das geht mit der Compiler-Option -O0.

Ansonsten bekommt der Debugger Schwierigkeiten, den Zusammenhang 
zwischen generiertem Maschinencode und Quelltext zu erfassen. Dies 
äussert sich typischerweise daran, dass Variablen scheinbar 
verschwinden, ihren Wert nicht wie erwartet ändern oder dass der 
Programmzeiger unerwartet springt.

von Jens J. (trial)


Bewertung
0 lesenswert
nicht lesenswert
Danke, hab ich schon gemacht, nachdem ich ewig gebraucht habe die 
Einstellungen dafür zu finden.


Achso: Oben in meinem Screenshot heißt zehntel noch hundertstel. Hoffe 
das verwirrt nicht zu sehr.


Und was mich grad überrascht:

habe sei(); mal rausgenommen und auf einmal funzt es. Aber wo soll ein 
Interrupt herkommen der so merkwürdig stört wenn die Taster nicht 
betätigt werden und TIMSK &= 0x00 ist?

: Bearbeitet durch User
von Yalu X. (yalu) (Moderator)


Bewertung
0 lesenswert
nicht lesenswert
Jens N. schrieb:
> Von PortA=0x03 springt er dann zurück zu PortA=0X02 und zeigt an
> PortB plötzlich eine 1 an obwohl kein Befehl dazu abgearbeitet wurde.

Da ist etwas faul, was nicht mit deinem Code zusammenhängt. Zumindest
die 8 aufeinanderfolgenden Port-Zugriffe müssen – unabhängig von den
Optimierungseinstellungen – sauber der Reihe nach abgearbeitet werden.

Bist du sicher, dass der Binärcode, den du im Simulator laufen lässt,
auch wirklich zu deinem Quellcode gehört? Gibt es im AVR-Studio einen
Menüpunkt "rebuild all" o.ä., mit dem sichergestellt werden kann, dass
das komplette Projekt noch einmal vollständig kompiliert und gelinkt
wird?

Jens N. schrieb:
> Von PortA=0x03 springt er dann zurück zu PortA=0X02 und zeigt an
> PortB plötzlich eine 1 an obwohl kein Befehl dazu abgearbeitet wurde.

Da ist etwas faul, was nicht mit deinem Code zusammenhängt. Zumindest
die 8 aufeinanderfolgenden Port-Zugriffe müssen – unabhängig von den
Optimierungseinstellungen – sauber der Reihe nach abgearbeitet werden.

Bist du sicher, dass der Binärcode, den du im Simulator laufen lässt,
auch wirklich zu deinem Quellcode gehört? Gibt es im AVR-Studio einen
Menüpunkt "rebuild all" o.ä., mit dem sichergestellt werden kann, dass
das komplette Projekt noch einmal vollständig kompiliert und gelinkt
wird?

Jens N. schrieb:
> Achso: Oben in meinem Screenshot heißt zehntel noch hundertstel. Hoffe
> das verwirrt nicht zu sehr.

Nicht, wenn das die einzige Änderung war.

> habe sei(); mal rausgenommen und auf einmal funzt es. Aber wo soll ein
> Interrupt herkommen der so merkwürdig stört wenn die Taster nicht
> betätigt werden und TIMSK &= 0x00 ist?

In dem von dir anfangs geposteten Code ist TIMSK=0x01. Gibt es da
vielleicht noch mehr Änderungen? Mach mal den sei() wieder rein, lass es
durch den Simulator laufen, um zu sehen, ob der Fehler dann wieder
auftritt, und poste dann den aktuellen Code ohne Kosmetik und sonstige
Änderungen, am besten als angehängte Datei.

von Jens J. (trial)


Bewertung
0 lesenswert
nicht lesenswert
Ich hatte TIMSK nur eben auf 0x00 gestellt um zu überprüfen ob es daran 
liegen könnte. Im Grunde dürfte der timer_overflow auch Garnicht 
dazwischen grätschen, denn solange On_off = 0 ist, wird ja innerhalb der 
Overflow ISR nichts ausgeführt. Dennoch ist TIMSK nun &= 0x00.
1
#define F_CPU 1000000LU /* Info für delay*/
2
3
4
#include <asf.h> // Bibliothek für STK600 (glaub ich)
5
#include <avr/interrupt.h>
6
#include <util/delay.h>
7
8
volatile char zaehler = 9; // damit eine Hundertstel Sekunde dazu gezählt wird
9
volatile char reset = 0; // für den Reset der Stoppuhr
10
volatile int ausgleich = 0; // Auslgiech für Ungenauigkeit
11
volatile char on_off = 0; // Stoppuhr zählt oder nicht
12
13
14
char segment_code[10]={0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,
15
            0x08,0x09}; //angepasst um die Werte besser in der Simmulation zu sehen
16
            
17
ISR(INT0_vect)
18
{
19
  if ( on_off == 0)
20
  {
21
     on_off = 1;
22
  }
23
  else
24
  {
25
    on_off = 0;
26
  }
27
}        
28
29
ISR(INT1_vect)
30
{
31
  reset = 1;
32
}
33
34
ISR (TIMER0_OVF_vect)
35
{
36
  if (on_off==1)
37
  {
38
    TCNT0=0x9E;
39
    zaehler++;
40
    ausgleich++;
41
    if(ausgleich==284)
42
    {
43
      zaehler++;
44
      ausgleich=0;
45
    }
46
  }
47
}
48
49
50
int main (void)
51
{
52
  /* Insert system clock initialization code here (sysclk_init()). */
53
54
  board_init();
55
56
  /* Insert application code here, after the board has been initialized. */
57
  
58
  char zehntel = 0;
59
  char ein_sec = 0;
60
  char zehn_sec = 0;
61
  char ein_min = 0;
62
  
63
  TCCR0 |= (1<<CS02) | (1<<CS00); 
64
  TIMSK &= 0x00;
65
  
66
  GICR  |= 0xFF;
67
  MCUCR |= 0xFA;
68
  
69
  DDRA = 0xff;
70
  DDRB = 0xff;
71
  DDRD = 0x00;
72
  
73
  sei();
74
  while (1)
75
{
76
    
77
  
78
  if(zaehler>=1) //wenn >1 darf eine zehntel dazu addiert werden
79
{  zaehler--;
80
  
81
    if (zehntel == 9)
82
    {
83
      if (ein_sec == 9)
84
      {
85
        if (zehn_sec == 5)
86
        {
87
          if (ein_min == 9)
88
          {
89
            on_off=0;
90
          } 
91
          else
92
          {
93
            ein_min++;
94
            zehn_sec = 0;
95
            ein_sec = 0;
96
            zehntel = 0;
97
          }
98
        } 
99
        else
100
        {
101
          zehn_sec++;
102
          ein_sec = 0;
103
          zehntel = 0;
104
        }
105
      }
106
      else
107
      {
108
        ein_sec++;
109
        zehntel = 0;
110
      }
111
    } 
112
    else
113
    {
114
      zehntel++;
115
    }
116
} // Ende zähler
117
118
119
120
  
121
  
122
  
123
    if (reset==1)
124
    {
125
      ein_min = 0;
126
      zehn_sec = 0;
127
      ein_sec = 0;
128
      zehntel = 0;
129
      reset = 0;
130
      ausgleich=0;
131
    }
132
    
133
      
134
        PORTA = 0x01;
135
        PORTB = segment_code[zehntel];
136
        //_delay_ms(5);
137
      
138
        PORTA = 0x02;
139
        PORTB = segment_code[ein_sec];
140
        //_delay_ms(5);
141
        
142
        PORTA = 0x03;
143
        PORTB = segment_code[zehn_sec];
144
        //_delay_ms(5);
145
        
146
        PORTA = 0x04;
147
        PORTB = segment_code[ein_min];
148
        //_delay_ms(5);
149
  
150
  
151
  
152
}
153
  
154
  
155
  
156
  return 0;
157
}

: Bearbeitet durch User
von Jens J. (trial)


Bewertung
0 lesenswert
nicht lesenswert
Yalu X. schrieb:

> Bist du sicher, dass der Binärcode, den du im Simulator laufen lässt,
> auch wirklich zu deinem Quellcode gehört? Gibt es im AVR-Studio einen
> Menüpunkt "rebuild all" o.ä., mit dem sichergestellt werden kann, dass
> das komplette Projekt noch einmal vollständig kompiliert und gelinkt
> wird?
>

Ausprobiert habe ich: clean solution, rebuild solution und natürlich 
build solution. Was anderes hab ich noch nicht gefunden.

Grad noch die build(Projektname) punkte probiert und das gleiche. 
Compile bringt uach nichts anderes. Da frag ich mich doch wo da die 
Unterschiede sind.

: Bearbeitet durch User
von M.K. B. (mkbit)


Bewertung
0 lesenswert
nicht lesenswert
Jens N. schrieb:
> mein Code stößt scheinbar deshalb auf Unverständnis weil Ihr das so
> nicht machen würdet.

Ja genau, aber das ist auch nicht böse gemeint. Ich begründe mal, warum 
ich es anders machen würde und du kannst dir ja überlegen, ob und wie du 
das umsetzt.

Der Mikrocontroller arbeitet im Binärsystem. Ich würde deshalb im Code 
Zeiten nicht als Sekunden, Minuten usw. zählen, sondern einfach nur als 
Summe der kleinsten Einheit z.B. Millisekunden. Genauso wenig würde ich 
im 10er System zählen. Die Konvertierung würde ich dann nur für die 
Anzeige machen. Beim Zeitstempel der Systemzeit (UNIX Zeit) ist das auch 
so.
Sollte man die Stoppuhr später erweitern, sodass man Zeiten speichern 
und vergleichen kann wird die Rechnung dadurch viel leichter.

von Marcus O. (marcus6100)


Bewertung
0 lesenswert
nicht lesenswert
Jens N. schrieb:

> if(zaehler>=1) //wenn >1 darf eine zehntel dazu addiert werden

Da stimmt Code und Kommentar aber nicht überein.
if (zaehler >= 1) kann man auch als if (zaehler > 0) schreiben.

von Toggle?! (Gast)


Bewertung
-1 lesenswert
nicht lesenswert
Moin,

Schau dir nochmal an wie man Daten an einen Port setzt. Du sendest dem 
Port einfach knallhart Bitmuster und hast nicht bedacht was mit einem 
Bit passiert das bereits 1 ist und was passiert wenn man eine weitere 1 
hinterher schmeißt.

hust toggle... hust

gruß (:

von Frank M. (ukw) (Moderator) Benutzerseite


Bewertung
0 lesenswert
nicht lesenswert
Toggle?! schrieb:
> hust toggle... hust

Toggle geht nur auf PINx, nicht auf PORTx.

von Phil E. (aktiver_mitleser)


Bewertung
0 lesenswert
nicht lesenswert
Stefanus F. schrieb:
> Zum Durchsteppen mit dem Debugger solltest du den Optimizer
> deaktivieren. Das geht mit der Compiler-Option -O0.

Besser noch, -Og:
Optimize debugging experience. -Og should be the optimization level of 
choice for the standard edit-compile-debug cycle, offering a reasonable 
level of optimization while maintaining fast compilation and a good 
debugging experience. It is a better choice than -O0 for producing 
debuggable code because some compiler passes that collect debug 
information are disabled at -O0.

von Stefan ⛄ F. (stefanus)


Bewertung
0 lesenswert
nicht lesenswert
Philipp M. schrieb:
> Besser noch, -Og:

-Og ist relativ neu, hat nicht jede avr-gcc Version.

von Jens J. (trial)


Bewertung
0 lesenswert
nicht lesenswert
Toggle?! schrieb:
> Moin,
>
> Schau dir nochmal an wie man Daten an einen Port setzt. Du sendest dem
> Port einfach knallhart Bitmuster und hast nicht bedacht was mit einem
> Bit passiert das bereits 1 ist und was passiert wenn man eine weitere 1
> hinterher schmeißt.
>
> hust toggle... *hust*
>
> gruß (:

Das musst du mir näher erklären.

Ich sende die ja per "=" und addiere nicht. Was hab ich da nicht 
verstanden?

von Jens J. (trial)


Bewertung
0 lesenswert
nicht lesenswert
M.K. B. schrieb:

> Ja genau, aber das ist auch nicht böse gemeint. Ich begründe mal, warum
> ich es anders machen würde und du kannst dir ja überlegen, ob und wie du
> das umsetzt.
>
> Der Mikrocontroller arbeitet im Binärsystem. Ich würde deshalb im Code
> Zeiten nicht als Sekunden, Minuten usw. zählen, sondern einfach nur als
> Summe der kleinsten Einheit z.B. Millisekunden. Genauso wenig würde ich
> im 10er System zählen. Die Konvertierung würde ich dann nur für die
> Anzeige machen. Beim Zeitstempel der Systemzeit (UNIX Zeit) ist das auch
> so.
> Sollte man die Stoppuhr später erweitern, sodass man Zeiten speichern
> und vergleichen kann wird die Rechnung dadurch viel leichter.

Mit dem was ich jetzt alles weiß, würde ich das so auch nicht mehr 
bauen. Trotzdem möchte ich nachvollziehen können wieso er so wild 
rumspringt.

Aber: Wenn ich eine INT Variable als zehntel Sekunde deklariere, und die 
dann hochzählen lasse, ist sie dann nicht quasi automatisch im 10er 
System?

von Stefan ⛄ F. (stefanus)


Bewertung
0 lesenswert
nicht lesenswert
Jens N. schrieb:
> Wenn ich eine INT Variable als zehntel Sekunde deklariere, und die
> dann hochzählen lasse, ist sie dann nicht quasi automatisch im 10er
> System?

Nein. Der Computer arbeitet immer noch mit Binärzahlen, nur dass deine 
Einheit dann 1/10 Sekunde ist, statt Sekunde. Ist bei den sonst üblichen 
Millisekunden nicht anders.

von Frank M. (ukw) (Moderator) Benutzerseite


Bewertung
0 lesenswert
nicht lesenswert
Jens N. schrieb:
> Das musst du mir näher erklären.
>
> Ich sende die ja per "=" und addiere nicht. Was hab ich da nicht
> verstanden?

Vergiss es, was "Toggle" Dir schrieb. Das war einfach Unsinn.

Neuere AVR besitzen das Feature, dass ein Schreiben von Einsen auf PINx 
(nicht PORTx!) ein Toggle der jeweiligen Pins bewirkt. Da Du gar nicht 
auf PINx schreibst, ist das für Dich ohne Belang.

von Jens J. (trial)


Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Soooo….

Ich habe nun mit neuem Wissen, von hier und aus anderen Quellen, eine 
neue Stoppuhr zusammen gebastelt.

Sie tut sogar was sie soll =)

Ich hatte allerdings Probleme bool variablen zu nutzen. Das soll in C ja 
jetzt gehen und sie sollen mit _bool betitelt werden. Das schlägt mir 
der Compiler auch vor, doch gibt's da immer Fehler Meldungen.
Die dabei auftauchende Fehlermeldung blieb auch, nachdem ich das _bool 
entfernt hatte und so musste ich den Code in ein neues Projekt einfügen. 
Fand ich schon ziemlich merkwürdig. vllt. würde das ja auch bei meinem 
ursprünglichem Problem helfen, hmmm.

Gibt's eigentlich irgendwo eine Liste mit den ganzen 
Bibliotheksfunktionen die man im Alltag so braucht?

vllt. hat auch einer Lust mir zu sagen ob das entprellen der Taster so 
Praxis tauglich ist.

Nachtrag: Müsste man nicht, um eine genauere Stoppuhr zu haben, auf den 
Preload soviele Takte hinzu aaddieren wie sie in Summe vom Overflow bis 
zum neuen Preload verbraucht werden?

: Bearbeitet durch User
von spess53 (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Hi

>Müsste man nicht, um eine genauere Stoppuhr zu haben, auf den
>Preload soviele Takte hinzu aaddieren wie sie in Summe vom Overflow bis
>zum neuen Preload verbraucht werden?

Nein, man benutzt einfach den CTC-Mode und nicht dieses vorsinflutliche 
Preload.

MfG Spess

von M.K. B. (mkbit)


Bewertung
0 lesenswert
nicht lesenswert
Jens N. schrieb:
> Gibt's eigentlich irgendwo eine Liste mit den ganzen
> Bibliotheksfunktionen die man im Alltag so braucht?

Die Funktionen, die in der C Standardbibliothek sind findest du hier. 
https://en.cppreference.com/w/c

von M.K. B. (mkbit)


Bewertung
0 lesenswert
nicht lesenswert
Ich hab mir mal den Code angeschaut. Deine Lesen und Schreiben auf den 
Port ist nicht ganz korrekt.

Beispiel:
1
if(PIND == 0x01)
Du vergleichst hier den ganzen Port D. Drückt dein Benutzer beide Tasten 
gleichzeitig, dann hat PIND aber den Wert 0x03.
Besser:
1
if(PIND & 0x01)

Das selbe gilt auch für das Schreiben auf PORTA bzw. PORTB, wenn du noch 
andere Pins an den Ports hängen hast.
Beispiel, wenn PORTA nur 3 Bits für die Displayübertragung benutzt (ich 
kenne deine Setup nicht genau).
1
PORTA = (PORTA & ~0x07) | (wert & 0x07)
2
// Das & 0x07 stellt sicher, dass der Wert nicht aus dem Bereich rausschreibt.
3
// Bei Konstanten kann man das auch weglassen.

Ich hoffe ich hab jetzt auf die Schnelle alles richtig gemacht.

Außerdem hat deine Umrechung noch eine Macke (vielleicht auch mehrere).
Dein zaehler hat die Einheit 0.01s. Wenn zaehler den Wert 6000 hat, dann 
würde ich min=1 erwarten. zaehler=10000 ist auch lustig.

von Jens J. (trial)


Bewertung
0 lesenswert
nicht lesenswert
M.K. B. schrieb:

> Beispiel:
>
1
if(PIND == 0x01)
> Du vergleichst hier den ganzen Port D. Drückt dein Benutzer beide Tasten
> gleichzeitig, dann hat PIND aber den Wert 0x03.
> Besser:
>
1
if(PIND & 0x01)

Das gleichzeitige drücken von Stopp und Reset habe ich mit Absicht 
ignoriert, man kann eben nicht alles haben ;) Das mit den "&" macht voll 
sinn, danke.

>
1
> PORTA = (PORTA & ~0x07) | (wert & 0x07)
2
> // Das & 0x07 stellt sicher, dass der Wert nicht aus dem Bereich 
3
> rausschreibt.
4
>

Das muss ich mir nochmal in Ruhe angucken...

>
> Außerdem hat deine Umrechung noch eine Macke (vielleicht auch mehrere).
> Dein zaehler hat die Einheit 0.01s. Wenn zaehler den Wert 6000 hat, dann
> würde ich min=1 erwarten. zaehler=10000 ist auch lustig.

Jup, da habe ich mich um eine Zehnerpotenz verhauen.


Ansonsten nochmal was grundsätzliches:
Der Code wird ja von oben nach unten abgearbeitet. Demnach würden oben 
erst alle Stellen ausgerechnet werden, und dann unten der Reihe nach 
ausgegeben. eine Änderung während der Ausgabe würde daher garnicht 
erfasst, bzw. erst im nächsten durchlauf ja? Denke das wäre wichtig 
falls ich sowas mal programmiere und die Werte sich schneller ändern als 
ein Durchgang der 7 Segment anzeige dauert.

: Bearbeitet durch User
von M.K. B. (mkbit)


Bewertung
0 lesenswert
nicht lesenswert
Jens N. schrieb:
> Denke das wäre wichtig falls ich sowas mal programmiere und die Werte
> sich schneller ändern als ein Durchgang der 7 Segment anzeige dauert.

Ja richtig, wenn deine Anzeige langsamer als die Zeit ist, dann sollte 
die Anzeige aber immerhin konsistent sein.
Dann würde ich aber auch am Anfang der Schleife den globalen Zähler in 
eine lokale Variable kopieren. Wenn dein Timerinterrupt z.B. zwischen 
dem Berechnen der Minuten und Sekunden zuschlägt und du genau bei 59 
Sekunden bist, dann könnte deine Anzeige kurzzeitig 1:59 anzeigen und 
danach dann 1:00.

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.