www.mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik ADC_Sensorwert_einlese


Autor: Philip Hahn (phayn)
Datum:
Angehängte Dateien:

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

Autor: Johannes M. (johnny-m)
Datum:

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

Autor: Philip Hahn (phayn)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
leider weiß ich nicht welcher Fehler auftritt :(

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

Autor: Johannes M. (johnny-m)
Datum:

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

Autor: Phayn (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.
.include "m16def.inc"  

;* benötigte Ressourcen:    *
;*  r16................Arbeitsregister    *
;*  r17................Arbeitsregister    *

;*  r20................Sensor0   *
;*  r21................Sensor1   *
;*  r22................Sensor2   *
;*  r23................Sensor3   *
;*  r24................Sensor4   *


.org 0x000
  rjmp RESET
.org 0x01C
  rjmp ADC_Fertig        ;ADC Conversation complete

.def vergleich = r19
.def Sensor0 = r20
.def Sensor1 = r21
.def Sensor2 = r22
.def Sensor3 = r23
.def Sensor4 = r24
.def kontrast = r25
.def mux = r18


initial:

; Initialisierung der benötigten Ports als Ein- und Ausgabeports                 
      ldi r16, 0xFF
      out DDRC, r16    ;PortC als Ausgabeport für LED-Anzeige
      ldi r16, 0x00
      out DDRA, r16    ;PortA als Eingabeport ADC
      ldi r16, 0x00
      out DDRB, r16    ;PortB als Eingabeport Taster

; Initialisierungen für Interrupts

; Setzen des Stackpointers
      ldi r16, HIGH(RAMEND)
      out SPH, r16
      ldi r16, LOW(RAMEND)
      out SPL, r16


  ldi r16, 0b10101101      ; ADIE setzen Interrupt Enable
  out ADCSRA, r16        ; ADEN setzen (ADC enable)
            ; ADC Prescaler = 32

  ldi r16, 0b01100000      ; Referenzspannung AVCC REFS0 = 1
  out ADMUX, r16        ; linksbündige Anzeige

  ldi r16, 0x00        ; 0000 0000b
  out SFIOR, r16        ; Free Running Mode

  ldi kontrast, 0b1000000

Hauptprogramm:
  sei
  ldi r16, 0b11101101      ; Start der AD-Konversion
  out ADCSRA, r16  

warte:
  com Vergleich
  out portc, vergleich 
  com vergleich

  call Sensorkontrastvergleich
  
  retSensor_aktiv:
  ori mux, 0b00100000
  out admux, mux

  ldi r16, 0b11101101      ; Start der AD-Konversion
  out ADCSRA, r16  

  call delay_100us

  
rjmp warte  
;*************************************************
Sensorkontrastvergleich:

  Vergleich_Sensor0:
  andi vergleich, 0b11101111
                ;Kontrast bzw. Empfindlichkeit
  cp  kontrast, sensor0    ;vergleiche Register mit ADC-Wert
  brlo Sensor0_aktiv    ;springe wenn r16 kleiner als sensor0

  Vergleich_Sensor1:
  andi vergleich, 0b11110111
  cp  kontrast, sensor1    ;vergleiche Register mit ADC-Wert
  brlo Sensor1_aktiv      ;springe wenn r16 kleiner als sensor0

  Vergleich_Sensor2:
  andi vergleich, 0b11111011
  cp  kontrast, sensor2    ;vergleiche Register mit ADC-Wert
  brlo Sensor2_aktiv      ;springe wenn r16 kleiner als sensor2

  Vergleich_Sensor3:
  andi vergleich, 0b11111101
  cp  kontrast, sensor3    ;vergleiche Register mit ADC-Wert
  brlo Sensor3_aktiv      ;springe wenn r16 kleiner als sensor3

  Vergleich_Sensor4:
  andi vergleich, 0b11111110
  cp  kontrast, sensor4    ;vergleiche Register mit ADC-Wert
  brlo Sensor4_aktiv      ;springe wenn r16 kleiner als sensor4
  
  ret
;*************************************************
  Sensor0_aktiv:        ;Sprung wenn Sensorwert größer als 1 ist
  ori vergleich, 0b00010000  ;Led zeigt welcher sensor anspricht
  jmp Vergleich_Sensor1

  Sensor1_aktiv:        ;Sprung wenn Sensorwert größer als 1 ist
  ori vergleich, 0b00001000  ;Led zeigt welcher sensor anspricht
  jmp Vergleich_Sensor2

  Sensor2_aktiv:        ;Sprung wenn Sensorwert größer als 1 ist
  ori vergleich, 0b00000100  ;Led zeigt welcher sensor anspricht
  jmp Vergleich_Sensor3

  Sensor3_aktiv:        ;Sprung wenn Sensorwert größer als 1 ist
  ori vergleich, 0b00000010  ;Led zeigt welcher sensor anspricht
  jmp Vergleich_Sensor4

  Sensor4_aktiv:        ;Sprung wenn Sensorwert größer als 1 ist
  ori vergleich, 0b00000001  ;Led zeigt welcher sensor anspricht
  jmp retSensor_aktiv
;*************************************************

ADC_Fertig:          ; ADC Konvertierung fertig, Interrupt hierher
  cli
  push r16

;Nächsten Sensor ermitteln
;  mov r16, mux        ; aktueller Sensor in r16 kopieren
  andi mux,0b00000111      ; Setzt Bit 3-7 auf 0, Bit 0 bis 2 bleiben unverändert            ; Sensor um 1 erhöhen

  ldi r17, 0b00000000
  cp r17, mux
  Breq Sensor0sprung_verlaengerung  

  ldi r17, 0b00000001
  cp r17, mux
  Breq Sensor1sprung_verlaengerung

  ldi r17, 0b00000010
  cp r17, mux
  Breq Sensor2sprung_verlaengerung

  ldi r17, 0b00000011
  cp r17, mux
  Breq Sensor3sprung_verlaengerung  

  ldi r17, 0b00000100
  cp r17, mux
  Breq Sensor4sprung_verlaengerung  

  Sensor0sprung_verlaengerung:
  jmp Sensor0sprung
  Sensor1sprung_verlaengerung:
  jmp Sensor1sprung  
  Sensor2sprung_verlaengerung:
  jmp Sensor2sprung
  Sensor3sprung_verlaengerung:
  jmp Sensor3sprung
  Sensor4sprung_verlaengerung:  
  jmp Sensor4sprung  
    
retsensor:
  inc mux            ; Sensor erhöhen

retsensor_ohne_increment:
;  ldi r16, 0b11101101      ; Start der AD-Konversion
;  out ADCSRA, r16  
  pop r16
  sei
  reti

Sensor0sprung:
  in sensor0, adch
jmp retsensor

Sensor1sprung:    
  in sensor1, adch
jmp retsensor

Sensor2sprung:
  in sensor2, adch
jmp retsensor

Sensor3sprung:
  in sensor3, adch
jmp retsensor

Sensor4sprung:
  in sensor4, adch
  ldi mux, 0x00
jmp retsensor_ohne_increment


RESET: jmp initial


delay_100us:
; ============================= 
;   Warteschleifen-Generator 
;     400 Zyklen:
; ----------------------------- 
; warte 399 Zyklen:
          ldi  R17, $85
WGLOOP0:  dec  R17
          brne WGLOOP0
; ----------------------------- 
; warte 1 Zyklus:
          nop
; ============================= 
ret

Autor: Johannes M. (johnny-m)
Datum:

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

Autor: Johannes M. (johnny-m)
Datum:

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

Autor: Rolf Magnus (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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:
in   r16, SREG
push r16

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

Autor: Philip Hahn (phayn)
Datum:

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

Autor: Philip Hahn (phayn)
Datum:

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

Autor: Johannes M. (johnny-m)
Datum:

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

Autor: Michael U. (amiga)
Datum:

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

Autor: Philip Hahn (phayn)
Datum:

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

Autor: Philip Hahn (phayn)
Datum:

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

Autor: Manuel Kauf (mkauf)
Datum:

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

Autor: Johannes M. (johnny-m)
Datum:

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

Autor: Manuel Kauf (mkauf)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
jups hab mich grad verlesen ^^

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

Autor: Phayn (Gast)
Datum:

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

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.