Forum: Mikrocontroller und Digitale Elektronik Atmega spinnt, weil er nichts zu tun hat??


von Carsten (Gast)


Lesenswert?

Hallo,
ich zweifle gerade an meinem logischen Denken.. Folgendes:
Poti an ATMEGA, Ausgabe des Wertes (0-1023) auf ein LCD Display.
Wenn ich diesen Code so ausführe, zeigt mir mein Display immer den 
korrekten Wert des ADC.

Jetzt will ich aber, dass das Display nur refresht wird, wenn sich der 
Wert ändert. Ich füge also die mit 1 bis 3 markierten Zeilen in die 
if-Abfrage (Sternchen) ein.

Aber jetzt friert mein Controller nach wenigen Zyklen ein, dh. es wird 
ein Wert angezeigt, aber am Poti drehen bewirkt gar nichts mehr.
Wenn ich schnell genug drehe, kann ich den Zeitpunkt hinauszögern...

Kann es sein, dass der Controller spinnt, weil er nichts zu tun hat??

Für Tips wäre ich sehr dankbar!

1
while(1){
2
3
aktuellerAdcWert = ADC_auslesen(0);
4
5
lcdClear();                          // 1
6
lcdWriteString("Wert: ");            // 2
7
lcdWriteNumber(aktuellerAdcWert);    // 3
8
9
if(aktuellerAdcWert != alterWert){
10
     alterWert = aktuellerAdcWert;
11
     // ***********
12
     }
13
14
}

von Paul P. (dorpreuss)


Lesenswert?

Hallo,

alterWert = aktuellerAdcWert muss außerhalb der Bedingung stehen.

von bitte löschen (Gast)


Lesenswert?

Er hat ja zu tun. Er liest ständig ADC-Werte ein.

Zeig' doch mal das ganze Listing und einen Schaltplan, dass man Deinen 
Aufbau exakt reproduzieren kann. Meist liegt der Fehler doch ganz 
woanders..

von Chris D. (m8nix)


Lesenswert?

Nur so ne Idee.....

kann es sein das die Funktion " ADC_auslesen(0)" schon eine neue 
Wandlung startet bevor die alte beendet ist ???

Gruß
Chris

von Carsten G. (carry)


Lesenswert?

Hi,

wie genau sieht denn 'ADC_auslesen' aus?

Ich könnte mir Vorstellen, dass der ständige Aufruf in der 
While-Schleife zu einem Fehler führt, da evtl. nicht korrekt gewartet 
wird bis der ADC fertig ist. Durch die LCD Routinen (außerhalb der 
IF-Abfrage) wird wahrscheinlich der Aufruf genügend lange verzögert, so 
dass der Fehler nicht auffält.


Carsten G.

von bitte löschen (Gast)


Lesenswert?

Paul P. schrieb:
> alterWert = aktuellerAdcWert muss außerhalb der Bedingung stehen.

Und das ändert .., weil ..?

von Paul P. (dorpreuss)


Lesenswert?

Hehe,

das ändert wirklich nichts... Ich hatte mal so ein ähnliches Beispiel 
und da war es so, wie ich geschrieben hatte. Vergesst es einfach, ich 
habe nicht richtig hingeschaut und bitte um Entschuldigung.

Wenn das ganze aber vorher funktioniert hat, denke ich nicht, dass es an 
irgend einem Timing liegt. Man kann zwar in die ADC_auslesen-Funktion 
nicht reinschauen...

Vielleicht ist alterWert nur ein 8-Bit Typ und es hängt da irgendwo.

von Carsten (Gast)


Lesenswert?

Danke für die Antworten schonmal!

Hier ist der gesamte Code.
Der ADC-Wert wird erst ausgelesen, wenn er fertig ist. Von daher glaube 
ich nicht, dass es für den ADC zu schnell geht..


1
#include <avr/io.h>
2
#include <avr/interrupt.h>
3
#define F_CPU 1000000
4
#include <util/delay.h>
5
6
#define  DB4     PD0 //Datenbit 5 
7
#define  DB5     PD1  
8
#define  DB6     PD2  
9
#define  DB7     PD3 
10
#define RegSelect   PD4
11
#define RW      PD5
12
#define Enable     PD6 
13
#define LCD_Port   PORTD
14
#define enableDelay  1000 //µs
15
 
