Forum: Mikrocontroller und Digitale Elektronik atmega adc im assembler


von Aladin M. (djas020)


Lesenswert?

Hallo Leute,

ich hätte gerne eine Frage und zwar da ich mich mit der 
Assemblerprogrammierung nicht auskenne, wie ich den channel von einem 
ADC eines Atmega's auswählen kann?

Ein Ausschnitt meines Programms:
1
    ldi  temp1, (1<<REFS0)   ; Kanal 0, interne Referenzspannung 5V
2
    out  ADMUX, temp1
3
    ldi  temp1, (1<<ADEN) | (1<<ADPS2) | (1<<ADPS1) | (1<<ADPS0)
4
    out  ADCSRA, temp1

Hier wird der Kanal 0 also PORTC0 ausgewählt vom ADC und die interne 
Referenzspannung wird gewählt. Ich google die ganze Zeit und kämpf mich 
durch die Tutorials durch aber ich finde einfach nicht raus, mit welchem 
Befehl ich den Port1 zum Beispiel auswählen kann statt dem Port0.

Kann mir vielleicht da wer helfen. Wäre sehr dankbar.

von Falk B. (falk)


Lesenswert?

@ Aladin Mujovic (djas020)

>durch die Tutorials durch aber ich finde einfach nicht raus, mit welchem
>Befehl ich den Port1 zum Beispiel auswählen kann statt dem Port0.

Tja, wie wäre es mit einem Blick ins Datenblatt?

http://www.atmel.com/dyn/resources/prod_documents/doc2486.pdf

Seite 205

MFG
Falk

von Karl H. (kbuchegg)


Lesenswert?

Aladin Mujovic wrote:

> Kann mir vielleicht da wer helfen. Wäre sehr dankbar.

Ganz einfach. Hol dir von Atmel das Datenblatt zu deinem
Controller und sieh im Kapitel über den ADC nach.
Abschnitt: Analog-to-Digital Converter
Unterkapitel: Changing Channel or Reference Selection
Dort findest du eine Tabelle in der die Belegung des ADMUX
Registers angegeben ist und aus der herauszulesen ist, welche
Bits gesetzt werden müssen, damit welcher Kanal aktiv wird.


Im übrigen findet sich selbige Tabelle auch im AVR-Tutorial
bei der Registerzusammenfassung wieder :-)
Soviel zum Thema: Ich habe das Tutorial studiert.

von Aladin M. (djas020)


Lesenswert?

Aha, danke daweil für euere Meldungen. Ich hab jetzt die verschiedenen 
Inputs gefunden und zwar:

Table 75. Input Channel Selections
MUX3..0 Single Ended Input
0000          ADC0
0001          ADC1
0010          ADC2
0011          ADC3
0100          ADC4
0101          ADC5
0110          ADC6
0111          ADC7

Wie schreibe ich das jetzt im Assembler?
     ldi  temp1, (1<<REFS0) | 0x0001  ; Kanal 0, interne 
Referenzspannung 5V
     out  ADMUX, temp1

Verstehe ich das richtig so oder?

von Otto (Gast)


Lesenswert?

indem Du das "1<<REFS0" zu Deinem MUX-Schleifenzähler "oderst"

Gruss Otto

von Falk B. (falk)


Lesenswert?

Es reicht

ldi  temp1, (1<<REFS0) | 0x01

Ist ja schleisslich nur ein 8 Bit Register.

MFG
Falk

von Karl H. (kbuchegg)


Lesenswert?

Oder du hältst dich an die Bit-Namen Schreibweise

    ldi  temp1, (1<<REFS0) | (1<<ADMUX0)

von Thomas (kosmos)


Lesenswert?

ich speichere solche Einstellungen die ich übergebe immer zusätzlich im 
SRAM(auch um Register wieder anderweitig verwenden zu können). Und bevor 
ich etwas ändere lese ich den alten Stand ein und ändere die 
entsprechenden Bits, so kann ich im weiterem Programmablauf auch 
verschiedene Zustände abfragen. Eine OR Verknüpfung ist natürlich auch 
möglich. Habe noch nicht probiert ob man vielleicht auch direkt ADMUX 
auslesen kann, dann würde sich das mit dem SRAm erübrigen.

