Forum: Mikrocontroller und Digitale Elektronik ADC_Sensorwert_einlese


von Philip H. (phayn)


Angehängte Dateien:

Lesenswert?

Ich habe folgendes Problem,
Mein Ziel ist es im Zuge eines Maturaprojektes ein Programm in Assembler 
für die Steuerung eines Roboters zu schreiben.
Der verwendete µController ist ein ATmega16 und die Programmierebene 
AVR-Studio 4.

In Assembler habe ich schon hineingeschnuppert und mir ein Grundwissen 
dieser Sprache angeeignet.
Mein Ziel ist es jetzt ein Programm auf die Beine zu stellen, welches 5 
Sensorwerte (0V - 2,54V) mit dem ADC über 5 Kanäle einlest und mit einem 
"Kontrastwert" vergleicht. Der Kontrastwert soll quasi eine Schwelle 
(zB. 1V) sein, bei deren Überschreitung der jeweilige Sensor auf aktiv 
gesetzt werden soll.

Die Sensoren sollen nun je nach dem welche(r) Sensor(en) aktiv ist/sind 
nach einer Tabelle je ein bestimmtes Ereignis resultieren.
Dieses Ereignis ist dann die PWM mit der ich einen Servomotor ansteuern 
werde.
Für die Ansteuerung des Servos steht bereits ein Programm, wer einen 
Servo ansteuern will und ein Programm braucht stelle ich es gerne 
bereit.

Hab schon einige verschiedene Programme geschrieben, wo der Fehler liegt 
bin ich selber nicht draufgekommen.

Freerunning Mode ist zur Zeit in Verwendung um ständig die Sensordaten 
einzulesen.
Reichen würde es aber jede millisekunde alle 5 Sensoren einzulesen.
Wie das genau funktioniert mit der ADC-Wandlung und wann sie fertig ist 
und beginnt konnte ich aus dem Datenblatt des µC nicht rauslesen.

Würd mich sehr freuen wenn sich jemand die mühe und aufwand nimmt und 
mir bei dieser Problemstellung unter die Arme greifen könnte.

vl. ist noch interessant zu wissen für was das ganze gut sein soll.
der Roboter soll in der lage sein eine Linie zu verfolgen und die 5 
Sensoren dienen zur Information, wo die Linie sich gerade befindet und 
in welche Richtung der Servo lenken soll.

dann bedank ich mich recht herzlich schon mal im vorhinein

mfg. Philip

ps: in den Anhang gebe ich mal ein Programm das ich geschrieben habe um 
die 5 Sensoren einzulesen und an Leds die aktiven Sensoren auszugeben.

von Johannes M. (johnny-m)


Lesenswert?

> Hab schon einige verschiedene Programme geschrieben, wo der Fehler liegt
> bin ich selber nicht draufgekommen.
Und wie sollen wir drauf kommen, wenn Du nicht erzählst, was für ein 
Fehler überhaupt auftritt bzw. was konkret nicht so funktioniert, wie 
es soll?

von Philip H. (phayn)


Lesenswert?