16
#define  Poti1    PC0
17
#define Backlight  PC5
18
19
//Variablen----------------------------------
20
char AusgabeString[];
21
uint8_t Interrupt0Zaehler =0 ;
22
uint8_t sekunde = 0;
23
24
uint16_t alterAdcWert =0;
25
uint16_t aktuellerAdcWert=0; 
26
27
//Prototypen der Funktionen------------------
28
void adcInit(void);
29
void lcdInit(void);
30
void enable(void);
31
void lcdClear(void);
32
void schreibeZeichen(unsigned char temp1);
33
void lcdWriteString(char *data);
34
void lcdWriteNumber(uint16_t z);
35
uint16_t ADC_auslesen(uint8_t adcSource);
36
37
//ISR-------------------------------------------
38
ISR (TIMER0_COMPA_vect){
39
Interrupt0Zaehler++;
40
}
41
42
43
44
int main (void) {
45
DDRB  =  0b00;      // Taster
46
DDRC  =  0b00100000;    // ADC und Backlight
47
PORTC  =  0x00;
48
DDRD  =  0xff;        // LCD (Ausgang) 
49
50
51
//Timer für Sekunden
52
TCCR0A    |=  (1<<WGM01);        //löscht timer wert bei interrupt
53
TCCR0B    |=  ((1<<CS01)|(1<<CS00));  // Prescaler 64
54
OCR0A     =  0x7D;          //8 Bit Register mit Wert, bei dem der Compare Match etzeugt wird
55
TIMSK0    |=  (1<<OCIE0A);       //Timer Interrupt Mask Register, Enable Compare Match Interrupt
56
TIFR0    |=  (1<<OCF1A);        //Flag (durch setzen von 1!) löschen, damit nicht sofort in ISR(comp_vect) gesprungen wird
57
58
59
60
//------------------
61
adcInit();
62
lcdInit();
63
sei();
64
65
while(1){
66
67
aktuellerAdcWert = ADC_auslesen(0)*5;
68
69
lcdClear();
70
lcdWriteString("Wert: ");
71
lcdWriteNumber(aktuellerAdcWert);
72
73
74
if(aktuellerAdcWert != alterAdcWert){
75
alterAdcWert = aktuellerAdcWert;
76
}
77
78
79
}//while..
80
}//main
81
82
83
84
85
86
87
//Funktionen------------------------------------
88
void adcInit(void){
89
//AD Wandler
90
ADMUX  = 0x00;
91
ADMUX  |=  (1<<REFS0); //interne Referenz Vcc
92
ADCSRA  |=  ((1<<ADEN)|(1<<ADSC)|(1<<ADPS0)|(1<<ADPS2)|(1<<ADPS1));//AD enable, AD Prescaler (ADC sollte zw. 50 und 100 kHz liegen, hier PSC=64)
93
//ERGEBNIS STEHT IN ADCH und ADCL (10Bit)
94
95
//Dummyreadout
96
while ( ADCSRA & (1<<ADSC) ) {} // auf Abschluss der Konvertierung warten}
97
uint16_t dummyResult = ADCW;
98
}
99
100
101
void lcdInit(void){
102
  LCD_Port  =  0x00;
103
  _delay_ms(20);    //Boot Zeit
104
105
  LCD_Port  |=  ((1<<DB4)|(1<<DB5));
106
  _delay_ms(5);
107
  enable();
108
109
  _delay_ms(5);
110
  enable();
111
112
  _delay_ms(5);
113
  enable();
114
115
  _delay_ms(5);
116
117
  //--------------------------------------------------------
118
  LCD_Port  = 0x00;
119
  LCD_Port  |=  (1<<DB5); //4 Bit Ansteuerung
120
  _delay_ms(5);
121
  enable();
122
  //--------------------------------------------------------
123
//  LCD_Port  |=  (1<<DB5); //2-zeilig, 5x8
124
  _delay_ms(5);
125
  enable();
126
  LCD_Port  |=  (1<<DB7); 
127
  _delay_ms(5);
128
  enable();
129
  //--------------------------------------------------------
130
131
132
133
  LCD_Port  = 0x00;
134
  _delay_ms(5);
135
  enable();
136
  LCD_Port  |= ((1<<DB7)|(1<<DB6));//|(1<<DB5)|(1<<DB4));
137
  _delay_ms(5);
138
  enable();
139
  _delay_ms(10);
140
141
}
142
143
void enable(void) {  
144
        
145
  LCD_Port    |= (1<<Enable);
146
  _delay_us(enableDelay);
147
  LCD_Port    &= ~(1<<Enable);
148
  _delay_us(enableDelay);
149
}
150
151
152
153
void lcdClear(void){
154
  LCD_Port &=~(1<<RegSelect);      //jetzt kommt Steuerbefehl
155
  _delay_us(10);
156
  LCD_Port  =  0x00;  //Display löschen
157
  enable();
158
  LCD_Port  |=  (1<<DB4); 
159
  enable();
160
161
}
162
163
164
165
166
167
void schreibeZeichen(unsigned char temp1)
168
{
169
   unsigned char temp2 = temp1;
170
 
171
   LCD_Port |= (1<<RegSelect);        // RS auf 1 setzen
172
 
173
   temp1 = temp1 >> 4;        // übergebenes Byte wird um 4 Stellen nach rechts verschoben, links 4 Nullen
174
   temp1 = temp1 & 0x0F;      // Obere 4 Bit bleiben wie sie sind, untere werden gelöscht
175
   LCD_Port &= 0xF0;        // Untere 4 Bit werden gesetzt zur Übertragung der ersten 4 Bit
176
   LCD_Port |= temp1;               // setzen
177
   enable();
178
   //Das Selbe für die zweiten 4 Bit
179
   temp2 = temp2 & 0x0F;
180
   LCD_Port &= 0xF0;
181
   LCD_Port |= temp2;               // setzen
182
   enable();
183
   
184
   _delay_us(5);
185
}
186
187
188
// Schreibt einen String auf das LCD
189
 
