mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik Problem mit Zähler


Autor: Phillip Hommel (philharmony)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hi, ich habe hier eine Funktion um den ADC 10 mal auszulesen, den 
Mittelwert abzuspeichern und dem MUX auf den nächsten Kanal zu stellen.
Für die 10 Messungen soll der conversion_counter je um eins 
incrementiert werden.
Zum testen gebe ich den Wert des Zählers mit var_out bei jedem Aufruf 
aus.
Und hier ist das Problem: der Zähler hat IMMER den Wert 1. Ich habe den 
ganzen Code durchsuchen lassen ob ich den Wert aus versehen irgendwo 
nochmal benutze oder resette, negativ. Er Zählt ihn einfach nicht hoch.
Sollte die If-Condition betreten werden soll ein "in" gesendet werden, 
auch das passiert nie, also wird der Zähler auch da nicht zurückgesetzt.
Andere Zähler im Code Funktionieren, jemand eine Idee warum das nicht 
klappt?
AVR-Studio und Atmega8535.
//Globale Variablen
#define ADC_CYCLES 10 //Umläufe Genauigkeit <-> Zeit
unsigned char admux_counter; //Zähler für ADC Kanal
unsigned char conversion_counter; //Zähler für Umläufe im ADC für höhere Genauigkeit
int adc_buffer;  //Pufferspeicher des ADC

void convert_adc_channels()
{
  adc_buffer + =ADCW; //Wert zu Pufferspeicher Addieren
  if (conversion_counter >= ADC_CYCLES) //wenn Wandlungszähler gleich Zahl der Umläufe ist
  {
/**/uart_puts("in\n");
    conversion_counter = 0; //Wandlungszähler zurücksetzen
    virtual_adc[admux_counter] = (adc_buffer / ADC_CYCLES);  //Wert durch Zahl d. Wandlungen teilen und speichern
    admux_counter = ((admux_counter + 1) % NUMBER_OF_ADC_CHANNELS); //Kanalzähler erhöhen
    ADMUX = ((ADMUX & 0xe0) | adc_used_pins[admux_counter]); //Nächsten Kanal setzen
  }
  conversion_counter++; //Wandlungszähler erhöhen
  /**/var_out(conversion_counter); //Zum Prüfen
  (ADCSRA |= 1<<ADSC); //nächste Wandlung einleiten
}

Autor: Benjamin S. (recycler)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
schreib mal da hardcore 10 rein. vlt gehts dann
if (conversion_counter >= ADC_CYCLES)

