Forum: Mikrocontroller und Digitale Elektronik Atmega32 ADC0 bis ADC7 nur High oder Low erkennen


von Frank N. (derprogrammierer78)


Lesenswert?

Hallo zusammen :)

Entweder ist mein Ziel nur über einen Trick erreichbar, oder ich sehe 
den Wald vor lauter Bäumen nicht ;)

Ich möchte an einem Atmega32 die Eingänge PA0 bis PA7 und PC0 bis PC6 
als digitale Eingänge für einen Joystick mit mehreren Buttons nutzen.

Bei den Eingängen PC0 bis PC6 ist das ja auch kein Problem.

Aber bei PA0 bis PA7 (ADC0 bis ADC7) berechne ich derzeit immer die 
anliegende Spannung und wenn das Ergebnis größer 3V ist, dann ist der 
Eingang high.

Das ist generell auch erst einmal so gewollt, da sich an diesen 
Eingängen 8 Feuerknöpfe befinden und ich so über die Spannung erkennen 
kann, ob ich zwischen 3V und 4V liege für Schnellfeuer oder über 4V für 
normales Feuer. Das hilft einem bei Spielen wie R-Type enorm weiter ;)

Das kostet aber enorm rechenzeit und wenn es (wie in meinem Fall) 
Zeitkritisch wird, dann bekommt man ein Problem.

Ich finde im Internet jedoch keine Anleitung, wie ich die ADCs 
deaktivieren kann. Eventuell suche ich auch einfach falsch. Und ohne 
diese zu deaktivieren ist die einzige Lösung, die mir einfällt, das 
höchste Bit auszulesen und wenn das gesetzt ist, dann ist das Signal 
high.

Gibt es da noch einen eleganteren Weg?

Danke vorab für eure Ideen :)

von H.Joachim S. (crazyhorse)


Lesenswert?

PORTA lässt sich doch ganz genau wie die anderen als digitaler I/O 
verwenden wie die anderen Ports? Oder verstehe ich dein Problem nicht?

von Michael F. (sharpals)


Lesenswert?

Hallo, das sollte eigentlich nicht so sein, dann mußt du die ADCs ja 
aktiviert haben.

Hast du jetzt vor, den eingang als reinen digitalen zu benutzen ?
Oder soll er abwechselnd als AD und digitaler dienen ?

Du kannst den ADC zur laufzeit auch wider ausmachen, dazu mußt du nur 
das register setzen.

ist das register

 ADCSRA

wichtig

dort mußt du das ADEN flag löschen, dann sind die adc aus.

von Stefan F. (Gast)


Lesenswert?

> Du kannst den ADC zur laufzeit auch wider ausmachen

Stimmt, ist jedoch nichtmal nötig. Alle Eingänge (auch die analogen) 
kann man bei AVR's jederzeit über die PIN Register abfragen, selbst wenn 
dort gerade Sonderfunktionen aktiviert sind.

von Peter D. (peda)


Lesenswert?

Bei den alten ATmega32 lassen sich die Digitaleingänge noch nicht 
abschalten, d.h. sie sind immer parallel zu den Analogeingängen aktiv.
Erst beim ATmega324 lassen sie sich abschalten.

von Oliver S. (oliverso)


Lesenswert?

Solange man die aber nicht explizit abschaltet, sind die Digitaleingänge 
aktiv.

Digital high/low lässt sich daher immer über die PINn - Register 
abfragen.

Oliver

von Frank N. (derprogrammierer78)


Lesenswert?

@crazyhorse: Genau das dachte ich auch erst. Aber aus irgend einem Grund 
hat das nicht funktioniert. Auch wenn ich die internen Pull-Up-Resistors 
aktiviert habe, haben die Eingänge gemacht was sie wollten.

@sharpals: Ich hatte sie im ursprünglichen Skript nicht explizit 
aktivert oder deaktiviert. Da ich zum ersten mal mit dem Atmega32 und 
diesen ADC-Eingängen arbeite dachte ich nun, dass die immer irgendwie 
als ADC genutzt werden müssen nachdem es ohne nicht funktioniert hat. 
Deshalb habe ich mir also einen Codeschnipsel aus dem Internet besorgt 
und diesen an meine Bedürfnisse angepasst und so lange damit rumgespielt 
bis ich diesen verstanden hatte.

