Forum: Mikrocontroller und Digitale Elektronik Potentiometer mit ADU Atmega328PB [HILFE]


von Phillip E. (phillipernst1998)


Lesenswert?

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")!!!
9
uint8_t PC_Bitmaske[10] = {0b00111,0b01111,0b00111,0b00111,0b01111,0b10111,0b10111,0b00111,0b00111,0b00111};
10
uint8_t PD_Bitmaske[10] = {0b11000011,0b11111011,0b10100111,0b10110011,0b10011011,0b10010011,0b10000011,0b11011011,0b10000011,0b10010011};
11
12
// Deklaration der globalen Variablen  
13
uint8_t number = 0;
14
15
// Interrupt-Service-Routine des ADC
16
ISR(TIMER0_COMPA_vect)          // An dieser Stelle muss die Deklarations-Kopfzeile korrekte vervollständigt werden 
17
{
18
  number =  ;      // Zuweisung des ADC-Ergebnisses reduziert auf einen mit 3 Bit darstellbaren Wert
19
              // (Wertebereich 0 - 7)
20
  
21
  PORTC = 0xFF;      // Alle Segmente der 7-Segemntanzeige ausschalten (wie bei der Initialisierung)
22
  PORTD = 0x7C;      
23
24
  
25
  // Ausgabe des auf 3-Bit reduzierten ADC-Werts auf der 7-Segmentanzeige (Wertebereich 0 - 7)
26
  // ACHTUNG:  Da die Bitmasken der Arrays (s. oben) jetzt nur die zu setzenden Bits markieren, muss das Modifizieren von
27
  //      PORTC und PORTD jetzt auch über Maskier-Operationen und nicht mehr über direkte Wertzuweisungen erfolgen
28
29
30
}
31
32
// Interrupt_Service-Routine des Timer/Counter 0
33
ISR(TIMER0_COMPA_vect)            // An dieser Stelle muss die Deklarations-Kopfzeile korrekt vervollständigt werden
34
{
35
  // Neue ADC-Sequenz starten
36
  
37
  
38
}
39
40
int main(void)
41
{
42
  // ADC initialisieren
43
  ADMUX =  (1 << REFS1 )  ;                          // ADC Referenzspannung AVcc, linksbündige Ausrichtung 
44
                                  // für 8 Bit, Kanal 0 als ADC-Eingang
45
  ADCSRA =   ;                          // ADC und ADC Interrupt aktivieren, Frequenzteiler 32
46
  
47
  // Timer/Counter 0 initialisieren: CTC Modus, Prescaler 1/1024, Interrupt-Intervall 10 ms
48
  TCCR0A = (1<<WGM01);
49
  TCCR0B = (1<<CS02)|(1<<CS00) ;
50
  OCR0A = 156;                          // s. Formel aus Versuchsbeschreibung zu Versuch 2
51
  TIMSK0 = (1<<OCIE0A);                      // Compare-Interrupt A freigeben
52
  
53
  // 7 Segment Anzeige initialisieren (Alle LEDs aus, restliche Bits passend für Input und Tristate)
54
  DDRC = 0x18;
55
  PORTC = 0xFF;
56
  DDRD = 0x7C;
57
  PORTD = 0x7C;
58
    
59
  
60
  sei();                              // Interrupts global freigeben
61
  
62
    while (1) 
63
    {
64
  // Hier ist kein Code erforderlich  
65
    }
66
}

von Phillip E. (phillipernst1998)


Lesenswert?

+bump

von S. Landolt (Gast)


Lesenswert?

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?

von Jack V. (jackv)


Lesenswert?

a) was ist die Frage?
b) nach nicht mal einer Stunde zu bumpen, ist höchst unhöflich

von HildeK (Gast)


Lesenswert?

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.

Beitrag #6551609 wurde von einem Moderator gelöscht.
von Phillip E. (phillipernst1998)


Lesenswert?

Besser hätte ich die Frage selber nicht stellen können.