Autor: Matthias (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
evtl. volatile?

Autor: Phillip Hommel (philharmony)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hatte es vorher auch als volatile deklariert weil ich das erst per ISR 
machen wollte. Gleiches Ergebnis. Hardcoded ändert auch nichts, hatte 
ich auch schon versucht.
Ist conversion_counter vielleicht irgendwie von GCC reserviert oä?
Hehe, scheint so, bin grade auf die Idee gekommen den Namen mal zu 
ändern. mit adc_conversion_counter gehts plötzlich...???

Autor: kurz (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Quatsch: volatile

Benjamin hat die Anzwort doch schon gegeben.

Autor: Läubi .. (laeubi) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Wenn der Zähler nur in der Funktion benötigt wird kannst du ihn auch 
dort lokal anlegen.
void convert_adc_channels()
{
  const unsigned char conversion_counter = 0;
  adc_buffer + = ADCW; //Wert zu Pufferspeicher Addieren
  if (conversion_counter >= ADC_CYCLES) {
    //wenn Wandlungszähler gleich Zahl der Umläufe ist
    uart_puts("in\n");
    conversion_counter = 0; //Wandlungszähler zurücksetzen
    virtual_adc[admux_counter] = (adc_buffer / ADC_CYCLES);  //Wert durch Zahl d. Wandlungen teilen und speichern
    admux_counter = ((admux_counter + 1) % NUMBER_OF_ADC_CHANNELS); //Kanalzähler erhöhen
    ADMUX = ((ADMUX & 0xe0) | adc_used_pins[admux_counter]); //Nächsten Kanal setzen
  } else {
    conversion_counter++; //Wandlungszähler erhöhen
  }
  var_out(conversion_counter); //Zum Prüfen
  (ADCSRA |= 1<<ADSC); //nächste Wandlung einleiten
}

Autor: Benjamin S. (recycler)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Läubi .. schrieb:
> Wenn der Zähler nur in der Funktion benötigt wird kannst du ihn auch
> dort lokal anlegen.
 ich glaube das ist falsch, weil wenn ich die funktion aufrufe, bekomme 
ich genau das resultat mit der 1 und weiter zählt er nicht. wenn dann 
musst du die das in einer while schleife machen, aber soweit ich mir das 
vorstellen kann, kommen die daten von einer interruptquelle und darin 
wird die fkt aufgerufen.

such mal in deinem quelltext, wo die variable sonst noch 
gebraucht/benutz/geschrieben wird.

Autor: Phillip Hommel (philharmony)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@Läubi:
Du meinst static oder? Ja kann ich auch machen, allerdings wundert es 
mich trotzdem warum es mit adc_conversion_counter geht und mit 
conversion_counter nicht.
Das ELSE hat da nichts zu suchen, er soll IMMER einen weiter zählen.

@Kurz: Wasn das schon wieder für ein Gezicke? War ein Vorschlag, und der 
von Benjamin hat ja leider auch nichts gebracht.

@Benjamin, nene das ist schon richtig, mit STATIC (nicht const) kann ich 
dem Compiler sagen, er soll die Variable nach Funktinsende  nicht 
löschen (Speicher nicht Freigeben) sondern beim nächsten Aufruf mit dem 
vorhandenen Wert weiter arbeiten.
An anderer Stelle im Text wird die Variable nicht mehr verwendet, habe 
eben schon eine textsuche laufen lassen um das auszuschliessen.

Autor: Benjamin S. (recycler)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
kommt mir komisch vor.
das mit static stimmt :) mit const gehts nicht.

Autor: Phillip Hommel (philharmony)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Noch ne Korrektur meinerseits, Läubi hatte recht mit dem ELSE.
Sonst macht er ja einen Durchgang zu wenig...

Autor: Läubi .. (laeubi) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Phillip Hommel schrieb:
> @Läubi:
> Du meinst static oder? Ja kann ich auch machen, allerdings wundert es
> mich trotzdem warum es mit adc_conversion_counter geht und mit
> conversion_counter nicht.
> Das ELSE hat da nichts zu suchen, er soll IMMER einen weiter zählen.
ja static... sorry. Ich vermute/befürchte das sich da irgenwas 
überlagert, daher sollten Variablen halt einen so klein wie möglichen 
Gültigkeitsbereich haben, dann ist die Gefahr geringer das was schief 
geht.
Vieleicht mal mit nem Clean des Projektes versuchen?

> Das ELSE hat da nichts zu suchen, er soll IMMER einen weiter zählen.
Ja aber nur wenn er nicht auf 0 gesezt wird, sonst ist die zählung 
nämlich:

0 1 2 ... 9, 1 ... 9, 1... 9, 1....9 weil sofort wieder erhöht wird.

Autor: Phillip Hommel (philharmony)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Also jetzt geht es ja, hab die Variable einfach umbenannt.
Nochmal zur sicherheit, da ich es jetzt doch per interrupt machen 
möchte:
Ich kann innerhalb des ISR auch static volatile definieren oder?
Dann würde ich den code mal dahingehend aufräumen und alles etwas 
übersichtlicher machen.

Autor: Läubi .. (laeubi) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Phillip Hommel schrieb:
> Also jetzt geht es ja, hab die Variable einfach umbenannt.
> Nochmal zur sicherheit, da ich es jetzt doch per interrupt machen
> möchte:
> Ich kann innerhalb des ISR auch static volatile definieren oder?
> Dann würde ich den code mal dahingehend aufräumen und alles etwas
> übersichtlicher machen.

volatile bracuhst du nur wenn Interupt UND Hauptprogramm auf die 
Variable zugreifen ODER sich der Wert auserhalb des Sichtbereiches des 
Compilers ändern könnte (z.B. weil es sich um ein Hardwareregister 
handelt)

Autor: Phillip Hommel (philharmony)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Stimmt, so war das. Dh einfach im ISR die variablen static 
initialisieren und gut is?
Nächstes kleines Problem ist, daß das ganze jetzt per interrupt läuft, 
hab den ADC Prescaler auf 64 gestellt damit kommen ca 58kHz raus.
Der ADC liefert jetzt aber seltsame Werte.
Habe mal nur einen Kanal, ADC0 in Benutzung. Wenn ich ihn testweise an 
gnd lege dann liefert er brav die 0, lege ich ihn aber an AVCC  (kommt 
vom USB-Port und ist die Versorgungssannung des Controllers) dann 
schickt er abwechselnd 768 und 1022/1023. Ne Idee wo die 768 herkommen?
Habe inzwischen den o.g. Code noch dahingehend ergänzt, daß in der 
If-Condition der ADC Buffer natürlich noch geleert wird.

Autor: Läubi .. (laeubi) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
AVCC ausreichend abgeblockt mit Spule+100nF? Kondesator an AREF?, ggf 
nen kleinen Kondesator an den ADC Pin gegen GND.
Ansonstent soltest du auch besser über 8 oder 16 Werte mitteln dann kann 
das der Compiler ggf. in einen Schift anstelle einer Division umsetzen.

Autor: Phillip Hommel (philharmony)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hardwaremäßig habe ich gar nichts abgesichert da es grade nur ums testen 
geht (nich schimpfen ;)).
Das Problem scheint aber wo anders zu liegen da er immer exakt 1022 und 
768 in ganz periodischer Sequenz sendet...Immer 8mal 768, dann einmal 
1022. Irgendwo im Code muß da noch was hängen, ich suche weiter...