190
void lcdWriteString(char *data){
191
  char *data_2;
192
  data_2=data;//String merken
193
194
// Schreibt die ersten 16 zeichen des Strings
195
  
196
  while(*data){
197
  schreibeZeichen(*data);
198
    data++;
199
  }
200
}
201
202
203
void lcdWriteNumber(uint16_t z)
204
{
205
    uint16_t i;
206
  uint16_t temp;
207
  uint16_t Dez;
208
    uint8_t k;                      // Merker ob vorne 0 ist
209
    k=0;
210
    Dez=100;                  // Dezimalstellen, z.B. bei 2 Stellen 10 eintragen
211
    while (Dez)                 // Solange Dezimalstelle nicht 0 ist
212
    {
213
        if(Dez==1) k=1;         // Um ein einzige 0 anzeigen können
214
        i=z/Dez;                // Wert der jeweilige Dezimalstelle bestimmen
215
        temp=i*Dez;
216
        z=z-temp;
217
        if(i||k)                // Damit wir vorne keine Nullen stehen haben.
218
        {
219
            schreibeZeichen((char)i+0x30);
220
            k=1;                // es gab schon eine Zahl die nicht 0 ist
221
        }
222
        else                    // wen 0 ist und vorne keine Zahl war, dann
223
        {
224
            schreibeZeichen(' ');  // Leerzeichen ausgeben
225
        }
226
        Dez=Dez/10;
227
    }
228
}
229
230
uint16_t ADC_auslesen(uint8_t adcSource){
231
232
uint16_t adcResult =0;
233
ADMUX = (ADMUX & ~(0x1F)) | (adcSource & 0x1F);
234
235
for (uint8_t i=0;i<8;i++){
236
ADCSRA  |=  (1<<ADSC); //Start
237
while  ((ADCSRA & (1<<ADSC))==0){}
238
239
adcResult += ADCW;}
240
return (uint16_t)(adcResult/128);
241
242
243
}

von Chris D. (m8nix)


Lesenswert?

> Hi,

> wie genau sieht denn 'ADC_auslesen' aus?

> Ich könnte mir Vorstellen, dass der ständige Aufruf in der
> While-Schleife zu einem Fehler führt, da evtl. nicht korrekt gewartet
> wird bis der ADC fertig ist. Durch die LCD Routinen (außerhalb der
> IF-Abfrage) wird wahrscheinlich der Aufruf genügend lange verzögert, so
> dass der Fehler nicht auffält.

.... möglich :-)
Bau doch mal zu Versuchszwecken ein Delay ausserhalb Deiner Schleife 
ein, was länger ist als die Wandlungszeit.


öhm Edit..... Ausserhalb der if-Bedingung..

von bitte löschen (Gast)


Lesenswert?

Ich weiß, dass sich der entsprechende Schaltplan aus dem Listing 
extrapolieren lässt, aber es ist sicher einfacher für alle, wenn Du ihn 
schnell hier herein stellst.

Wenn Du noch einen Pin frei hast, könntest Du den am Anfang von 
ADC_auslesen high und am Ende auf low setzten, und schauen, was das Oszi 
an dem Pin sagt.

Vielleicht baue ich das morgen mal nach..

von Carsten (Gast)


Lesenswert?

Super!

Hat geklappt!!


DANKE!


ich dachte eigentlich, dass man mit der zeile
while  ((ADCSRA & (1<<ADSC))==0){}

wartet, bis die conversion fertig ist...  verstehe ich irgendwie nicht.
Aber schön dass es geht!

von Paul P. (dorpreuss)


Lesenswert?

((ADCSRA & (1<<ADSC))==0){} ?

ADSC will read as one as long as a conversion is in progress. When the 
conversion is complete,  it returns to zero. Writing zero to this bit 
has no effect.

