Forum: Compiler & IDEs Falsche Daten auf LCD


von R. S. (relaxed)


Lesenswert?

Hallo miteinander,


ich bin relativ neu in der Mikrocontroller-Programmierung. Ich habe mit 
Arduino angefangen und bin mittlerweile mit normaler C-Programmierung am 
experimentieren.

Nun habe ich ein kleines Problem. Zwar habe einen Testaufbau mit einem 
Atmega 328p, einem Drehgeber, einem LCD, ein paar Tastern und LEDs.

Auf dem LCD lasse ich die Zustände der LEDs, die Position des Drehgebers 
und einen Sekundenzähler anzeigen.

Ab und zu habe ich das Problem, dass es mir die Position des Drehgebers 
an einer anderen Position auf dem LCD nochmals anzeigt.

Ich habe das Gefühl, dass der Befehl zum ausgeben des Drehgeberwertes 
vom Drehgeberinterrupt gestört wird und so der Fehler entsteht. Jedoch 
weiss ich nicht wie man dies anders lösen könnte.

Ich benutze übrigens die LCD Library von Peter Fleury.


Ich weiss, dass die Programmierung ziemlich lausig ist, ich bin noch 
Anfänger und hatte nie eine richtige Einführung zum Thema.
Gewisse Dinge wurden weggelassen wie z.B. debouncing der Taster und des 
Drehgebers(Hardwaremässig gelöst) und die Drehgeberauswertung ist auch 
ziemlich schlecht.



Danke und Gruss
Robin




Hier noch das Programm:

1
#define F_CPU 16000000
2
3
4
#include <avr/io.h>
5
#include <util/delay.h>
6
#include <avr/interrupt.h>
7
8
9
#include "lcd.h"
10
11
12
char Buffer[10];
13
14
int encoderpos = 50;
15
int encoderpos_last = 0;
16
int n = 0;
17
18
int extraTime = 0;
19
20
21
int counter = 101;
22
int sek = 0;
23
24
25
int encoder_flag = 0;
26
int timer_flag = 0;
27
28
29
#define encoderA  (PINC & 1<<PC4)
30
#define encoderB  (PIND & 1<<PD5) 
31
32
int encoderA_state = 0;
33
int encoderB_state = 0;
34
int encoderA_state_last = 0;
35
36
    
37
38
39
40
41
int main() {
42
  
43
  lcd_init(LCD_DISP_ON);
44
  lcd_clrscr();
45
  
46
  
47
  DDRB |= 0b00000001;          
48
  DDRD |= 0b00001100;
49
50
  TCCR0A = (1 << WGM01);
51
  OCR0A = 156;
52
  TIMSK0 = (1 << OCIE0A);
53
  
54
  sei();
55
  
56
  TCCR0B = (1 << CS02) | (1 << CS00);
57
  
58
  
59
  init_encoder();
60
  
61
  
62
  
63
  
64
    while (1) {
65
      
66
      
67
68
      check_buttons();
69
      
70
      read_encoder();
71
          
72
      update_display();
73
      
74
      set_outputs();
75
76
77
78
      
79
    }//end while(1)
80
    
81
    
82
}//end main()
83
  
84
  
85
  
86
  ISR (PCINT1_vect)
87
    {
88
      
89
      cli();
90
      
91
      encoder_flag = 1;
92
      
93
      sei();
94
      
95
    }
96
    
97
  ISR (TIMER0_COMPA_vect)
98
    {
99
      
100
      cli();
101
      
102
      timer_flag = 1;
103
      
104
      sei();
105
      
106
    }
107
    
108
  
109
  void init_encoder()
110
    {
111
      
112
      PCICR = (1<<PCIE1);
113
      PCMSK1 = (1<<PCINT12);
114
      
115
      sei();
116
      
117
    }
118
  
119
  
120
  
121
  void check_buttons()
