www.mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik Interrupts am attiny13


Autor: Felix (Gast)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Hallo Allerseits,

ich wuerd mich ueber ein wenig Hilfe zu meinem unten angefuegten Code 
freuen ;]
Ich benutze einen ATTiny13 und compile so:
avr-gcc -g -mmcu=attiny13 -Wall -O2 main13.c -o main13.hex

Da ich keine Delays und dergleichen verwende denke ich, dass
es okay ist, wenn ich die Taktrate dem gcc nicht uebergebe.
Soweit richtig?

Das Programm soll eigentlich mit jeder Flanke an PB1 (da haengt
eine langsame Clock dran) den Ausgang PB4 mal kurz high schalten
und dann gleich wieder low. Es macht allerdings irgendwas voellig
anderes. Ich habe den Resetpin schon ueberprueft, der ist stabil
auf high. In den angefuegten Bildern sieht man die Messung mit
einem Logikanalysator:
Kanal0 - Clock vom Attiny
Kanal1 - langsames Clocksignal an PB1
Kanal2 - PB4
Kanal3 - Resetpin

Das Problem ist also, dass der Interrupt irgendwann ausgeloest
wird, aber nicht dann wenn er soll und merkwuerdigerweise noch
regelmaessig. Alle anderen Pins, die das verursachen koennten
liegen stabil auf high oder low, wo sie auch sein sollen.
Im Code sieht man, dass ich statt INT0 auch mal PCINT benutzt
habe, was zum gleichen Ergebnis fuehrte.

und hier der Code:
#include <avr/io.h>
#include <avr/interrupt.h>

// Interrupthandler - B1
ISR(INT0_vect) {
  PORTB = 0b00010000;
  PORTB = 0b11100000;
}//*/
/*ISR(PCINT0_vect) {
}//*/

int main(void) {
  // Ports konfigurieren
  DDRB = 0b00010101;
  PORTB = 0b00000000;
  PORTB = 0b00010000; // wird die Zeile weggelassen bleibt PB4 immer low
  // Interrupts konfigurieren
  MCUCR &= 0b11111101;
  MCUCR |= 0b00000001;
  GIMSK &= 0b11011111;
  GIMSK |= 0b01000000;
  //GIMSK |= 0b00100000;
  //PCMSK |= 0b00000010;
  // Interrupts an
  sei();

  while(1) {
  }
  return 0;
}

Ich hoffe irgendjemand findet einen haarstraeubenden Fehler im Code,
denn die Hardware scheints irgendwie nicht zu sein. ^^