Ich mache später mal einen Rückbau des Codes und poste dann hier den 
Code wie er vor der Umstellung auf ADC war. Evtl. sieht ja einer den 
Fehler :)

Danke für eure Zeit :)

von Frank N. (derprogrammierer78)


Lesenswert?

Danke für eure Zeit @stefanus, @peda und @oliverso :)

von Michael F. (sharpals)


Lesenswert?

Nein keine angst , du bist nicht verpflichtet, sie als ADC zu nutzen. 
Behandle sie als ganz normale eingänge und beseitige den codeteil und 
alles wird gut.

von Ingo L. (corrtexx)


Lesenswert?

Frank N. schrieb:
> Das kostet aber enorm rechenzeit und wenn es (wie in meinem Fall)
> Zeitkritisch wird, dann bekommt man ein Problem.
Ui ui ui, du kannst deinen Joystick schneller bedienen als ein AVR ein 
paar Messung macht und auswertet?!?

von Frank N. (derprogrammierer78)


Lesenswert?

@corrtexx: gg Das nicht, aber der SNES und der MegaDrive geben ein 
sehr straffes Timing mit sehr knappen Reaktionszeiten vor ;)

Ich hatte mittlerweile angefangen mit Interrupts zu arbeiten und 
entsprechend auf die Signale von den Konsolen zu reagieren. Aber auch da 
hatte ich das Problem, dass die Berechnungsdauer länger ist als die 
maximal zugelassene Reaktionszeit von den Konsolen.

Darum versuche ich da zeit zu sparen um die Codestruktur nicht noch 
weiter verschachteln zu müssen ;)

von da1l6 (Gast)


Lesenswert?

Hallo

Das SNES verwendet AFAIK eine Shift-Register im Controller. Das kannst 
du mit SPI im slave mode simulieren.

Das macht die Sache viel entspannter. Du kannst das nächste Byte 
schreiben während die Hardware gerade das aktuelle ausgibt.

da1l6

von Stefan F. (Gast)


Lesenswert?

> Entweder ist mein Ziel nur über einen Trick erreichbar, oder ich
> sehe den Wald vor lauter Bäumen nicht ;)

Manchmal sieht man Vögel, die Mühsam eine lange Treppe herunter hoppeln. 
Und wenn sie dann unten angekommen sind, fliegen sie davon.

Muss man nicht verstehen.

von Peter II (Gast)


Lesenswert?

Frank N. schrieb:
> Ich hatte mittlerweile angefangen mit Interrupts zu arbeiten und
> entsprechend auf die Signale von den Konsolen zu reagieren. Aber auch da
> hatte ich das Problem, dass die Berechnungsdauer länger ist als die
> maximal zugelassene Reaktionszeit von den Konsolen.

dann wird deine Software wohl noch sehr suboptimal sein. Das sollte so 
ein Amtel bequem im Millisekundenbereich schaffen.

von Peter D. (peda)


Lesenswert?

Frank N. schrieb:
> Das kostet aber enorm rechenzeit und wenn es (wie in meinem Fall)
> Zeitkritisch wird, dann bekommt man ein Problem.

Dann machst Du was falsch.
Nimm den ADC-Interrupt, lasse ihn das Ergebnis in ein Array schreiben 
und den MUX weiter schalten.
Und für die Auswertung vergleichst Du einfach den Wert im Array mit der 
Schwelle (8Bit sollten reichen).
Und natürlich rechnet man die ADC-Werte nicht in float Volts um, sondern 
läßt zur Compilezeit per #define die Volts in ADC-Schritte umrechnen.
1
#define ADC_STEPS(volt) (uint8_t)(volt / 5.0 * 255)
Dann dauert ein Vergleich <1µs und das ist nicht enorm.

von Clemens W. (daxmus)


Lesenswert?

Ist es mir gestattet eine dumme Frage zu stellen?

Peter D. schrieb:
> Und natürlich rechnet man die ADC-Werte nicht in float Volts um, sondern
> läßt zur Compilezeit per #define die Volts in ADC-Schritte
> umrechnen.
> #define ADC_STEPS(volt) (uint8_t)(volt / 5.0 * 255)

