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.
> 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?
leider weiß ich nicht welcher Fehler auftritt :( jedenfalls wird am Ausgang (Leds) ein falsches Ergebnis geliefert.
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?
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 |
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.
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.
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 |
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?
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?
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.
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
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.
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?
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
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.
jups hab mich grad verlesen ^^ hab nicht gesehen dass nu 8 bit :) (habs schon korrigiert ^^)
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.