Forum: Mikrocontroller und Digitale Elektronik Interrupt-Vektor umbiegen möglich?


von Micha R. (michaavr)


Lesenswert?

Hallo zusammen,

habe eine Frage:
Kann man in Bascom den Interrupt-Vektor z.B. Timer1-Overflow auf eine 
andere Routine (Label) umbiegen?

Ich möchte von einer Variablen (Mode) gesteuert unterschiedliche 
Konfigurationen machen.

Select Case Mode
   Case 0 : gosub ConfigurationA
   Case 1 : gosub ConfigurationB
End select

Nun möchte ich je nach Konfiguration den Timer1-Overflow Interrupt auf 
unterschiedliche Unterroutinen (Labels) legen.

ConfigurationA:
   On Timer1 ISR_ConfigA
Return

ConfigurationB:
   On Timer1 ISR_ConfigB
Return

Und jetzt habe ich ein Problem.
Bascom 1.11.9.1 meckert beim Kompilieren mit der Fehlermeldung, dass die 
ISR bereits definiert ist.

Der Compoler hat ja recht, aber wie kann ich diese Fehlermeldung 
umgehen?

Kann ich mit Out oder einem anderen Bascom Befehl die Adresse des 
Interrupt-Handlers andern?

Wenn nein, wie kann ich es in einem Assemblerblock

$ASM

$End ASM

anstellen?

Weis jemand bescheid?

Mit LoadLabel kann ich die Adresse der ISR-Routine ermitteln.
Aber wie bekomme ich den Wert in die Vektortabelle Adresse $008?

von Analog (Gast)


Lesenswert?

Mach Dir ne globale Variable und pack eine If-Abfrage in die (eine) ISR. 
Geht bestimmt auch noch anders. Aber so in die Richtung musst Du gehen.

von Micha R. (michaavr)


Lesenswert?

Globale Variable und in der ISR abfragen funktioniert bei unkritischen 
Interrupts.
Meiner muß sehr schnell abgearbeitet werden.
Deshalb möchte ich eine 2. Interrupt-Routine in der Vektor-Tabelle 
eintragen!

Aber wie?

von Peter (Gast)


Lesenswert?

>... sehr schnell abgearbeitet werden.

Dann würde ich aber C anstelle von Basic empfehlen!

von Johnny Maxwell (Gast)


Lesenswert?

> Deshalb möchte ich eine 2. Interrupt-Routine in der Vektor-Tabelle
> eintragen!
>
> Aber wie?

Das geht einfach nicht. Ein Interrupt hat genau eine Interrupt Routine. 
Woher sollte der Prozessor (welcher eigentlich?) wissen welche Routine 
jetzt gerade drankommen soll?

Was z.B. geht:
 - If Abfrage oder Switsch-Case
 - Deine ISR kann die aktuell gewünschte Routine über einen
   Funktionspointer aufrufen, da musst du dann keine IFs abarbeiten.
   (Keine Ahnung ob Bascom Funktionspointer kann)

von Johnny Maxwell (Gast)


Lesenswert?

"Switsch", oh Mann :)

von Michael L. (zvpunry)


Lesenswert?

Hast nichtmal geschrieben um was für eine CPU es sich handelt. Ich nehm 
mal an ein AVR (der Nickname und Bascom).

Mit Bascom kenn ich mich nicht aus, aber nen AVR hat seine 
Interrupt-Vektoren im Flash. Den Flash kann man auch im Betrieb löschen 
und neu schreiben, hab ich aber noch nicht gemacht (kenn ich mich also 
nicht mit aus). Vielleicht gibt es da ja ein Weg... Wenn das geht, dann 
darf man das ca. 10000 mal. ;-)

Wenn es Zeitkritisch ist, warum dann nicht die Interrupt Funktion in 
Assembler oder C und wie bereits geschrieben eine globale Variable 
verwenden.

In C wäre das sowas wie:
1
#define CONFIG_A 1
2
#define CONFIG_B 2
3
volatile static uint8_t configuration;


... irgendwo in der initialisierung / main() ...
1
configuration = CONFIG_A; /* oder CONFIG_B */

und dann die Interrupt-Routine
1
ISR(TIMER1_OVF_vect)
2
{
3
        switch(configuration) {
4
        case CONFIG_A:
5
                /* dies und jenes erledigen */
6
                break;
7
        case CONFIG_B:
8
                /* was völlig anderes erledigen */
9
                break;
10
        default:
11
                /* die schaltung in brand setzen weil
12
                 * der Interrupt mit falscher Konfig
13
                 * gestartet wurde - oder vielleicht
14
                 * was weniger drastisches wie einen
15
                 * fehler signalisieren und anhalten
16
                 * oder neustarten? */
17
        }
18
}

Wie das nun in Bascom umzusetzen ist ist mir völlig unklar, aber
vielleicht kann man in Bascom ja auch assembler und/oder C untermischen.
Auf jeden Fall sollte dir Idee doch nun klarer sein, oder? Wenn ja, dann
kann man das vielleicht auch in Bascom so umsetzen. Wenn es aber so
Zeitkritisch ist daß es auf einzelne Instruktionen ankommt und Bascom 
keine Kontrolle darüber bietet, dann wäre sicher C und/oder Assembler 
eine bessere Wahl.