leider weiß ich nicht welcher Fehler auftritt :(

jedenfalls wird am Ausgang (Leds) ein falsches Ergebnis geliefert.

von Johannes M. (johnny-m)


Lesenswert?

Ich habe mal über den Code drübergeschaut und keine Stelle gefunden, an 
der Du das Ergebnis der A/D-Wandlung ausliest und irgendwie 
verarbeitest. Hab ich da was übersehen oder hast Du da was ganz 
elementar wichtiges vergessen?

von Phayn (Gast)


Lesenswert?

Ich lass das Ergebnis des ADC linksbündig ins Register schreiben.
Dazu nehme ich aber im ADC-Register nur 8 der 10 bit-Auflösung.
diese 8 Höchstwertigen bits lese ich dann "einfach" vom ADCH-Register 
aus.

ich kopier mal vl. den quelltext rein damit wir auch über das selbe 
reden.
1
.include "m16def.inc"  
2
3
;* benötigte Ressourcen:    *
4
;*  r16................Arbeitsregister    *
5
;*  r17................Arbeitsregister    *
6
7
;*  r20................Sensor0   *
8
;*  r21................Sensor1   *
9
;*  r22................Sensor2   *
10
;*  r23................Sensor3   *
11
;*  r24................Sensor4   *
12
13
14
.org 0x000
15
  rjmp RESET
16
.org 0x01C
17
  rjmp ADC_Fertig        ;ADC Conversation complete
18
19
.def vergleich = r19
20
.def Sensor0 = r20
21
.def Sensor1 = r21
22
.def Sensor2 = r22
23
.def Sensor3 = r23
24
.def Sensor4 = r24
25
.def kontrast = r25
26
.def mux = r18
27
28
29
initial:
30
31
; Initialisierung der benötigten Ports als Ein- und Ausgabeports                 
32
      ldi r16, 0xFF
33
      out DDRC, r16    ;PortC als Ausgabeport für LED-Anzeige
34
      ldi r16, 0x00
35
      out DDRA, r16    ;PortA als Eingabeport ADC
36
      ldi r16, 0x00
37
      out DDRB, r16    ;PortB als Eingabeport Taster
38
39
; Initialisierungen für Interrupts
40
41
; Setzen des Stackpointers
42
      ldi r16, HIGH(RAMEND)
43
      out SPH, r16
44
      ldi r16, LOW(RAMEND)
45
      out SPL, r16
46
47
48
  ldi r16, 0b10101101      ; ADIE setzen Interrupt Enable
49
  out ADCSRA, r16        ; ADEN setzen (ADC enable)
50
            ; ADC Prescaler = 32
51
52
  ldi r16, 0b01100000      ; Referenzspannung AVCC REFS0 = 1
53
  out ADMUX, r16        ; linksbündige Anzeige
54
55
  ldi r16, 0x00        ; 0000 0000b
56
  out SFIOR, r16        ; Free Running Mode
57
58
  ldi kontrast, 0b1000000
59
60
Hauptprogramm:
61
  sei
62
  ldi r16, 0b11101101      ; Start der AD-Konversion
63
  out ADCSRA, r16  
64
65
warte:
66
  com Vergleich
67
  out portc, vergleich 
68
  com vergleich
69
70
  call Sensorkontrastvergleich
71
  
72
  retSensor_aktiv:
73
  ori mux, 0b00100000
74
  out admux, mux
75
76
  ldi r16, 0b11101101      ; Start der AD-Konversion
77
  out ADCSRA, r16  
78
79
  call delay_100us
80
81
  
82
rjmp warte  
83
;*************************************************
84
Sensorkontrastvergleich:
85
86
  Vergleich_Sensor0:
87
  andi vergleich, 0b11101111
88
                ;Kontrast bzw. Empfindlichkeit
89
  cp  kontrast, sensor0    ;vergleiche Register mit ADC-Wert
90
  brlo Sensor0_aktiv    ;springe wenn r16 kleiner als sensor0
91
92
  Vergleich_Sensor1:
93
  andi vergleich, 0b11110111
94
  cp  kontrast, sensor1    ;vergleiche Register mit ADC-Wert
95
  brlo Sensor1_aktiv      ;springe wenn r16 kleiner als sensor0
96
97
  Vergleich_Sensor2:
98
  andi vergleich, 0b11111011
99
  cp  kontrast, sensor2    ;vergleiche Register mit ADC-Wert
100
  brlo Sensor2_aktiv      ;springe wenn r16 kleiner als sensor2
101
102
  Vergleich_Sensor3:
103
  andi vergleich, 0b11111101
104
  cp  kontrast, sensor3    ;vergleiche Register mit ADC-Wert
105
  brlo Sensor3_aktiv      ;springe wenn r16 kleiner als sensor3
106
107
  Vergleich_Sensor4:
108
  andi vergleich, 0b11111110
109
  cp  kontrast, sensor4    ;vergleiche Register mit ADC-Wert
110
  brlo Sensor4_aktiv      ;springe wenn r16 kleiner als sensor4
111
  
112
  ret
113
;*************************************************
114
  Sensor0_aktiv:        ;Sprung wenn Sensorwert größer als 1 ist
115
  ori vergleich, 0b00010000  ;Led zeigt welcher sensor anspricht
116
  jmp Vergleich_Sensor1
117
118
  Sensor1_aktiv:        ;Sprung wenn Sensorwert größer als 1 ist
119
  ori vergleich, 0b00001000  ;Led zeigt welcher sensor anspricht
120
  jmp Vergleich_Sensor2
121
122
  Sensor2_aktiv:        ;Sprung wenn Sensorwert größer als 1 ist
123
  ori vergleich, 0b00000100  ;Led zeigt welcher sensor anspricht
124
  jmp Vergleich_Sensor3
125
126
  Sensor3_aktiv:        ;Sprung wenn Sensorwert größer als 1 ist
127
  ori vergleich, 0b00000010  ;Led zeigt welcher sensor anspricht
128
  jmp Vergleich_Sensor4
129
130
  Sensor4_aktiv:        ;Sprung wenn Sensorwert größer als 1 ist
131
  ori vergleich, 0b00000001  ;Led zeigt welcher sensor anspricht
132
  jmp retSensor_aktiv
133
;*************************************************
134
135
ADC_Fertig:          ; ADC Konvertierung fertig, Interrupt hierher
136
  cli
137
  push r16
138
139
;Nächsten Sensor ermitteln
140
;  mov r16, mux        ; aktueller Sensor in r16 kopieren
141
  andi mux,0b00000111      ; Setzt Bit 3-7 auf 0, Bit 0 bis 2 bleiben unverändert            ; Sensor um 1 erhöhen
142
143
  ldi r17, 0b00000000
144
  cp r17, mux
145
  Breq Sensor0sprung_verlaengerung  
146
147
  ldi r17, 0b00000001
148
  cp r17, mux
149
  Breq Sensor1sprung_verlaengerung
150
151
  ldi r17, 0b00000010
152
  cp r17, mux
153
  Breq Sensor2sprung_verlaengerung
154
155
  ldi r17, 0b00000011
156
  cp r17, mux
157
  Breq Sensor3sprung_verlaengerung  
158
159
  ldi r17, 0b00000100
160
  cp r17, mux
161
  Breq Sensor4sprung_verlaengerung  
162
163
  Sensor0sprung_verlaengerung:
164
  jmp Sensor0sprung
165
  Sensor1sprung_verlaengerung:
166
  jmp Sensor1sprung  
167
  Sensor2sprung_verlaengerung:
168
  jmp Sensor2sprung
169
  Sensor3sprung_verlaengerung:
170
  jmp Sensor3sprung
171
  Sensor4sprung_verlaengerung:  
172
  jmp Sensor4sprung  
173
    
174
retsensor:
175
  inc mux            ; Sensor erhöhen
176
177
retsensor_ohne_increment:
178
;  ldi r16, 0b11101101      ; Start der AD-Konversion
179
;  out ADCSRA, r16  
180
  pop r16
181
  sei
182
  reti
183
184
Sensor0sprung:
185
  in sensor0, adch
186
jmp retsensor
187
188
Sensor1sprung:    
189
  in sensor1, adch
190
jmp retsensor
191
192
Sensor2sprung:
193
  in sensor2, adch
194
jmp retsensor
195
196
Sensor3sprung:
197
  in sensor3, adch
198
jmp retsensor
199
200
Sensor4sprung:
201
  in sensor4, adch
202
  ldi mux, 0x00
203
jmp retsensor_ohne_increment
204
205
206
RESET: jmp initial
207
208
209
delay_100us:
210
; ============================= 
211
;   Warteschleifen-Generator 
212
;     400 Zyklen:
213
; ----------------------------- 
214
; warte 399 Zyklen:
215
          ldi  R17, $85
216
WGLOOP0:  dec  R17
217
          brne WGLOOP0
218
; ----------------------------- 
219
; warte 1 Zyklus:
220
          nop
221
; ============================= 
222
ret

von Johannes M. (johnny-m)


Lesenswert?

Ah, hatte das unten überlesen.

Hast Du mal ne vereinfachte Version des Programms gemacht, um die 
Funktion des ADC und eine einfache Ausgabe mal zu testen? Der Code oben 
ist eigentlich  schon etwas zu komplex, um einen "generellen 
funktioniert-nicht"-Fehler zu finden.

von Johannes M. (johnny-m)


Lesenswert?

Ich verstehe aber nicht ganz, was da in der warte-Routine im 
Hauptprogrammteil gemacht wird. Speziell scheint mir, da wird ohne auf 
das Ende einer Wandlung zu warten einfach am ADCSRA und am ADMUX 
rumgeschraubt. Warum startest Du die Wandlung immer neu? Der 
Free-Running-Modus zeichnet sich schließlich dadurch aus, dass man das 
nur einmal machen muss, nämlich ganz am Anfang. Außerdem braucht man 
dafür nicht jedes Mal das ganze ADCSRA neu zu schreiben. Das ADCSRA 
liegt im bitadressierbaren Bereich. Es reicht dann völlig, das ADSC zu 
setzen.

von Rolf Magnus (Gast)


Lesenswert?

Was mir zu deiner ISR auffällt, ist daß du am Anfang ein unnötiges cli 
und am Ende ein potenziell eher schädliches sei machst. Dafür fehlt dir 
die notwendige Sicherung des SREG. Am Anfang nach dem ersten push r16 
sollte da noch stehen:
1
in   r16, SREG
2
push r16

und direkt vor dem pop r16 am Schluß das dazugehörige:
1
pop r16
2
out SREG, r16

von Philip H. (phayn)


Lesenswert?

ich versuche eben 5 kanäle des ADCs zu nutzen und sie nacheinander 
abzufragen.
darum versuche ich es mir immer wenn der ADC fertig ist den Multiplexer 
(mux) um 1 zu erhöhen.
Das mit dem SREG Speichern anstatt die Interrupts zu sperren ist eine 
gute Idee.
Werds gleich mal ändern.
Soviel ich weiß braucht der ADC 13 Zyklen für eine Wandlung.
Das würde ja bedeuten, mein Hauptprogramm wird nicht einmal durchlaufen 
und der ADC ist schon paar mal fertig.

Fällt euch eine möglichkeit ein bzw. habt ihr schon erfahrungen wie es 
besser funktionieren könnte 5 Kanäle hintereinander auszulesen und in 5 
Register zu schreiben und danach diese 5 Register mit einem 
Vergleichswert(Kontrast) zu vergleichen und dann laut einer 5 Bit 
breiten Tabelle ein Ereignis treffen?

von Philip H. (phayn)


Lesenswert?

das hab ich jetz noch vergessen.
ich setze das ADSC Bit weil es in der Simulation im AVR Studio immer 
zurückfällt. Anscheinend dürfte das AVR Studio den Freerunning mode 
nicht berücksichtigen in der Simulation.
Und ohne das Admux zu ändern, weiß ich ja nicht welcher Kanal gerade vom 
Multiplexer ausgewählt ist und könnte es passieren dass ich den ADC-Wert 
ins falsche Sensorregister schreibe?

von Johannes M. (johnny-m)


Lesenswert?

Philip Hahn wrote:
> Soviel ich weiß braucht der ADC 13 Zyklen für eine Wandlung.
Der braucht 13 ADC-Zyklen für eine Wandlung, nicht 13 CPU-Zyklen! 1 
ADC-Zyklus sind bei Deiner Einstellung 32 CPU-Zyklen.

von Michael U. (amiga)


Lesenswert?

Hallo,

>ich versuche eben 5 kanäle des ADCs zu nutzen und sie nacheinander
abzufragen.
darum versuche ich es mir immer wenn der ADC fertig ist den Multiplexer
(mux) um 1 zu erhöhen.

Du kannst den MUX bereits im dritten ADC-Takt nach Beginn der Wandlung 
umschalten, nach dem 2. ADC-Takt ist S&H fertig.

Nicht versehentlich die Referenzspannung verändern, nach einem 
Umschalten dieser ist der nächste Wandlerwert Schrott.

Gruß aus Berlin
Michael

von Philip H. (phayn)


Lesenswert?

Hab den fehler noch immer nicht gefunden :(

Schön langsam schließt sich der Kreis des Wissens bei mir aber noch ist 
im Programm ein kleiner fehler bzw. ein verständnisproblem.

von Philip H. (phayn)


Lesenswert?

Fällt euch eine möglichkeit ein bzw. habt ihr schon erfahrungen wie es
besser funktionieren könnte 5 Kanäle hintereinander auszulesen und in 5
Register zu schreiben?

von Manuel K. (mkauf)


Lesenswert?

hmm

habs grad nur überflogen aber ich mein mir fehlt hier irgendwie ADCL

du MUSST wenn du die 10 Bit nutzt ADCL und dann ADCH auslesen (ob du das 
L willst oder nicht)

oder Du liest ADCW bzw bei alten Prozessoren ADC aus.

Aber nur ADCH auslesen ist nicht gut.

ach mist sehe grad du nutzt ja nur das high

hmm

von Johannes M. (johnny-m)


Lesenswert?

Manuel Kauf wrote:
> habs grad nur überflogen aber ich mein mir fehlt hier irgendwie ADCL
>
> du MUSST
>
> ADCL und dann ADCH auslesen (ob du das L willst oder nicht)
>
> oder Du liest ADCW bzw bei alten Prozessoren ADC aus.
>
> Aber nur ADCH auslesen ist nicht gut.
Das ist Quatsch! Nur, wenn ADCL gelesen wurde, muss anschließend ADCH 
gelesen werden, um die Register wieder freizugeben. Wenn ADCL nicht 
angefasst wird, reicht es, nur ADCH zu lesen! Und ADC bzw. ADCW gibt es 
in Assembler nicht.

In diesem Fall ist ADLAR gesetzt (also das Ergebnis linksbündig 
abgelegt). Wenn man nur 8 Bit braucht, muss man auch nur ADCH lesen.

von Manuel K. (mkauf)


Lesenswert?

jups hab mich grad verlesen ^^

hab nicht gesehen dass nu 8 bit :) (habs schon korrigiert ^^)

von Phayn (Gast)


Lesenswert?

bin schon am verzweifeln =(
es will einfach nicht funktionieren.

Hat jemand von euch schon mal mehrere Kanäle "gleichzeitig" abgefragt 
und in ein Register geschrieben?

wenn ja bitte ich mir zu helfen!

mfg. Philip

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.