von Christian H. (netzwanze) Benutzerseite


Lesenswert?

Phillip E. schrieb:
> Hier habe ich nochmal den zu vervollständigenden Code reinkopiert.

Lückentext? Ist das ein Test?

von Phillip E. (phillipernst1998)


Lesenswert?

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")!!!
9
uint8_t PC_Bitmaske[10] = {0b00111,0b01111,0b00111,0b00111,0b01111,0b10111,0b10111,0b00111,0b00111,0b00111};
10
uint8_t PD_Bitmaske[10] = {0b11000011,0b11111011,0b10100111,0b10110011,0b10011011,0b10010011,0b10000011,0b11011011,0b10000011,0b10010011};
11
12
// Deklaration der globalen Variablen  
13
uint8_t number = 0;
14
15
// Interrupt-Service-Routine des ADC
16
ISR(ADC_vect)          // An dieser Stelle muss die Deklarations-Kopfzeile korrekte vervollständigt werden 
17
{
18
  number =(ADC << 5);      // Zuweisung des ADC-Ergebnisses reduziert auf einen mit 3 Bit darstellbaren Wert
19
              // (Wertebereich 0 - 7)
20
  
21
  PORTC = 0xFF;      // Alle Segmente der 7-Segemntanzeige ausschalten (wie bei der Initialisierung)
22
  PORTD = 0x7C;      
23
24
  
25
  // Ausgabe des auf 3-Bit reduzierten ADC-Werts auf der 7-Segmentanzeige (Wertebereich 0 - 7)
26
  // ACHTUNG:  Da die Bitmasken der Arrays (s. oben) jetzt nur die zu setzenden Bits markieren, muss das Modifizieren von
27
  //      PORTC und PORTD jetzt auch über Maskier-Operationen und nicht mehr über direkte Wertzuweisungen erfolgen
28
29
30
}
31
32
// Interrupt_Service-Routine des Timer/Counter 0
33
ISR(TIMER0_COMPA_vect)            // An dieser Stelle muss die Deklarations-Kopfzeile korrekt vervollständigt werden
34
{
35
  ADCSRA |= (1<<ADSC); // Neue ADC-Sequenz starten
36
  while(ADCSRA&(1<<ADSC))
37
  {
38
    
39
  }
40
  
41
}
42
43
int main(void)
44
{
45
  // ADC initialisieren
46
  ADMUX =  (1 << REFS0 ) | (1 << MUX0) | (1 << MUX2) ;          // ADC Referenzspannung AVcc, linksbündige Ausrichtung 
47
                                      // für 8 Bit, Kanal 0 als ADC-Eingang
48
  ADCSRA =   (1 << ADEN) | (1 << ADIE) |( 1 << ADPS0) | ( 1 << ADPS2);  // ADC und ADC Interrupt aktivieren, Frequenzteiler 32
49
  
50
  // Timer/Counter 0 initialisieren: CTC Modus, Prescaler 1/1024, Interrupt-Intervall 10 ms
51
  TCCR0A = (1<<WGM01);
52
  TCCR0B = (1<<CS02)|(1<<CS00) ;
53
  OCR0A = 156;                          // s. Formel aus Versuchsbeschreibung zu Versuch 2
54
  TIMSK0 = (1<<OCIE0A);                      // Compare-Interrupt A freigeben
55
  
56
  // 7 Segment Anzeige initialisieren (Alle LEDs aus, restliche Bits passend für Input und Tristate)
57
  DDRC = 0x18;
58
  PORTC = 0xFF;
59
  DDRD = 0x7C;
60
  PORTD = 0x7C;
61
    
62
  
63
  sei();                              // Interrupts global freigeben
64
  
65
    while (1) 
66
    {
67
  // Hier ist kein Code erforderlich  
68
    }
69
}

: Bearbeitet durch User
von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

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.

