ADCA.CH0.CTRL = 0b10000001; //ADCA Channel 0 starten
30
}
31
32
}
33
34
35
36
ISR (ADCA_CH0_vect)
37
{
38
temp1 = ADCA_CH0RESL;
39
PORTD.OUT = temp1;
40
}
Ich möchte mir den Messwert auf 8LEDs anzeigen lassen. Allerdings stimmt
nur jede zweite Messung. Bei den Messungen zwischendrin leuchtet immer
nur die LED an Pin7. Woran liegt das?
Es wäre hilfreich gewesen, den konkreten Mikrocontroller zu benennen.
Xmega ist ja ein weites Feld.
Ein Schuss ins Blaue: Musst du in der ISR eventuell zwei Register in
einer bestimmten Reihenfolge lesen, da der ADC (prinzipiell) mehr als
8bit unterstützt?
https://www.e-lab.de/downloads/DOCs/XMEGA_A_Manual.pdf
Kapitel 3.11 Accessing 16-bit Registers:
The AVR data bus is 8 bits wide, and so accessing 16-bit registers
requires atomic operations. These registers must be byte-accessed using
two read or write operations...
For a read operation, the low byte of the 16-bit register must be read
before the high byte. When the low byte register is read by the CPU, the
high byte of the 16-bit register is copied into the temporary register
in the same clock cycle as the low byte is read. When the high byte is
read, it is then read from the temporary register.
Es hat bestimmt irgendwie damit zu tun.
Ich nutze den ATXmega16D4
Nacheinander auslesen wie z.B.
1
ISR (ADCA_CH0_vect)
2
{
3
temp1 = ADCA_CH0RESL;
4
temp2 = ADCA_CH0RESH;
5
PORTD.OUT = temp1;
6
}
funktioniert leider auch nicht.
Hat jemand einen funktionsfähigen Code für den ADC eines xmega?
Das blöde Teil bringt mich so langsam zur Verzweiflung.
Stefanus F. schrieb:> Es wäre hilfreich gewesen, den konkreten Mikrocontroller zu benennen.> Xmega ist ja ein weites Feld.
Bzgl. ADC ist das völlig egal, da sind alle Xmega gleich. Das ist ja
einer deren Vorteile.
@TE schau mal hier nach, da gibts ein Tutorial zum ADC auf dem Xmegas,
was IMO recht gut ist:
https://www.kampis-elektroecke.de/?page_id=1644
Hallo,
du solltest die Wandlungs-Routine abwarten
while ((ADCA.CH0.INTFLAGS & ADC_CH_IF_bm)==0);
ADCA.CH0.INTFLAGS=ADC_CH_IF_bm;
Vollständiger Code:(ev. Register und ADC ändern!)
unsigned int adca_read(void)
{
unsigned int data;
// Start the AD conversion
ADCA.CH0.CTRL|= 1<<ADC_CH_START_bp;
// Wait for the AD conversion to complete
while ((ADCA.CH0.INTFLAGS & ADC_CH_IF_bm)==0);
// Clear the interrupt flag
ADCA.CH0.INTFLAGS=ADC_CH_IF_bm;
// Read the AD conversion result
((unsigned char *) &data)[0]=ADCA.CH0.RESL;
((unsigned char *) &data)[1]=ADCA.CH0.RESH;
return data;
}
Auf Kampis-elektroecke wird leider keine ISR verwendet. Die Lösung ist
ähnlich komplex wie:
Gerhard G. schrieb:> Hallo,>> du solltest die Wandlungs-Routine abwarten>> while ((ADCA.CH0.INTFLAGS & ADC_CH_IF_bm)==0);> ADCA.CH0.INTFLAGS=ADC_CH_IF_bm;>> Vollständiger Code:(ev. Register und ADC ändern!)>> unsigned int adca_read(void)> {> unsigned int data;>> // Start the AD conversion> ADCA.CH0.CTRL|= 1<<ADC_CH_START_bp;> // Wait for the AD conversion to complete> while ((ADCA.CH0.INTFLAGS & ADC_CH_IF_bm)==0);> // Clear the interrupt flag> ADCA.CH0.INTFLAGS=ADC_CH_IF_bm;> // Read the AD conversion result> ((unsigned char *) &data)[0]=ADCA.CH0.RESL;> ((unsigned char *) &data)[1]=ADCA.CH0.RESH;> return data;> }
Danke soweit, aber es muss doch noch eine einfachere Lösung geben. Bei
den Atmegas gehts ja auch mit z.B.:
Peet schrieb:> aber es muss doch noch eine einfachere Lösung geben
Gibt es auch. Du stellst den ADC auf FreeRun, startest ihn einmal und
holst dir dann nur noch die Resultate aus dem CH0RES Register ab (und
zwar als 16-bit in einem Schwupps, denn der Kompiler erledigt das für
dich).
Wenn das alles automatisch passieren soll, ist evtl. auch das Event
System interessant.
Hier mal für 3 Kanäle auf dem STM32F100RB vom DiscoveryVL Board:
1
// Setup the ADC for 3-Channel sampling
2
// runs free with 3 channels , zero effort
3
// setup your desired current sensor in PMSM.h first
4
staticvoidADCInit(void)
5
{
6
ADC.CTRLA=0x01;// switch it on
7
ADC.CTRLB=ADC_RESOLUTION_12BIT_gc|(ADC_MODE<<4);// 12 bit right adjusted
8
ADC.PRESCALER=ADC_PRESCALER_DIV256_gc;
9
ADC.REFCTRL=ADC_REFSEL_VCC_gc;
10
ADC.EVCTRL=ADC_SWEEP_012_gc;// sweep over 3 channels
Peet schrieb:> Auf Kampis-elektroecke wird leider keine ISR verwendet. Die Lösung ist> ähnlich komplex wie:
Lies dir auf der Seite mal alles zum Xmega durch, ISR wird da auch
beschrieben wie das gehandhabt wird (Hi, Mid und Low-Level Interrupts
usw) und es ist wirklich sehr sehr einfach, es ist halt Fleißarbeit.
Ich vermisse in der Diskussion den Hinweis, was der TO konkret falsch
gemacht hat.
Das Einzige, was mir aufgefallen ist, dass er zunächst nur ADCA_CH0RESL
gelesen hat und dann im zweiten Versuch ADCA_CH0RESL gefolgt von
ADCA_CH0RESH, was nach meiner Einschätzung dem Datenblatt entspricht.
Allerdings werden in allen genannten Beispielen die beiden Register in
einem Rutsch in eine 16bit Variable gelesen. Kann das der Knackpunkt
sein?
Meiner Meinung nach fehlen dem TE hier noch einige Grundlagen. Ich hab
aktuell leider nur Platinen (fürs Breadboard) für die ATXMEGAs aber
keine Controller. Sonst hätte ich schon selbst mal einen Bleistiftcode
erstellt. Hab mir u.a. mit Hilfe von Kampis Elektronikecke die
ATXMEGA-Programmierung beigebracht. Hab hier noch nen Code rumfliegen,
der ne FFT auf den ATXMEGA macht und hab dabei den internen ADC
verwendet. Allerdings hatte ich das Seinerzeit ohne ISR benutzt.
Ich habe kaum Erfahrung mit Xmega, weil ich nur eine einzige Anwendung
von ATmega auf Xmega portiert habe. Zufälligerweise hatte ich dort den
ADC auch im Single-Shot Verfahren verwendet, allerdings ohne Interrupt.
Vielleicht hilft mein Code:
1
#include"ADC.h"
2
#include<avr/io.h>
3
#include<string.h>
4
#include<stdint.h>
5
#include"../hw-layout.h"
6
7
// Functions to access the internal ADC of Xmega targets.
8
9
// required settings:
10
// #define F_CPU 32000000
11
// #define ADC_CHANNELS 4
12
13
// Note: For all functional registers, I used the old syntax because
14
// the structures of the ADC are incorrectly defined in WinAVR.
15
16
// get a sample from ADC chip.
17
// chanell must be in range 0-15.
18
uint16_tgetADC(constintchannel){
19
ADCA_CH0_MUXCTRL=(channel<<3);
20
// flush the previous result
21
ADCA_CTRLA|=2;// 2=ADC_FLUSH_bm but not defined in WinAVR
22
// start one sample
23
ADCA_CH0_INTFLAGS=1;
24
ADCA_CH0_CTRL|=ADC_CH_START_bm;
25
// wait for the result
26
while((ADCA_CH0_INTFLAGS&1)==0);
27
returnADCA_CH0RES;
28
}
29
30
// initialize the ADC
31
voidinitADC(){
32
ADCA_CTRLA=ADC_ENABLE_bm;
33
ADCA_CTRLB=ADC_RESOLUTION_12BIT_gc;
34
ADCA_REFCTRL=ADC_REFSEL_INT1V_gc|ADC_BANDGAP_bm;
35
ADCA_PRESCALER=ADC_PRESCALER_DIV8_gc;
36
ADCA_CH0_CTRL=ADC_CH_INPUTMODE_SINGLEENDED_gc;
37
}
38
39
// select reference
40
// The value can be "INT1V", "INTVCC", "AREFA" or "AREFB"
41
// returns 1 on success
42
uint8_tsetAREF(constchar*name){
43
if(strcmp(name,"INT1V")==0)
44
ADCA_REFCTRL=ADC_REFSEL_INT1V_gc|ADC_BANDGAP_bm;
45
elseif(strcmp(name,"INTVCC")==0)
46
ADCA_REFCTRL=ADC_REFSEL_VCC_gc|ADC_BANDGAP_bm;
47
elseif(strcmp(name,"AREFA")==0)
48
ADCA_REFCTRL=ADC_REFSEL_AREFA_gc|ADC_BANDGAP_bm;
49
elseif(strcmp(name,"AREFB")==0)
50
ADCA_REFCTRL=ADC_REFSEL_AREFB_gc|ADC_BANDGAP_bm;
51
else
52
return0;
53
// Perform one sample to initialize the ADC on the new reference
54
getADC(0);
55
return1;
56
}
Auch ich lese hier das Ergebnis in einem Rutsch in eine 16bit Variable
aus.
Peet schrieb:
(...)
> #define F_CPU 32000000UL
(...)
> ADCA.PRESCALER = 0; // Prescaler 4
(...)
> Ich möchte mir den Messwert auf 8LEDs anzeigen lassen. Allerdings stimmt> nur jede zweite Messung. Bei den Messungen zwischendrin leuchtet immer> nur die LED an Pin7. Woran liegt das?
Bist Du Dir sicher, dass der ADC mit 8 MHz noch sinnvoll arbeitet?
Das Datenblatt sagt max. 1800 kHz.
Ich verstehe auch nicht, warum Du eine dermaßen hohe ADC-Frequenz
einstellst, wenn Du nur alle 4 s einen Messwert anforderst...
Ansonsten kann ich keinen (offensichtlichen) Fehler erkennen. Das Lesen
von ADCA_CH0RESL sollte entgegen der hier geäußerten Mutmaßungen im
gewählten 8-Bit-Modus korrekt sein.
Grüßle
Volker
Volker B. schrieb:> Bist Du Dir sicher, dass der ADC mit 8 MHz noch sinnvoll arbeitet?> Das Datenblatt sagt max. 1800 kHz.>> Ich verstehe auch nicht, warum Du eine dermaßen hohe ADC-Frequenz> einstellst, wenn Du nur alle 4 s einen Messwert anforderst..
Die 4 sek sind nur für Testzwecke.
Du hast recht. Genau das scheint der Fehler zu sein. Bis Prescaler 32
funktioniert es. Bei 16 nicht mehr. Danke!
Das wundert mich aber. Damit würde ja eine Wandlung ca 250µs dauern. Ich
kann mich erinnern einem Atmega88A Wandlungen von ca 30µs realisiert zu
haben. Das muss doch irgendwie schneller gehen??
Peet schrieb:> Du hast recht. Genau das scheint der Fehler zu sein. Bis Prescaler 32> funktioniert es. Bei 16 nicht mehr. Danke!
Freut mich, dass das Problem gelöst werden konnte -- und danke für die
Rückmeldung!
> Das wundert mich aber. Damit würde ja eine Wandlung ca 250µs dauern. Ich> kann mich erinnern einem Atmega88A Wandlungen von ca 30µs realisiert zu> haben.
Naja, die Xmegas haben einen 12-bit ADC ggü. den 10 Bit der AVRs.
Und 400kHz ADC-Takt für beim mega88A sind wohl auch schon deutlich
außerhalb der Spezifikationen. Das kann, muss aber nicht funktionieren.
> Das muss doch irgendwie schneller gehen??
Wenn's auf Geschwindigkeit ankommt, warum konfigurierst Du den ADC dann
nicht im free-running Mode? So sollten dann auch die "Up to 300 thousand
samples per second" aus dem Datenblatt möglich sein.
Ansonsten solltest Du auf den XmegaAU umsteigen. Dank der vier Kanäle
kann er mit einem ADC bis zu vier Quellen überlappend wandeln, wobei man
natürlich auch die selbe Signalquelle auf alle Kanäle schalten kann.
Grüßle
Volker
Die Behauptung von mir bezüglich der Wandlungsdauer ist Unsinn. Bin aus
irgendeinem Grund beim ADC von einer ähnlichen, taktweisen,
inkrementation (256 Takte) wie bei einem 8-Bit-Timer ausgegangen.
Die Oszi-Messung ergibt rund 6µs!
Peet schrieb:> Die Oszi-Messung ergibt rund 6µs!
OK, dann passt's ja zu den Angaben im Datenblatt. 300 kS/s wären 3.3 µs
pro Wandlung. Im Single-Shot-Mode benötigt er doppelt so viele Zyklen,
was dann die von Dir gemessenen 6 µs wären.
Grüßle
Volker
Danke.
Ich möchte nun gerne eine zweite Messung an einem anderen Pin
durchführen und auch eine weitere ISR nutzen. Das müsste doch durch
einen anderen Kanal realisierbar sein, oder?
Das heißt doch, wenn ich alle CH0 im Code durch CH1 ersetze müsste es
doch funktionieren. Tut es aber nicht. Wieso?
Peet schrieb:> Danke.>> Ich möchte nun gerne eine zweite Messung an einem anderen Pin> durchführen und auch eine weitere ISR nutzen. Das müsste doch durch> einen anderen Kanal realisierbar sein, oder?
...wenn der ATXmega16D4, den Du lt. obiger Aussage verwendest, mehr als
einen hätte...
> Das heißt doch, wenn ich alle CH0 im Code durch CH1 ersetze müsste es> doch funktionieren. Tut es aber nicht. Wieso?
Probier' den 16A4U...
Grüßle
Volker.
P.S.: "funktioniert nicht" ist eine saumäßig doofe Aussage! Vermutlich
weigert sich bereits der Compiler. Die entsprechende Fehlermeldung wäre
hier extrem hilfreich...
Ich erhalte keine Fehlermeldung.
Im Datenblatt steht:
One twelve-channel, 12-bit, 200ksps Analog to Digital Converter
Schade, ich habe mir bei dem Satz sogar 12 Kanäle vorgestellt :)
Peet schrieb:> Ich erhalte keine Fehlermeldung.
Merkwürdig, in dem iox16d4.h auf meinem System ist weder ein
ADCA.CH1 noch ein ADCA_CH1_vect definiert.
> Im Datenblatt steht:>> One twelve-channel, 12-bit, 200ksps Analog to Digital Converter
Das ist mal wieder dümmliches Marketing-Geschwafel, das sich hier auf
die Multiplexer-Kanäle bezieht.
M.W. hat nur die Ax(U)-Familie mehrere echte ADC-Kanäle, vier um genau
zu sein.
> Schade, ich habe mir bei dem Satz sogar 12 Kanäle vorgestellt :)
Dann musst Du eben im Interrupt den Multiplexer umschalten. Wo ist das
Problem?
Hinweis: Falls der ADC frei läuft, ist die nächste Wandlung bereits im
Gang, wenn der Irpt-Handler ausgeführt wird. Das Umschalten des MUX
wirkt also erst auf das Ergebnis, das im übernächsten Interrupt gelesen
wird. Das erfordert etwas Gehirnartistik, ist aber machbar :-) und war
bei den AVRs auch nicht anders.
Grüßle
Volker.
Ziel ist es am Ende 4 Pins zu haben an denen zu bestimmten Zeitpunkten
eine Spannung gemessen werden soll. Die Messwerte müssen in Variablen
gespeichert werden. Pro Pin eine Variable, die stetig neu beschrieben
wird.
OK, die Pins kann ich durch MUXCTRL einzeln abfragen/messen.
Aber wie mache ich das, wenn ich für alle 4 Pins nur eine ISR habe. Ich
brauche eine eindeutige Zuordnung zwischen Pin und Variable.. Ich hoffe
man versteht mein Problem...
Frage nebenbei.. wieso muss das Multiplexer umschalten unbedingt in der
ISR erfolgen??
Peet schrieb:> Ziel ist es am Ende 4 Pins zu haben an denen zu bestimmten Zeitpunkten> eine Spannung gemessen werden soll.
Juhu, ich liebe Salami!
> OK, die Pins kann ich durch MUXCTRL einzeln abfragen/messen.
Wo ist dann das Problem?
> Aber wie mache ich das, wenn ich für alle 4 Pins nur eine ISR habe. Ich> brauche eine eindeutige Zuordnung zwischen Pin und Variable.. Ich hoffe> man versteht mein Problem...
Meine Güte! Wieviel RAM besitzt der 16D4? Da sollte es doch möglich
sein, eine 8-Bit-Zählvariable zu opfern, die gleichzeitig als Index für
das Feld mit den Ergebnissen dient?
Außerdem interessant: Oben forderst Du 12 ADC-Kanäle, nun genügen
plötzlich vier -- weißt Du wirklich was Du willst?
> Frage nebenbei.. wieso muss das Multiplexer umschalten unbedingt in der> ISR erfolgen??
Weil Du weiter oben darüber gejammert hast, dass Dir der ADC nicht
schnell genug ist, bin ich davon ausgegangen, dass Du ein
Super-Power-User bist, dem nichts schnell genug geht -- Salami eben...
Natürlich kannst Du den ADC im Single-Shot-Modus betreiben und in aller
Ruhe den MUX einstellen und dann den ADC wieder starten. Dadurch wird
die Samplerate aber mindestens halbiert. Da ich Deine Anwendung nicht
kenne -- die Salami lässt grüßen -- kann ich leider nicht (hell-)sehen,
ob das in Deiner Anwendung noch zulässig ist...
Grüßle
Volker
P.S.: Die anderen Menschen sind in der Regel nicht des Hellsehens
mächtig!