Hallo STM Experten!
Entweder habe ich nach den falschen Schlüsselwörtern gesucht oder ich
stehe ein bisschen auf dem Schlauch...
Mein Vorhaben:
Ich will bei einem STM8S mittels TIM4 Interrupt einen Counter
inkrementieren. Soweit so fein. Ich hab jetzt gefühlt schon 100
Beispiele aus dem Netz durchgelesen und versucht zu verstehen wie dieser
Vorgang funktioniert. Eines vorneweg, Ich verwende die STDLib und IAR EW
for STM8 und einen STM8003f3. Es geht mir hier rein ums Verständnis.
Meine Überlegung:
Wenn ich den Clock und den Timer konfiguriert habe, dann generiert mir
der TIM4 bei jedem Überlauf einen Interrupt. Die Preisfrage die sich mir
stellt ist. Wie kann ich diesen Interrupt "abgreifen"? Es gibt Beispiele
im Netz bei denen einfach der TIM4 Counter im main.c verwendet wird und
per if-Bedingung überprüft wird ob eine gewisse Anzahl an Counts
abgelaufen ist. Das ist aber nicht Sinn und Zweck der Veranstaltung...
(meiner Meinung nach). Dann gibts noch Beispiele wo im stm8s_it.c in der
Methode vom Interrupt Handler Codezeilen hinzugefügt werden, zb um
Ausgänge zu ändern...
Und zu guter Letzt finde ich noch Beispiele wo mit #pragma vector =
HEX-Wert gearbeitet wird.
TIM4_OVR_UIF dürfte das Bit(oder Konstrukt) sein dass beim Overflow den
Interrupt sozusagen auslöst. Aber ganz blicke ich da nicht durch.
Vielleicht hab ich auch nur etwas übersehen.
Vielleicht könnt ihr mir erklären wann welche Vorgehensweise zu wählen
ist, welche Vorteile/Nachteile diese drei haben und wie der Interrupt
funktioniert.
Anbei drei exemplarische Beispiele anhand derer ich versucht habe mir
das ganze herzuleiten...
https://programmer.group/stm8s-timer-basic-interrupt-timing.htmlhttp://www.emcu.it/STM8/STM8-Discovery/Tim4/TIM4.htmlhttp://embedded-lab.com/blog/starting-stm8-microcontrollers/18/
Ich hoffe ihr könnt mir den entscheidenden Puzzlestein liefern, Danke
schon mal!
Ohne den stm8 zu kennen
MB schrieb:> Wenn ich den Clock und den Timer konfiguriert habe, dann generiert mir> der TIM4 bei jedem Überlauf einen Interrupt. Die Preisfrage die sich mir> stellt ist. Wie kann ich diesen Interrupt "abgreifen"? Es gibt Beispiele
Vielleicht solltest du dich noch einmal mit dem Grundprinzip "Interrupt"
beschäftigen, da du es scheinbar noch nicht ganz verstanden hast. Beim
überfliegen der links wird es dort ja scheinbar ganz gut erklärt.
Datenblatt lesen hilft da ungemein.
Der Interrupt wird nicht abgegriffen und es gibt auch keinen
Konstruktor. Bei aktivierten Interrupts wird das Hauptprogramm bzw. die
Main Funktion unterbrochen und die ISR (interrupt service routine)
wird ausgeführt.
:
> im Netz bei denen einfach der TIM4 Counter im main.c verwendet wird und> per if-Bedingung überprüft wird ob eine gewisse Anzahl an Counts> abgelaufen ist. Das ist aber nicht Sinn und Zweck der Veranstaltung...> (meiner Meinung nach).
Das macht dann Sinn, wenn man interrupts vermeiden möchte oder einfach
nicht benötigt. Beispiel (sehr rudimentär): es soll ein einfacher
Scheduler implementiert werden, der Tasks zyklisch alle 1ms, 5ms und
10ms aufruft. Hier benötigt man keine interrupts. Das timer register
wird in einer Schleife gepollt und bei entsprechendem Wert die Task
ausgeführt. Bei erreichen der 10ms wird das timer register zurück
gesetzt. Benötigte interrupts: 0
> Dann gibts noch Beispiele wo im stm8s_it.c in der> Methode vom Interrupt Handler Codezeilen hinzugefügt werden, zb um> Ausgänge zu ändern...
Der ausgeführt Code in der ISR sollte nicht zu groß werden, deine Idee
einen counter in der isr zu inkrementieren und in deiner main
auszuwerten ist richtig. Interrupt flag löschen nicht vergessen.
> Und zu guter Letzt finde ich noch Beispiele wo mit> #pragma vector = HEX-Wert gearbeitet wird.
Wie gesagt, ohne je mit dem stm8 gearbeitet zu haben., schätze ich mal,
hier wird die isr dem Interrupt vector zugewiesen. RTFM.
Gruß
> IAR EW for STM8
Da wird auch Dokumentation mit installiert.
Z.B. der "IAR C/C++ Development Guide".
Der enthaelt auch einen Abschnitt:
"Primitives for interrupts, concurrency, and OS-related programming"
Den musst du lesen, und nicht
> 100 Beispiele aus dem Netz.
Wenn der Controller nichts weiter zu tun hat, kann man natuerlich
> per if-Bedingung
den Timer auswerten. In diesem Fall reicht das als "Sinn und Zweck".
Tu dir einen Gefallen und benutze fuers Kennenlernen
etwas groesseres als den STM8003f3. Z>B. den STM8
der auf dem STM8-Discovery sitzt. Das hat auch gleich
einen STM8 tauglichen ST-Link dabei.
Ich benutze hier zur Entwicklung z.B. einen STM8S208RBT6.
Larry schrieb:> Tu dir einen Gefallen und benutze fuers Kennenlernen> etwas groesseres als den STM8003f3
Käse, gerade kleine controller sind zum Einstieg besser als große IMHO,
oder ist der STM8003F3 so eine Gurke, a la PIC10, ohne on-chip debugger?
(nicht on-board debugger)
Larry schrieb:> Da wird auch Dokumentation mit installiert.> Z.B. der "IAR C/C++ Development Guide".
Das, und das Datenblatt bzw ANs des Chips/Herstellers.
Ganz Klar schrieb:> Larry schrieb:>> Da wird auch Dokumentation mit installiert.>> Z.B. der "IAR C/C++ Development Guide".>> Das, und das Datenblatt bzw ANs des Chips/Herstellers.
*lesen
Etwas unglücklich formuliert.
Eines vorneweg, mir ist klar was ein Interrupt macht, nur nicht wie er
"genau" funktioniert. Dass der Interrupt das laufende Programm
unterbricht(weswegen er auch Interrupt heisst) ist bekannt, die
Datenblätter und sonstige RefMans hab ich auch gelesen... auch mehrmals.
Ganz Klar schrieb:> Der Interrupt wird nicht abgegriffen und es gibt auch keinen> Konstruktor. Bei aktivierten Interrupts wird das Hauptprogramm bzw. die> Main Funktion unterbrochen und die ISR (interrupt service routine)> wird ausgeführt.
Das ist der fehlende Teil der sich mir, warum auch immer nicht
erschlossen hat.
Das pollen will ich mir ersparen, denn dass funktioniert doch nur
solange richtig bis das Timer-Counter Intervall kürzer wird als ein
gesamter Programmdurchlauf... Das läuft dann als "Hardware-Schleife" dh.
nicht eine Methode zählt rauf sondern die Hardware. Macht der Timer
jedoch in der Zeit eines Programmdurchlaufs mehr als einen Count kann es
passieren dass man beim pollen ein paar Counts über Ziel hinaushaut.
Ganz Klar schrieb:>Der ausgeführt Code in der ISR sollte nicht zu groß werden, deine Idee>einen counter in der isr zu inkrementieren und in deiner main>auszuwerten ist richtig. Interrupt flag löschen nicht vergessen.
Soweit war meine Überlegung also richtig.
Larry schrieb:> Da wird auch Dokumentation mit installiert.> Z.B. der "IAR C/C++ Development Guide".> Der enthaelt auch einen Abschnitt:> "Primitives for interrupts, concurrency, and OS-related programming"...
Asche auf mein Haupt, DIESE Hilfe hab ich tatsächlich noch nicht
gelesen...
Auszug:
1
#pragma vector = UART1_R_RXNE_vector /* Symbol from I/O */
2
__interruptvoidMyInterruptRoutine(void)
3
{
4
/* Do something */
5
}
Nur damit ich das jetzt richtig verstanden habe... Mit dem "__interrupt"
definiere ich eine sogenannte "Interrupt-Funktion", dann kommt das
verpflichtende void für den Rückgabewert, dann kommt der Funktionsname.
und zum Schluss der Anweisungsblock... so wie bei jeder anderen
Methode/Funktion auch.
um zb den Interrupt des TIM4 zu verwenden im main.c :
1
...
2
#pragma vector = 0x19 //TIM4 Update/Overflow
3
__interruptvoidTIM4OVR(void)
4
{
5
count++
6
if(count>3)
7
{
8
count=0;
9
action=true;
10
}
11
TIM4_ClearITPendingBit(TIM4_IT_UPDATE);
12
}
13
...
muss ich den #pragma vector immer direkt vor der __interrupt Funktion
schreiben damit er weiss bei welchem Interrupt ich welche
Interruptfunktion ausführen will? Weil der Name der Funktion, also das
TIM4OVR kann ich mir ja aussuchen oder?
Danke für die prompte Hilfe!
MB schrieb:> #pragma vector = 0x19 //TIM4 Update/Overflow
Schreib wie in dem Beispiel den Namen hin und kein magische Nummer. Dann
brauchst Du auch den Kommentar nicht.
Bei einem anderen Derivat kann nämlich die Nummer eine andere sein.
Der STM8003F3 ist m.W. einer der Kandidaten, aus dem man sich
bei "ungeschickter" Programmierung ganz aussperren kann.
Verifizieren darf der TO die Aussage gerne selber.
> gerade kleine controller sind zum Einstieg besser
Beim STM8S sind die Funktionseinheiten immer die selben.
Nur haben manche STM8 eben nicht alle oder weniger davon.
Im Sinne der Komplexitaet gibt es also kein klein oder gross.
Ein STM8S208RBT6 hat ein Superset all dieser Einheiten.
D.h. alles was im Referenzmanual zum STM8S steht, kann man
mit ihm ausprobieren. Die Art und Weise wie die Peripherie
angesprochen wird, ist auch innerhalb der Serie gleich,
und daher nicht schwieriger oder leichter.
Zum Kennenlernen ist daher der abgespeckte STM8003F3
denkbar ungeeignet.
MB schrieb:> muss ich den #pragma vector immer direkt vor der __interrupt Funktion> schreiben damit er weiss bei welchem Interrupt ich welche> Interruptfunktion ausführen will?
Das steht in der Dokumentation des Compilers. Was glaubst du, warum dir
alle immer wieder sagen, daß du die lesen sollst?
Interrupts sind kein Teil des C-Standards. Deswegen kann das jeder
C-Compiler anders machen. Und macht es auch. Für den STM8 gibt es
Compiler mindestends von Keil (IAR), von Cosmic und den SDCC. Jeder
macht das mit den Interrupts ein bißchen anders.
> Weil der Name der Funktion, also das> TIM4OVR kann ich mir ja aussuchen oder?
Bei deinem Compiler anscheinend ja. Andere Compiler verwenden einfach
"magische" Namen, an denen der Compiler erkennt, an welchen Vektor er
die Funktion anbinden muß.
MB schrieb:> Das ist der fehlende Teil der sich mir, warum auch immer nicht> erschlossen hat.
Na hat es denn jetzt klick gemacht oder muss es noch weiter erklärt
werden?
MB schrieb:> Das pollen will ich mir ersparen, denn dass funktioniert doch nur> solange richtig bis das Timer-Counter Intervall kürzer wird als ein> gesamter Programmdurchlauf... Das läuft dann als "Hardware-Schleife" dh.> nicht eine Methode zählt rauf sondern die Hardware. Macht der Timer> jedoch in der Zeit eines Programmdurchlaufs mehr als einen Count kann es> passieren dass man beim pollen ein paar Counts über Ziel hinaushaut.
Es kommt ganz auf den Anwendungsfall und die benötigte Genauigkeit an,
gleiches gilt für die Timer Auflösung (ticks per us/ms/s). Interrupts
können auch problematisch werden.
MB schrieb:> muss ich den #pragma vector immer direkt vor der __interrupt Funktion> schreiben damit er weiss bei welchem Interrupt ich welche> Interruptfunktion ausführen will?
Das hängt vom Compiler ab, ich vermute sehr stark: ja.
RTFM!
> Weil der Name der Funktion, also das> TIM4OVR kann ich mir ja aussuchen oder?
RTFM! Und wenn dem so ist, sollte man wie immer "meaningful names'
vergeben.
Larry schrieb:> Ein STM8S208RBT6 hat ein Superset all dieser Einheiten.
So habe ich mir es schon gedacht, war aber schlicht weg zu faul es
herauszufinden.
Sodala...
Nach einem ausgiebigem Mittagessen und nochmaligem Studium der
IAR-eigenen Hilfeseiten habe ich auch noch die Hilfeseite zu den
"Pragma-Directives" gefunden.
Konklusio: Nach der Pragma-Directive folgt immer eine Interrupt- oder
Trap-Funktionsdeklaration.
UND
Beim nächsten mal, noch länger die Dokumentation(en) durchlesen.
Ob ich bei den kryptischen Zahlen werde, die man direkt aus dem
Datenblatt des jeweiligen µC entnehmen kann, oder ob die
Aussagekräftigeren Namen(zu finden im stm8s_it.c) zur anwendung kommen
weiss ich noch nicht. Jedenfalls hat sich ein relativ großes
Fragezeichen in Wohlgefallen aufgelöst.
Danke an alle!
> kryptischen Zahlen werde, die man direkt aus dem> Datenblatt des jeweiligen µC entnehmen kann
Da hast du wohl noch nicht genug gelesen.
Lies mal weiter. Lesen bildet!
MB schrieb:> Ob ich bei den kryptischen Zahlen werde, die man direkt aus dem> Datenblatt des jeweiligen µC entnehmen kann
Die Vectornamen lassen sich auch sehr leicht aus dem Datenblatt
herleiten.
Larry schrieb:> Beim STM8S sind die Funktionseinheiten immer die selben.> Nur haben manche STM8 eben nicht alle oder weniger davon.> Im Sinne der Komplexitaet gibt es also kein klein oder gross.> Ein STM8S208RBT6 hat ein Superset all dieser Einheiten.> D.h. alles was im Referenzmanual zum STM8S steht, kann man> mit ihm ausprobieren. Die Art und Weise wie die Peripherie> angesprochen wird, ist auch innerhalb der Serie gleich,> und daher nicht schwieriger oder leichter.
Das stimmt so leider nicht:
es gibt Varianten der verschiedenen Funktionseinheiten mit verschiedenen
Leistungsmerkmalen. Einige Leistungsmerkmale gibt es nur bei RM0016 "Low
density devices" wie STM8S003 oder STM8S903. So hat z.B. der ADC1 in Low
Density hat einen "Buffered continuous conversion mode", diesen gibt es
beim ADC2 des STM8S208RB nicht.
Bei den UART-Varianten gibt es noch mehr Unterschiede (UART1..4)
(STM8S208RB hat UART1 und UART3, STM8S105K6 hat einen UART2). Nicht
immer kann man diesen Nummern wirklich trauen (teilweise Zweitmarkt für
Automotive-Produkte).
Weitere Unterschiede ergeben sich im Speicherzugriff (Read-While-Write):
Testen von EEPROM-Schreiben mit High Density dürfte zu Überraschungen
bei Low Density führen.
Es ist übrigens spannend, wie hier Komplexität mit Kompliziertheit
verwechselt wird. Leider ist das Thema nicht nur kompliziert. Der
Umstand, dass dies nicht leicht zu sehen ist, liegt an der Komplexität.
> Zum Kennenlernen ist daher der abgespeckte STM8003F3> denkbar ungeeignet.
Auch da kann ich nicht zustimmen. Ein STM8S103F3P6 kann ohne weiteres
auf einem Steckbrett getestet werden.
Das geht beim großen High Density Bruder in der Variante STM8S207K8T6C
zwar auch, aber die ist letztlich ebenfalls abgespeckt (32pin), ist bei
weitem nicht so leicht zu bekommen und muss erst auf einen
LQFP32-Adapter gelötet werden.
Den STM8S103F3P6 auf einer blauen Leiterkarte gibt's dagegen für 80ct
fertig aus China (oder für 30ct wenn man ein wenig löten möchte).
Hi
Na ja, so wirklich aufschlußreich fand ich eure Erklärungen zu
Interrupts nicht. Daher hab ich mich mal entschlossen, solche Begriffe
wie Interrupt Service Routine (ISR) und Interrupt Vektor Tabelle (IVT)
nochmal zu erläutern. Richtig ist, das ein Hauptprogramm gar nichts vor
der ISR weiß und diese auch nicht aufruft. Es ist die interne Struktur
eines Controllers oder Prozessors, die bei einem Interrupt dem
Programmcounter nach Ablegen der Rücksprungadresse eine fest verdrahtete
Adresse im Programmspeicher zuweist. Sozusagen eine
Hardwareadressierung. Da diese Speicherzelle nur für einen Befehl Platz
hat, ist es ziemlich übersichtlich. Nur, wenn nur ein Befehl dort Platz
hat, wie funktioniert dann ein Interrupt. ganz einfach, entweder steht
dort ein RETI, also ein Return from Interrupt, oder ein Sprungbefehl zu
der ISR. Diese schließt dann mit dem RETI ab. Hier ist genug Platz für
die erforderlichen Sicherungen der verwendeten Register, denn ein
Interrupt kann zu jeder Zeit an einer x-beliebigen Programmstelle
auftreten und da sind dann u. U. die Inhalte der Rechenregister
verloren, wenn sie nicht mit Push und Pop in der ISR gesichert werden.
In meinen Anfängerjahren sprach man oft davon, eine Interruptadresse zu
verbiegen. Nun, ganz einfach, die in der IVT hinterlegte Adresse der
ursprünglichen ISR mit der Adresse des eigenen Programmes versehen und
dieses dann abschließend mit einem Sprung in die ursprüngliche ISR zu
versehen. Bis Win98 war dies auf jedem Windowsrechner ohne Probleme
möglich. So konnte eine Schnittstelle ganz schnell für eigene
Bedürfnisse interruptfähig angepaßt werden.
Aber jetzt noch mal ganz kurz:
Interrupt :
Prorammcounter wird wie beim Aufruf eines Unterprogramms gesichert
Programmcounter erhält eine dem Interrupt zugewiesene Adresse in der IVT
Dort steht entweder ein RETI (keine Bearbeitung) oder ein Sprungbefehl
(ISR)
Rücksprung aus der ISR erfolgt mit einem RETI, (gesicherte Adresse wird
in den Programmcounter eingetragen)
Das sollte immer noch für alle Prozessoren und Controller im Prinzip so
ablaufen.
Gruß oldmax
Beim STM8 steht die Vektortabelle fix im Flash ab 0x008004.
Verbiegen muss man ihn dafuer nicht :-).
Ein VTOR Register wie bei einigen ARMs, um die Adresse dieser
Tabelle festzulegen hat der STM8 leider nicht.
In der Vektortabelle steht die Adresse der ISR und kein
Sprungbefehl.
Statt eines RETI, wird bei vom Programmierer unbelegten Vektoren,
eine Default ISR ausgefuehrt. Das kann auch ein Sprung auf
sich selbst sein. Das sollte der TO bei seiner Compilerversion
noch verifizieren. Die Default ISR ist Bestandteil der
Laufzeitumgebung des Compilers. In den IAR Kickstartversionen
hat man u.U. keinen Zugriff auf die Quellen dieser Umgebung.
Das muss man sich dann im Debugger ansehen wie die Default ISR
programmiert wurde.
Wenn man sich selbst bei den Vektoren vertut, wird diese
Default ISR dann bei einem Interrupt ausgeloest.
Mal so als Ergaenzung zu oldmax hilfreichen Beitrag.
> STM8S003 oder STM8S903
Da hab ich ja Glueck. Die setze ich nicht ein.
Meine kleinsten sind die STM8S105K4T6.
Martin V. schrieb:> Hi> Na ja, so wirklich aufschlußreich fand ich eure Erklärungen zu> Interrupts nicht. Daher hab ich mich mal entschlossen, solche Begriffe> wie Interrupt Service Routine (ISR) und Interrupt Vektor Tabelle (IVT)> nochmal zu erläutern....
Na da ist aber auch Halbwissen und Pauschalisierung im Spiel...
Natürlich passiert das context switching, Sicherung der register, PC,
etc...
Ich hatte auch kurz überlegt, damit anzufangen - hatte auch schon einen
Satz zu RETI getippt, aber auf Grund der Fragestellungen wieder Abstand
genommen.
Erst Doku lesen, dann fragen!
Hi
Ich will darauf noch mal kurz eingehen..
Larry schrieb:> Beim STM8 steht die Vektortabelle fix im Flash ab 0x008004.> Verbiegen muss man ihn dafuer nicht :-).> Ein VTOR Register wie bei einigen ARMs, um die Adresse dieser> Tabelle festzulegen hat der STM8 leider nicht.> In der Vektortabelle steht die Adresse der ISR und kein> Sprungbefehl.
Ok, Die IVT steht wohl in jedem Prozessor / Controller irgendwo und wird
auch nicht verbogen. Irgendwie müssen ja die Adressen hingepackt werden,
die eine eingebaute Hardware bei einem ausgelösten Interrupt in den
Programmcounter pakt, um die ISR abzuarbeiten. Aber, sind schon ISR
eingebaut, ist ein "Verbiegen" der Adresse der ISR auf eine eigene
Ergänzung der vorhandenen ISR durch ein Zwischenschalten hier möglich.
Und der Ausdruck "Spruungbefehl" ist natürlich nicht so wörtlich zu
nehmen, hat sich aber eingebürgert. Ein Unterproramm wird auch nicht
"gecalled" oder zu deutsch aufgerufen. Wenn da eine Adresse steht landet
diese im Programmcounter. Bei einem "Sprung" ohne eine Sicherung auf dem
Stack, bei einem "Call" mit einer Ablage der Rücksprungadresse auf dem
Stack und ein Interrupt ist im Prinzip wie ein "Call" mit ein paar
Besonderheiten. Dir Larry brauch ich es nicht erklären, aber der
Fragesteller hat zwar gesagt, das ihm ISR und IVT irgenwie etwas sagen,
er aber trotzdem nicht weiß, wo er die ISR "abgreifen" kann. Und ich
denke, das meine Erklärung dazu ein wenig hilfreich war. Ach ja, sorry,
das ich Begriffe wie Jmp und Call nutze. Ein C Programm hat da
sicherlich einen anderen Befehlssatz für, aber da hab ich nun keine
Ahnung von....
Gruß oldmax
Martin V. schrieb:> Ein C Programm hat da sicherlich einen anderen Befehlssatz für, aber da> hab ich nun keine Ahnung von....
Aua. Ein C-Programm hat gar keinen Befehlssatz in diesem Sinne. Die tool
chain (compiler, linker assembler) übersetzt das ganze von C "in den
Befehlssatz" der Zielarchitektur. Als C-Entwickler muss man sich eben
nicht mit RETI, context switching und nur bedingt mit IVT herum
schlagen. Das. Macht eben die tool chain mehr oder weniger gut
automatisch (über pragma, Namen oder Linker Skript).