: Bearbeitet durch Moderator
Beitrag #6551682 wurde von einem Moderator gelöscht.
Beitrag #6552022 wurde von einem Moderator gelöscht.
von wendelsberg (Gast)


Lesenswert?

Jeden Tag neu schrieb im Beitrag #6552022:
> EUER MOBBING FUNKTIONIERT NICHT -JEDER BEITRAG ERSCHEINT ERNEUT!

Du scheinst ernste Wahrnehmungsstoerungen zu haben.

wendelsberg

von Christian H. (netzwanze) Benutzerseite


Lesenswert?

Lothar M. schrieb:
> zeigst die Zahl an

Gerade das würde ich in einer Interruptroutine nicht machern.

von HildeK (Gast)


Lesenswert?

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.

Beitrag #6552223 wurde von einem Moderator gelöscht.
Beitrag #6552457 wurde von einem Moderator gelöscht.
von Phillip E. (phillipernst1998)


Lesenswert?

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")!!!
11
//uint8_t PC_Bitmaske[10] = {0b00111,0b01111,0b00111,0b00111,0b01111,0b10111,0b10111,0b00111,0b00111,0b00111};
12
//uint8_t PD_Bitmaske[10] = {0b11000011,0b11111011,0b10100111,0b10110011,0b10011011,0b10010011,0b10000011,0b11011011,0b10000011,0b10010011};
13
14
uint8_t PC_Bitmaske[10] = {0b00000,0b01000,0b00000,0b00000,0b01000,0b10000,0b10000,0b00000};
15
uint8_t PD_Bitmaske[10] = {0b010000000,0b011110000,0b001001000,0b001100000,0b000110000,0b000100000,0b000000000,0b010110000};
16
                                                            
17
// Deklaration der globalen Variablen  
18
uint8_t number = 0;
19
20
// Interrupt-Service-Routine des ADC
21
ISR(ADC_vect)          // An dieser Stelle muss die Deklarations-Kopfzeile korrekte vervollständigt werden 
22
{
23
  number = (5 << ADCL);      // Zuweisung des ADC-Ergebnisses reduziert auf einen mit 3 Bit darstellbaren Wert
24
                  // (Wertebereich 0 - 7)
25
  
26
  PORTC = 0xFF;      // Alle Segmente der 7-Segemntanzeige ausschalten (wie bei der Initialisierung)
27
  PORTD = 0x7C;      
28
29
  
30
                  // Ausgabe des auf 3-Bit reduzierten ADC-Werts auf der 7-Segmentanzeige (Wertebereich 0 - 7)
31
  PORTB |= PC_Bitmaske[number];  // ACHTUNG:  Da die Bitmasken der Arrays (s. oben) jetzt nur die zu setzenden Bits markieren, muss das Modifizieren von
32
  PORTD |= PD_Bitmaske[number];  //      PORTC und PORTD jetzt auch über Maskier-Operationen und nicht mehr über direkte Wertzuweisungen erfolgen
33
34
35
}
36
37
// Interrupt_Service-Routine des Timer/Counter 0
38
ISR(TIMER0_COMPA_vect)            // An dieser Stelle muss die Deklarations-Kopfzeile korrekt vervollständigt werden
39
{
40
  ADCSRA = (1<<ADSC); // Neue ADC-Sequenz starten
41
  
42
}
43
44
int main(void)
45
{
46
  // ADC initialisieren
47
  ADMUX =  (1 << REFS0 ) | (1 << ADLAR)  // (1 << MUX0) | (1 << MUX2) ;          // ADC Referenzspannung AVcc, linksbündige Ausrichtung 
48
                                      // für 8 Bit, Kanal 0 als ADC-Eingang
49
  ADCSRA =   (1 << ADEN) | (1 << ADIE) |( 1 << ADPS0) | ( 1 << ADPS2);  // ADC und ADC Interrupt aktivieren, Frequenzteiler 32
50
  
51
  // Timer/Counter 0 initialisieren: CTC Modus, Prescaler 1/1024, Interrupt-Intervall 10 ms
52
  TCCR0A = (1<<WGM01);
53
  TCCR0B = (1<<CS02)|(1<<CS00) ;
54
  OCR0A = 156;                          // s. Formel aus Versuchsbeschreibung zu Versuch 2
55
  TIMSK0 = (1<<OCIE0A);                      // Compare-Interrupt A freigeben
56
  
57
  // 7 Segment Anzeige initialisieren (Alle LEDs aus, restliche Bits passend für Input und Tristate)
58
  DDRC = 0x18;
59
  PORTC = 0xFF;
60
  DDRD = 0x7C;
61
  PORTD = 0x7C;
62
    
63
  
64
  sei();                              // Interrupts global freigeben
65
  
66
    while (1) 
67
    {
68
  // Hier ist kein Code erforderlich  
69
    }
70
}