Durch den Define kopiert der Präprozessor den Code ja einfach nur, aber 
ausgeführt wird er doch zur Laufzeit. Sprich es findet eine 
Multiplikation und eine Division mit float Werten statt. Einzig der 
Wertevergleich ist dann auf Integerbasis und sollte schneller gehen. Das 
Rechnen mit float Werten spart man sich dadurch ja nicht.
Oder meintest Du, dass damit das Rechnen mit float Werten in den 
Interrupt verlagert wird und in der Hauptschleife nur mit Integer Werten 
gerechnet/verglichen werden muß?

von Peter D. (peda)


Lesenswert?

Clemens W. schrieb:
> Durch den Define kopiert der Präprozessor den Code ja einfach nur, aber
> ausgeführt wird er doch zur Laufzeit.

Der Compiler ist faul. Wenn er einen konstanten Ausdruck sieht, rechnet 
er ihn sofort aus und fügt das Ergebnis ein, statt dafür noch 
umständlich Laufzeitcode zu erzeugen.
Das Define dient nur der besseren Lesbarkeit für Dich.
Schau einfach mal ins Assemblerlisting (*.lss).

von Clemens W. (daxmus)


Lesenswert?

Danke für Deine schnelle Antwort!
Daran das der Ausdruck teilweise zur Kompilierzeit ausgewertet wird, hab 
ich nicht gedacht. Aber hab dann nochmal kurz darüber nachgedacht und 
bin darauf gekommen, dass der Vorteil auch in den kompletten späteren 
Zugriffen auf das Array und im Speicherverbrauch des Arrays selber 
liegen.

von Michael F. (sharpals)


Lesenswert?

wird eigentlich der echte spannungsbereich gebraucht ?

Wenn nicht reicht es doch aus den schwellwert , oder was sonst auf der 
basis von dem ADC zu ermitteln ..

wenn also 4V gefordert werden, dann wäre es doch der wert 204

klassischer dreisatz :

const wert = byte(geforderte_spannung / V_ref * 255) ;

in diesem falle braucht die CPU garnichts ausrechnen;

: Bearbeitet durch User
von Frank N. (derprogrammierer78)


Lesenswert?

Hallo zusammen :)

Es tut mir Leid, dass es nun doch etwas länger gedauert hat, bis ich 
hier antworte.

Ich habe keine Ahnung, was ich bei meinem ersten Versuch falsch gemacht 
hatte, da es nun auf Anhieb tadellos lief ...

Hier mein Code und vielen Dank für eure Meinungen und eure Hilfe :)
1
/*
2
 * 15ButtonDigitalControllerTest.c
3
 *
4
 * This is a small program to test the controller with the specific test-dongle
5
 *
6
 * PB0 OUT LED 1
7
 * PB1 OUT LED 2
8
 * PB2 OUT LED 3
9
 * PB3 OUT LED 4
10
 *
11
 * PA0 IN BTN1
12
 * PA1 IN BTN2
13
 * PA2 IN BTN3
14
 * PA3 IN BTN4
15
 * PA4 IN BTN5
16
 * PA5 IN BTN6
17
 * PA6 IN BTN7
18
 * PA7 IN BTN8
19
 * PC0 IN BTN9
20
 * PC1 IN BTN10
21
 * PC2 IN BTN11
22
 * PC3 IN UP
23
 * PC4 IN DOWN
24
 * PC5 IN LEFT
25
 * PC6 IN RIGHT
26
 * 
27
 * Created: 26.04.2017
28
 * Last modified: 26.04.2017 
29
 * Author : Frank
30
 */ 