.DSEG           ;Reserve jeweils 1 Byte im SRAM
Status1:  .byte 1

.CSEG
.org 0x000
  jmp RESET      ;Interuppt-Tabelle
        .........
        .........

ldi temp, 0b1110000    ;Auswahl Referenzquelle, ADLAR und Kanal 0
out ADMUX, temp
sts Status1, temp
...
...
lds temp, Status1
sbr temp, 1        ;Auswahl des 2ten Kanals (0-8 möglich)
out ADMUX, temp
sts Status1, temp

von Aladin M. (djas020)


Lesenswert?

naja ich weiss nicht ob das die gescheiteste lösung ist, aber ich habe 
in der mainschleife 3 verschiedene abfragungen ich habe auch eine 
zwischenspeicherung von den daten bis ich sie an die schnittstelle 
schicke, dann lösche ich sie und verwende die register für die nächste 
messung, der vorgang wiederholt sich 3 mal. für mich reicht es. und 
danke für eure hilfe, jetzt hab ich das programm komplett.

falls es wer braucht:
1
.include "m8def.inc"
2
 
3
 ;Spannung1 auf PortC0
4
.def tempspg1  = r16
5
;Spannung2 auf PORTC1
6
.def tempspg2  = r17
7
;Spannung3 auf PORTC3
8
.def tempspg3  = r18
9
.def temp2  = r19
10
.def temp3  = r20
11
.def temp4  = r21
12
.def adlow  = r22
13
.def adhigh = r23
14
.def temp5  = r24
15
.def temp6  = r25
16
.def temp7  = r26
17
.def temp8  = r27
18
19
.equ quartz = 12000000      ; Taktfrequenz
20
.equ baud   = 9600          ; Baudrate 
21
22
;UART Initalisierung
23
 
24
    sbi  UCSRB, TXEN        ; enable transmit
25
    ldi  tempspg1, quartz / (baud * 16) - 1
26
    ldi  tempspg2, quartz / (baud * 16) - 1
27
    ldi  tempspg3, quartz / (baud * 16) - 1
28
29
30
    out  UBRRL, tempspg1       ; BAUD Rate
31
    out  UBRRL, tempspg2       ; BAUD Rate
32
    out  UBRRL, tempspg3       ; BAUD Rate
33
34
35
;Main Funktion
36
37
Main:
38
    ldi  tempspg1, LOW(RAMEND)     ; Stackpointer initialisieren Spannung1
39
    out  SPL, tempspg1
40
    ldi  tempspg1, HIGH(RAMEND)
41
    out  SPH, tempspg1
42
43
; ADC initialisieren: Single Conversion, Vorteiler 128
44
    ldi  tempspg1, (1<<REFS0)  ; Kanal 0, interne Referenzspannung 5V
45
    out  ADMUX, tempspg1
46
    ldi  tempspg1, (1<<ADEN) | (1<<ADPS2) | (1<<ADPS1) | (1<<ADPS0)
47
    out  ADCSRA, tempspg1 
48
49
    clr  tempspg1
50
  clr  adlow
51
  clr  adhigh
52
    clr  temp2
53
    clr  temp3
54
    clr  temp4
55
    ldi  temp5, 0           ; 256 Schleifendurchläufe
56
 
57
; neuen ADC-Wert lesen  (Schleife - 256 mal)
58
sample_adca:
59
    sbi  ADCSRA, ADSC       ; den ADC starten
60
 
61
wait_adca:
62
    sbic ADCSRA, ADSC       ; wenn der ADC fertig ist, wird dieses Bit gelöscht
63
    rjmp wait_adca
64
 
65
; ADC einlesen:
66
    in   adlow, ADCL        ; immer zuerst LOW Byte lesen
67
    in   adhigh, ADCH       ; danach das mittlerweile gesperrte High Byte
68
 
69
; alle 256 ADC-Werte addieren
70
    add  temp2, adlow       ; addieren
71
    adc  temp3, adhigh      ; addieren über Carry
72
    adc  temp4, tempspg1       ; addieren über Carry, temp1 enthält 0