von S. Landolt (Gast)


Lesenswert?

Muss dieses number nicht volatile sein?

von Phillip E. (phillipernst1998)


Lesenswert?

S. Landolt schrieb:
> Muss dieses number nicht volatile sein?
1
volatile uint8_t number = 0;

Habe dies nun so ergänzt, hat sich aber trotzdem nicht geändert. 
Vielleicht habe ich dies falsch implementiert?

: Bearbeitet durch User
von Geert H. (geerth)


Lesenswert?

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...

von Phillip E. (phillipernst1998)


Lesenswert?

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

von S. Landolt (Gast)


Lesenswert?

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.

von HildeK (Gast)


Lesenswert?

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.

von HildeK (Gast)


Lesenswert?

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.

von Phillip E. (phillipernst1998)


Lesenswert?

> 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?

von HildeK (Gast)


Lesenswert?

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.

von Phillip E. (phillipernst1998)


Lesenswert?

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.

von HildeK (Gast)


Lesenswert?

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.

von Phillip E. (phillipernst1998)


Lesenswert?

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.

von S. Landolt (Gast)


Lesenswert?

> 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.

von Phillip E. (phillipernst1998)


Lesenswert?

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.
1
 
2
3
while(ADCSRA&(1<<ADSC))
4
  {
5
6
  }

: Bearbeitet durch User
von S. Landolt (Gast)


Lesenswert?

Nein, nicht hinter while, sondern innerhalb der Timer0-ISR.

von S. Landolt (Gast)


Lesenswert?

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)?

von S. Landolt (Gast)


Lesenswert?

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.

von HildeK (Gast)


