Hallo liebe Xmega-Benutzer, mir waren die im Datenblatt zum ATxmega128A4U angegebenen Fehler zum ADC nicht ganz klar, ich verstehe z.B. nicht, warum keine Abhängigkeit des "INL error" (integral non-linearity error) von der Verstärkung im ADC-Modus "differential with gain" angegeben wird. Ich habe nun ein paar Tests des ADC durchgeführt, deren Ergebnisse ich hier vorstellen möchte. Der ATxmega128A4U und dessen ADC sind folgendermaßen konfiguriert: Vcc=3,6V, f_CPU=32MHz, ADC-prescaler=64, CONVMODE=SIGNED, RESOLUTION=12BIT, IMPMODE=LOWIMP, CURRLIMIT=LOW, REFSEL=AREFA: 1,60V an PA0. Die zu digitalisierende Spannung liegt an PA1 an und wird über einen 50kOhm-Poti eingestellt, wobei zwischen Pin und GND ein 100nF-Kerko verlötet ist. Die Spannung habe ich auf gemessene 30,96mV eingestellt, damit sie bei einer Verstärkung von 64 vom ADC noch erfasst werden kann. Getestet habe ich folgende ADC-Modi: single ended, DIFF (ohne gain) und DIFFWGAIN (mit gain) für alle Verstärkungsfaktoren von 0,5 bis 64. Bei den differential modi habe ich jeweils MUXNEG=GND (PAD ground) eingestellt. In allen Fällen sollte der ADC das gleiche Ergebnis liefern. Um das zu überprüfen habe ich den ADC jeweils 500000 Konversionen durchführen lassen und die Ergebnisse in ein Histogramm gefüllt. Die Ergebnisse sollten sich gaussförmig um den Mittelwert µ mit der Standardabweichung sigma verteilen. Bekanntlich liegt die Wahrscheinlichkeit dafür, dass eine Konversion in den Bereich (µ +/- sigma) fällt bei 68%. Als Beispiel habe ich die Verteilung der ADC-Ergebnisse für die Verstärkung 32 als Bilddatei angehängt (die Histogrammeinträge sind rot dargestellt, die angepasste Gausskurve mit den entsprechenden Parametern µ und sigma in gelb). Auffallend sind hier bereits scharfe, signifikante peaks, die aus der Gausskurve herausragen und keine statistischen Fluktuationen sein können. Diese peaks sind Artefakte und haben vermutlich ihre Ursache im Verstärker des ADC. Es ergaben sich nun folgende Parameter: ADC-Modus | µ | sigma ---------------+---------+------ single ended | 32,86 | 0,84 DIFF ohne gain | 32,80 | 0,84 gain 0,5 | 9,21 | 1,28 gain 1 | 26,16 | 1,23 gain 2 | 58,46 | 1,62 gain 4 | 120,96 | 2,28 gain 8 | 242,74 | 3,64 gain 16 | 494,48 | 7,37 gain 32 | 978,99 | 13,96 gain 64 | 1927,56 | 27,16 Wenn man im Fall der Modi mit gain die Parameter µ und sigma durch den jeweiligen Verstärkungsfaktor dividiert (normiert), sollte man die Ergebnisse direkt mit "single ended" und "DIFF ohne gain" vergleichen können. Die zweite angehängte Bild-Datei zeigt diesen Vergleich (die Fehlerbalken geben die normierten sigma-Werte an). Offenichtlich erfordern alle Modi ihre eigene Nullpunktsmessung. Die Differenz zum jeweiligen Nullpunkt entspricht dann erst der gemessenen Spannung. Da der ADC für eine Messung von 0V (Kurzschluss PA1 mit GND) negative Werte liefert, verschieben sich alle Messungen nach oben, aber eben unterschiedlich. Das Ergebnis unter Berücksichtigung der jeweiligen Nullpunktsmessung (ebenfalls 500000 Konversionen) zeigt die dritte angehängte Bild-Datei. Hier ist der theoretische ADC-Wert für die anliegende Spannung von 30,96mV als rote Linie eingezeichnet. Fast alle ADC-Ergebnisse sind damit konsistent. Aus diesen und weiteren Tests ergeben sich für mich folgende Schlussfolgerungen: 1) "single ended" und "DIFF ohne gain" liefert das gleiche Ergebnis, vorausgesetzt für "DIFF" ist MUXNEG=GND (PAD ground) eingestellt. Vermutlich bezieht sich "single ended" auch auf PAD ground und nicht auf INTGND wie im Manual angegeben. 2) Wenn es auf die Genauigkeit der Messung ankommt, sollte gain=0,5 und gain=1 vermieden werden. Erst oberhalb von gain=2 erhöht der Verstärker die Messgenauigkeit. 3) gain=64 sollte vermieden werden, da sich auf die Messung die oben beschriebenen Artefakte negativ auswirken, die gerade für kleine Spannungen (Nullpunktsmessung) und große Verstärkung eklatant hervortreten. 4) Die Verteilung der ADC-Ergebnisse wird stark asymmetrisch und verschiebt sich signifikant nach oben, wenn die Sampling-Rate verdoppelt wird. Anders als im Manual beschrieben, ist der Verstärker bei einer Samplingrate von 1MSPS nicht brauchbar! Das gilt auch für die Einstellung CURRLIMIT=NO (No limit). 5) Bei einer Sampling-Rate von 0,5MSPS sind alle CURRLIMIT-Einstellungen wählbar. Hier stimmen die Aussagen des Manuals. 6) Das Potential von INTGND liegt bei "DIFF ohne gain" um 7,4mV höher als GND (PAD ground). Bei "DIFFWGAIN" liegt INTGND dagegen nur 3,4mV oberhalb von GND. LG Christian
hallo, das ist eine interessante Studie. hast du bezüglich des ADC auch die interne Kalibrierung durchgeführt. damit sollten die Offsets unter Punkt 6 verschwinden. Wie hast du die Evaluierung durchgeführt? Kannst du bitte Firmware und benötigte Software anhängen bzw. nennen. .. ich würde gern den gleichen Test mit meiner Hardware durchführen. Wie sieht der Aufbau deiner Hardware aus. Grüße Martin
Hallo Martin, eine Kalibrierung des ADC habe ich wie im Manual beschrieben durchgeführt. Die Evaluierung ist selbst gestrickt. Im Wesentlichen geschieht Folgendes: Der Xmega sampelt mit der vorgegebenen Samplingrate und schreibt die Ergebnisse in den RAM. Dann gibt es eine Unterbrechung, und die Werte werden aus dem RAM über USART mit Paritätsprüfung an einen USB/RS485-Schnittstellenkonverter gesendet, von wo sie der PC über einen virtuellen COM-Port einliest. Gleichzeitiges Samplen des ADC und Senden von Daten sollte meiner Erfahrung nach vermieden werden, weil das sonst zu einem zusätzlichen Verrauschen der Messungen führt. Die Daten werden von einem C++-Programm ausgewertet: jeder 16-Bit-Wert inkrementiert das entsprechende Element eines Arrays der Dimension 2048. Mittelwert und Standarabweichung werden während der Datenaufnahme berechnet. Wenn 500000 16-Bit-Werte gesammelt sind, wird das Array (=Histogramm) als csv-Datei auf die Festplatte geschrieben. Der Rest wird in Excel gemacht, d.h. die Bilder, die ich angehängt habe. Vielleicht reicht Dir das, um das nachzubauen, ich kann Dir natürlich auch gerne Code-Schnipsel geben, kenne aber Deine Entwicklungsumgebung nicht. Viele Grüße Christian
Hallo, ich wäre an den Code-Schnipseln interessiert. Mit der Konfiguration des Controllers habe ich keine Probleme, könnte das auch selber machen. Da du aber eh schon alles fertig hast würde ich gern gleich deine Routinen und Programm zum Sammeln der Daten auf dem Rechner und das Excel-File nehmen. Führt einfach schneller zum Ziel ;-) Grüße martin
So, wie das so ist, ist das Test-Programm ein gewachsener Teil eines größeren Projekts. Jetzt habe ich mal alles eingedampft auf die Testfunktionen. Die Port-Belegungen musst Du sicher noch an Deine Gegebenheiten anpassen. Das Einfachste wäre, Du schreibst Dir eine Routine zum Senden der Daten anstelle des Aufrufs DSP_WRD_0 selbst und löscht die Include-Anweisung. Ich kann Dir auch die Include-Datei schicken, ist nur etwas umfangreicher. Die habe ich geschrieben, damit die MPU den PC als Display benützen kann. Grüße Christian
1 | /* |
2 | * ADC_Test.asm |
3 | * |
4 | * Created: 03.08.2013 18:25:48 |
5 | * Author: Christian Felix |
6 | * |
7 | * testet den ADC des ATxmega128A4U |
8 | * konvertiert permanent die Spannung am Eingang Pin PA1 |
9 | * und sendet die Ergebnisse als Datenpakete von jeweils 1792 words |
10 | * über USARTE0 an den PC |
11 | * |
12 | */ |
13 | |
14 | .def zero = r11 ; immer 0, für 16-bit Addition |
15 | .def count = r12 |
16 | .def count2 = r13 |
17 | .def count3 = r14 |
18 | .def count4 = r15 |
19 | .def temp = r16 |
20 | .def temp2 = r17 |
21 | .def temp3 = r18 |
22 | .def temp4 = r19 |
23 | .def temp5 = r20 |
24 | |
25 | .equ DISPLAY_SIZE = 100 |
26 | |
27 | |
28 | ; setze Interrupt Sprung-Adressen: |
29 | ; -------------------------------- |
30 | |
31 | .org 0x000 rjmp main |
32 | .org USARTE0_TXC_vect rjmp USARTE0_TXC ; TXC (data transmit complete) Interrupt-Adresse USARTE0 |
33 | |
34 | |
35 | ; Start Hauptprogramm: |
36 | ; -------------------- |
37 | |
38 | main: ; Stack initialisieren |
39 | ldi temp, HIGH(RAMEND) |
40 | out CPU_SPH, temp |
41 | ldi temp, LOW(RAMEND) |
42 | out CPU_SPL, temp |
43 | |
44 | ; initialisiere zero register: |
45 | clr zero |
46 | |
47 | ; schalte System-Clock auf 32MHz um (zusätzlich 16,7mA, mit 2MHz sind's 3,22mA) |
48 | rcall CLOCK_32MHZ |
49 | |
50 | ; initialisiere USARTE: |
51 | rcall USART_INI |
52 | |
53 | ; Datenrichtung Ports, Remap, Pin Konfiguration, Ladungspumpe ein: |
54 | rcall DEFINE_PORTS |
55 | |
56 | ; Ringspeicher / Pointer für PC-Display initialisieren: |
57 | rcall DSP_INI |
58 | |
59 | ; ADCA kalibrieren: |
60 | rcall ADCA_CALIBRATE |
61 | |
62 | ; setze Event-System: |
63 | //rcall SET_EVENTSYSTEM |
64 | |
65 | |
66 | ; =================================================================== |
67 | ; |
68 | ; ADC konfigurieren |
69 | ; |
70 | ; ------------------------------------------------------------------- |
71 | |
72 | ; Conversion mode |
73 | ldi temp, 0b10110000 ; IMPMODE = 1 = low impedance source; CURRLIMIT = 01 = LOW; CONVMODE = 1 = signed mode |
74 | ; FREERUN = 0; RESOLUTION = 00 = 12bit right adjusted |
75 | sts ADCA_CTRLB, temp |
76 | |
77 | ; external voltage reference pin A0, AREF0 (1,6V): |
78 | ldi temp, 0b00100000 ; REFSEL[2:0] = 010 = AREFA |
79 | sts ADCA_REFCTRL, temp |
80 | |
81 | ; ADC clock setzen: 0.5MHz |
82 | // ldi temp, 0b00000011 ; prescaler 32 |
83 | ldi temp, 0b00000100 ; prescaler 64 |
84 | // ldi temp, 0b00000101 ; prescaler 128 |
85 | // ldi temp, 0b00000111 ; prescaler 512 |
86 | sts ADCA_PRESCALER, temp |
87 | |
88 | ; Kanäle setzen: |
89 | ; -------------- |
90 | |
91 | ; Channel 0: für alle Input-Pins single ended (ADCA in signed mode): |
92 | ldi temp, 0b00000001 ; START = 0 |
93 | ; INPUTMODE[1:0] = 01 = Single ended |
94 | sts ADCA_CH0_CTRL, temp |
95 | |
96 | ; Poti: Channel 0 ADC pin 1: PA1, single ended, signed mode |
97 | ldi temp, 0b00001000 ; MUXPOS[3:0] = 0001 = PIN1 = ADC1 pin = PA1 |
98 | ; MUXNEG[2:0] = 000 |
99 | sts ADCA_CH0_MUXCTRL, temp |
100 | |
101 | ; ADCA enablen und flushen: |
102 | ldi temp, 0b00000011 ; no DMA, no START, FLUSH, ENABLE |
103 | sts ADCA_CTRLA, temp |
104 | |
105 | |
106 | ; =================================================================== |
107 | ; |
108 | ; Interrupt level für USART definieren |
109 | ; |
110 | ; ------------------------------------------------------------------- |
111 | |
112 | ; enable low level interrupt für Datenübertragung zum PC: |
113 | ldi temp, 0b00000001 ; RREN = 0: no round robin for low level interrupts |
114 | ; IVSEL = 0: interrupt vectots are placed at the start of the application section |
115 | ; HILVLEN = 0: high level interrupt disabled |
116 | ; MEDLVLEN = 0: medium level interrupt disabled |
117 | ; LOLVLEN = 1: enable low level interrupt |
118 | sts PMIC_CTRL, temp |
119 | |
120 | ; global interrupt einschalten: |
121 | sei |
122 | |
123 | |
124 | ; =================================================================== |
125 | ; |
126 | ; ADC-Test-Start |
127 | ; |
128 | ; ------------------------------------------------------------------- |
129 | |
130 | ; warten bis Kondensatoren aufgeladen sind: |
131 | ldi temp3, 200 |
132 | rcall wait |
133 | |
134 | ADC_repeat: |
135 | rcall DSP_CLR_ALL |
136 | |
137 | clr count |
138 | |
139 | clr XL |
140 | clr XH |
141 | |
142 | ldi YH, HIGH(Poti_Samples) |
143 | ldi YL, LOW(Poti_Samples) |
144 | |
145 | |
146 | ADC_loop_0: |
147 | ; warten. Hier Zahl erhöhen, falls PC zu langsam (kein Handshake mit PC). |
148 | ldi temp3, 22 |
149 | rcall shortwait |
150 | |
151 | ; starte Channel 0: |
152 | ldi temp, 0b00000101 ; no DMA, START CH_0, no FLUSH, ENABLE |
153 | sts ADCA_CTRLA, temp |
154 | |
155 | ADC_wait_0: |
156 | ; prüfe, ob Konversion für Channel 0 fertig: |
157 | lds temp, ADCA_CH0_INTFLAGS |
158 | tst temp |
159 | breq ADC_wait_0 |
160 | |
161 | /* |
162 | ; lösche nun Flag (nicht nötig, wenn ADC nach Warteschleifen von Hand gestartet wird): |
163 | ldi temp, 1 |
164 | sts ADCA_CH0_INTFLAGS, temp |
165 | */ |
166 | |
167 | ; 12bit Wert direkt von channel 0 lesen: |
168 | lds temp, ADCA_CH0_RES ; Lo-Byte |
169 | lds temp2, ADCA_CH0_RES+1 ; Hi-Byte |
170 | |
171 | ; Werte speichern: |
172 | st Y+, temp |
173 | st Y+, temp2 |
174 | |
175 | adiw X, 1 |
176 | cpi XH, 7 ; 7*256 = 1792 Words |
177 | brne ADC_loop_0 |
178 | |
179 | ; Werte senden: |
180 | ldi YH, HIGH(Poti_Samples) |
181 | ldi YL, LOW(Poti_Samples) |
182 | |
183 | ADC_send: |
184 | ld temp, Y+ |
185 | ld temp2, Y+ |
186 | |
187 | rcall DSP_WRD_0 |
188 | |
189 | sbiw X, 1 |
190 | cpi XH, 0 |
191 | brne ADC_send |
192 | tst XL |
193 | brne ADC_send |
194 | |
195 | rjmp ADC_repeat |
196 | |
197 | |
198 | |
199 | |
200 | ; #################################################################################### |
201 | ; ########################### ################################ |
202 | ; ########################### Interrupt Handler ################################ |
203 | ; ########################### ################################ |
204 | ; #################################################################################### |
205 | |
206 | ; ==================================================================================== |
207 | ; |
208 | ; USARTE0_TXC: Data transmit complete, nach erstem Byte alle weiteren versenden |
209 | ; |
210 | ; Nach Sendung des letzten Bytes wird Interrupt gestoppt. |
211 | ; |
212 | ; Kein Input, keine benutzten Register |
213 | ; |
214 | ; ------------------------------------------------------------------------------------ |
215 | |
216 | USARTE0_TXC: |
217 | |
218 | push temp |
219 | in temp, CPU_SREG |
220 | push temp |
221 | push ZL |
222 | push ZH |
223 | |
224 | ; sende Byte: |
225 | ldi ZL, LOW(Display_Pointer) ; Z: absolute Adresse für relativen Pointer auf zu sendendes Byte |
226 | ldi ZH, HIGH(Display_Pointer) |
227 | ld ZL, Z ; Z ist jetzt absoluter Pointer auf zu sendendes Byte |
228 | ld temp, Z+ ; lade das Byte und setze Z auf nächstes zu sendendes Byte ... |
229 | sts USARTE0_DATA, temp ; ... und senden |
230 | cpi ZL, DISPLAY_SIZE ; hat relativer Pointer Ende des Ringspeichers erreicht? |
231 | brlo USARTE0_TXC_pointer_beneath_limit |
232 | clr ZL |
233 | |
234 | USARTE0_TXC_pointer_beneath_limit: |
235 | ; speichere neuen Wert des relativen Pointers: |
236 | mov temp, ZL |
237 | ldi ZL, LOW(Display_Pointer) ; Z: absolute Adresse für relativen Pointer auf zu sendendes Byte |
238 | st Z+, temp ; speichere neuen relativen Pointer und lade zugleich absolute Adresse Z für relativen Ende-Pointer |
239 | |
240 | ; vergleiche neuen Pointer mit Ende-Pointer: wenn gleich, Übertragung stoppen |
241 | ld ZL, Z ; temp ist jetzt rel. Ende-Pointer |
242 | cp temp, ZL ; rel. Ende-Pointer = rel. Pointer? |
243 | brne USARTE0_TXC_end |
244 | |
245 | ; stoppe Interrupt, da gerade letztes Byte gesendet wird: |
246 | sts USARTE0_CTRLA, zero ; disable interrupt |
247 | |
248 | USARTE0_TXC_end: |
249 | pop ZH |
250 | pop ZL |
251 | pop temp |
252 | out CPU_SREG, temp |
253 | pop temp |
254 | |
255 | reti |
256 | |
257 | |
258 | ; #################################################################################### |
259 | ; ########################### ################################### |
260 | ; ########################### Unterprogramme ################################### |
261 | ; ########################### ################################### |
262 | ; #################################################################################### |
263 | |
264 | ; lade Display-Programme: |
265 | .include "../DisplayLib.inc" |
266 | |
267 | |
268 | ; ==================================================================================== |
269 | ; |
270 | ; DEFINE_PORTS: legt Datenrichtungen aller Ports fest, konfiguriert alle Pins, remap |
271 | ; |
272 | ; ------------------------------------------------------------------------------------ |
273 | |
274 | DEFINE_PORTS: |
275 | |
276 | ; Virtuelle Ports definieren: |
277 | ; --------------------------- |
278 | |
279 | ; Port C als virtuellen Port 0 |
280 | ; Port D als virtuellen Port 1 |
281 | ; Port E als virtuellen Port 2 |
282 | ; Port A als virtuellen Port 3 |
283 | |
284 | ; definiere PORTC als virtuellen Port 0 und PORTD als virtuellen Port 1: |
285 | ldi temp, 0b00110010 ; code für PORTC->virtuell 0 und PORTD->virtuell 1 |
286 | sts PORTCFG_VPCTRLA, temp ; Register für virtuelle Ports 0 und 1 |
287 | ; definiere PORTE als virtuellen Port 2 und PORTA als virtuellen Port 3: |
288 | ldi temp, 0b00000100 ; code für PORTE->virtuell 2 und PORTA->virtuell 3 |
289 | sts PORTCFG_VPCTRLB, temp ; Register für virtuelle Ports 2 und 3 |
290 | |
291 | |
292 | ; Setze Port Pins auf Eingang / Ausgang je nach Bestimmung: |
293 | ; --------------------------------------------------------- |
294 | |
295 | ; PA7: Eingang: leer (unused pin) |
296 | ; PA6: Analog-Eingang: MUXNEG, verdrahtet mit AREF = 1,6V (=PA0) |
297 | ; PA5: Analog-Eingang: MUXPOS, Ausgang Tiefpass Mikrofon-OpAmp MAX4488 |
298 | ; PA4: Analog-Eingang: PTC |
299 | ; PA3: Analog-Eingang: LDR |
300 | ; PA2: Analog-Eingang: Farbtemperatur-Poti |
301 | ; PA1: Analog-Eingang: Helligkeits-Poti |
302 | ; PA0: Analog-Eingang: AREF = 1,6V, verdrahtet mit MUXNEG (=PA6) |
303 | |
304 | ldi temp, 0b00000000 |
305 | sts VPORT3_DIR, temp ; Datenrichtungen für Port A festlegen |
306 | |
307 | ; PB3: Eingang: leer (unused pin) |
308 | ; PB2: Eingang: leer (unused pin) |
309 | ; PB1: Eingang: leer (unused pin) |
310 | ; PB0: Ausgang: An/Aus Stromversorgung PTC |
311 | |
312 | |
313 | ldi temp, 0b00000001 |
314 | sts PORTB_DIR, temp ; Datenrichtungen für Port B festlegen |
315 | |
316 | ; PC7: Ausgang: CLK_PER für Ladungspumpe |
317 | ; PC6: Eingang: leer (unused pin) |
318 | ; PC5: Ausgang: An/Aus Touch (Spannungsversorgung AT42QT1010 über 3,3V-Referenz) |
319 | ; PC4: Eingang: Ergebnis Touch, Ausgang vom ATV42QT1010 |
320 | ; PC3: Ausgang: Timer TCC0 verdrahtet mit PC2 (Eingang), Mikrophon-Trigger 1:3 |
321 | ; PC2: Eingang: verdrahtet mit PC3, Mikrophon-Trigger 1:3 |
322 | ; PC1: Ausgang: Mikrofon-Verstärkerkreis An/Aus, enable OpAmp und 3,3V-LDO |
323 | ; PC0: Eingang: leer (unused pin) |
324 | |
325 | ldi temp, 0b10101010 |
326 | out VPORT0_DIR, temp ; Datenrichtungen für Port C festlegen |
327 | |
328 | ; PD7: Ausgang: Blau RGB-LED, Remap TCD0 / OC0D |
329 | ; PD6: Eingang: leer (unused pin) |
330 | ; PD5: Ausgang: PWM Licht Kanal B, TCD1 / OC1B |
331 | ; PD4: Ausgang: PWM Licht Kanal A, TCD1 / OC1A |
332 | ; PD3: Eingang: leer (unused pin) |
333 | ; PD2: Ausgang: enable OpAmps 1 und 2: Kanäle A und B |
334 | ; PD1: Eingang: leer (unused pin) |
335 | ; PD0: Ausgang: Umschaltung PWM / Analog, vier FET-Gates |
336 | |
337 | ldi temp, 0b10110101 |
338 | out VPORT1_DIR, temp ; Datenrichtungen für Port D festlegen |
339 | |
340 | ; PE3: Ausgang: USARTE0, TXD0 |
341 | ; PE2: Ausgang: USARTE0, RXD0 |
342 | ; PE1: Ausgang: Grün RGB-LED |
343 | ; PE0: Ausgang: Rot RGB-LED |
344 | |
345 | ldi temp, 0b00001111 |
346 | sts VPORT2_DIR, temp ; Datenrichtungen für Port E festlegen |
347 | |
348 | ; PR1: Eingang: leer (unused pin) |
349 | ; PR0: Eingang: leer (unused pin) |
350 | |
351 | ldi temp, 0b00000000 |
352 | sts PORTR_DIR, temp ; Datenrichtungen für Port R festlegen |
353 | |
354 | |
355 | ; Lege die Eingänge nicht benutzter Pins auf Low (Port A, B, C, D, R): |
356 | ; -------------------------------------------------------------------- |
357 | |
358 | ldi temp2, 0b00010000 ; code für Totem-pole Pull-down (Pin level low) |
359 | |
360 | ; Port A: |
361 | sts PORTA_PIN7CTRL, temp2 ; Totem-pole Pull-down |
362 | |
363 | ; Port B: |
364 | ldi temp, 0b00001110 ; folgende PINnCTRL gilt für pins 3, 2, 1 (PB3, PB2, PB1) |
365 | sts PORTCFG_MPCMASK, temp |
366 | sts PORTB_PIN1CTRL, temp2 ; Totem-pole Pull-down |
367 | |
368 | ; Port C: |
369 | ldi temp, 0b01000001 ; folgende PINnCTRL gilt für pins 6 und 0 (PC6 und PC0) |
370 | sts PORTCFG_MPCMASK, temp |
371 | sts PORTC_PIN0CTRL, temp2 ; Totem-pole Pull-down |
372 | |
373 | ; Port D: |
374 | ldi temp, 0b01001010 ; folgende PINnCTRL gilt für pin 6, 3, 1 (PD6, PD3, PD1) |
375 | sts PORTCFG_MPCMASK, temp |
376 | sts PORTD_PIN1CTRL, temp2 ; Totem-pole Pull-down |
377 | |
378 | ; Port R: |
379 | ldi temp, 0b00000011 ; folgende PINnCTRL gilt für pin 1 und 0 (PR1 und PR0) |
380 | sts PORTCFG_MPCMASK, temp |
381 | sts PORTR_PIN0CTRL, temp2 ; Totem-pole Pull-down |
382 | |
383 | |
384 | ; Port A: schalte digitalen Input-Buffer für Analog-Eingänge aus: |
385 | ; --------------------------------------------------------------- |
386 | |
387 | ldi temp2, 0b00000111 ; code für Digital input buffer disabled |
388 | ldi temp, 0b01111111 ; folgende PINnCTRL gilt für pins 6...0 (PA6...PA0) |
389 | sts PORTCFG_MPCMASK, temp |
390 | sts PORTA_PIN0CTRL, temp2 ; Digital input buffer disabled |
391 | |
392 | |
393 | ; Schalte Ladungspumpe für negative Spannung ein: |
394 | ; ----------------------------------------------- |
395 | |
396 | ldi temp, 0b10000000 ; slew rate limitation enable on PC7 |
397 | sts PORTC_PIN7CTRL, temp |
398 | ldi temp, PORTCFG_CLKOUT_PC7_gc ; CLKper, also 32MHz (manual S.154), an PC7 |
399 | sts PORTCFG_CLKEVOUT, temp |
400 | |
401 | |
402 | ; Remap pin PD3/PD7, PWM Timer/Counter Output Compare D für Blau RGB: |
403 | ; ------------------------------------------------------------------- |
404 | |
405 | ldi temp, 0b00001000 ; bit 3 - TC0D from PD3 to PD7 (manual p.150) |
406 | sts PORTD_REMAP, temp |
407 | |
408 | ret |
409 | |
410 | |
411 | ; ==================================================================================== |
412 | ; |
413 | ; SET_EVENTSYSTEM |
414 | ; |
415 | ; setzt die event channels, um ADC-Kanäle zu triggern |
416 | ; |
417 | ; kein Input |
418 | ; |
419 | ; temp wird zerstört |
420 | ; |
421 | ; ------------------------------------------------------------------------------------ |
422 | |
423 | SET_EVENTSYSTEM: |
424 | |
425 | ; Event channel 0 wird über die Clk_per getriggert, um ADCA-channel 0 zu |
426 | ; triggern, an dem die Potis, PTC und LDR hängen: |
427 | ; events sollen mit einer Taktrate von 62500 Hz getriggert werden, das |
428 | ; ist ausreichend, um mit 1024 Hz für alle vier Pins, die an ADCA-Channel 0 |
429 | ; hängen, je 16 neue Konversionen zur Verfügung zu stellen, aus denen dann |
430 | ; per CPU der Mittelwert gebildet werden kann. |
431 | ldi temp, 0b10001001 ; PRESCALER M (1000) mit M = 9 (1001) = 32MHz/2e9 = 62500Hz |
432 | ; wenn CPU mit 2MHz getaktet wird, dann diesen Wert verwenden: |
433 | // ldi temp, 0b10000101 ; PRESCALER M (1000) mit M = 5 (0101) = 2MHz/2e5 = 62500Hz |
434 | sts EVSYS_CH0MUX, temp |
435 | |
436 | ; Event channel 1 wird aktiv beim Flankenwechsel Pin PC2 (rising/falling edge on Pin PC2) |
437 | ldi temp, 0b01100010 ; group configuration PORTC_PIN2 für CH1MUX |
438 | sts EVSYS_CH1MUX, temp ; event channel 1 |
439 | |
440 | ret |
441 | |
442 | |
443 | ; ==================================================================================== |
444 | ; |
445 | ; CLOCK_32MHZ |
446 | ; |
447 | ; change of internal clock from 2MHz to 32MHz |
448 | ; Stromaufnahme der MPU steigt dabei um 22mA. |
449 | ; Der interne 32MHz Oszillator läuft z.B. 5% zu schnell und muss |
450 | ; kalibriert werden! |
451 | ; |
452 | ; ------------------------------------------------------------------------------------ |
453 | |
454 | CLOCK_32MHZ: |
455 | |
456 | ldi temp, (1<<OSC_RC32KEN_bp | 1<<OSC_RC32MEN_bp | 1<<OSC_RC2MEN_bp) ; enable 32kHz and 32MHz clock and keep 2MHz clock running |
457 | sts OSC_CTRL, temp |
458 | ; warte bis 32MHz clock stabil läuft |
459 | CLOCK_32MHZ_clock_wait: |
460 | lds temp, OSC_STATUS |
461 | andi temp, (1<<OSC_RC32KRDY_bp | 1<<OSC_RC32MRDY_bp) ; 32kHz and 32MHz clock ready bits ausmaskieren |
462 | cpi temp, (1<<OSC_RC32KRDY_bp | 1<<OSC_RC32MRDY_bp) ; wenn bits gesetzt, dann laufen die Oszillatoren stabil |
463 | brne CLOCK_32MHZ_clock_wait ; Schleife wird in der Regel nur zweimal durchlaufen |
464 | ; 32MHz clock läuft jetzt stabil, ändere interne System clock über protected write sequence |
465 | ldi temp, 0xD8 ; code für Zugriff auf Protected IO register |
466 | sts CPU_CCP, temp ; ins CPU Configuration Change Protection schreiben, ab jetzt nur 4 clock cycles write möglich |
467 | ldi temp, 0b00000001 ; code für Auswahl interne 32MHz clock |
468 | sts CLK_CTRL, temp ; fertig - nach 3 clock cycles |
469 | ; disable nun interne 2MHz clock: |
470 | ldi temp, (1<<OSC_RC32KEN_bp | 1<<OSC_RC32MEN_bp | 0<<OSC_RC2MEN_bp) ; keep 32kHz and 32MHz clock running and stop 2MHz clock |
471 | sts OSC_CTRL, temp |
472 | |
473 | ; calibrate the 32MHz internal Oszillator frequency: |
474 | ldi temp, 0 |
475 | sts OSC_DFLLCTRL, temp ; calibration source for the 32MHz DFLL is the 32.768kHz internal oscillator |
476 | ldi temp, 1<<DFLL_ENABLE_bp ; this bit enables the DFLL and auto-calibration of the internal oscillator |
477 | sts DFLLRC32M_CTRL, temp ; the reference clock must be enabled and stable before the DFLL is enabled |
478 | |
479 | ret |
480 | |
481 | ; ==================================================================================== |
482 | ; |
483 | ; ADCA_CALIBRATE |
484 | ; |
485 | ; ADCA kalibrieren. |
486 | ; Achtung, hier keinen Code einfügen, kein Interrupt. |
487 | ; Command Register des NVM-Controllers wird verändert. |
488 | ; |
489 | ; kein Input |
490 | ; |
491 | ; zerstört temp, temp2, temp3, Z-pointer |
492 | ; |
493 | ; ------------------------------------------------------------------------------------ |
494 | |
495 | ADCA_CALIBRATE: |
496 | |
497 | ; Lo-Byte der ADCA-Kalibrierung zuerst lesen und in temp speichern |
498 | ; Load the Z-pointer with the byte address to read (-> manuel p. 431) |
499 | ldi ZL, PROD_SIGNATURES_START + NVM_PROD_SIGNATURES_ADCACAL0_offset |
500 | clr ZH |
501 | ; Load the NVM CMD register with the read user signature row / calibration row command |
502 | ldi temp, NVM_CMD_READ_CALIB_ROW_gc |
503 | sts NVM_CMD, temp |
504 | ; Execute the LPM instruction |
505 | lpm temp, Z ; in temp liegt jetzt das Lo-Byte der Kalibrierung |
506 | |
507 | ; nun das Hi-Byte der ADCA-Kalibrierung lesen und in temp2 speichern |
508 | ; Load the Z-pointer with the byte address to read |
509 | ldi ZL, PROD_SIGNATURES_START + NVM_PROD_SIGNATURES_ADCACAL1_offset |
510 | ; Execute the LPM instruction: |
511 | lpm temp2, Z ; in temp2 liegt jetzt das Hi-Byte der Kalibrierung |
512 | |
513 | ; NVM CMD register wieder in den Normalzustand versetzen: |
514 | ldi temp3, NVM_CMD_NO_OPERATION_gc |
515 | sts NVM_CMD, temp3 |
516 | |
517 | ; ADC-Kalibrierungsregister beschreiben: |
518 | sts ADCA_CAL, temp ; Lo-Byte |
519 | sts ADCA_CAL+1, temp2 ; Hi-Byte |
520 | |
521 | ret |
522 | |
523 | |
524 | ; ================================================================================== |
525 | ; |
526 | ; USART_INI |
527 | ; |
528 | ; Kommunikationseinstellungen RS485-Schnittstelle für USARTE0 @ f_CPU = 32 MHz |
529 | ; |
530 | ; ---------------------------------------------------------------------------------- |
531 | |
532 | USART_INI: |
533 | |
534 | ; schalte zunächst Receiver und Transmitter von USARTE0 aus, dadurch wird Receiver Buffer geflushed: |
535 | ldi temp, 0 |
536 | sts USARTE0_CTRLB, temp |
537 | |
538 | |
539 | ; initialize USARTE0 using initialization sequence as described in the manual p. 296: |
540 | ldi temp, 0b00000001 ; setze Baudrate auf 1MHz |
541 | sts USARTE0_BAUDCTRLA, temp ; BSEL[7:0] = 1 |
542 | ldi temp, 0b00000000 |
543 | sts USARTE0_BAUDCTRLB, temp ; BSCALE[3:0] = 0 BSEL[11:8] = 0 und => Baudrate = f_CPU/(16*(1+1)) = 1MHz |
544 | |
545 | ldi temp, 0b00100011 ; CMODE[1:0] = 00 = asynchronous mode |
546 | ; PMODE[1:0] = 10 = even parity |
547 | ; SBMODE = 0 = 1 stop bit |
548 | ; CHSIZE[2:0] = 011 = 8bit |
549 | sts USARTE0_CTRLC, temp ; set communication mode and frame format |
550 | |
551 | ; schalte Transmitter von USARTE0 an: |
552 | ldi temp, 0b00001000 ; RXEN = 0 = disable receiver |
553 | ; TXEN = 1 = enable Transmitter |
554 | ; CLK2X = 0 = no double transmission speed |
555 | ; MPCM = 0 = no multiprocessor communication mode |
556 | ; TXB8 = 0 = no ninth data bit |
557 | sts USARTE0_CTRLB, temp |
558 | |
559 | ret |
560 | |
561 | |
562 | ; ================================================================================== |
563 | ; |
564 | ; Warteschleifen |
565 | ; |
566 | ; ---------------------------------------------------------------------------------- |
567 | |
568 | ; Aufruf von wait: Wert in Einheiten von 6,25ms muss in temp3 stehen |
569 | ; maximaler Wert: 0 (=256), entspricht 1,6s, Wert 160 entspricht 1s |
570 | |
571 | wait: push temp3 |
572 | push temp4 |
573 | push count |
574 | wait1: clr temp4 |
575 | wait2: clr count |
576 | wait3: dec count |
577 | brne wait3 |
578 | dec temp4 |
579 | brne wait2 |
580 | dec temp3 |
581 | brne wait1 |
582 | pop count |
583 | pop temp4 |
584 | pop temp3 |
585 | ret |
586 | |
587 | ; Aufruf von shortwait: Wert in Einheiten von 0,025ms muss in temp3 stehen |
588 | ; maximaler Wert: 0 (=256), entspricht 6,5ms |
589 | |
590 | shortwait: |
591 | push temp3 |
592 | push temp4 |
593 | shortwait1: |
594 | clr temp4 |
595 | shortwait2: |
596 | dec temp4 |
597 | brne shortwait2 |
598 | dec temp3 |
599 | brne shortwait1 |
600 | pop temp4 |
601 | pop temp3 |
602 | ret |
603 | |
604 | ; ================================================================================== |
605 | |
606 | .DSEG |
607 | |
608 | Poti_Samples: .BYTE 7168 |
609 | |
610 | .org (RAMEND-255) |
611 | ; Speicherort für das Display-Memory muss mit Low-Byte 0 beginnen (neue Seite), sonst funktioniert der Code nicht |
612 | Display_Memory: .BYTE DISPLAY_SIZE ; Ringspeicher der Display-Daten |
613 | ; relativer Pointer auf nächstes zu sendende Byte + relativer Ende-Pointer auf letztes zu sendendes Byte: |
614 | Display_Pointer: .BYTE 2 |
Hallo Martin, hast Du noch Interesse? Dann schicke ich Dir den Rest. Auf Hardwareseite ist als Verbindungsstück zwischen Controller und PC noch ein RS485-Schnittstellenkonverter nötig, z.B. der USB-Nano-485 von CTI leancom. Grüße Christian
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.