Hallo,
ich bin blutiger Cortex M3 Anfänger.
Ich habe bisher den SYSTICK Timer benutzt. Dessen Interrupt
funktionierte einwandfrei. Ich habe lediglich die Register SYSTICKCSR
und SYSTICKRVR initialisiert, und schon rennt das Ding. Super.
Jetzt möchte ich den ADC Interrupt benutzen.
Ich habe den ADC1 also initialisiert, und das Bit EOCIE im CR1 gesetzt.
Leider ist nix passiert und meine ISR wurde nie aufgerufen. Der Fehler
war auch schnell gefunden: der Interrupt muss im NVIC erst noch
freigegeben werden! Also habe ich im Handbuch nachgeschaut zum
STM32F103VC. Dort gibt es eine Tabelle, wo die einzelnen Interrupts
aufgeführt sind. Der ADC ist an Position 18. Somit habe ich im SETENA0
Register vom VIC BIT18 auf 1 gesetzt. Nun kommt zwar der ADC interrupt,
aber dafür landet die CPU nachher sofort im NMI-handler?? was soll das?
Warum macht die das?
Könnt ihr mir weiterhelfen?
Gruss carl
ja, der Takt ist eingeschaltet.
Der ADC funktioniert ja auch, solange ich mit dem Debugger im Single
Step modus bleibe. Sobald ich den Prozessor mit Go laufen lasse, dann
springt er in den NMI.
ich habe es herausgefunden.
Ich habe mich beim Beschreiben eines Arrays bei der Überprüfung der
Grenzen geirrt und übers Array hinaus geschrieben und damit wohl den
Stack zerschossen. Kaum macht man es richtig, funktioniert es ;-)
Jedenfalls stürzt die Kiste jetzt nicht mehr ab.
Dafür habe ich ein anderes Problem.
Ich möchte 4 analoge Eingänge sampeln. Dazu benutze ich den continuous
scan mode (CONT = 1, SCAN = 1 im ADC_CR1 bzw. ADC_CR2).
Im Handbuch heisst es allerdings zum SCAN mode:
"Bit 8 SCAN: Scan mode
This bit is set and cleared by software to enable/disable Scan mode. In
Scan mode, the
inputs selected through the ADC_SQRx or ADC_JSQRx registers are
converted.
0: Scan mode disabled
1: Scan mode enabled
Note: An EOC or JEOC interrupt is generated only on the end of
conversion of the last
channel if the corresponding EOCIE or JEOCIE bit is set."
Das bedeutet doch: der ADC wandelt die 4 Kanäle zwar, so wie ich es in
den SQRx-Registern sage, und schreibt das auch schön brav ins ADC_DR,
aber was bringt mir das, wenn er nur nach dem letzten Kanal einen IRQ
auslöst? Da es m.W. nur ein ADC-Datenregister gibt, wird doch dann da
immer nur der Wert des letzten Kanals drin stehen, wenn der ADC
Interrupt kommt. Oder?
Mache das mit einem DMA. Du stellt den AD Wandler auf die 4 Kanäle ein
und der DMA schreibt alle Werte in einen Buffer.
Definiere den Buffer auf z.B. 16 Elemente, dann steht dort jeder Wert 4
mal drin:
1 2 3 4 1 2 3 4 1 2 3 4 1 2 3 4
also jeder vierte ist der Kanal 1, immer abwechselnd.
Zum Schluss kommt der DMA Ready Interrupt und du kannst aus den 4
Kanälen einen Mittelwert rechnen.
Ist viel einfacher und die CPU muss deutlich weniger Interrupts machen.
Demos dazu gibt es in der FW-Lib.
Markus Müller,
danke soweit. Mit dem DMA werde ich das morgen gleich ausprobieren.
Ohne DMA geht es wohl nicht? Ist zwar schon sehr sexy, wenn der DMA die
Messwerte allesamt selbständig im Memory ablegt, aber ich möchte für
eine so triviale Aufgabe nicht unbedingt einen kostbaren DMA-Kanal
anbrauchen, wenns geht :-)
Besser einen DMA verwenden als so viele Interrupts.
Mein Buffer ist meist gleich 50 oder mehr Messungen dick (je Kanal), das
spart dann schon viele Interrupts.
Für "Dringende" Mesungen verwende ich den ADC2 oder ADC3 und lasse den
einfach immer wandeln und hole die Werte so wie ich brauche, ohne DMA.
Beachte, es benötigt den DMA Kanal 1, steht auch so in der Doku.
Hallo,
ich hab das jetzt implementiert. Das ist ja genial! Der DMA haut das
alles schön ordentlich in mein Array von Messwerten rein. Perfekt!
Besser könnte es gar nicht sein :-) und es ist wirklich einfach.
Ich habe mir jetzt sogar eine Implementierung von memcpy gebaut, die den
DMA verwendet. Damit wird das alles sauschnell und ohne einen einzigen
wertvollen CPU Takt zu verbraten ;-)
Danke für den Tip mit dem DMA. Sonst hätte ich nämlich noch stundenlang
an der Interruptlösung herumgedoktert ;-)
Hallo Markus,
hab doch noch mal eine Frage :-)
Ich habe den ADC so eingestellt, dass er mir 5 Kanäle periodisch misst
(SCAN=1, CONT=1) und per DMA in ein Array der Grösse 50 von short ints
transferiert. Jeder Messwert ist somit 10x vorhanden.
Jetzt möchte ich, so wie du das angedeutet hast, einen Mittelwert über
die letzten 10 Messungen bilden. Das ist ja einfach, denn in dem Array
gehört immer jeder zehnte Messwert dazu ;-) Nur frage ich mich, ob es
eine gute Idee ist, einfach "blind" auf das Array zuzugreifen. Kann es
dadurch bestimmt nicht passieren, dass ich inkonsistente Daten erhalte?
Was passiert, wenn ich im Array gerade einen Messwert auslese, auf den
der DMA gerade schreibend zugreift? Kriege ich immer den "alten", oder
den "neuen" Messweert, oder kann es sein, dass ich ein zufälliges
Gemisch erhalte von "alt" & "neu" ?
Gruss
Nein, das passiert eigentlich nicht.
Wenn man im DMA Interrupt eine Schleife ab dem ersten laufen lässt, dann
wird innerhalb weniger ns (Prozessor-Takte) schon der erste Messwert für
die Summe berechnet.
Wenn der dann beim 50sten angekommen ist, dann kann es schon sein dass
die nächste AD Wandlung fertig ist, ist aber auch egal, da der erste
schon berechnet wurde.
Messe doch einfach wie viele Prozessor-Takte die Routine benötigt und
vergleiche das mit der AD Einstellung, wie schnell die wandelt.
Dann weißt Du das genau und braucht nicht raten oder vermuten.
Tipp:
1
// Sys-Tick Counter - Messen der Anzahl der Befehle des Prozessors:
Hmm, das verstehe ich jetzt nicht ganz.
Den DMA Interrupt benutze ich auch gar nicht, sondern ich lasse den DMA
schön brav im Hintergrund zyklisch alle Messwerte ins Array speichern,
und dann kann ich je nach Bedarf die Messwerte da raus holen. Oder habe
ich jetzt etwas nicht verstanden? :-/
Ja, geht natürlich auch.
Exakt zur gleichen Zeit eine Speicherstelle zu beschreiben (DMA) und zu
lesen (CPU) wird ohnehin nicht gehen. Das wird sich gegenseitig
verriegeln, bzw. der DMA / CPU wird dann ein Wait ausführen müssen.
Entweder greift der DMA in dem Takt auf das SRAM zu oder die CPU aber
beides gleichzeitig wird nicht gehen.
Aus dem Grund hat der STM32F4xx 3 verschiedene integrierte SRAM Bänke,
so dass die sich gegenseitig weniger blockieren.
Das heisst also, wenn ich im dümmsten Fall grade denjenigen ADC-Messwert
auslesen will, den der DMA gerade beschreibt, ist sicher gestellt, dass
ich kein Datenchaos bekomme? Ob ich jetzt einen topaktuellen oder einen
etwas älteren Messwert kriege ist nicht so wichtig. Aber ich hatte zu
Beginn eben befürchtet, dass es vorkommen könnte, dass die CPU z.B. das
Highbyte schon ausgelesen hat, und der DMA dann einen neuen Messwert
hinschreibt, und die CU liest dann im nächsten Takt das Lowbyte des
neuen Messwerts - das wäre natürlich sehr gruselig, denn dann hätte ich
ja ein Datengemisch! Aber wenn du sagst, dass es sich verriegelt, dann
ist ja alles gut :-)
über dies hätte ich es bis jetzt auch noch nicht gesehen, dass der Fall
eingetreten wäre, meine Testsoftware werkelt schon einige Zeit vor sich
hin. Aber ich wollte jetzt doch noch auf Nummer sicher gehen :-)
Da das DMA vom ADC wohl 16-Bit Worte schreibt, kann es vorkommen, dass
ein 32-Bit Ladebefehl ein altes und ein neues 16-Bit Wort liest. Es kann
aber bei 16- oder 32-Bit Ladebefehlen nicht vorkommen, dass ein 16-Bit
Wert bitweise gemischt ist. Dies kann jedoch sehr wohl geschehen, wenn
du den Speicher byteweise ausliest.
Bei manchen Controllern ist jedoch bei solchen Zugriffen darauf zu
achten, welche Kopie des SRAMs für den Zugriff verwendet wird, weil
sonst Pufferung und Caching relevant werden. Die STM32 gehören m.W.
nicht dazu, aber beispielsweise STR9.
In der umgekehrten Richtung (CPU => Speicher => DMA) kann es vmtl. auch
beim STM32 dazu kommen, dass ein frisch nach einem Store ins SRAM
gestarteter DMA alte Daten im RAM sieht, wenn dazwischen nicht ein
entsprechender Barrier-Befehl für Ordnung sorgt.
http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0489c/CIHGHHIE.html
Ok, grad mal nachgesehen, bei CM3 passiert vermutlich noch nichts
unerwartetes, weil Puffer nicht tief genug.
Generell gilt es aber, bei komplexen Architekturen und DMA das memory
ordering model der Adressbereiche im Auge zu behalten. Die CM3 scheinen
aber simpel genug zu sein, um hier wenig Überraschungen zu bieten. Nur,
wie gesagt, bei den STR9 sieht das schon deutlich anders aus.
Ah, Stichwort "Barrier Befehl" ! kann ich da etwas mit der DMB
Instruktion machen? Data memory barrier.
Mein Array von messwerten habe ich als unsigned short int gekennzeichnet
und greife auch immer nur so darauf zu. Ich gehe mal davon aus, dass der
Compiler dann selbst dafür schaut, dass die Zugriffe 16bit breit
erfolgen. Auf jedenfall lese ich das Array sicher nicht byteweise aus
:-)
Ja, 16 Bit breite ist nur ein Maschinenbefehl und somit braucht man sich
keine Gedanken zu machen.
Mit einem 8-Bitter (AVR) hätte man schon Probleme.
Ich berechne die Werte-Reihe immer im DMA-Interrupt und schreibe das
Ergebnis in eine Variable. Das Programm (Main-Loop) nimmt dann nur noch
diese fertige Variable.
Damit hatte ich noch nie ein Problem und der aktuelle Wert steht immer
bereit in der Variable.
carl schrieb:> Mein Array von messwerten habe ich als unsigned short int gekennzeichnet
Wenn du unsigned 16bit meinst schreib das doch auch so hin: uint16_t.
Ist deutlich klarer was gemeint ist und bei einigen Datentypen kann man
auch nicht so ohne weiteres wissen wie groß sie auf verschiedenen
Architekturen sind, die uint*_t / int*_t Schreibweise sorgt da für
Klarheit und Portabilität.