73
    dec  temp5              ; Schleifenzähler MINUS 1
74
    brne sample_adca         ; wenn noch keine 256 ADC Werte -> nächsten Wert einlesen
75
76
; allerdings wird der Wert noch gerundet
77
    cpi  temp2,128          ; "Kommastelle" kleiner als 128 ?
78
    brlo no_rounda           ; ist kleiner ==> Sprung
79
 
80
; Aufrunden
81
    subi temp3, low(-1)       ; addieren von 1
82
    sbci temp4, high(-1)      ; addieren des Carry
83
 
84
no_rounda:
85
;   Ergebnis nach adlow und adhigh kopieren
86
;   damit die temp Register frei werden
87
    mov  adlow, temp3
88
    mov  adhigh, temp4
89
 
90
;in ASCII umwandeln
91
outpa:
92
    ldi  temp5, -1 + '0'
93
_a6aser:
94
    inc  temp5
95
    subi adlow, low(10000)   ; -10,000
96
    sbci adhigh, high(10000)
97
    brcc _a6aser
98
 
99
    ldi  temp6, 10 + '0'
100
_a7aser:
101
    dec  temp6
102
    subi adlow, low(-1000)   ; +1000
103
    sbci adhigh, high(-1000)
104
    brcs _a7aser
105
 
106
    ldi  temp7, -1 + '0'
107
_a8aser:
108
    inc   temp7
109
    subi  adlow, low(100)    ; -100
110
    sbci  adhigh, high(100)
111
    brcc  _a8aser
112
 
113
    ldi  temp8, 10 + '0'
114
_a9aser:
115
    dec  temp8
116
    subi adlow, -10          ; +10
117
    brcs _a9aser
118
    subi adlow,-'0'
119
120
;an UART Senden
121
    mov   tempspg1, temp5       ; Zehntausender Stelle
122
    rcall transmit1
123
    mov   tempspg1, temp6       ; Tausender Stelle
124
    rcall transmit1
125
    mov   tempspg1, temp7       ; Hunderter Stelle
126
    rcall transmit1
127
    mov   tempspg1, temp8       ; Zehner Stelle
128
    rcall transmit1
129
    mov   tempspg1, adlow       ; Einer Stelle
130
    rcall transmit1
131
  ldi   tempspg1, ','
132
  rcall transmit1
133
134
;___________________Spannung2_________________________  
135
136
    ldi  tempspg2, LOW(RAMEND)     ; Stackpointer initialisieren Spannung 2
137
    out  SPL, tempspg2
138
    ldi  tempspg2, HIGH(RAMEND)
139
    out  SPH, tempspg2
140
141
    ldi  tempspg2, (1<<REFS0) | 0x01 ; Kanal PORTC1, interne Referenzspannung 5V
142
    out  ADMUX, tempspg2
143
    ldi  tempspg2, (1<<ADEN) | (1<<ADPS2) | (1<<ADPS1) | (1<<ADPS0)
144
    out  ADCSRA, tempspg2 
145
  
146
  clr  tempspg2
147
  clr  adlow
148
  clr  adhigh  
149
    clr  temp2
150
    clr  temp3
151
    clr  temp4
152
    ldi  temp5, 0           ; 256 Schleifendurchläufe
153
 
154
; neuen ADC-Wert lesen  (Schleife - 256 mal)
155
sample_adcb:
156
    sbi  ADCSRA, ADSC       ; den ADC starten
157
 
158
wait_adcb:
159
    sbic ADCSRA, ADSC       ; wenn der ADC fertig ist, wird dieses Bit gelöscht
160
    rjmp wait_adcb
161
 
162
; ADC einlesen:
163
    in   adlow, ADCL        ; immer zuerst LOW Byte lesen
164
    in   adhigh, ADCH       ; danach das mittlerweile gesperrte High Byte
165
 
166
; alle 256 ADC-Werte addieren
167
    add  temp2, adlow       ; addieren
168
    adc  temp3, adhigh      ; addieren über Carry
169
    adc  temp4, tempspg2       ; addieren über Carry, temp1 enthält 0
170
    dec  temp5              ; Schleifenzähler MINUS 1