122
  {
123
    
124
         if (PIND & (1<<PD1))
125
         {
126
           
127
           PORTD ^= (1<<PD2);
128
           _delay_ms(200);
129
           
130
           
131
           lcd_home();
132
           
133
           if (PIND & (1<<PD2))
134
           {
135
             lcd_puts("                    ");
136
             lcd_home();
137
             lcd_puts("Gruene LED:      ein");
138
           }
139
           
140
           else {
141
             
142
             lcd_puts("                    ");
143
             lcd_home();
144
             lcd_puts("Gruene LED:      aus");
145
146
           }
147
           
148
           
149
         }
150
         
151
         
152
         else if (PIND & (1<<PD6))
153
         {
154
           
155
           PORTD ^= (1<<PD3);
156
           _delay_ms(200);
157
           
158
           lcd_gotoxy(0,1);
159
           
160
           if (PIND & (1<<PD3))
161
           {
162
             lcd_puts("                    ");
163
             lcd_gotoxy(0,1);
164
             
165
             lcd_puts("Rote   LED:      ein");
166
167
           }
168
           
169
           else {
170
             
171
             lcd_puts("                    ");
172
             lcd_gotoxy(0,1);
173
             lcd_puts("Rote   LED:      aus");
174
             
175
           }
176
           
177
         }
178
         
179
         else if (PIND & (1<<PD7))
180
         {
181
           
182
           PORTB ^= (1<<PB0);
183
           _delay_ms(200);
184
           
185
           lcd_gotoxy(0,2);
186
           
187
           if (PINB & (1<<PB0))
188
           {
189
             
190
             lcd_puts("                    ");
191
             lcd_gotoxy(0,2);
192
             lcd_puts("Weisse LED:      ein");
193
             
194
           }
195
           
196
           else {
197
             
198
             lcd_puts("                    ");
199
             lcd_gotoxy(0,2);
200
             lcd_puts("Weisse LED:      aus");
201
202
           }
203
           
204
         }
205
    
206
  }
207
  
208
  void update_display()
209
  {
210
    
211
    if ((encoderpos_last > 9) && (encoderpos < 10)){
212
      lcd_gotoxy(0,3);
213
      lcd_puts("   ");
214
    }
215
216
    if ((encoderpos_last > 99) && (encoderpos < 100)){
217
      lcd_gotoxy(0,3);
218
      lcd_puts("  ");
219
    }
220
    
221
    if ((encoderpos_last > 999) && (encoderpos < 1000)){
222
      lcd_gotoxy(0,3);
223
      lcd_puts(" ");
224
    }
225
    
226
    
227
    
228
    if (encoderpos < 10){
229
230
      lcd_gotoxy(3,3);
231
    }
232
    
233
    
234
    if (encoderpos > 9){
235
      
236
      lcd_gotoxy(2,3);
237
    }
238
    
239
    if (encoderpos > 99){
240
      
241
      lcd_gotoxy(1,3);
242
    }
243
    
244
    if (encoderpos > 999){
245
      
246
      lcd_gotoxy(0,3);
247
    }
248
    
249
    
250
    itoa(encoderpos, Buffer, 10);
251
    lcd_puts(Buffer);
252
    
253
    encoderpos_last = encoderpos;
254
    
255
  }
256
257
  void read_encoder()
258
  {
259
    
260
    if (encoder_flag == 1)
261
    {
262
      
263
      
264
      if (encoderA){
265
        
266
        encoderA_state = 1;
267
      }
268
      
269
      else {
270
        encoderA_state = 0;
271
      }
272
      
273
      
274
      if (encoderB){
275
        
276
        encoderB_state = 1;
277
      }
278
      
279
      else {
280
        encoderB_state = 0;
281
      }
282
      
283
      
284
      
285
      if ((encoderA_state_last == 0) && (encoderA_state == 1)){
286
        
287
        if (encoderB_state == 0){
288
          
289
          if (encoderpos < 1){
290
            
291
            encoderpos = 0;
292
          }
293
          
294
          else {
295
            
296
            encoderpos--;
297
          }
298
          
299
        }
300
        
301
        else {
302
          encoderpos++;
303
          
304
        }
305
        
306
      }
307
      
308
      encoderA_state_last = encoderA_state;
309
      
310
      encoder_flag = 0;
311
  }
312
  }
313
  
314
  void set_outputs()
315
  {
316
    
317
          if (timer_flag == 1)
318
          {
319
            
320
            extraTime++;
321
            
322
            if (extraTime > (encoderpos))
323
                {
324
              
325
              PORTD ^= (1 << PD2);
326
              extraTime = 0;
327
              
328
            }
329
            
330
            sekunden();
331
            
332
            timer_flag = 0;
333
            
334
          }
335
    
336
  }
