Forum: Mikrocontroller und Digitale Elektronik Test ADC ATxmega128A4U


von Christian (Gast)



Lesenswert?

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

von Martin J. (bluematrix) Benutzerseite


Lesenswert?

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

von Christian (Gast)


Lesenswert?

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

von Martin J. (bluematrix) Benutzerseite


Lesenswert?

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

von Christian (Gast)


Lesenswert?

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

von Christian (Gast)


Lesenswert?

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
Noch kein Account? Hier anmelden.