171
    brne sample_adcb         ; wenn noch keine 256 ADC Werte -> nächsten Wert einlesen
172
173
; allerdings wird der Wert noch gerundet
174
    cpi  temp2,128          ; "Kommastelle" kleiner als 128 ?
175
    brlo no_roundb           ; ist kleiner ==> Sprung
176
 
177
; Aufrunden
178
    subi temp3, low(-1)       ; addieren von 1
179
    sbci temp4, high(-1)      ; addieren des Carry
180
 
181
no_roundb:
182
;   Ergebnis nach adlow und adhigh kopieren
183
;   damit die temp Register frei werden
184
    mov  adlow, temp3
185
    mov  adhigh, temp4
186
 
187
;in ASCII umwandeln
188
outpb:
189
    ldi  temp5, -1 + '0'
190
_a6bser:
191
    inc  temp5
192
    subi adlow, low(10000)   ; -10,000
193
    sbci adhigh, high(10000)
194
    brcc _a6bser
195
 
196
    ldi  temp6, 10 + '0'
197
_a7bser:
198
    dec  temp6
199
    subi adlow, low(-1000)   ; +1000
200
    sbci adhigh, high(-1000)
201
    brcs _a7bser
202
 
203
    ldi  temp7, -1 + '0'
204
_a8bser:
205
    inc   temp7
206
    subi  adlow, low(100)    ; -100
207
    sbci  adhigh, high(100)
208
    brcc  _a8bser
209
 
210
    ldi  temp8, 10 + '0'
211
_a9bser:
212
    dec  temp8
213
    subi adlow, -10          ; +10
214
    brcs _a9bser
215
    subi adlow,-'0'
216
 
217
;Ausgabe
218
    mov   tempspg2, temp5       ; Zehntausender Stelle
219
    rcall transmit2
220
    mov   tempspg2, temp6       ; Tausender Stelle
221
    rcall transmit2
222
    mov   tempspg2, temp7       ; Hunderter Stelle
223
    rcall transmit2
224
    mov   tempspg2, temp8       ; Zehner Stelle
225
    rcall transmit2
226
    mov   tempspg2, adlow       ; Einer Stelle
227
    rcall transmit2
228
  ldi   tempspg2, ','
229
  rcall transmit2
230
231
;__________________________Spannung3________________________
232
233
    ldi  tempspg3, LOW(RAMEND)     ; Stackpointer initialisieren Spannung 3
234
    out  SPL, tempspg3
235
    ldi  tempspg3, HIGH(RAMEND)
236
    out  SPH, tempspg3
237
238
    ldi  tempspg3, (1<<REFS0) | 0x03 ; Kanal PORTC2, interne Referenzspannung 5V
239
    out  ADMUX, tempspg3
240
    ldi  tempspg3, (1<<ADEN) | (1<<ADPS2) | (1<<ADPS1) | (1<<ADPS0)
241
    out  ADCSRA, tempspg3 
242
  
243
  clr  tempspg3
244
  clr  adlow
245
  clr  adhigh  
246
    clr  temp2
247
    clr  temp3
248
    clr  temp4
249
    ldi  temp5, 0           ; 256 Schleifendurchläufe
250
 
251
; neuen ADC-Wert lesen  (Schleife - 256 mal)
252
sample_adcc:
253
    sbi  ADCSRA, ADSC       ; den ADC starten
254
 
255
wait_adcc:
256
    sbic ADCSRA, ADSC       ; wenn der ADC fertig ist, wird dieses Bit gelöscht
257
    rjmp wait_adcc
258
 
259
; ADC einlesen:
260
    in   adlow, ADCL        ; immer zuerst LOW Byte lesen
261
    in   adhigh, ADCH       ; danach das mittlerweile gesperrte High Byte
262
 
263
; alle 256 ADC-Werte addieren
264
    add  temp2, adlow       ; addieren
265
    adc  temp3, adhigh      ; addieren über Carry
266
    adc  temp4, tempspg3       ; addieren über Carry, temp1 enthält 0
267
    dec  temp5              ; Schleifenzähler MINUS 1
268
    brne sample_adcc         ; wenn noch keine 256 ADC Werte -> nächsten Wert einlesen
