Forum: Mikrocontroller und Digitale Elektronik Interrupts am attiny13


von Felix (Gast)


Angehängte Dateien:

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:
1
#include <avr/io.h>
2
#include <avr/interrupt.h>
3
4
// Interrupthandler - B1
5
ISR(INT0_vect) {
6
  PORTB = 0b00010000;
7
  PORTB = 0b11100000;
8
}//*/
9
/*ISR(PCINT0_vect) {
10
}//*/
11
12
int main(void) {
13
  // Ports konfigurieren
14
  DDRB = 0b00010101;
15
  PORTB = 0b00000000;
16
  PORTB = 0b00010000; // wird die Zeile weggelassen bleibt PB4 immer low
17
  // Interrupts konfigurieren
18
  MCUCR &= 0b11111101;
19
  MCUCR |= 0b00000001;
20
  GIMSK &= 0b11011111;
21
  GIMSK |= 0b01000000;
22
  //GIMSK |= 0b00100000;
23
  //PCMSK |= 0b00000010;
24
  // Interrupts an
25
  sei();
26
27
  while(1) {
28
  }
29
  return 0;
30
}

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

von g457 (Gast)


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

von Felix (Gast)


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?

von Wolfgang (Gast)


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
1
PORTB |= (1<<PIN4);
2
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

von Felix (Gast)


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.
1
#include <avr/io.h>
2
#include <avr/interrupt.h>
3
4
// Interrupthandler - B1
5
ISR(INT0_vect) {
6
  PORTB |= (1<<PIN4);
7
  PORTB &= ~(1<<PIN4);
8
}//*/
9
10
int main(void) {
11
  // Ports konfigurieren
12
  DDRB |= (1<<DDB4) | (1<<DDB2) | (1<<DDB0);
13
  DDRB &= ~( (1<<DDB3) | (1<DDB1) );
14
  PORTB &= ~( (1<<PIN4) | (1<<PIN3) | (1<<PIN2) | (1<<PIN1) | (1<<PIN0) );
15
  PORTB |= (1<<PIN4);// wenn ich das weglasse bleibt PB4 immer auf low
16
  // Interrupts konfigurieren
17
  MCUCR &= ~(1<<ISC01);
18
  MCUCR |= (1<<ISC00);
19
  GIMSK &= ~(1<<PCIE);
20
  GIMSK |= (1<<INT0);
21
  // Interrupts an
22
  sei();
23
24
  while(1) {
25
  }
26
  return 0;
27
}

Hat irgendjemand noch eine Idee?

von Wolfgang (Gast)


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

von Felix (Gast)


Angehängte Dateien:

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:
1
  while(1) {
2
    PORTB |= (1<<PIN0);
3
  }

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 ^^

von Felix (Gast)


Lesenswert?

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

Ergebnis: PB0 bleibt immer low.

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

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? ;]

von Wolfgang (Gast)


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

von Wolfgang (Gast)


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.

von Felix (Gast)


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!

von Felix (Gast)


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 :]

von Felix (Gast)


Angehängte Dateien:

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
1
.include "tn13def.inc"
2
3
.def rmp = R16
4
.def rimp = R17
5
6
.cseg
7
.org $0000
8
9
rjmp main
10
reti
11
rjmp intpcint
12
reti
13
reti
14
reti
15
reti
16
reti
17
reti
18
reti
19
20
intpcint:
21
        nop ; nops einfuegen um das Timing zu konfigurieren
22
        nop
23
        nop
24
        nop
25
        cbi PORTB,4
26
        sbi PORTB,4
27
        cbi PORTB,0
28
        sbi PORTB,0
29
        reti
30
31
main:
32
        ldi rmp,LOW(RAMEND)
33
        out SPL,rmp
34
35
        ; Hardware Init
36
        sbi DDRB,0 ; PB0 als Ausgang definieren
37
        sbi DDRB,2 ; PB2 als Ausgang definieren
38
        sbi DDRB,4 ; PB4 als Ausgang definieren
39
        cbi PORTB,0 ; PB0 low setzen
40
        cbi PORTB,2 ; PB2 low setzen
41
        cbi PORTB,4 ; PB4 low setzen
42
        cbi DDRB,1 ; PB1 als Eingang definieren
43
        
44
        ; Interrupt Init
45
        ldi rmp,0b00000010
46
        out PCMSK,rmp ; Maskieren der Pinchangeinterrupts
47
        ldi rmp,0b00100000
48
        out GIMSK,rmp ; PCINT0 Interrupts zulassen
49
50
        ; Interrupt an
51
        sei
52
53
loop:
54
        rjmp loop

Zusammenfassung: solved durch Wechsel von C zu Assembler ^^

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.