Autor: g457 (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> MCUCR &= 0b11111101;
> MCUCR |= 0b00000001;
> GIMSK &= 0b11011111;
> GIMSK |= 0b01000000;

..such erst mal raus (und schreibs auch hin :-) was die einzelnen Bits 
bedeuten, das will sich hier keiner freiwillig antun. Dann reden wir 
weiter.

HTH

Autor: Felix (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Haste Recht, hab ich nich dran gedacht ^^

> MCUCR &= 0b11111101;
> MCUCR |= 0b00000001;
Laut Datenblatt ist MCUCR so aufgebaut:
7  6  5   4   3  2   1     0
– PUD SE SM1 SM0 – ISC01 ISC00
was ich da machen will ist ISC01 auf 0 setzen und ISC00 auf 1,
damit ich folgendes Verhalten bekomme:
Any logical change on INT0 generates an interrupt request.

> GIMSK &= 0b11011111;
> GIMSK |= 0b01000000;
in GIMSK interessieren mich nur folgende Bits:
  6   5
INT0 PCIE
PCIE setze ich auf 0 und INT0 auf 1 (External Interrupt Enable)

Ist die Erklaerung aufschlussreich, oder immer noch zu kryptisch?

Autor: Wolfgang (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo, Felix!

Felix schrieb:
> ISR(INT0_vect) {
>   PORTB = 0b00010000;
>   PORTB = 0b11100000;
> }

Damit überschreibst Du Dir außer PB4 auch alle anderen Leitungen. 
Kodiere Dir das besser als
PORTB |= (1<<PIN4);
PORTB &= ~(1<<PIN4);

"PIN4" durch Deine Pin-Definition Deines Compilers ersetzen - arbeite 
nicht mir avrgcc. Ersetze Deine kryptischen Bits gleich im Quelltext und 
setze ihn nochmals rein, falls die Kiste weiterhin spinnt.

Gruß - Wolfgang

Autor: Felix (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Danke dir Wolfgang. Was meinst du mit:
> arbeite nicht mir avrgcc
Welche Alternative gibt es dazu? (Ich arbeite unter Linux,
kann mir aber auch von jemandem nen Windowsrechner leihen).
Und warum soll ich den avr-gcc nicht nehmen?

Die Aenderungsvorschlaege habe ich mal in den Code eingebaut
und zwar an allen in Frage kommenden Stellen, damit es
einheitlich wird. Das Problem ist das gleiche, er tuts einfach
nicht, bzw. PB4 ist die ganze Zeit high und geht wie oben im
Anhang ab und an mal kurz auf low, allerdings in einem vollkommen
eigenen Rhythmus, der aber auch garnichts mit dem zu tun hat, was
an PB1 anliegt, der den Interrupt ausloesen soll.
#include <avr/io.h>
#include <avr/interrupt.h>

// Interrupthandler - B1
ISR(INT0_vect) {
  PORTB |= (1<<PIN4);
  PORTB &= ~(1<<PIN4);
}//*/

int main(void) {
  // Ports konfigurieren
  DDRB |= (1<<DDB4) | (1<<DDB2) | (1<<DDB0);
  DDRB &= ~( (1<<DDB3) | (1<DDB1) );
  PORTB &= ~( (1<<PIN4) | (1<<PIN3) | (1<<PIN2) | (1<<PIN1) | (1<<PIN0) );
  PORTB |= (1<<PIN4);// wenn ich das weglasse bleibt PB4 immer auf low
  // Interrupts konfigurieren
  MCUCR &= ~(1<<ISC01);
  MCUCR |= (1<<ISC00);
  GIMSK &= ~(1<<PCIE);
  GIMSK |= (1<<INT0);
  // Interrupts an
  sei();

  while(1) {
  }
  return 0;
}

Hat irgendjemand noch eine Idee?

Autor: Wolfgang (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Tippfehler; sollte heißen: "arbeite nicht mit avrgcc" und war nicht 2. 
Person Sg. Imp., sondern 1. Pers. Sing. Ind.. 8-D

Jetzt sieht der Quelltext schon viel besser aus: lesbar. Es scheint 
alles korrekt initialisiert zu sein.

Zwei mögliche Fehlerquellen fallen mir noch ein:

1. Der Compiler trägt Deine Interrupt-Routine nicht als ISR in die 
Vektortabelle ein. Ist rel. unwahrscheinlich, da ja auf PB4 
herumgezappelt wird.

2. Zwischen den beiden Zeilen innerhalb des ISRs liegt noch soviel Code, 
den Du uns nicht mitgeteilt hast, daß ein ISR-Durchlauf zweieinhalb mal 
soviel Zeit benötigt wie zwischen zwei Taktflanken Deines PB1 liegt. 
Dann wird die ISR sofort wieder angesprungen, sobald sie beendet ist. 
Das ließe sich auch damit feststellen, daß Du in der Hauptschleife 
GIFR->INTF0 irgendwie ausgibst, z.B. auf einen Port-Anschluß.

Prüf das nochmal.

Gruß - Wolfgang

Autor: Felix (Gast)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Wolfgang schrieb:
> 2. Zwischen den beiden Zeilen innerhalb des ISRs liegt noch soviel Code,
> den Du uns nicht mitgeteilt hast, daß ein ISR-Durchlauf zweieinhalb mal
> soviel Zeit benötigt wie zwischen zwei Taktflanken Deines PB1 liegt.
> Dann wird die ISR sofort wieder angesprungen, sobald sie beendet ist.
Ich lade das programm so runter, wie ichs hier gepostet habe.
> Das ließe sich auch damit feststellen, daß Du in der Hauptschleife
> GIFR->INTF0 irgendwie ausgibst, z.B. auf einen Port-Anschluß.
Werd ich mal machen. Ich hab mal geschaut, ob ich generell die
Pins ansprechen kann und deswegen folgende Modifikation an
der Endlosschleife vorgenommen:
  while(1) {
    PORTB |= (1<<PIN0);
  }

Das Ergebnis ist im Anhang zu bestaunen. o_O
Kanal0: Clock
Kanal1: Signal an PB1 bzw INT0
Kanal2: PB4
Kanal3: PB0
Das ganze wiederholt sich regelmaessig in keinem erkennbaren Zusammehang
zum Signal an INT0/PB1. Ich aeussere mal eine Vermutung:
AVR startet -> alle Pins auf low. AVR setzt PB4 laut Programm auf high,
(siehe Bild), setzt die ganzen anderen Bits laut Programm, kommt dann
nach ein paar Clockcycles in die Endlosschleife und setzt PB0 auf
high (siehe Bild), danach crasht das Teil auf irgendein periodisch
wiederkehrendes Event hin und startet wieder von vorne.
Kann denn sowas sein? Ob ichs mal mit ner anderen Schaltung versuch?

Mal zur Hardware: beide Clocks, die schnelle und die langsame stammen
von einem Stereo-ADC. Der Attiny soll ein paar Shiftregistern dann
im richtigen Moment mitteilen, wann da drin valide Werte anzutreffen
sind. Die Stromversorgung kommt von einer USB-Leitung vom Rechner, ist
aber an einigen Stellen mit 1uF und 100nF Kondensatoren gefiltert.
Der Resetpin des Attiny ist ueber 10k an Vcc gelegt. Den ISP Programer
schliesse ich fuer die Messungen ab und trenne die Schaltung
mal kurz vom Strom, damit alles nochmal resettet wird. Die Shiftregister
sind noch nicht in ihren Fassungen drin, also ausser 1 Quarzoszi,
dem ADC und dem Attiny und besagten Filterkondensatoren ist nix
auf der Platine drauf.

Ich stehe vor einem Raetsel ^^

Autor: Felix (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Jetzt hab ich mal grade noch folgendes mit der Endlosschleife
gemacht:
while(1) {
  if( (GIFR & (1<<INTF0))!=0 )
    PORTB |= (1<<PIN0);
  else
    PORTB &= ~(1<<PIN0);
}

Ergebnis: PB0 bleibt immer low.

oder das:
while(1) {
  PORTB |= (1<<PIN0);
  PORTB |= (1<<PIN2);
}

Ergebnis: PB0 macht das gleiche wie im letzten Posting, PB2 bleibt immer
low.

Ich werd mal den Attiny wechseln und das ganze auf ner 2.
Platine aufbauen. Oder hat jemand nen besseren Vorschlag? ;]

Autor: Wolfgang (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Sachlich komme ich jetzt auch nicht viel weiter, also kurz 
durchschnaufen. Vielleicht geht es dann wieder.

Was Du aber bis dann alles machen kannst: Der *Reset-Eingang (Anschluß 
1, PB5) ist ganz sicher nicht mit einem anderen Anschluß gebrückt? Wie 
ich bereits an anderer Stelle in diesem Forum ausführte, sind die AVRs 
am *Reset-Eingang mit einem Pull-up-Widerstand ausgerüstet. Am Tiny13 
ist es ein 60kR-Typ (30kR-80kR laut Datenblatt 2535I-AVR-05/08, S. 116). 
Der 10k gegen +5V darf weg, dafür muß ein Kondensator (100n X7R 
bevorzugt) gegen Masse geschaltet werden. Immer für saubere Rücksetzung 
beim Kaltstart sorgen!

Du kannst auch einen Neustart des µCs auslösen, indem Du PB5 auf 0 
programmierst.

Gruß - Wolfgang

Autor: Wolfgang (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hoppla, Schnellschuß:

Das Datenblatt sagt zu INTF0 "The flag is cleared when the interrupt 
routine is executed." Falls also nicht der wenig wahrscheinliche Fall 
auftritt, die Hauptschleife so weit auszuführen, daß INTF0 als 1 erkannt 
wird, bevor die ISR angesprungen wird und damit INTF0 gelöscht, wird 
auch immer eine 0 'rauskommen.

Autor: Felix (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Wolfgang schrieb:
> Hoppla, Schnellschuß:
>
> Das Datenblatt sagt zu INTF0 "The flag is cleared when the interrupt
> routine is executed." Falls also nicht der wenig wahrscheinliche Fall
> auftritt, die Hauptschleife so weit auszuführen, daß INTF0 als 1 erkannt
> wird, bevor die ISR angesprungen wird und damit INTF0 gelöscht, wird
> auch immer eine 0 'rauskommen.

Stimmt ^^
ist aber auch erstmal egal, da das Ding ja aus irgend einem Grund
sowieso macht, was es will.

Danke dir fuer den Hinweis mit dem Resetpin, hab ich grad mal 
umgeloetet,
tut aber auch keinen Unterschied :[

Ich werd jetzt erstmal wirklich ne 2. Platine aufbauen mit eigener
Stromversorgung und von der anderen wirklich nur das Signal rueberholen,
was den Interrupt ausloesen soll. Damit werd ich ne Weile beschaeftigt
sein. Ich lass heut abend mal von mir hoeren, inwiefern mein Treiben
erfolgreich war.

nochmals herzlichen Dank Wolfgang!

Autor: Felix (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
So jetzt bin ich einen Schritt weiter. Mein Messgeraet taugt nix ^^
Der Logikanalysator ist neu und extra fuer dieses Projekt besorgt.
Es ist ein Open Bench Logic Sniffer, den ich unter Linux mit der
OLS Software betreibe. Der zeigt mir immer nur 41us an und dann
wiederholt er die Anzeige. Soviel also zu dem merkwuerdigen
Takt, von dem ich nicht wusste woher er kommt. Leider war meine
langsame Clock grade irgendwie ein Teiler der Samplingclock,
mit der der Logic Sniffer sampelt, deswegen war der Anzeigefehler
nicht auf allen Kanaelen zu sehen.
Naja, es kann also erstmal weitergehen und ich kann mich wieder
dem eigentlichen Problem widmen, ausschliessen kann ich jetzt aber
einen Reset oder Neustart des Attinys :]

Autor: Felix (Gast)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
So, der Thread kann nicht gerade als "solved" markiert werden,
aber mit dem Assemblercode unten bekomme ich das Zeitverhalten,
das ich brauche ziemlich gut hin. Sollte es noch Probleme dabei
geben, hab ich immer noch die Moeglichkeit die Clock doppelt
so schnell zu machen. Schade, dass ich das mit dem C-Code nicht
hinbekomme. Auf die Idee mit Assembler bin ich gekommen, als ich
mal an den Compilerflags rumgefummelt hab, da hat sich jedes
Mal irgendwas geaendert und das kam mir spanisch vor. Bei der
Anwendung gehts um ganz exakte Zeiten und da ich leider von C
und den gcc flags nicht wirklich nen Plan habe bleib ich mal
bei Assembler. ^^
Im angehaengten Bild ist wieder
Kanal0: Clock
Kanal1: Interruptsignal
Kanal2/3: vom Interrupt gesteuerte Ausgaben
.include "tn13def.inc"

.def rmp = R16
.def rimp = R17

.cseg
.org $0000

rjmp main
reti
rjmp intpcint
reti
reti
reti
reti
reti
reti
reti

intpcint:
        nop ; nops einfuegen um das Timing zu konfigurieren
        nop
        nop
        nop
        cbi PORTB,4
        sbi PORTB,4
        cbi PORTB,0
        sbi PORTB,0
        reti

main:
        ldi rmp,LOW(RAMEND)
        out SPL,rmp

        ; Hardware Init
        sbi DDRB,0 ; PB0 als Ausgang definieren
        sbi DDRB,2 ; PB2 als Ausgang definieren
        sbi DDRB,4 ; PB4 als Ausgang definieren
        cbi PORTB,0 ; PB0 low setzen
        cbi PORTB,2 ; PB2 low setzen
        cbi PORTB,4 ; PB4 low setzen
        cbi DDRB,1 ; PB1 als Eingang definieren
        
        ; Interrupt Init
        ldi rmp,0b00000010
        out PCMSK,rmp ; Maskieren der Pinchangeinterrupts
        ldi rmp,0b00100000
        out GIMSK,rmp ; PCINT0 Interrupts zulassen

        ; Interrupt an
        sei

loop:
        rjmp loop

Zusammenfassung: solved durch Wechsel von C zu Assembler ^^

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.