269
270
; allerdings wird der Wert noch gerundet
271
    cpi  temp2,128          ; "Kommastelle" kleiner als 128 ?
272
    brlo no_roundc           ; ist kleiner ==> Sprung
273
 
274
; Aufrunden
275
    subi temp3, low(-1)       ; addieren von 1
276
    sbci temp4, high(-1)      ; addieren des Carry
277
 
278
no_roundc:
279
;   Ergebnis nach adlow und adhigh kopieren
280
;   damit die temp Register frei werden
281
    mov  adlow, temp3
282
    mov  adhigh, temp4
283
 
284
;in ASCII umwandeln
285
outpc:
286
    ldi  temp5, -1 + '0'
287
_a6cser:
288
    inc  temp5
289
    subi adlow, low(10000)   ; -10,000
290
    sbci adhigh, high(10000)
291
    brcc _a6cser
292
 
293
    ldi  temp6, 10 + '0'
294
_a7cser:
295
    dec  temp6
296
    subi adlow, low(-1000)   ; +1000
297
    sbci adhigh, high(-1000)
298
    brcs _a7cser
299
 
300
    ldi  temp7, -1 + '0'
301
_a8cser:
302
    inc   temp7
303
    subi  adlow, low(100)    ; -100
304
    sbci  adhigh, high(100)
305
    brcc  _a8cser
306
 
307
    ldi  temp8, 10 + '0'
308
_a9cser:
309
    dec  temp8
310
    subi adlow, -10          ; +10
311
    brcs _a9cser
312
    subi adlow,-'0'
313
 
314
;Ausgabe
315
    mov   tempspg3, temp5       ; Zehntausender Stelle
316
    rcall transmit3
317
    mov   tempspg3, temp6       ; Tausender Stelle
318
    rcall transmit3
319
    mov   tempspg3, temp7       ; Hunderter Stelle
320
    rcall transmit3
321
    mov   tempspg3, temp8       ; Zehner Stelle
322
    rcall transmit3
323
    mov   tempspg3, adlow       ; Einer Stelle
324
    rcall transmit3
325
  ldi   tempspg3, 10
326
  rcall transmit3
327
  ldi   tempspg3, 13
328
  rcall transmit3
329
330
    rjmp  Main
331
 
332
transmit1:
333
    sbis  UCSRA,UDRE      ; Warten, bis UDR bereit ist ...
334
    rjmp  transmit1
335
    out   UDR, tempspg1      ; und Zeichen ausgeben
336
    ret
337
     
338
transmit2:
339
    sbis  UCSRA,UDRE      ; Warten, bis UDR bereit ist ...
340
    rjmp  transmit2
341
    out   UDR, tempspg2      ; und Zeichen ausgeben
342
    ret
343
transmit3:
344
    sbis  UCSRA,UDRE      ; Warten, bis UDR bereit ist ...
345
    rjmp  transmit3
346
    out   UDR, tempspg3      ; und Zeichen ausgeben
347
    ret

von Falk B. (falk)


Lesenswert?

@ Thomas O. (kosmos)

>ich speichere solche Einstellungen die ich übergebe immer zusätzlich im
>SRAM(auch um Register wieder anderweitig verwenden zu können).

Speicherplatzverschwendung! Ausserdem kann man sich da fix 
Inkonsistenzen an den Hals holen (Register im SRAM ungleich realem 
Steuerregister).

>ich etwas ändere lese ich den alten Stand ein und ändere die
>entsprechenden Bits,

Das ist meist der bessere Weg, um in komplexen Programmen keine bösen 
Seiteneffekte zu erzeugen.

>lds temp, Status1
>sbr temp, 1        ;Auswahl des 2ten Kanals (0-8 möglich)
>out ADMUX, temp

Das ist schlechter Stil, nicht nur wegen der Zwischenspeicherung im 
SRAM. Besser so.
1
in    r16,ADMUX    ; Register lesen
2
andi  r16,0xF0      ; ADMUX löschen
3
ori   r16,0x03      ; neuen Kanal auswählen
4
out   ADMUX,r16

MFG
Falk

von Karl H. (kbuchegg)


