Hallo
Ich hantiere nun schon ein paar Mal damit herum, aber irgendwie schaffe
ich es nicht, dass meine Interrupts richtig funktionieren.
Ich habe vor die Digits per Multiplex anzusteuern.
Dazu habe ich mir überlegt.
Jede 500us wird der Systick Interrupt aufgerufen und der digitCounter
inkrementiert. Die Periode kann ich ja noch anpassen.
Und jede Sekunde wird der Interrupt Timer 6 aufgerufen, der den
digitValue dekrementiert.
Ich habe 5 Digits und der digitValue startet bei 99999. Die Daten werden
bei jedem Aufruf der Systick Interrupts gesendet und die Digits
geswitched.
Jetzt habe ich 2 Probleme:
1. Mein 1s-Takt Interrupt kommt irgendwie öfters als der Systick
Interrupt.
Eigentlich sollte es ja genau umgekehrt sein. Im Debugger zählt der
digitValue nämlich schneller ab als der andere digitCounter hoch.
2. Habe ich das Problem, dass immer im Anschluss an den Systick
Interrupt den Counter hochzählen und gleich die die Daten senden und die
Digits switchen muss. Das heisst also, dass ich die Funktion
switchDigit(digitCounter, digitValue, digitCode); in der der Switch-Case
enthalten ist, ausführen muss.
Aber für den Interrupt ist diese Funktionsausführung zeitlich sicher zu
lange.
Wie kann ich das irgendwie besser machen?
Mir fällt hierzu leider nichts Besseres ein.
Ich bedanke mich jetzt schon für eure Hilfestellung.
PS: Init Timer 6 ist im tim6.c, für den Systick im delay.c.
Die Funktion die die Daten sendet und digits switchet im digitcommands
und in definitions sind die Definitionen. Im it.c stecken die
Interrupts.
Ohne jetzt den Code angesehen zu haben... Die Inebtriebnahme von
Interrups ist nicht trivial. Ich mach das ueblicherweise so.
1. Interruptprozedur deklarieren, und das Trivialste machen lassen,
bedeutet
1. die Quelle befriedigen, zB Zuruecksetzen
2. einen Pin toggeln
3. interrupt verlassen
2. Interrupt vor dem loop() initialisieren
3. Interrupt vor der loop() enablen
4. main() schreiben, etwas wie
main() {
init();
while (1) {
togglepin();
delay()
}
}
4. alle interrupt enablen
mit dem ozilloskop die beiden toggel pins nachmessen.
Sebastian Kaulitz schrieb:> Aber für den Interrupt ist diese Funktionsausführung zeitlich sicher zu> lange.
Es gibt auch keinerlei Grund, im Interrupt mehr als nötig zu machen.
Sobald ein neuer Wert vorliegt, berechnet das Main die neuen Digits und
legt sie in einem Ausgabearray ab. Möglichst nicht schneller als 2..5/s,
damit es ergonomisch ist (nicht flackert).
Der MUX-Interupt nimmt dann nur das Digit aus dem Array und schaltet ein
Digit weiter.
Damit kein Ghosting auftritt, schaltet er erst alle Digittreiber aus,
gibt das neue Digit aus und schaltet den dazu gehörenden Digittreiber
ein.
Im Moment laufen bei mir alle Interrupts durchgehend. Das ist vielleicht
in diesem Fall doch nicht ganz geschickt.
Vielen Dank also für die Hinweise. Ich werde das mal versuchen so zu
implementieren wie ihr es vorschlägt.
Peter D. schrieb:> Sebastian Kaulitz schrieb:> Sobald ein neuer Wert vorliegt, berechnet das Main die neuen Digits und> legt sie in einem Ausgabearray ab. Möglichst nicht schneller als 2..5/s,> damit es ergonomisch ist (nicht flackert).
Könntest du mir kurz noch erklären, was du mit 2...5/s meinst?
2..5mal pro Sekunde?
Ich habe leider noch nicht verstanden, wann ich die Berechnung im Main
durchführen soll. Die Berechnung sollte ja nur durchgeführt werden, wenn
sich auch der Wert geändert hat, sprich der TIM6 Interrupt aktiv war und
der Counter um 1 dekrementiert wurde.
Wenn die Main das aber in fixen Zeitabständen immer berechnen soll,
heisst das ja, dass ich ein Delay einbauen muss, was ja schlecht ist.
Oder meinst du eher, dass ich in der while eine if-Anweisung einbauen
und überprüfen muss ob sich das digitValue geändert hat?
Sebastian Kaulitz schrieb:> Könntest du mir kurz noch erklären, was du mit 2...5/s meinst?
2..5 Werte/s kann der Mensch bequem ablesen. Daher haben auch viele
Meßgeräte diese Wandlerrate. Man sollte also nicht schneller neue Werte
anzeigen.
Hallo zusammen
Ich habe den Code nun fertig gebracht und funktioniert auch recht gut.
Im Systick Handler behandle ich das Multiplexen und schalte alle 1ms
weiter.
Nun würde ich das gerne noch weiter auflösen können.
Wenn ich bei der SystemClock_Config nun den SysTickDevider von 1000 auf
1000000 ändere, sprich 1us, dann funktionert mein Programm nicht mehr..
Das mit den Teilern ist von ST selbst irgendwie definiert.
Warum funktioniert mit dem Wert 1000000 das Programm nicht mehr? Ist die
Anweisung im Interrupt dann zu lange und der nächste steht bereits an?
Mir geht es dabei nur um das Verständnis, warum das Eine funktioniert
und das Andere nicht.
1
voidSysTick_Handler(void)
2
{
3
TIM6->DIER&=~TIM_DIER_UIE;
4
5
/* USER CODE BEGIN SysTick_IRQn 0 */
6
if(counter>=SYSTICK_MULTIPLICATOR)
7
{
8
counter=0;
9
digitCounter=((++digitCounter)%NUMBEROFMUXMEMBERS);// increments first digitCounter and calculates modulo
10
// e.g digitCounter = 4, then digitCounter will be 0
Sebastian Kaulitz schrieb:> Ist die> Anweisung im Interrupt dann zu lange und der nächste steht bereits an?
Sehr warscheinlich. Schau mal ins Assemblerlisting, wieviel Code erzeugt
wird.
Z.B.:
Sebastian Kaulitz schrieb:> digitCounter= ((++digitCounter) % NUMBEROFMUXMEMBERS);
Hat Dein MC eine Hardwaredivision?
Sparsamer ist ein Vergleich mit dem Endwert und auf 0 setzen.
Sebastian Kaulitz schrieb:> digitCounter= ((++digitCounter) % NUMBEROFMUXMEMBERS);
Dieser Code ist außerdem unbestimmt. Er enthält 2 Zuweisungen auf die
selbe Variable ohne Sequence Point. Stell mal den Warnlevel des
Compilers so ein, daß er bei sowas warnt.
Peter D. schrieb:> Hier mal ein einfaches MUX-Beispiel für den AVR:#include> <avr/io.h>>> #define DIGITPORT PORTB> #define SEGMENTPORT PORTC> #define DIGITNUM 8 // 2 .. 8>> uint8_t muxram[DIGITNUM];>> uint8_t *muxptr = muxram;>> void muxer( void )> {> SEGMENTPORT = 0xFF; // segments off (low active)> DIGITPORT >>= 1; // next digit on> if( DIGITPORT == 0 ){> DIGITPORT = 1<<DIGITNUM-1;> muxptr = muxram;> }> SEGMENTPORT = *muxptr++; // next segment pattern on> }
Ich muss denmach also noch ein wenig optimieren. Danke, das werde ich
gleich mal umsetzen.
> Sebastian Kaulitz schrieb:>> digitCounter= ((++digitCounter) % NUMBEROFMUXMEMBERS);>> Dieser Code ist außerdem unbestimmt. Er enthält 2 Zuweisungen auf die> selbe Variable ohne Sequence Point. Stell mal den Warnlevel des> Compilers so ein, daß er bei sowas warnt.
Ich verstehe nicht ganz was du meinst hiermit.
Könntest du genauer erklären, was mein Fehler ist? Wieso 2 Zuweisungen?
Εrnst B. schrieb:> Sebastian Kaulitz schrieb:>> Wieso 2 Zuweisungen>> eine mit "digitCounter=" und eine mit "++digitCounter".>> Schreib halt> digitCounter= ((digitCounter + 1) % NUMBEROFMUXMEMBERS);>> Dann ist's nur noch eine Zuweisung.>> oder z.B. if (++digitCounter==NUMBEROFMUXMEMBERS)> digitcounter=0;
Danke
Und warum genau darf man so etwas in C nicht machen?
Sebastian Kaulitz schrieb:> Und warum genau darf man so etwas in C nicht machen?
Weil der C-Compiler die Freiheit hat, die Zuweisungen bis zum nächsten
Sequence Point so umzusortieren, wie es ihm passt.
Das muss nicht von links-nach-rechts oder von oben-nach-unten sein.
d.H. der Compiler darf in der Theorie aus deiner Zeile Maschinencode
erzeugen, bei der Werte > NUMBEROFMUXMEMBERS in digitcounter stehen.
Ich denke zwar nicht, dass es einen Compiler gibt, der das machen würde,
aber trotzdem ist es schlechter Stil sich da auf Implementierungsdetails
zu verlassen anstatt gleich laut Standard eindeutig definierten und
damit richtigen Code zu schreiben.
Der GCC warnt, wenn mit "-Wall" oder "-Wsequence-point" aktiviert, vor
solchen Konstrukten.
Εrnst B. schrieb:> Sebastian Kaulitz schrieb:> Das muss nicht von links-nach-rechts oder von oben-nach-unten sein.
Entschuldige, dass ich so frage, aber irgendwie kann ich mir das nicht
vorstellen. Der Compiler kann ja den Code bis zum sequence point ; nicht
so umsortieren, dass da zB steht:
digitCounter = (NUMBEROFMUXMEMBERS % (++digitCounter));
Wie digitCounter auf einmal grösser werden kann, kann ich mir auch
irgendwie nicht vorstellen, wenn da steht
digitCounter = ((++digitCounter)%NUMBEROFMUXMEMBERS);
digitCounter mit 0 initialisiert
NUMBEROFMUXMEMBERS = 5
Im Systick Handler beim 1. Mal wird er 1
zählt hinauf bis 4
beim letzten Mal wird ++digitCounter = 5 und 5%NUMBEROFMUXMEMBERS
ergibt 0
Wie also kann da jemals der Wert > 5 werden?
Vielen Dank für die Erklärung
Sebastian Kaulitz schrieb:> Entschuldige, dass ich so frage, aber irgendwie kann ich mir das nicht> vorstellen.
Der Prozessor kann kein C. Daraus wird also ungefähr sowas:
>> digitCounter= ((++digitCounter) % NUMBEROFMUXMEMBERS);
(1) lade register A mit digitalCounter
(2) addiere 1 zum register A
(3) lade register B mit NUMBEROFMUXMEMBERS
(4) dividiere register A mit register B, schreibe ergebnisse nach
register C (wert) und D (rest)
(5) schreibe register A mit register D
(6) schreibe register A nach digitalCounter
(Ganz so blöd sind moderne Compiler natürlich nicht.)
Es ist nicht definiert, in welcher Reihenfolge (2) und (4) passieren,
weil "zwei Schreibzugriffe auf die gleiche Variable ohne Sequenzpunkt"
laut Standard ungültiges Verhalten ist.
Theoretisch darf der Compiler an der Stelle auch einen Absturz
produzieren, deinen Controller auf Koreanisch umstellen, deinen
Schreibtisch grün malen oder den gesamten Code wegschmeißen. Oder "-1"
(alle Bits gesetzt) in digitCounter schreiben.
S. R. schrieb:>>> digitCounter= ((++digitCounter) % NUMBEROFMUXMEMBERS);>> (1) lade register A mit digitalCounter> (2) addiere 1 zum register A> (3) lade register B mit NUMBEROFMUXMEMBERS> (4) dividiere register A mit register B, schreibe ergebnisse nach> register C (wert) und D (rest)> (5) schreibe register A mit register D> (6) schreibe register A nach digitalCounter>> (Ganz so blöd sind moderne Compiler natürlich nicht.)>> Es ist nicht definiert, in welcher Reihenfolge (2) und (4) passieren,> weil "zwei Schreibzugriffe auf die gleiche Variable ohne Sequenzpunkt"> laut Standard ungültiges Verhalten ist.
Danke
Sebastian Kaulitz schrieb:> Ich muss denmach also noch ein wenig optimieren.
Ich versuche, möglichst nicht einfach drauflos zu programmieren, sondern
erstmal zu überlegen, worin sich die einzelnen Aktionen unterscheiden.
Und beim Multiplexen bietet sich das Schieben für die Digitauswahl
geradezu an, wenn die Digittreiber auf einem Port liegen.
If/else-Ketten oder switch/case nehme ich nur, wenn sich keine
gemeinsame Regel finden läßt.
Eine gemeinsame Regel hat auch den Vorteil, daß Fehler nur an einer
Stelle auftreten und nicht in vielen Copy&Paste Sequenzen. D.h. Fehler
müssen nur an einer Stelle behoben werden.
Auch kann sie einfach erweitert werden, ohne daß man Copy&Paste Fehler
hinzufügt.
Ich habe die Anweisungen mal im Systick Handler gemessen.
Mit dem Oszilloskop messe ich ca. 68us.
Damit ist klar, warum die Division des Systick Interrupts mit dem Teiler
1.000.000 für ein 1us Intervall nicht funktioniert.
Danke euch allen für die Unterstützung.