PS: Ich habe keinerlei Ahnung von Bascom, kann leider kein Passenderes 
Beispiel fabrizieren.

PPS: Ach Mist, während ich hier rumtipsel wurde nu schon X mal switsch 
erwähnt. Und da Johnny Maxwell schon "Switch-Case" gesagt hab glaub ich 
auch Bascom kann sowas, also kann man das Beispiel ja so übernehmen ;-)

von Unbekannter (Gast)


Lesenswert?

> Kann man in Bascom den Interrupt-Vektor z.B. Timer1-Overflow auf
> eine andere Routine (Label) umbiegen?

Ja, das ist generell kein Problem, egal bei welchem Controller und/oder 
Sprache.

Der Trick ist, Du musst den Interrupt-Vektor einfach nur ausreichend 
warm machen, bis er hellrot glühend ist, dann kannst Du ihn problemlos 
verbiegen.

von Michael L. (zvpunry)


Lesenswert?

Unbekannter wrote:
...
> Der Trick ist, Du musst den Interrupt-Vektor einfach nur ausreichend
> warm machen, bis er hellrot glühend ist, dann kannst Du ihn problemlos
> verbiegen.

LOL

Wichtig ist es auch Sand einzufüllen, sonst kann er beim Biegen 
einknicken und das wars dann. ;)

/me hat ein steampunk mikrocontroller vor augen, da ist immer etwas 
klempnerarbeit nötig ;)

von AVRFan (Gast)


Lesenswert?

>Globale Variable und in der ISR abfragen funktioniert bei unkritischen
>Interrupts.
>Meiner muß sehr schnell abgearbeitet werden.

Die Anwendung, bei der eine Switch-Verzweigung innerhalb einer 
Interruptroutine nachweislich zu einem Zeitproblem führt, möchte ich 
aber auch mal sehen.

>Deshalb möchte ich eine 2. Interrupt-Routine in der Vektor-Tabelle
>eintragen! Aber wie?

Nein, das kannst Du vergessen. Der Interruptvektor ist fest im 
Controller verdrahtet.  Beispiel: Der T/C1-Overflow-Interrupt beim 
ATmega8 springt immer auf die Flash-Adresse 9 (siehe Datenblatt).  Das 
kannst Du nicht ändern.  Normalerweise steht in der Interrupttabelle für 
jeden benutzten Interrupt eine "rjmp [Sprungmarke]"-Instruktion.  Dies 
ist der theoretisch frühestmögliche Ansatzpunkt für ein Springen zu 
unterschiedlichen Interruptroutinen.  Mit einem 'ijmp' statt des 'rjmp 
...' wäre das möglich.  Das Sprungziel muss dann in ZH:ZL abgelegt 
werden und ist somit jederzeit zur Laufzeit änderbar.  Ein ijmp braucht 
soviel Taktzyklen wie ein rjmp, nämlich 2.

von Johnny Maxwell (Gast)


Lesenswert?

Michael Löffler schrieb:
> Und da Johnny Maxwell schon "Switch-Case" gesagt hab glaub ich
> auch Bascom kann sowas, also kann man das Beispiel ja so übernehmen ;-)

Ich hab keine Ahnung von Bascom! Aber kann mir kaum vorstellen, dass es 
da kein Switch-Case Äquivalent gibt.

Zur Originalfrage: Switch-Case sollte als jump table kompiliert werden, 
viel schneller kriegt man das auch anders nicht hin. Wenn das schon 
zeitlich nicht ausreicht, was soll dann die eigentliche Funktion noch 
machen?

Ausnahme: Der Interrupt Vektor ist RAM (gibts z.B. beim Cypress FX2). 
Dann kann man den einfach bei Bedarf ändern. Spielt hier aber glaub ich 
keine Rolle, leider wissen wir aber immer noch nicht welcher Prozessor 
gemeint ist.

PS: @Micha R.: Das hier fast niemand Bascom verwendet, sollte dir 
vielleicht auch zu denken geben :)

von Peter D. (peda)


Lesenswert?

AVRFan wrote:
>>Globale Variable und in der ISR abfragen funktioniert bei unkritischen
>>Interrupts.
>>Meiner muß sehr schnell abgearbeitet werden.
>
> Die Anwendung, bei der eine Switch-Verzweigung innerhalb einer
> Interruptroutine nachweislich zu einem Zeitproblem führt, möchte ich
> aber auch mal sehen.

Sehe ich auch so.

Insbesondere die super exakte Zeitangabe "sehr schnell" läßt vermuten, 
daß überhaupt keinerlei Zeitbetrachtungen angestellt wurden.


Warscheinlich klemmts an irgendeiner ganz anderen Stelle und dannn wird 
planlos versucht an irgendnem Schräubchen zu drehen, in der vagen 
Hoffnung, es könnte vielleicht helfen (wird es zu 99,99% aber nicht).