Lesenswert?

Aladin Mujovic wrote:

> danke für eure hilfe, jetzt hab ich das programm komplett.

Wow.
So einen ausgeprägten Fall von Copy&Paste Programmierung und
'ich weis nicht was ich tue' habe ich selten gesehen.
Da hast du dir ja eine Menge Arbeit gemacht, für eine
Änderung, die aus dem Originalprogramm, einer kleinen Umstellung
und vielleicht 8 bis 10 zusätzlichen Codezeilen besteht.

Die meisten hätten wenigstens die Initialisierung des Stackpointers
nur einmal am Programmanfang gemacht :-)

Schon mal was von Unterprogrammen gehört?

Edit: Die letzte Frage nehme ich zurück. Sogar die Transmit
Funktionen wurden dupliziert (tripliziert), was nicht gerade
für ein Verständnis dessen was man tut spricht.

von Karl H. (kbuchegg)


Lesenswert?

Das hier
1
    sbi  UCSRB, TXEN        ; enable transmit
2
    ldi  tempspg1, quartz / (baud * 16) - 1
3
    ldi  tempspg2, quartz / (baud * 16) - 1
4
    ldi  tempspg3, quartz / (baud * 16) - 1
5
6
7
    out  UBRRL, tempspg1       ; BAUD Rate
8
    out  UBRRL, tempspg2       ; BAUD Rate
9
    out  UBRRL, tempspg3       ; BAUD Rate

sollte man sich allerdings auf der Zunge zergehen lassen.
Das sollte man sich einrahmen und an die Wand hängen.
So nach dem Motto: Dreifach hält besser :-)

von Falk B. (falk)


Lesenswert?

@  Karl heinz Buchegger (kbuchegg)

>Edit: Die letzte Frage nehme ich zurück. Sogar die Transmit
>Funktionen wurden dupliziert (tripliziert), was nicht gerade
>für ein Verständnis dessen was man tut spricht.

Du kennst doch den James Dean Film "Denn sie wissen nicht . . .

;-)
Falk

von Aladin M. (djas020)


Lesenswert?

hehe.. na gut. wenigstens gibt es leute die den ganzen tag was besseres 
zu tun haben als den ganzen tag vor dem pc zu sitzen und für die 
programmierung bzw. elektronik nicht das EINZIGE leben ist.

rechtschreibfehler enthalten, sonst kommen euch deswegen auch noch die 
tränen. schönen tag noch.

von Karl H. (kbuchegg)


Lesenswert?

Aladin Mujovic wrote:

> falls es wer braucht:

Nicht böse sein.
Aber das ist ein Musterbeispiel dafür, wie man es nicht macht.

von Karl H. (kbuchegg)


Lesenswert?

Aladin Mujovic wrote:
> hehe.. na gut. wenigstens gibt es leute die den ganzen tag was besseres
> zu tun haben als den ganzen tag vor dem pc zu sitzen und für die
> programmierung bzw. elektronik nicht das EINZIGE leben ist.

Natürlich gibt es die.
Aber diese Leute können dir nicht weiterhelfen, wenn du ein
Problem in der Programmierung hast.
Es gibt auch eine ganze Menge Leute die nicht den ganzen Tag
in der KFZ-Werkstatt stehen und die trotzdem nicht einen Vergaser
reparieren indem sie mit dem Hammer draufdreschen.

von Falk B. (falk)


Lesenswert?

@ Aladin Mujovic (djas020)

>hehe.. na gut. wenigstens gibt es leute die den ganzen tag was besseres
>zu tun haben als den ganzen tag vor dem pc zu sitzen und für die
>programmierung bzw. elektronik nicht das EINZIGE leben ist.

>rechtschreibfehler enthalten, sonst kommen euch deswegen auch noch die
>tränen. schönen tag noch.

Und das ist mal wieder ein Musterbeispiel für Arroganz der neuen Zeit. 
Kaum hat man geholfen, verlieren sie die Bodenhaftung.

Möge dich der Blitz beim Scheissen treffen!

Der Admin sollte deinen Account sperren (jaja, kann man ja billig nen 
neuen anlegen).

Schönen Tag noch
Falk

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.