Autor: Läubi .. (laeubi) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Vieleicht schwankt einfach deine Spannungsversorgung, häng das ganze mal 
ans Labornetzteil.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Phillip Hommel schrieb:
> Also jetzt geht es ja, hab die Variable einfach umbenannt.

Du hast das Symptom beseitigt, aber nicht die Ursache :-)
Dein Programmfehler, der nach deiner Schilderung ganz woanders steckt, 
ist immer noch im Programm. Wahrscheinlich irgendwo ein Arrayüberlauf, 
so wie in 95% aller Fälle, in denen es zu solch seltsamen Symptomen 
kommt.


Edit: Deine weitere Schilderung der Ereignisse bestärkt mich eigentlich 
in meiner Vermutung, dass du den 'Fehler' durch Umbenennen der Variable 
nicht behoben hast :-)

Autor: Phillip Hommel (philharmony)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Naja, ich habe ja jetzt ein ganz anderes Problem. Das eine war daß er 
partout einen Zähler nicht incrementieren wollte, das zweite scheint 
jetzt ein Anderes Problem zu sein:
Da ich ein Timer-interrupt und das ADC interrupt benutze, vermute ich 
mal daß genau dort das problem liegen könnte.
Und zwar ist das ADCW ja eignetlich zweiteilig.
Die 768 sind genau 11 00 00 00 00. Also genau der High-Part von 1023.
Eventuell funkt jedes mal (ausser jedes 8. mal, da hab ich nämlich 
1023))der Timer genau beim auslesen des ADCW dazwischen und der Low Part 
geht verloren.
Beim Initialisiren, also der first conversion braucht er ja etwas länger 
(25 anstatt 13 cycles) und da sind es auch keine 8 mal...
Mal schauen ob und wie ich das geregelt kriege...

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Phillip Hommel schrieb:
> Naja, ich habe ja jetzt ein ganz anderes Problem. Das eine war daß er
> partout einen Zähler nicht incrementieren wollte,

Das ist Blödsinn.
Das ist das Symptom, welches du gesehen hast. Die Ursache war aber mit 
Sicherheit etwas anderes.
Was du getan hast war nichts anderes, als den Zähler an eine andere 
Stelle im Speicher zu lanzieren. Dadurch wirkt sich dein Programmfehler 
nicht mehr an diesem Zähler aus. Stattdessen wird halt irgendeine andere 
Variable unzulässig verändert. Dein ursprüngliches Symptom ist weg, 
dafür hast du ein anderes Symptom.
Und solange du das eigentliche Problem nicht behebst, wird das immer so 
weitergehen. Solange bis du entweder das Problem behebst oder aber bis 
du zufällig eine Konfiguration hast, in der das Problem eine 
Speicherstelle verändert, die ansonsten keiner braucht und daher das 
Problem etwas anstellt, was sich nicht mehr auswirkt. Aber wehe, du 
veränderst dein Programm dann noch einmal. Dann ist er ganz plötzlich 
wieder da :-)

Poste mal das komplette Programm, wenn es nicht zu lang ist (unter 300 
Zeilen hat). Ich wette, da findet sich irgendwo ein zu klein 
dimensinoiertes Array.