Lesenswert?

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_t ADC_Read( void )
3
{
4
  uint16_t adc_res;
5
  ADCSRA |= (1<<ADSC);            // eine Wandlung "single conversion"
6
  while (ADCSRA & (1<<ADSC) ) {;} // auf Abschluss der Konvertierung warten
7
  adc_res = ADC;
8
  return adc_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.

von Phillip E. (phillipernst1998)


Lesenswert?

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")!!!
9
uint8_t PC_Bitmaske[10] = {0b00111,0b01111,0b00111,0b00111,0b01111,0b10111,0b10111,0b00111,0b00111,0b00111};
10
uint8_t PD_Bitmaske[10] = {0b11000011,0b11111011,0b10100111,0b10110011,0b10011011,0b10010011,0b10000011,0b11011011,0b10000011,0b10010011};
11
12
//uint8_t PC_Bitmaske[8] = {0b00000,0b01000,0b00000,0b00000,0b01000,0b10000,0b10000,0b00000};
13
//uint8_t PD_Bitmaske[8] = {0b010000000,0b011110000,0b001001000,0b001100000,0b000110000,0b000100000,0b000000000,0b010110000};
14
                                                            
15
// Deklaration der globalen Variablen  
16
uint8_t number = 0;
17
18
// Interrupt-Service-Routine des ADC
19
ISR(ADC_vect)          // An dieser Stelle muss die Deklarations-Kopfzeile korrekte vervollständigt werden 
20
{
21
  number = ( ADCL/32);      // Zuweisung des ADC-Ergebnisses reduziert auf einen mit 3 Bit darstellbaren Wert
22
  number = ( ADCH/32);          // (Wertebereich 0 - 7)
23
  
24
  PORTC = 0xFF;      // Alle Segmente der 7-Segemntanzeige ausschalten (wie bei der Initialisierung)wwrwrw
25
  PORTD = 0x7C;      
26
27
  
28
                  // Ausgabe des auf 3-Bit reduzierten ADC-Werts auf der 7-Segmentanzeige (Wertebereich 0 - 7)
29
  PORTC = PC_Bitmaske[number];  // ACHTUNG:  Da die Bitmasken der Arrays (s. oben) jetzt nur die zu setzenden Bits markieren, muss das Modifizieren von
30
  PORTD = PD_Bitmaske[number];  //      PORTC und PORTD jetzt auch über Maskier-Operationen und nicht mehr über direkte Wertzuweisungen erfolgen
31
32
33
}
34
35
// Interrupt_Service-Routine des Timer/Counter 0
36
ISR(TIMER0_COMPA_vect)            // An dieser Stelle muss die Deklarations-Kopfzeile korrekt vervollständigt werden
37
{
38
  ADCSRA |= (1<<ADSC); // Neue ADC-Sequenz starten
39
  
40
}
41
42
int main(void)
43
{
44
  // ADC initialisieren
45
  ADMUX =  (1 << REFS0 ) | (1 << ADLAR);  // (1 << MUX0) | (1 << MUX2) ;          // ADC Referenzspannung AVcc, linksbündige Ausrichtung 
46
                                      // für 8 Bit, Kanal 0 als ADC-Eingang
47
  ADCSRA =   (1 << ADEN) | (1 << ADIE) |( 1 << ADPS0) | ( 1 << ADPS2);  // ADC und ADC Interrupt aktivieren, Frequenzteiler 32
48
  
49
  // Timer/Counter 0 initialisieren: CTC Modus, Prescaler 1/1024, Interrupt-Intervall 10 ms
50
  TCCR0A = (1<<WGM01);
51
  TCCR0B = (1<<CS02)|(1<<CS00) ;
52
  OCR0A = 156;                          // s. Formel aus Versuchsbeschreibung zu Versuch 2
53
  TIMSK0 = (1<<OCIE0A);                      // Compare-Interrupt A freigeben
54
  
55
  // 7 Segment Anzeige initialisieren (Alle LEDs aus, restliche Bits passend für Input und Tristate)
56
  DDRC = 0x18;
57
  PORTC = 0xFF;
58
  DDRD = 0x7C;
59
  PORTD = 0x7C;
60
    
61
  
62
  sei();                              // Interrupts global freigeben
63
  
64
    while (1) 
65
    {
66
  // Hier ist kein Code erforderlich  
67
    }
68
}

von S. Landolt (Gast)


Lesenswert?

Vorschlag:
PORTC &= PC_Bitmaske[number];

(Vielleicht auch PORTC &= ~(PC_Bitmaske[number]), je nachdem, wie 
PC_Bitmaske codiert wurde)

von Stefan H. (stefan_hb)


Lesenswert?

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.

: Bearbeitet durch User
von HildeK (Gast)


Lesenswert?

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_t ergebnis;
2
 uint8_t number;
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!
13
14
// oder für seine Aufgabe:
15
  number = ADCH/32;  // nur 3 Bit von Interesse

von HildeK (Gast)


Lesenswert?

HildeK schrieb:
> und erhält die ganzen 12 Bit

Mist, man sieht es immer nur direkt nach dem Abschicken 😀
Es sind nur 10 Bit.

Beitrag #6553910 wurde von einem Moderator gelöscht.
Beitrag #6553927 wurde von einem Moderator gelöscht.
Beitrag #6554199 wurde von einem Moderator gelöscht.
Beitrag #6554718 wurde von einem Moderator gelöscht.
von Phillip E. (phillipernst1998)


Lesenswert?

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")!!!
9
10
11
uint8_t PC_Bitmaske[8] = {0b00011000,0b00010000,0b00011000,0b00011000,0b00010000,0b00001000,0b00001000,0b00011000};
12
uint8_t PD_Bitmaske[8] = {0b00111100,0b00000100,0b01011000,0b01001100,0b01100100,0b01101100,0b01111100,0b00000100};
13
                                                            
14
// Deklaration der globalen Variablen  
15
uint8_t number = 0;
16
17
// Interrupt-Service-Routine des ADC
18
ISR(ADC_vect)          // An dieser Stelle muss die Deklarations-Kopfzeile korrekte vervollständigt werden 
19
{
20
  //number = ( ADCL/32);      // Zuweisung des ADC-Ergebnisses reduziert auf einen mit 3 Bit darstellbaren Wert
21
  number = ( ADCH/32);
22
     //number = ( ADCH>>5);          // (Wertebereich 0 - 7)
23
  
24
  PORTC = 0xFF;      // Alle Segmente der 7-Segemntanzeige ausschalten (wie bei der Initialisierung)wwrwrw
25
  PORTD = 0x7C;      
26
27
  
28
                  // Ausgabe des auf 3-Bit reduzierten ADC-Werts auf der 7-Segmentanzeige (Wertebereich 0 - 7)
29
  PORTC &= ~PC_Bitmaske[number];  // ACHTUNG:  Da die Bitmasken der Arrays (s. oben) jetzt nur die zu setzenden Bits markieren, muss das Modifizieren von
30
  PORTD &= ~PD_Bitmaske[number];  //  PORTC und PORTD jetzt auch über Maskier-Operationen und nicht mehr über direkte Wertzuweisungen erfolgen
31
32
33
}
34
35
// Interrupt_Service-Routine des Timer/Counter 0
36
ISR(TIMER0_COMPA_vect)            // An dieser Stelle muss die Deklarations-Kopfzeile korrekt vervollständigt werden
37
{
38
  ADCSRA |= (1<<ADSC); // Neue ADC-Sequenz starten
39
  
40
}
41
42
int main(void)
43
{
44
  // ADC initialisieren
45
  ADMUX =  (1 << REFS0 ) | (1 << ADLAR);                                  // (1 << MUX0) | (1 << MUX2) ;
46
                                                                            // ADC Referenzspannung AVcc, linksbündige Ausrichtung 
47
                                      // für 8 Bit, Kanal 0 als ADC-Eingang
48
  ADCSRA =   (1 << ADEN) | (1 << ADIE) |( 1 << ADPS0) | ( 1 << ADPS2);  // ADC und ADC Interrupt aktivieren, Frequenzteiler 32
49
  
50
  // Timer/Counter 0 initialisieren: CTC Modus, Prescaler 1/1024, Interrupt-Intervall 10 ms
51
  TCCR0A = (1<<WGM01);
52
  TCCR0B = (1<<CS02)|(1<<CS00) ;
53
  OCR0A = 156;                // s. Formel aus Versuchsbeschreibung zu Versuch 2
54
  TIMSK0 = (1<<OCIE0A);              // Compare-Interrupt A freigeben
55
  
56
  // 7 Segment Anzeige initialisieren (Alle LEDs aus, restliche Bits passend für Input und Tristate)
57
  DDRC = 0x18;
58
  PORTC = 0xFF;
59
  DDRD = 0x7C;
60
  PORTD = 0x7C;
61
    
62
  
63
  sei();                  // Interrupts global freigeben
64
  
65
    while (1) 
66
    {
67
  // Hier ist kein Code erforderlich  
68
    }
69
}

von Bernd N (Gast)


Angehängte Dateien:

Lesenswert?

Und Formatiert, gehört auch zur Lösung.

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.