337
  
338
  void sekunden()
339
  {
340
    
341
    if (counter > 100)
342
    {
343
      sek++;
344
      lcd_gotoxy(9,3);
345
      lcd_puts("Time:");
346
      lcd_gotoxy(15,3);
347
      itoa(sek, Buffer, 10);
348
      lcd_puts(Buffer);
349
      counter = 0;
350
    }
351
    
352
    else {
353
      
354
      counter++;
355
      
356
    }
357
    
358
  }

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

Du behandelst zb negative Zahlen nicht richtig.

Ersetz mal deine komplette UpdateDisplay durch diese hier
1
void update_display()
2
{
3
  char buff[10];
4
5
  sprintf( buff, "%4d", encoderpos );
6
  lcd_gotoxy( 0, 3 );
7
  lcd_puts( buff );
8
9
  encoderpos_last = encoderpos;
10
}


PS:
Die sei() und cli() in den ISR sind kontrapoduktiv. Die willst du da 
nicht haben. Wobei der anfängliche cli() harmlos ist, denn während eine 
ISR läuft, sind die Interrupts sowieso automatisch gesperrt. Aber der 
sei() kommt zu früh! Allerdings musst du dich auch darum nicht kümmern, 
denn die Interrupts werden bei Beendigung einer ISR automatisch wieder 
freigegeben. Und zwar zum richtigen Zeitpunkt, so dass sich Interrupts 
nicht endlos ineinander schachteln können.

von R. S. (relaxed)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Ersetz mal deine komplette UpdateDisplay durch diese hier



Danke für die Hilfe. Habe die Interrupts bereinigt und das 
update_display() ersetzt.

Das mit dem %4d ist sehr hilfreich. Habe mich immer gefragt ob man das 
Problem nicht einfach lösen könnte.


Es funktioniert alles noch wie gewohnt. Jedoch kriege ich ab und zu 
immer noch Zahlen an falschen Stellen. Es scheint ziemlich zufällig zu 
passieren. Manchmal kann ich minutenlang am Drehgeber drehen, ohne ein 
Problem und manchmal passiert es schon beim ersten Dreh.

von Karl H. (kbuchegg)


Lesenswert?

> und des Drehgebers(Hardwaremässig gelöst)

was ist darunter zu verstehen?
Dir ist bewusst, dass du mit einer falsch ausgelegten 
Hardware-Entprellung den µC massiv stören kannst? Bis hin zum Absturz 
des µC?

: Bearbeitet durch User
von Mr. X (Gast)


Lesenswert?

R. S. schrieb:
> debouncing ... des Drehgebers(Hardwaremässig gelöst)
Was willst du mit Debouncing beim Drehgeber?

Bei Grey-Code, wie er vom Drehgeber generiert wird, hat ein Prellen der 
Kontakte keinen störenden Einfluß auf den Zählerstand. Der Code wurde 
genau für Abtastung von mehrphasigen Zählsignalen entwickelt.

von R. S. (relaxed)


Lesenswert?

Karl Heinz Buchegger schrieb:
> was ist darunter zu verstehen?


Ich habe jeweils einen 0.1 uF Keramikkondensator zwischen den Outputs 
und Ground.


Dies funktioniert bis jetzt einwandfrei. Dies aber auch nur, da ich 
einen relativ hochwertigen Alps Drehgeber verwende. Bei einem no-name 
Drehgeber hatte ich mit meinem Code grosse Schwierigkeiten.

Ich habe diesen Code verwendet, da ich mit den anderen gefundenen 
Beispielen nichts habe anfangen können. Dies liegt daran, dass sie für 
mich zu kompliziert aufgebaut waren und ich den Code nicht verstanden 
habe.

Ich werde mich nochmal damit auseinander setzten müssen.


Meinst du das LCD Problem kann da her kommen?

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Die beiden in den ISR gesetzten Variablen müssen als volatile definiert 
werden, also

volatile int encoder_flag = 0;
volatile int timer_flag = 0;

Warum verschwendest Du für zwei simple Flags einen ganzen int? uint8_t 
wäre angebrachter, damit das Rücksetzen der beiden Flags auch atomar 
passieren kann - außerhalb einer ISR schon sinnvoll.

