mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik atmega adc im assembler


Autor: Aladin Mujovic (djas020)
Datum:

Bewertung
0 lesenswert
nicht 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:
    ldi  temp1, (1<<REFS0)   ; Kanal 0, interne Referenzspannung 5V
    out  ADMUX, temp1
    ldi  temp1, (1<<ADEN) | (1<<ADPS2) | (1<<ADPS1) | (1<<ADPS0)
    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.

Autor: Falk Brunner (falk)
Datum:

Bewertung
0 lesenswert
nicht 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/...

Seite 205

MFG
Falk

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Aladin Mujovic (djas020)
Datum:

Bewertung
0 lesenswert
nicht 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?

Autor: Otto (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
indem Du das "1<<REFS0" zu Deinem MUX-Schleifenzähler "oderst"

Gruss Otto

Autor: Falk Brunner (falk)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Es reicht

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

Ist ja schleisslich nur ein 8 Bit Register.

MFG
Falk

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Oder du hältst dich an die Bit-Namen Schreibweise

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

Autor: Thomas O. (kosmos)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Aladin Mujovic (djas020)
Datum:

Bewertung
0 lesenswert
nicht 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:
.include "m8def.inc"
 
 ;Spannung1 auf PortC0
.def tempspg1  = r16
;Spannung2 auf PORTC1
.def tempspg2  = r17
;Spannung3 auf PORTC3
.def tempspg3  = r18
.def temp2  = r19
.def temp3  = r20
.def temp4  = r21
.def adlow  = r22
.def adhigh = r23
.def temp5  = r24
.def temp6  = r25
.def temp7  = r26
.def temp8  = r27

.equ quartz = 12000000      ; Taktfrequenz
.equ baud   = 9600          ; Baudrate 

;UART Initalisierung
 
    sbi  UCSRB, TXEN        ; enable transmit
    ldi  tempspg1, quartz / (baud * 16) - 1
    ldi  tempspg2, quartz / (baud * 16) - 1
    ldi  tempspg3, quartz / (baud * 16) - 1


    out  UBRRL, tempspg1       ; BAUD Rate
    out  UBRRL, tempspg2       ; BAUD Rate
    out  UBRRL, tempspg3       ; BAUD Rate


;Main Funktion

Main:
    ldi  tempspg1, LOW(RAMEND)     ; Stackpointer initialisieren Spannung1
    out  SPL, tempspg1
    ldi  tempspg1, HIGH(RAMEND)
    out  SPH, tempspg1

; ADC initialisieren: Single Conversion, Vorteiler 128
    ldi  tempspg1, (1<<REFS0)  ; Kanal 0, interne Referenzspannung 5V
    out  ADMUX, tempspg1
    ldi  tempspg1, (1<<ADEN) | (1<<ADPS2) | (1<<ADPS1) | (1<<ADPS0)
    out  ADCSRA, tempspg1 

    clr  tempspg1
  clr  adlow
  clr  adhigh
    clr  temp2
    clr  temp3
    clr  temp4
    ldi  temp5, 0           ; 256 Schleifendurchläufe
 
; neuen ADC-Wert lesen  (Schleife - 256 mal)
sample_adca:
    sbi  ADCSRA, ADSC       ; den ADC starten
 
wait_adca:
    sbic ADCSRA, ADSC       ; wenn der ADC fertig ist, wird dieses Bit gelöscht
    rjmp wait_adca
 
; ADC einlesen:
    in   adlow, ADCL        ; immer zuerst LOW Byte lesen
    in   adhigh, ADCH       ; danach das mittlerweile gesperrte High Byte
 
; alle 256 ADC-Werte addieren
    add  temp2, adlow       ; addieren
    adc  temp3, adhigh      ; addieren über Carry
    adc  temp4, tempspg1       ; addieren über Carry, temp1 enthält 0
    dec  temp5              ; Schleifenzähler MINUS 1
    brne sample_adca         ; wenn noch keine 256 ADC Werte -> nächsten Wert einlesen

; allerdings wird der Wert noch gerundet
    cpi  temp2,128          ; "Kommastelle" kleiner als 128 ?
    brlo no_rounda           ; ist kleiner ==> Sprung
 
; Aufrunden
    subi temp3, low(-1)       ; addieren von 1
    sbci temp4, high(-1)      ; addieren des Carry
 
no_rounda:
;   Ergebnis nach adlow und adhigh kopieren
;   damit die temp Register frei werden
    mov  adlow, temp3
    mov  adhigh, temp4
 
;in ASCII umwandeln
outpa:
    ldi  temp5, -1 + '0'
_a6aser:
    inc  temp5
    subi adlow, low(10000)   ; -10,000
    sbci adhigh, high(10000)
    brcc _a6aser
 
    ldi  temp6, 10 + '0'
_a7aser:
    dec  temp6
    subi adlow, low(-1000)   ; +1000
    sbci adhigh, high(-1000)
    brcs _a7aser
 
    ldi  temp7, -1 + '0'
_a8aser:
    inc   temp7
    subi  adlow, low(100)    ; -100
    sbci  adhigh, high(100)
    brcc  _a8aser
 
    ldi  temp8, 10 + '0'
_a9aser:
    dec  temp8
    subi adlow, -10          ; +10
    brcs _a9aser
    subi adlow,-'0'

;an UART Senden
    mov   tempspg1, temp5       ; Zehntausender Stelle
    rcall transmit1
    mov   tempspg1, temp6       ; Tausender Stelle
    rcall transmit1
    mov   tempspg1, temp7       ; Hunderter Stelle
    rcall transmit1
    mov   tempspg1, temp8       ; Zehner Stelle
    rcall transmit1
    mov   tempspg1, adlow       ; Einer Stelle
    rcall transmit1
  ldi   tempspg1, ','
  rcall transmit1

;___________________Spannung2_________________________  

    ldi  tempspg2, LOW(RAMEND)     ; Stackpointer initialisieren Spannung 2
    out  SPL, tempspg2
    ldi  tempspg2, HIGH(RAMEND)
    out  SPH, tempspg2

    ldi  tempspg2, (1<<REFS0) | 0x01 ; Kanal PORTC1, interne Referenzspannung 5V
    out  ADMUX, tempspg2
    ldi  tempspg2, (1<<ADEN) | (1<<ADPS2) | (1<<ADPS1) | (1<<ADPS0)
    out  ADCSRA, tempspg2 
  
  clr  tempspg2
  clr  adlow
  clr  adhigh  
    clr  temp2
    clr  temp3
    clr  temp4
    ldi  temp5, 0           ; 256 Schleifendurchläufe
 
; neuen ADC-Wert lesen  (Schleife - 256 mal)
sample_adcb:
    sbi  ADCSRA, ADSC       ; den ADC starten
 
wait_adcb:
    sbic ADCSRA, ADSC       ; wenn der ADC fertig ist, wird dieses Bit gelöscht
    rjmp wait_adcb
 
; ADC einlesen:
    in   adlow, ADCL        ; immer zuerst LOW Byte lesen
    in   adhigh, ADCH       ; danach das mittlerweile gesperrte High Byte
 
; alle 256 ADC-Werte addieren
    add  temp2, adlow       ; addieren
    adc  temp3, adhigh      ; addieren über Carry
    adc  temp4, tempspg2       ; addieren über Carry, temp1 enthält 0
    dec  temp5              ; Schleifenzähler MINUS 1
    brne sample_adcb         ; wenn noch keine 256 ADC Werte -> nächsten Wert einlesen

; allerdings wird der Wert noch gerundet
    cpi  temp2,128          ; "Kommastelle" kleiner als 128 ?
    brlo no_roundb           ; ist kleiner ==> Sprung
 
; Aufrunden
    subi temp3, low(-1)       ; addieren von 1
    sbci temp4, high(-1)      ; addieren des Carry
 
no_roundb:
;   Ergebnis nach adlow und adhigh kopieren
;   damit die temp Register frei werden
    mov  adlow, temp3
    mov  adhigh, temp4
 
;in ASCII umwandeln
outpb:
    ldi  temp5, -1 + '0'
_a6bser:
    inc  temp5
    subi adlow, low(10000)   ; -10,000
    sbci adhigh, high(10000)
    brcc _a6bser
 
    ldi  temp6, 10 + '0'
_a7bser:
    dec  temp6
    subi adlow, low(-1000)   ; +1000
    sbci adhigh, high(-1000)
    brcs _a7bser
 
    ldi  temp7, -1 + '0'
_a8bser:
    inc   temp7
    subi  adlow, low(100)    ; -100
    sbci  adhigh, high(100)
    brcc  _a8bser
 
    ldi  temp8, 10 + '0'
_a9bser:
    dec  temp8
    subi adlow, -10          ; +10
    brcs _a9bser
    subi adlow,-'0'
 
;Ausgabe
    mov   tempspg2, temp5       ; Zehntausender Stelle
    rcall transmit2
    mov   tempspg2, temp6       ; Tausender Stelle
    rcall transmit2
    mov   tempspg2, temp7       ; Hunderter Stelle
    rcall transmit2
    mov   tempspg2, temp8       ; Zehner Stelle
    rcall transmit2
    mov   tempspg2, adlow       ; Einer Stelle
    rcall transmit2
  ldi   tempspg2, ','
  rcall transmit2

;__________________________Spannung3________________________

    ldi  tempspg3, LOW(RAMEND)     ; Stackpointer initialisieren Spannung 3
    out  SPL, tempspg3
    ldi  tempspg3, HIGH(RAMEND)
    out  SPH, tempspg3

    ldi  tempspg3, (1<<REFS0) | 0x03 ; Kanal PORTC2, interne Referenzspannung 5V
    out  ADMUX, tempspg3
    ldi  tempspg3, (1<<ADEN) | (1<<ADPS2) | (1<<ADPS1) | (1<<ADPS0)
    out  ADCSRA, tempspg3 
  
  clr  tempspg3
  clr  adlow
  clr  adhigh  
    clr  temp2
    clr  temp3
    clr  temp4
    ldi  temp5, 0           ; 256 Schleifendurchläufe
 
; neuen ADC-Wert lesen  (Schleife - 256 mal)
sample_adcc:
    sbi  ADCSRA, ADSC       ; den ADC starten
 
wait_adcc:
    sbic ADCSRA, ADSC       ; wenn der ADC fertig ist, wird dieses Bit gelöscht
    rjmp wait_adcc
 
; ADC einlesen:
    in   adlow, ADCL        ; immer zuerst LOW Byte lesen
    in   adhigh, ADCH       ; danach das mittlerweile gesperrte High Byte
 
; alle 256 ADC-Werte addieren
    add  temp2, adlow       ; addieren
    adc  temp3, adhigh      ; addieren über Carry
    adc  temp4, tempspg3       ; addieren über Carry, temp1 enthält 0
    dec  temp5              ; Schleifenzähler MINUS 1
    brne sample_adcc         ; wenn noch keine 256 ADC Werte -> nächsten Wert einlesen

; allerdings wird der Wert noch gerundet
    cpi  temp2,128          ; "Kommastelle" kleiner als 128 ?
    brlo no_roundc           ; ist kleiner ==> Sprung
 
; Aufrunden
    subi temp3, low(-1)       ; addieren von 1
    sbci temp4, high(-1)      ; addieren des Carry
 
no_roundc:
;   Ergebnis nach adlow und adhigh kopieren
;   damit die temp Register frei werden
    mov  adlow, temp3
    mov  adhigh, temp4
 
;in ASCII umwandeln
outpc:
    ldi  temp5, -1 + '0'
_a6cser:
    inc  temp5
    subi adlow, low(10000)   ; -10,000
    sbci adhigh, high(10000)
    brcc _a6cser
 
    ldi  temp6, 10 + '0'
_a7cser:
    dec  temp6
    subi adlow, low(-1000)   ; +1000
    sbci adhigh, high(-1000)
    brcs _a7cser
 
    ldi  temp7, -1 + '0'
_a8cser:
    inc   temp7
    subi  adlow, low(100)    ; -100
    sbci  adhigh, high(100)
    brcc  _a8cser
 
    ldi  temp8, 10 + '0'
_a9cser:
    dec  temp8
    subi adlow, -10          ; +10
    brcs _a9cser
    subi adlow,-'0'
 
;Ausgabe
    mov   tempspg3, temp5       ; Zehntausender Stelle
    rcall transmit3
    mov   tempspg3, temp6       ; Tausender Stelle
    rcall transmit3
    mov   tempspg3, temp7       ; Hunderter Stelle
    rcall transmit3
    mov   tempspg3, temp8       ; Zehner Stelle
    rcall transmit3
    mov   tempspg3, adlow       ; Einer Stelle
    rcall transmit3
  ldi   tempspg3, 10
  rcall transmit3
  ldi   tempspg3, 13
  rcall transmit3

    rjmp  Main
 
transmit1:
    sbis  UCSRA,UDRE      ; Warten, bis UDR bereit ist ...
    rjmp  transmit1
    out   UDR, tempspg1      ; und Zeichen ausgeben
    ret
     
transmit2:
    sbis  UCSRA,UDRE      ; Warten, bis UDR bereit ist ...
    rjmp  transmit2
    out   UDR, tempspg2      ; und Zeichen ausgeben
    ret
transmit3:
    sbis  UCSRA,UDRE      ; Warten, bis UDR bereit ist ...
    rjmp  transmit3
    out   UDR, tempspg3      ; und Zeichen ausgeben
    ret


Autor: Falk Brunner (falk)
Datum:

Bewertung
0 lesenswert
nicht 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.
in    r16,ADMUX    ; Register lesen
andi  r16,0xF0      ; ADMUX löschen
ori   r16,0x03      ; neuen Kanal auswählen
out   ADMUX,r16

MFG
Falk

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Das hier
    sbi  UCSRB, TXEN        ; enable transmit
    ldi  tempspg1, quartz / (baud * 16) - 1
    ldi  tempspg2, quartz / (baud * 16) - 1
    ldi  tempspg3, quartz / (baud * 16) - 1


    out  UBRRL, tempspg1       ; BAUD Rate
    out  UBRRL, tempspg2       ; BAUD Rate
    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 :-)

Autor: Falk Brunner (falk)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Aladin Mujovic (djas020)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Aladin Mujovic wrote:

> falls es wer braucht:

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

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Falk Brunner (falk)
Datum:

Bewertung
0 lesenswert
nicht 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

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.