Das kann doch vorher nie funktioniert haben!?

von Chris D. (m8nix)


Lesenswert?

Wieso nicht?

vorher war immer die LCD-Routine mit entsprechendem Delay im Weg.

von Paul P. (dorpreuss)


Lesenswert?

Schade zu spät. Ich dachte schon ich könnte meinen Fauxpas ausbügeln.

Die Hauptsache ist, dass es jetzt funktioniert.

von Chris D. (m8nix)


Lesenswert?

Das ist doch egal...

wichtig ist das Ergebnis

von Paul P. (dorpreuss)


Lesenswert?

Chris D. schrieb:
> Wieso nicht?
>
> vorher war immer die LCD-Routine mit entsprechendem Delay im Weg.

Ja, aber in der for-Schleife wird der Mittelwert über 8 Wandlungen 
gebildet die aber eigentlich nur aus einer Messung besteht, weil das 
blockierende Polling nicht greift.

von Chris D. (m8nix)


Lesenswert?

Hm.... also ich mache das immer so:

nachdem ich einen ADC-Wert eingelesen hab, starte ich schon die nächste 
Wandlung, ob ich die nun benötige oder nicht. Wenn ich sie benötige, 
erspare ich mir meist die Wartezeit bis das Wandlerflag gesetzt ist.

von Michel (Gast)


Lesenswert?

Paul P. schrieb:
> alterWert = aktuellerAdcWert muss außerhalb der Bedingung stehen.

Wieso? Wenn die Werte gleich sind, sind sie schon gleich und alterWert 
muß nicht noch mal aktualisiert werden ;-)

von Vlad T. (vlad_tepesch)


Lesenswert?

Carsten schrieb:
> Für Tips wäre ich sehr dankbar!
>
> while(1){
>
> aktuellerAdcWert = ADC_auslesen(0);
>
> lcdClear();                          // 1
> lcdWriteString("Wert: ");            // 2
> lcdWriteNumber(aktuellerAdcWert);    // 3
>
> if(aktuellerAdcWert != alterWert){
>      alterWert = aktuellerAdcWert;
>      // ***********
>      }
>
> }

das ganze ist ein bisschen sinnlos oder?
warum wird das LCD-Update nicht nur gemacht, wenn sie die Werte 
unterscheiden?
Bloß für die Zuweisung die If-Abfrage macht keinen Sinn, da die 
Zuweisung wahrscheinlich genauso schnell ist, wie die Abfrage und es 
auch nix schaded, wenn man eine Variable mit dem gleichen Wert 
nocheinmal beschreibt.

warum nicht einfach:
1
aktuellerAdcWert = ADC_auslesen(0);
2
if(aktuellerAdcWert != alterWert)
3
{
4
    lcdClear();                          // 1
5
    lcdWriteString("Wert: ");            // 2
6
    lcdWriteNumber(aktuellerAdcWert);    // 3    
7
    alterWert = aktuellerAdcWert;
8
}

von Felix (Gast)


Lesenswert?

Hab ich auch zuerst gedacht, aber er schrieb

von Christian Erker (Gast)


Lesenswert?

... lies dir das Ursprungsposting nochmal genau durch ... ;)

Gruß,
Christian

von Felix (Gast)


Lesenswert?

Carsten schrieb:
> Ich füge also die mit 1 bis 3 markierten Zeilen in die
> if-Abfrage (Sternchen) ein.

Habe zuerst auch gedacht, dass das keinen Sinn ergibt.
Aber ich habe oben zitierte Zeile überlesen...

von Falk B. (falk)


Lesenswert?

@  Vlad Tepesch (vlad_tepesch)

>warum nicht einfach:

>aktuellerAdcWert = ADC_auslesen(0);
>if(aktuellerAdcWert != alterWert)
>{
>    lcdClear();                          // 1
>    lcdWriteString("Wert: ");            // 2
>    lcdWriteNumber(aktuellerAdcWert);    // 3
>    alterWert = aktuellerAdcWert;
>}

lcd_clear() macht man bei einem zyklischen Update nicht, das flackert. 
Man überschreibt einfach die alten Werte, ggf. Leerzeichen einfügen.

MFG
Falk

von Vlad T. (vlad_tepesch)


Lesenswert?

Falk Brunner schrieb:
> lcd_clear() macht man bei einem zyklischen Update nicht, das flackert.
> Man überschreibt einfach die alten Werte, ggf. Leerzeichen einfügen.
>
> MFG
> Falk

in meinem Code ist kein zyklisches clear, sondern nur eins bei 
Veränderung.
In seinem Ursprungsposting wird in jedem Zyklus ein clear gemacht.
die Abfrage, ob sich der Wert geändert hat, war vorher komplett umsonst.

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.