31
32
#define F_CPU 1000000UL      // CPU-Frequency 1MHz
33
34
#include <avr/io.h>        // Headerfile for Input & Output
35
#include <util/delay.h>      // Headerfile for delays
36
37
int main(void)
38
{
39
  // Deactivate JTAG-debugging
40
  MCUCSR=(1<<JTD);
41
  MCUCSR=(1<<JTD);
42
  
43
  // Define LED-outputs for the test-dongle
44
    DDRB = 0xff; // Set as output
45
    
46
  // Define inputs for 15 button controller
47
  DDRA = 0x00; // Set as input
48
  DDRC = 0x00; // Set as input
49
  PORTA = 0xff; // Activate PULL-UP-RESISTORS
50
  PORTC = 0xff; // Activate PULL-UP-RESISTORS
51
  
52
  // Test the LEDs on Port B for 500ms
53
  PORTB |= (1<<PORTB0);  // Set PORTB0 ON
54
  PORTB |= (1<<PORTB1);  // Set PORTB1 ON
55
  PORTB |= (1<<PORTB2);  // Set PORTB2 ON
56
  PORTB |= (1<<PORTB3);  // Set PORTB3 ON
57
  _delay_ms(500);      // Wait 0,5s
58
  PORTB &= ~(1<<PORTB0);  // Set PORTB0 OFF
59
  PORTB &= ~(1<<PORTB1);  // Set PORTB1 OFF
60
  PORTB &= ~(1<<PORTB2);  // Set PORTB2 OFF
61
  PORTB &= ~(1<<PORTB3);  // Set PORTB3 OFF
62
  
63
    while (1) 
64
    {    
65
    if(!(PINA & (1<<PORTA0))) {
66
      // PORTA0 ACTIVE - BTN1
67
      PORTB |= (1<<PORTB0);  // Set PORTB0 ON
68
      PORTB &= ~(1<<PORTB1);  // Set PORTB1 OFF
69
      PORTB &= ~(1<<PORTB2);  // Set PORTB2 OFF
70
      PORTB &= ~(1<<PORTB3);  // Set PORTB3 OFF
71
    }  else if(!(PINA & (1<<PORTA1))) {
72
      // PORTA1 ACTIVE - BTN2
73
      PORTB &= ~(1<<PORTB0);  // Set PORTB0 OFF
74
      PORTB |= (1<<PORTB1);  // Set PORTB1 ON
75
      PORTB &= ~(1<<PORTB2);  // Set PORTB2 OFF
76
      PORTB &= ~(1<<PORTB3);  // Set PORTB3 OFF
77
    }  else if(!(PINA & (1<<PORTA2))) {
78
      // PORTA2 ACTIVE - BTN3
79
      PORTB |= (1<<PORTB0);  // Set PORTB0 ON
80
      PORTB |= (1<<PORTB1);  // Set PORTB1 ON
81
      PORTB &= ~(1<<PORTB2);  // Set PORTB2 OFF
82
      PORTB &= ~(1<<PORTB3);  // Set PORTB3 OFF
83
    }  else if(!(PINA & (1<<PORTA3))) {
84
      // PORTA3 ACTIVE - BTN4
85
      PORTB &= ~(1<<PORTB0);  // Set PORTB0 OFF
86
      PORTB &= ~(1<<PORTB1);  // Set PORTB1 OFF
87
      PORTB |= (1<<PORTB2);  // Set PORTB2 ON
88
      PORTB &= ~(1<<PORTB3);  // Set PORTB3 OFF
89
    }  else if(!(PINA & (1<<PORTA4))) {
90
      // PORTA4 ACTIVE - BTN5
91
      PORTB |= (1<<PORTB0);  // Set PORTB0 ON
92
      PORTB &= ~(1<<PORTB1);  // Set PORTB1 OFF
93
      PORTB |= (1<<PORTB2);  // Set PORTB2 ON
94
      PORTB &= ~(1<<PORTB3);  // Set PORTB3 OFF
95
    }  else if(!(PINA & (1<<PORTA5))) {
96
      // PORTA5 ACTIVE - BTN6
97
      PORTB &= ~(1<<PORTB0);  // Set PORTB0 OFF
98
      PORTB |= (1<<PORTB1);  // Set PORTB1 ON
99
      PORTB |= (1<<PORTB2);  // Set PORTB2 ON
100
      PORTB &= ~(1<<PORTB3);  // Set PORTB3 OFF
101
    }  else if(!(PINA & (1<<PORTA6))) {
102
      // PORTA6 ACTIVE - BTN7
103
      PORTB |= (1<<PORTB0);  // Set PORTB0 ON
104
      PORTB |= (1<<PORTB1);  // Set PORTB1 ON
105
      PORTB |= (1<<PORTB2);  // Set PORTB2 ON
106
      PORTB &= ~(1<<PORTB3);  // Set PORTB3 OFF
107
    }  else if(!(PINA & (1<<PORTA7))) {
108
      // PORTA7 ACTIVE - BTN8
109
      PORTB &= ~(1<<PORTB0);  // Set PORTB0 OFF
110
      PORTB &= ~(1<<PORTB1);  // Set PORTB1 OFF
111
      PORTB &= ~(1<<PORTB2);  // Set PORTB2 OFF
112
      PORTB |= (1<<PORTB3);  // Set PORTB3 ON
113
    } else if(!(PINC & (1<<PORTC0))) {
114
      // PORTC0 ACTIVE - BTN9
115
      PORTB |= (1<<PORTB0);  // Set PORTB0 ON
116
      PORTB &= ~(1<<PORTB1);  // Set PORTB1 OFF
117
      PORTB &= ~(1<<PORTB2);  // Set PORTB2 OFF
118
      PORTB |= (1<<PORTB3);  // Set PORTB3 ON
119
    } else if(!(PINC & (1<<PORTC1))) {
120
      // PORTC1 ACTIVE - BTN10
121
      PORTB &= ~(1<<PORTB0);  // Set PORTB0 OFF
122
      PORTB |= (1<<PORTB1);  // Set PORTB1 ON
123
      PORTB &= ~(1<<PORTB2);  // Set PORTB2 OFF
124
      PORTB |= (1<<PORTB3);  // Set PORTB3 ON
125
    } else if(!(PINC & (1<<PORTC2))) {
126
      // PORTC2 ACTIVE - BTN11
127
      PORTB |= (1<<PORTB0);  // Set PORTB0 ON
128
      PORTB |= (1<<PORTB1);  // Set PORTB1 ON
129
      PORTB &= ~(1<<PORTB2);  // Set PORTB2 OFF
130
      PORTB |= (1<<PORTB3);  // Set PORTB3 ON
131
    } else if(!(PINC & (1<<PORTC3))) {
132
      // PORTC3 ACTIVE - UP
133
      PORTB &= ~(1<<PORTB0);  // Set PORTB0 OFF
134
      PORTB &= ~(1<<PORTB1);  // Set PORTB1 OFF
135
      PORTB |= (1<<PORTB2);  // Set PORTB2 ON
136
      PORTB |= (1<<PORTB3);  // Set PORTB3 ON
137
    } else if(!(PINC & (1<<PORTC4))) {
138
      // PORTC4 ACTIVE - DOWN
139
      PORTB |= (1<<PORTB0);  // Set PORTB0 ON
140
      PORTB &= ~(1<<PORTB1);  // Set PORTB1 OFF
141
      PORTB |= (1<<PORTB2);  // Set PORTB2 ON
142
      PORTB |= (1<<PORTB3);  // Set PORTB3 ON
143
    } else if(!(PINC & (1<<PORTC5))) {
144
      // PORTC5 ACTIVE - LEFT
145
      PORTB &= ~(1<<PORTB0);  // Set PORTB0 OFF
146
      PORTB |= (1<<PORTB1);  // Set PORTB1 ON
147
      PORTB |= (1<<PORTB2);  // Set PORTB2 ON
148
      PORTB |= (1<<PORTB3);  // Set PORTB3 ON
149
    } else if(!(PINC & (1<<PORTC6))) {
150
      // PORTC6 ACTIVE - RIGHT
151
      PORTB |= (1<<PORTB0);  // Set PORTB0 ON
152
      PORTB |= (1<<PORTB1);  // Set PORTB1 ON
153
      PORTB |= (1<<PORTB2);  // Set PORTB2 ON
154
      PORTB |= (1<<PORTB3);  // Set PORTB3 ON
155
    } else {
156
      PORTB &= ~(1<<PORTB0);  // Set PORTB0 OFF
157
      PORTB &= ~(1<<PORTB1);  // Set PORTB1 OFF
158
      PORTB &= ~(1<<PORTB2);  // Set PORTB2 OFF
159
      PORTB &= ~(1<<PORTB3);  // Set PORTB3 OFF
160
    }
161
    
162
    _delay_ms(5);      // Wait 5ms
163
    }
164
}

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.