Also:

volatile uint8_t encoder_flag = 0;
volatile uint8_t timer_flag = 0;

Weiterhin solltest Du prüfen, ob weitere Variablen nicht zumindest als 
unsigned erklärt werden könnten.

: Bearbeitet durch Moderator
von Karl H. (kbuchegg)


Lesenswert?

R. S. schrieb:
> Karl Heinz Buchegger schrieb:
>> was ist darunter zu verstehen?
>
>
> Ich habe jeweils einen 0.1 uF Keramikkondensator zwischen den Outputs
> und Ground.

Jau. Das hatte Pollin auch.
Mit dem Effekt, dass deren Boards auch immer abgestürzt und seltsame 
Sachen gemacht haben.

Jedesmal, wenn du den Taster schliesst, erzeugt dein Kondensator einen 
kleinen Kurzschluss, wenn er geladen wird.

von R. S. (relaxed)


Lesenswert?

Frank M. schrieb:
> Die beiden in den ISR gesetzten Variablen müssen als volatile definiert
> werden

Danke für den Tipp.

Den Integer volatile zu deklarieren ist vor allem dann wichtig wenn der 
Compiler den Code optimiert, oder? Bei einer Debug Version sollte dies 
jedoch keinen Einfluss haben, richtig?


Karl Heinz Buchegger schrieb:
> Jedesmal, wenn du den Taster schliesst, erzeugt dein Kondensator einen
> kleinen Kurzschluss, wenn er geladen wird.

So weit ich informiert bin, besteht die Gefahr hauptsächlich darin, dass 
wenn der Drehgeber zwischen zwei Pinstates alterniert, das Programm 
ständig Interrupts ausführt und so die anderen Funktionen blockiert.

Die Kondensatoren werden ja durch die Pull-up Widerstände geladen. 
Kannst du bitte noch genauer erläutern, was das für einen Einfluss auf 
die Stabilität haben könnte?

von Karl H. (kbuchegg)


Lesenswert?

R. S. schrieb:

> Die Kondensatoren werden ja durch die Pull-up Widerstände geladen.
Wo kommen da jetzt auf einmal Pull-up Widerstände her?

Dann eben bei dir anders rum. Wird der Taster betätigt, dann schliesst 
er den Kondensator kurz. Auch nicht gut.

> Kannst du bitte noch genauer erläutern, was das für einen Einfluss auf
> die Stabilität haben könnte?

Kurzschlüsse sind nie gut, egal wie kurz sie sind. Sie verursachen 
zumindest Spikes auf der Versorgungsspannung, die wiederrum das 
restliche System gefährden können.

Such im Forum nach der MOdifikation des Pollin Boards. Normalerweise 
steht da dann die Erklärung dabei (oder es gibt einen Link darauf)

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

R. S. schrieb:
> Den Integer volatile zu deklarieren ist vor allem dann wichtig wenn der
> Compiler den Code optimiert, oder? Bei einer Debug Version sollte dies
> jedoch keinen Einfluss haben, richtig?

Für mich ist diese Regel unabhängig vom Optimierungsgrad des Compilers. 
Ich machs einfach grundsätzlich, um mich an die Regel zu halten. Diese 
heisst: Wird eine globale Variable in einer ISR und außerhalb einer ISR 
benutzt, muss die Variable als volatile deklariert werden.

Und ich würde niemals den Optimierer ausschalten, weil ich evtl. mit 
Optimierer ein Laufzeitproblem mit meinem Programm sehe. Damit verstecke 
ich nur meine eigenen Programmierfehler.

Ausserdem: Im Gegensatz zu vielen anderen Compilern kann man beim gcc 
den Debug-Mode unabhängig vom Optimierungsgrad wählen. D.h. wenn ich ein 
Programm mit Debug-Mode übersetze, habe ich deswegen noch lange nicht 
den Optimierer abgeschaltet.

von R. S. (relaxed)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Wo kommen da jetzt auf einmal Pull-up Widerstände her?


Mein Drehgeber ist wie folgt angeschlossen:

http://www.gammon.com.au/images/Rotary_Encoder_Schematic.png


Jedoch hab ich von A zu VCC und von B zu VCC 10k Pull-ups damit die 
μC-Eingänge nicht in der Luft hängen.

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.