Man muß schon die Ursachen genauer ergründen und hat dann konkrete Werte 
(xx Zyklen oder xx µs), mit denen man auch was anfangen kann.
Und erst dann kann man an der richtige Schraube drehen.


Oftmals ist aber nur eine Umstellung des Programmablaufs die richtige 
Schraube.
Blöd nur, wenn man erst gar keinen Programmablaufplan erstellt hatte.
Dann weiß man aber zumindest die genaue Ursache, warum es klemmt.
Nämlich weil man völlig planlos drauf los programmiert hat.


Peter

von Micha R. (michaavr)


Lesenswert?

Planlos drauflos programmiert ist dies keines wegs.

Hier handelt es sich um einen Prozessor AVR ATmega 8, getaktet mit 
3,686400 MHZ.

Die 3,6864 MHz weil noch eine serielle schnittstelle implementiert ist.

Zur Anwendung:

An Port T1 liegt eine Frequenz an bis zu 1MHz. Die Frequenz soll 
ermittelt werden. Dazu zählt der Timer 1 die ankommenden Flanken.

Timer 2 verwende ich als Zeitbasis (10ms)

Nun verwende ich 2 Interrupts, die Timer1 Overflow und die Timer2 OC2.

' OC2-Interrupt von Timer 2
' wird alle 0,01 Sekunden aufgerufen
ISR_Zeitbasis:
   incr ZeitBasisCount
   select case ModeNr
      case ModeFrequency
         if ZeitBasisCount = ZeitBasisMax then
            ZaehlerLow = Timer1
            Timer1 = 0
            Zaehler = ZaehlerHigh
            Shift Zaehler , Left , 16
            Zaehler = Zaehler or ZaehlerLow
            Flags.0 = 1
            ZaehlerHigh = 0
            ZeitBasisCount = 0
         end if
      case ModePulseCounter
         nop
   End Select
Return

' Überlauf Timer1
ISR_Timer1:
   incr ZaehlerHigh
   'Toggle LEDRed
Return

Wenn ich in die OC2-Interrupt-Routine noch mehr select Case-Fälle 
reinmache, stimmt meine Frequenz nicht mehr (zeigt zu wenig).

Schnelleres Wuarz hilft nicht viel, habe schon 16MHz probiert.

Deshalb war meine Idee, die Select Case-Fälle in einzelne ISR-Routinen 
zu packen und den Interrupt von OC2 auf unterschiedlichen Routinen zu 
legen.

Eine Idee?

von Peter D. (peda)


Lesenswert?

Micha R. wrote:

> Wenn ich in die OC2-Interrupt-Routine noch mehr select Case-Fälle
> reinmache, stimmt meine Frequenz nicht mehr (zeigt zu wenig).

Das ist in der Tat sehr merkwürdig.
Bei 10ms Interruptrate sind ja 36000 Zyklen Zeit zwischen den 
Interrupts.
Derweil müßte ein Interrupthandler eigentlich ganze Romane schreiben 
können.

Schau Dir mal das Assemblerlisting des Interrupthandlers an.


Peter

von Micha R. (michaavr)


Lesenswert?

Welches Assembler-Listing?

Bascom erzeugt ein BIN-File und ein HEX-File. Da stehen nur Zahlen.

Habe im Bascom Simulator mal die Laufzeit geprüft von ISR OC2, wenn 
Bedingung ZeitBasicCount=ZeitBasisMax erfüllt ist.

Bascom zeigt mit bis an die Position Return (ISR OC2) 327 Zyklen an mit 
0,08870 Milli-Sekunken an.

Wenn Bedingung nicht erfüllt ist:

91 Zyclen bei 0,02468 ms

Hilft das weiter?

von Winfried (Gast)


Lesenswert?

Dei Bin- oder Hexfile kannst du durch einen Dissassembler schicken. Aber 
bestimmt wird Bascom auch ein Listfile rausschreiben können, wo der 
erzeugte Assemblercode ausgegeben wird.

von Micha R. (michaavr)


Lesenswert?

Bascom kann das wohl nicht.

Konnte jedenfalls keine einstellbare option im Compiler finden.

von Peter D. (peda)


Lesenswert?

Micha R. wrote:
> Bascom zeigt mit bis an die Position Return (ISR OC2) 327 Zyklen an mit
> 0,08870 Milli-Sekunken an.

327 Zyklen klingt o.k. für Bascom.

Jetzt mußt Du bloß noch rauskriegen, warum das die Messung beeinflußt.

Wenn man die Messung per Interrupt startet und stoppt, sollten sich die 
Ausführungszeiten ja weitgehend kompensieren, bzw. eine konstante 
Differenz kann man vom Zählergebnis abziehen.


Für genaue Frequenzmessung ist es besser, die Eingangsfrequenz auf T0 
und ICP1 zu legen. T1 mißt dann die Zeit für T0 Perioden.


Peter

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.