Autor: Phillip Hommel (philharmony)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Sorry, das sollte gar nicht so sehr nach Widerspruch klingen wie es tut. 
Eher als Gegenfrage ob diese beiden Phänomene wirklich zusammenhängen 
können, aber das hast Du ja schon erklärt.
Habt Ihr nen Tip wie ich bei der Fehlersuche Vorgehen könnte? Da steh 
ich nämlich grade echt im Wald.
Aufgetreten ist das ganze ab dem Moment wo ich das ADC-Interrupt ins 
Spiel gebracht habe...Ich nehme das ganze nochmal aus dem ISR raus und 
lass die Funkt mal in der Main laufen, mal sehn wie es dann aussieht. 
Ich meld mich gleich nochmal.
Das Programm hat über 700 Zeilen plus config.h, das will ich Euch nicht 
antun. Aber ich werde mal alle arrays checken.

Autor: Läubi .. (laeubi) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
uart_puts("in\n");
virtual_adc[admux_counter] = (adc_buffer / ADC_CYCLES);
admux_counter = ((admux_counter + 1) % NUMBER_OF_ADC_CHANNELS);
All sowas ist arg Böswillig im Interupt!
Uart ausgaben dauern je nach Baudrate extrem lange, ebenso die Division 
und Modulo mit nicht 2er Potenzen...
Außerdem solltest du deine Arrays/Zähler überprüfen auf das gleiche 
Problem was du eben schon hattest mit dem zurücksetzen.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Phillip Hommel schrieb:
> Das Programm hat über 700 Zeilen plus config.h, das will ich Euch nicht
> antun.

Das ginge zur Not auch noch, wenn es nicht zu chaotisch ist.

Autor: Phillip Hommel (philharmony)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@ Läubi.
Die Ausgabe über den uart hatte ich nur drin um zu checken was er macht. 
Die kommt in der eigentlichen Anwendung natürlich nicht vor.
Werde die Cycle-Zahl mal auf 8 setzen.
Arrays sehen eigentlich alle ok aus.

Autor: Phillip Hommel (philharmony)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich habs!
In tiefster Ehrfurcht verneige ich mich vor Karl Heinz' 7.Sin inkl 
seiner Erfahrung.
Danke natürlich auch an alle anderen die geholfen haben.
Problem lag hier
#define NUMBER_OF_ADC_CHANNELS (sizeof adc_used_pins / sizeof adc_used_pins[0]) 
volatile unsigned char adc_used_pins[] = {0};
volatile int virtual_adc[NUMBER_OF_ADC_CHANNELS];
Geändert in
#define NUMBER_OF_ADC_CHANNELS (sizeof adc_used_pins / sizeof adc_used_pins[0]) 
volatile unsigned char adc_used_pins[1] = {0};
volatile int virtual_adc[1] = {0};
Verstehe jetzt zwar noch nicht ganz warum das vorher nicht geklappt hat, 
aber vielleicht nachdem ich ne runde an der frischen Luft war... ;)

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Phillip Hommel schrieb:

>
> #define NUMBER_OF_ADC_CHANNELS (sizeof adc_used_pins / sizeof
> adc_used_pins[0])
> volatile unsigned char adc_used_pins[] = {0};
> volatile int virtual_adc[NUMBER_OF_ADC_CHANNELS];
> 
> Geändert in
>
> #define NUMBER_OF_ADC_CHANNELS (sizeof adc_used_pins / sizeof
> adc_used_pins[0])
> volatile unsigned char adc_used_pins[1] = {0};
> volatile int virtual_adc[1] = {0};
> 

Hmm.
Das kanns aber noch nicht gewesen sein.
Rein formal besteht zwischen den beiden Formen kein Unterschied.

Autor: Phillip Hommel (philharmony)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Dachte ich eigentlich auch, Problem is: wie finde ich den Fehler wenn 
das Symptom nicht mehr auftritt?
Edit: Habe die 2. wieder auf die erste Version zurück-geändert und das 
alte Problem taucht wieder auf...
Optimierung steht auf -Os.
Zusatzinfo, die o.g. Zuweisung mache ich in einem eingebundenen 
Header-file, könnte es damit zusammenhängen?

Autor: Phillip Hommel (philharmony)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Aaaaaah, verflixte 0-Indizierung.
Wenn ich
int zantralspeicher[6]
definiere, und dann auf die
Zentralspeicher[0]...Zentralspeicher[6] 
zugreife dann geht das natürlich schief.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Phillip Hommel schrieb:
> Aaaaaah, verflixte 0-Indizierung.
> Wenn ich
>
> int zantralspeicher[6]
> 
> definiere, und dann auf die
>
> Zentralspeicher[0]...Zentralspeicher[6]
> 
> zugreife dann geht das natürlich schief.

Ja. Das sieht als Ursache schon viel besser aus.

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]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [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.