Guten Tag liebe Community,
dies ist mein erster Post, deswegen entschuldige ich mich vorab bei
falscher Formatierung.
Für die Universität muss ich folgendes Projekt fertig kriegen. Da wegen
der Coronapandemie die praktische Seite des Projekts weggefallen ist und
ich somit große Probleme mit dem Code habe, bitte ich hier um Hilfe.
Ich soll eine 7 Segmentanzeige, welche von 0-7 laufen soll, mit einem
Potentiometer steuern. Die Dekleration der Arrays und das Konfigurieren
der 7 Segmente Anzeige habe ich schon hinbekommen. Probleme habe ich
beim Verständnis von einlesen analoger Signale und die Nutzung der ADU.
Hier habe ich nochmal den zu vervollständigenden Code reinkopiert.
Danke im Voraus
Phillip
1
#define F_CPU 16000000
2
3
//Nachfolgend müssen zwei Bibliotheks-Dateien eingebunden werden (bitte Punkte passend ersetzen)
4
#include<avr/interrupt.h>
5
#include<avr/io.h>
6
7
// Felddeklarationen (Arrays) passend für die LED-Segmentbits zu den jeweiligen Ziffern 0..9 (1=an, 0=aus, 0=Restbits)
8
// ACHTUNG: Geändert gegenüber den bisher verwendeten Zustandszuordnungen (nicht genutzte Bits sind "0")!!!
Nun ja - ich nehme an, das generelle Prinzip wurde in der Vorlesung
behandelt, die controllerspezifischen Details stehen im Datenblatt, an
welcher Stelle hapert es denn beim Verständnis? Und verfügen Sie über
die Hardware, oder ist das nur eine rein theoretische Übung?
Phillip E. schrieb:> // Interrupt-Service-Routine des ADC> ISR(TIMER0_COMPA_vect)
Das ist keine ADC-ISR. Der ADC hat (oft, meist, immer? Welcher µC wird
verwendet) eine eigne ISR. Die kann man nutzen, muss es aber nicht.
Außerdem sehe ich die TIMER0_COMPA-ISR zweimal im Code.
Dann sehe ich noch so Zeilen wie "number = ;" oder "ADCSRA = ;"?
Wie geht man vor:
Setup ADC, Geschwindigkeit (Vorteiler), Kanalwahl, Auflösung 8Bit
reichen.
Poti einlesen. Wert durch 32 teile, so dass 0-7 als Ergebnis
herauskommt.
Den Wert als Index deiner Bitmaske nehmen und die Segmente ansteuern.
Kurze Zeit warten (oder schlafen und über Timerinterrupt nächste
Konversion starten) und das ganze wiederholen.
Christian H. schrieb:> Phillip E. schrieb:>> Hier habe ich nochmal den zu vervollständigenden Code reinkopiert.>> Lückentext? Ist das ein Test?
Das ist ein Projekt welches ich abgeben werde.
Code UPDATE:
Ich habe das ADC Setup nun vervollständigt.
Ich habe eine ADC Startsequenz geschrieben.
Ich habe aber Probleme dem ADC-ISR, ich weiß nicht wie ich das ADC
Ergebnis in einen Wert zwischen 0 und 7 umwandeln und es anschließend
anzeigen soll.
1
#define F_CPU 16000000
2
3
//Nachfolgend müssen zwei Bibliotheks-Dateien eingebunden werden (bitte Punkte passend ersetzen)
4
#include<avr/interrupt.h>
5
#include<avr/io.h>
6
7
// Felddeklarationen (Arrays) passend für die LED-Segmentbits zu den jeweiligen Ziffern 0..9 (1=an, 0=aus, 0=Restbits)
8
// ACHTUNG: Geändert gegenüber den bisher verwendeten Zustandszuordnungen (nicht genutzte Bits sind "0")!!!
Ich würde das ADC-Handling und auch den kompletten Rest des Programms
für diese schnarchlangsame Aufgabe dort reinschreiben, wo derzeit das
steht:
1
// Hier ist kein Code erforderlich
Denn es ist im Grund völlig irre, diese AD-Wandlung per Interrupt zu
machen...
Das war die Frage schrieb im Beitrag #6551609:
> Der TO schrieb:>> Probleme habe ich beim Verständnis von einlesen analoger Signale und die>> Nutzung der ADU.
An jeder Ecke des Internets gibt es dazu Beispielcode:
https://www.google.com/search?q=atmega328+adc+example+c+code
Wo genau klemmt es da?
Phillip E. schrieb:> Ich habe aber Probleme dem ADC-ISR
Das ist KEINE ADC-ISR! Sondern da steht voll ausführlich:
1
// Interrupt_Service-Routine des Timer/Counter 0
Und in einer Interruptroutine wird niemals(!!) auf irgendwas gewartet.
Schon gar nicht auf das Ende eines so abartig langsamen Prozesses wie
der einer AD-Wandlung. ISR werde kurz&knackig ausgeführt. Gewartet wird
irgendwo anders. Am besten nirgends.
Und jetzt kommts:
was du machen kannst (wenn es dein Lehrer schon unbedingt so will),
wäre, dass du den Timer 0 so konfigurierst, dass der Overflow etwa alle
20ms kommt (die 10ms passen auch...)
In der ISR des Timer 0 Overflows liest du dann den AD-Wert aus, schiebst
ihn um ein paar Bits nach rechts, zeigst die Zahl an und startest dann
wieder den ACD. Ende des Interrupts.
Kurze Zeit später kommt der nächste Timer 0 Overflow Interrupt, du liest
den ADC-Wert aus, zeigst die Zahl an und startest wieder den ADC.
Interruptende.
Kurz danach kommt wieder der Interrupt, du liest wieder den AD-Wert aus,
... uswusf.
Dann musst du nicht mal das Busy-Flag vom ADC abfragen, wenn du den so
konfiguriert hast, dass er in weniger als 20ms (oder eben weniger als
10ms) fertig ist.
Jeden Tag neu schrieb im Beitrag #6552022:
> EUER MOBBING FUNKTIONIERT NICHT -JEDER BEITRAG ERSCHEINT ERNEUT!
Du scheinst ernste Wahrnehmungsstoerungen zu haben.
wendelsberg
Christian H. schrieb:> Lothar M. schrieb:>> zeigst die Zahl an>> Gerade das würde ich in einer Interruptroutine nicht machern.
Warum denn nicht? Gerade, wenn hier ein Raster von 10ms oder 20ms oder
mehr genommen wird, hat man massig Zeit, dies zu tun. Und sonst wird
laut Aufgabenstellung ja nichts gefordert. Ob der Prozessor in der ISR
oder in der main Däumchen dreht, ist doch egal.
Moin,
ich habe den Code mittlerweile vervollständigt nur sind dort Fehler die
ich nicht finden kann. Das Gerät gibt keinerlei Rückmeldung von sich.
Ich habe mich in das Thema mittlerweile gut eingearbeitet,
höchstwahrscheinlich habe ich Fehler in der ISR. Vielleicht kann mir da
einer etwas expliziter weiterhelfen.
1
2
3
#define F_CPU 16000000
4
5
//Nachfolgend müssen zwei Bibliotheks-Dateien eingebunden werden (bitte Punkte passend ersetzen)
6
#include<avr/interrupt.h>
7
#include<avr/io.h>
8
9
// Felddeklarationen (Arrays) passend für die LED-Segmentbits zu den jeweiligen Ziffern 0..9 (1=an, 0=aus, 0=Restbits)
10
// ACHTUNG: Geändert gegenüber den bisher verwendeten Zustandszuordnungen (nicht genutzte Bits sind "0")!!!
Phillip E. schrieb:> Probleme habe ich> beim Verständnis von einlesen analoger Signale und die Nutzung der ADU.
You know what an ADU is(?). Please explain it to me, because I don't
know...
Geert H. schrieb:> Phillip E. schrieb:>> Probleme habe ich>> beim Verständnis von einlesen analoger Signale und die Nutzung der ADU.>> You know what an ADU is(?). Please explain it to me, because I don't> know...
if you dont then you can leave the post :D
Entschuldigung, das war eben Unsinn, number wird ja nur lokal
verwendet.
In der Hoffnung, dass dies brauchbarer ist:
> PORTB |= PC_Bitmaske[number];
Ist das korrekt? So beim Überfliegen sehe ich sonst nichts mit Kanal B.
S. Landolt schrieb:> Muss dieses number nicht volatile sein?
Unklar, es wird ja außerhalb der ISR gar nicht verwendet, damit kann der
Compiler auch ohne Nachteil optimieren. Ich würde 'number' nur innerhalb
dieser definieren.
Phillip E. schrieb:> number = (5 << ADCL);
Ich habe irgendwie in Erinnerung, dass man ADCH auch lesen muss. Sonst
geht es nicht weiter.
Außerdem gibst du das Ergebnis linksbündig aus (ADLAR=1), damit steht
die Info sowieso in ADCH mit 8Bit und nur zwei LS-Bits in ADCL. Also:
ADCH lesen und ADCL vergessen!
Und der zitierte Befehl müsste eh lauten:
1
number=(ADCL>>5);// bez. nach meinen Ausführungen eben ADCH.
Du könntes auch einfacher schreiben:
1
number=ADCH/32;
Der Compiler macht schon eine Schiebeoperation daraus. Für den Leser
besser verständlich.
Geert H. schrieb:> You know what an ADU is(?). Please explain it to me, because I don't> know...
German abbreviation of ADC. Analog-Digital-Umsetzer ←→ analog digital
converter.
> Ich habe irgendwie in Erinnerung, dass man ADCH auch lesen muss. Sonst> geht es nicht weiter.> Außerdem gibst du das Ergebnis linksbündig aus (ADLAR=1), damit steht> die Info sowieso in ADCH mit 8Bit und nur zwei LS-Bits in ADCL. Also:> ADCH lesen und ADCL vergessen!> Und der zitierte Befehl müsste eh lauten:>
1
>number=(ADCL>>5);// bez. nach meinen Ausführungen eben ADCH.
2
>
>> Du könntes auch einfacher schreiben:>
1
>number=ADCH/32;
2
>
> Der Compiler macht schon eine Schiebeoperation daraus. Für den Leser> besser verständlich.
Ich habe alle varianten ausprobiert und sehe leider keine Veränderungen.
Reicht es, wenn ich die Werte von ADCH bzw- ADCL einfach in eine
Variable speicher, damit dann beide Register ausgelesen wurden?
Phillip E. schrieb:> Reicht es, wenn ich die Werte von ADCH bzw- ADCL einfach in eine> Variable speicher, damit dann beide Register ausgelesen wurden?
Es reicht, wenn du ADCH einfach liest. Musst du ja sowieso, wenn du den
Wert weiterverwenden willst.
Es ist sogar ausreichend sein zu schreiben
1
PORTB|=PC_Bitmaske[ADCH/32];// beinhaltet schon den ADCH-Lesevorgang
Und wenn du die vollen 10Bit Auflösung brauchst, dann schreibst du ADC
in eine uint16_t-Variable.
HildeK schrieb:> Phillip E. schrieb:>> Reicht es, wenn ich die Werte von ADCH bzw- ADCL einfach in eine>> Variable speicher, damit dann beide Register ausgelesen wurden?>> Es reicht, wenn du ADCH einfach liest. Musst du ja sowieso, wenn du den> Wert weiterverwenden willst.> Es ist sogar ausreichend sein zu schreiben>
1
>PORTB|=PC_Bitmaske[ADCH/32];// beinhaltet schon den ADCH-Lesevorgang
2
>
> Und wenn du die vollen 10Bit Auflösung brauchst, dann schreibst du ADC> in eine uint16_t-Variable.
Ich habe mittlerweile alle Varianten ausprobiert und es passiert einfach
gar nichts. Ich bin am verzweifeln.
Vergessen:
Phillip E. schrieb:> Ich habe alle varianten ausprobiert und sehe leider keine Veränderungen.
Ich habe deinen Codeablauf nicht untersucht und kenne auch die
HW-Beschaltung nicht (Schaltplan?). Ich habe nur auf offensichtliche
Fehler in der SW hingewiesen.
Hast du den Hinweis von S. Landolt schon berücksichtigt?
S. Landolt schrieb:> Ist das korrekt? So beim Überfliegen sehe ich sonst nichts mit Kanal B.
HildeK schrieb:> Vergessen:> Phillip E. schrieb:>> Ich habe alle varianten ausprobiert und sehe leider keine Veränderungen.>> Ich habe deinen Codeablauf nicht untersucht und kenne auch die> HW-Beschaltung nicht (Schaltplan?). Ich habe nur auf offensichtliche> Fehler in der SW hingewiesen.> Hast du den Hinweis von S. Landolt schon berücksichtigt?>> S. Landolt schrieb:>> Ist das korrekt? So beim Überfliegen sehe ich sonst nichts mit Kanal B.
Ich habe den Vorschlag von S. Landolt in meinem Code ergänzt, kein
Unterschied.
> Ich bin am verzweifeln.
Keine Ursache - "kriegen wir hin, kriegen wir alles hin" (auch wenn's
dauert)
Nächster Fehler:
> ADCSRA = (1<<ADSC); // Neue ADC-Sequenz starten
Das war oben noch korrekt, jetzt fehlt die Ver-Oder-ung.
S. Landolt schrieb:>> Ich bin am verzweifeln.> Keine Ursache - "kriegen wir hin, kriegen wir alles hin" (auch wenn's> dauert)>> Nächster Fehler:>> ADCSRA = (1<<ADSC); // Neue ADC-Sequenz starten> Das war oben noch korrekt, jetzt fehlt die Ver-Oder-ung.
Habe dies nun ergänzt und getestet, ohne Erfolg.
Mich irritiert dies hier
> PORTC = 0xFF; // Alle Segmente der 7-Segemntanzeige ...
Anschließend wird mit PC_Bitmaske verodert, da ändert sich folglich dann
gar nichts. Muss das nicht PORTC = 0 heißen (je nach
Segment-Beschaltung)?
So, auf meinem Steckbrett tut sich jetzt etwas; keine Ahnung, ob es das
Richtige ist (ich habe nur drei einzelne LEDs), aber besser als nichts.
Die Vorschläge von K. Hilde sowie meine befolgt, und es müsste bei Ihnen
auch laufen. Am besten stellen Sie zeitnah den jetzigen Zustand Ihres
Programms noch einmal vor.
Du solltest nicht zu viel auf einmal testen. Ich habe den Eindruck, du
hast keine Ahnung, an welcher Stelle das hapert.
Nimm mal eine einfache ADC-Leseroutine, ohne Interrupt, z.B. in der Form
(Tinyx5)
1
/* ADC Einzelmessung */
2
uint16_tADC_Read(void)
3
{
4
uint16_tadc_res;
5
ADCSRA|=(1<<ADSC);// eine Wandlung "single conversion"
6
while(ADCSRA&(1<<ADSC)){;}// auf Abschluss der Konvertierung warten
7
adc_res=ADC;
8
returnadc_res;// ADC auslesen und zurückgeben
9
}
Nimm das while(1) in der main und rufe ADC_Read() wiederholt nach
einfachem _delay_ms(100) auf. Und gibt ADCH an einem PORT aus; am besten
auf LEDs.
Wenn das geht, dann mache ähnliches für den Timer, aber anstatt den ADC
zu starten, lasse eine LED blinken, ggf. den Timer temporär mit größerem
Teiler betreiben.
Wenn jedes für sich brauchbare Ergebnisse liefert, dann beides
kombinieren.
In deiner Anwendung sehe ich keine Notwendigkeit, den ADC-Interrupt zu
verwenden.
Ich habe soweit alles verbessert. Das Endergebnis funktioniert im Grunde
genau wie es das auch soll, aber die Aufgabe verbietet die direkte
Wertezuweisung und erwartet dass modifizieren von PortC und PortD über
Maskieroperationen.
1
#define F_CPU 16000000
2
3
//Nachfolgend müssen zwei Bibliotheks-Dateien eingebunden werden (bitte Punkte passend ersetzen)
4
#include<avr/interrupt.h>
5
#include<avr/io.h>
6
7
// Felddeklarationen (Arrays) passend für die LED-Segmentbits zu den jeweiligen Ziffern 0..9 (1=an, 0=aus, 0=Restbits)
8
// ACHTUNG: Geändert gegenüber den bisher verwendeten Zustandszuordnungen (nicht genutzte Bits sind "0")!!!
Ich habe zwar noch nie mit Atmega gearbeitet sondern nur mit
PIC-Controllern, aber die Grundlagen der C-Programmierung sind ja
identisch.
Die zwei aufeinander folgenden Zuweisungen
1
number = ( ADCL/32);
2
number = ( ADCH/32);
werden jedenfalls nicht dazu führen, daß in number der durch 32 geteilte
(bzw. um 5 Bits nach rechts verschobene) 10-Bit-Wandlerwert steht.
Im AVR-GCC-Tutorial, Kapitel 8.5, ist doch beschrieben, wie der
Zugriff auf 16Bit-Werte realisiert werden soll. Erstmal muß die Variable
number eine 16-Bit-Variable (uint16_t) sein, und das Lesen erfolgt über
das Pseudoregister ADC. Der Compiler erzeugt dann die richtige
Zugriffssequenz automatisch. Anschließend wird number durch 32
dividiert.
PS: Ich sehe gerade, das wurde im Code von HildeK ja schon gemacht.
Stefan H. schrieb:> Die zwei aufeinander folgenden Zuweisungen number = ( ADCL/32);> number = ( ADCH/32);> werden jedenfalls nicht dazu führen, daß in number der durch 32 geteilte> (bzw. um 5 Bits nach rechts verschobene) 10-Bit-Wandlerwert steht.
Das ist richtig. Er will jedoch nur ein 3-Bit-Ergebnis.
Entweder spricht man den 16-Bit-Wert aus dem im iom46p.h definierten ADC
(bei manchen auch ADCW oder beides) an und erhält die ganzen 12 Bit, je
nach ADLAR links- oder rechtsbündig und muss dann durch 128 teilen oder
man nimmt nur den Wert ADCH mit den 8 MSBs bei ADLAR=1 und teilt die
durch 32.
Die Zuweisung "number = ( ADCL/32);" bewirkt einfach nichts.
Um bereit zu sein für die nächste Wandlung muss entweder ADC bzw. ADCW
gelesen werden oder ADCL und dann ADCH oder nur ADCH, wenn 8Bit reichen.
Also: Lesen mit der vollen Auflösung
1
uint16_tergebnis;
2
uint8_tnumber;
3
ADMUX|=(1<<ADLAR);// linksbündiges ADC-Ergebnis
4
5
ergebnis=ADC;
6
// bzw.
7
ergebnis=ADCW;
8
9
// ist das selbe wie
10
ergebnis=ADCL;
11
ergebnis+=(ADCH<<8);
12
// Reihenfolge beibehalten, weil das Lesen von ADCH den Update für die nächste Wandlung wieder frei macht!
Guten Tag,
ich habe mittlerweile eine Lösung zum Problem gefunden. Somit ist die
Aufgabe vollständig. Die Lösung wollte ich euch nicht vorenthalten.
mfg
1
#define F_CPU 16000000
2
3
//Nachfolgend müssen zwei Bibliotheks-Dateien eingebunden werden (bitte Punkte passend ersetzen)
4
#include<avr/interrupt.h>
5
#include<avr/io.h>
6
7
// Felddeklarationen (Arrays) passend für die LED-Segmentbits zu den jeweiligen Ziffern 0..9 (1=an, 0=aus, 0=Restbits)
8
// ACHTUNG: Geändert gegenüber den bisher verwendeten Zustandszuordnungen (nicht genutzte Bits sind "0")!!!