Hallo,
verstehe das Verhalten meines ATtiny13 nicht. Sieht jemand meinen
Denkfehler?
Bin am Experimentieren, um externe Interrupts kennen zu lernen.
Habe folgenden Code:
1
#include<avr/io.h>
2
#include<avr/interrupt.h>
3
#include<util/delay.h>
4
5
volatileuint8_tint0_flag=0;
6
7
intmain(void){
8
/* enable output pin, set low */
9
DDRB|=(1<<PB4);
10
11
/* enable pullup on interrupt pin */
12
PORTB|=(1<<PB1);
13
14
/* allow logic level to rise */
15
_delay_ms(500);
16
17
/* set interrupt flag for INT0 extern int. */
18
GIMSK|=(1<<INT0);
19
20
/* enable interrupts globally */
21
//sei();
22
23
while(1){
24
if(int0_flag){
25
PORTB|=(1<<PB4);
26
}
27
}
28
29
return0;
30
}
31
32
ISR(INT0_vect){
33
int0_flag=1;
34
}
Der PB4-Pin wird high, obwohl ich erwarten würde, dass er low bleibt.
sei() habe ich auskommentiert, um auszuschließen, dass int0_flag vom
Interrupt manipuliert wird.
Warum wird der Pin trotzdem high? Wo ist mein Denkfehler?
Liebe Grüße,
Lukas
Lukas P. schrieb:> Warum wird der Pin trotzdem high? Wo ist mein Denkfehler?
Der Mechanismus, der Interrupts trackt ist bereits nach Anlegen der
Versorgungsspannung aktiv. Egal ob du später dann einen Pullup
dazuschaltest oder nicht. Egal ob du den Interrupt dann frei gibst oder
nicht. Egal ob du die Interrupts insgesamt frei gibst oder nicht.
Erst mal (noch ehe dein Programm überhaupt eine Chance hat loszulegen),
ist der Pin ein Eingang. Und je nachdem, was du da extern angeschlossen
hast, kann das auch ein offener Eingang sein, der sich alles mögliche
einfängt. Unter anderem auch Pegel, die du zu diesem Zeitpunkt nicht
haben willst. Das ist aber dem µC egal. Er sieht dort einen Pegel
und/oder Flanke und setzt dementsprechend das 'Ereignis aufgetreten
Bit'. Es ist daher sinnvoll, dieses Bit erst mal zurückzusetzen, ehe du
dann mit einem sei() alles scharf schaltest.
Lukas P. schrieb:> Der PB4-Pin wird high, obwohl ich erwarten würde, dass er low bleibt.
Wiso? Den setzt niemand auf Low.
Könntest Du aber selbst machen:
Karl H. schrieb:> Es ist daher sinnvoll, dieses Bit erst mal zurückzusetzen, ehe du> dann mit einem sei() alles scharf schaltest.
Vielen Dank für den Hinweis, das werde ich einarbeiten.
Karl H. schrieb:> Bit'. Es ist daher sinnvoll, dieses Bit erst mal zurückzusetzen, ehe du> dann mit einem sei() alles scharf schaltest.
Die Rede ist vom INT0F im Register GIFR
(musste erst mal im Datenblatt nachsehen)
Karl H. schrieb:> Die Rede ist vom INT0G im Register GIFR> (musste erst mal im Datenblatt nachsehen)
Hatte ich verstanden - trotzdem danke fürs extra Nachschauen!
Jim M. schrieb:> Den setzt niemand auf Low.
Aber auch niemand auf high. So wie ich das Datenblatt verstehe und
meinen Tests nach ist der Inhalt von PORTB nach dem Start 0x00.
Wenn ich die while-schleife leer lasse, dann bleibt der Pin brav low.
Karl M. schrieb:> und nicht vergessen die globalen Interrupts frei zu schalten
Die sind grade absichtlich aus.
Ich will verstehen, warum der Pin ohne Interrupt high wird.
Karl H. schrieb:> Wenn du keinen sei() hast, dann kann der nicht high werden. Denn dann> kann die Variable auch nie 1 werden.
Das hätte ich auch gedacht, wird er aber.
Habe nochmal kompiliert, geflasht und gemessen, Pin wird high.
Mist :(
Dein Compiler tut da scheinbar etwas, was du nicht möchtest. Poste bitte
mal das .lss-File, in dem man nachsehen kann, was vom Code geblieben und
was entfernt wurde.
Lukas P. schrieb:> Dann werde ich mal einen anderen tiny13 und eine andere Spannungsquelle> testen, irgendwo muss der Fehler ja sein.
Ergebnis:
Beim neuen Tiny geht die LED nicht an, auch nicht, wenn ich PB1 mit
einem Taster low ziehe.
Beim alten geht nachwievor die LED an. Wenn ich PB1 low ziehe, geht sie
aus und geht nach dem loslassen von PB1 mit einer kleinen Verzögerung
wieder an.
Beides nicht, wie es sein sollte ...
Muss jetzt leider weg, Ideen und Erleuchtungen sind weiterhin willkommen
;)
Vielen Dank für eure bisherige Hilfe!
Ich schätze mal:
deine LED geht an, wenn PB4 low ist und sie geht aus, wenn PB4 high ist.
Wenn du den Taster betätigst und PB1 auf Masse gezogen wird, wird PB4
high gesetzt und die LED geht aus, weil dann in deine If-Abfrage
gesprungen wird.
Ein Zurück setzen ist nicht vorgesehen in der Schleife, passiert aber
trotzdem. Das Teil macht nicht zufällig ständig Resets?
Bei nächster Gelegenheit wären ein Schaltplan und/oder zumindest
ein/zwei gute Bilder vom Aufbau toll ;-).
WebDeveloper schrieb:> Bei nächster Gelegenheit wären ein Schaltplan und/oder zumindest> ein/zwei gute Bilder vom Aufbau toll ;-).
Ich gelobige Besserung :)
WebDeveloper schrieb:> deine LED geht an, wenn PB4 low ist und sie geht aus, wenn PB4 high ist.> Wenn du den Taster betätigst und PB1 auf Masse gezogen wird, wird PB4> high gesetzt und die LED geht aus, weil dann in deine If-Abfrage> gesprungen wird.
Die Pegel sind mit dem Multimeter gemessen, die LED ist gegen GND
geschaltet.
Evtl habe ich beim Fummeln dem ersten Tiny den PORTB zerzappt (ESD) und
der resetted wirklich durch den an PB1 hängenden Taster.
Aber der neue Tiny lässt garnichts leuchten, das ist auch faul. Ist der
INT0-Taster gedrückt, wird der Pin auf 0.2V = 0.04*VCC runtergezogen.
Laut Datenblatt sind für low nur 0.1*VCC nötig. Der Interrupt sollte
also auslösen.
Ist doch alles haarig ...
WebDeveloper schrieb:> if (int0_flag) {> PORTB |= (1<<PB4);> }
Müsste dies nicht anders lauten: if (int0_flag != 0)... oder wird in
dieser Form auf 1 abgefragt?
Nutze testweise mal einen externen Pullup am !Reset-Pin.
Marco G. schrieb:> Müsste dies nicht anders lauten: if (int0_flag != 0)... oder wird in> dieser Form auf 1 abgefragt?
Nein (oder fast): die Form testet auf "ungleich 0".
Als Lebenszeichentest könntest du mal die LED nach deinen 500ms Delay
anschalten und dann testen, ob der Reset(taster) funktioniert. Danach
kann man dann weiter überlegen, was das noch sein kann.
Lukas P. schrieb:> Beim neuen Tiny ...> Beim alten ...
Watchdog unterschiedlich eingestellt?
Oszillator verbogen?
Tut der uC überhaupt noch was?
Ich würde während der Entwicklung auch immer in der Mainloop eine LED
vor sich hinblinken lassen. Wenn die mal nicht mehr blinkt, dann hängt
das Programm...
BTW: wenn das tatsächlich ein "Klotimer" werden soll, wozu sind dann
Interrupts nötig?
Marco G. schrieb:> Müsste dies nicht anders lauten: if (int0_flag != 0)...
if() testet seinen Parameter sowieso schon auf 0. Und so wird der Code
in der if-Bedingung nur dann ausgeführt, wenn der Parameter ungleich 0
ist. Ergo ergibgt "if (irgendwas!=0){}" genau das selbe wie "if
(irgendwas){}"
Das kann man dann auch gut für leserliche Programme verwenden:
Habe es gerade eben aufgebaut und getestet. Der einzige mögliche Fehler
der mir am Aufbau aufgefallen ist wäre der Taster, den kann man leicht
mal falsch verdrahten. Ein 100nF zwischen VCC+GND kann nicht schaden
ebenso wie ein 10k Pullup am Resetpin.
Die Software (die letzte Version) lief sofort Problemlos. Das einzige
was fehlte war #define F_CPU 1200000.
Die .hex ist angehängt, wenn du es damit versuchen möchtest.
(9.6MHz + Ckdiv8 = 1.2 MHz)
Ich hatte mal mangels Lust auf nem Steckbrett keinen 100nF an der
Versorgung und das Ding machte was es wollte, nur nicht was es sollte.
100nF dran und alles lief einwandfrei...
mach mal:
1
while(1){
2
if(int0_flag){
3
PORTB|=(1<<PB4);
4
int0_flag--;
5
_delay_ms(200);
6
}else{
7
PORTB&=~(1<<PB4);
8
}
9
}
10
11
ISR(INT0_vect){
12
int0_flag=10;
13
}
EDIT:
Die 1k Vorwiderstand sind schon grenzwertig, eher weniger
Die 1k in Reihe zum Taster würde ich eher so auf 100R setzen
Lukas P. schrieb:> Ist doch alles haarig ...
Du hast den 100nF Kerko (Abblockkondensator) vergessen. Deine
geschilderten Probleme (der eine ATTiny123 funktioniert, der andere
nicht) passen wie Topf auf Deckel.
Löte ihn an und schon werden Deine Probleme verschwinden.
Heureka!
Abblockkondensator hinzugefügt, Holgers Firmware geflashed -> kein
Erfolg.
Alles vom Steckbrett gerissen und neu verkabelt -> jetzt läufts.
Komme mir grade doof vor…
Jetzt arbeite ich mal alle Vorschläge ein und poste dann den
resultierenden Code.
Vielen, vielen Dank für eure große Hilfsbereitschaft und Geduld -
wirklich toll hier :)
Jaja ... es war anscheinend nicht das Steckbrett alleine.
Ich bekomme den Code nachwievor nicht funktionstüchtig compiliert.
Flashe ich Holgers hex-file, läufts. Nehme ich mein eigenes, läufts
nicht.
Habe ihm schon eine PN geschrieben und um die Befehle gebeten, die er
benutzt.
Der Wurm steckte im Makefile.
Habe dem Linker nicht -mmcu=attiny13 übergeben. Jetzt läufts.
kopfgegenwandhau
Implementierte Änderungsvorschläge:
- zusätzliche LED in der Main-Schleife blinken lassen, um zu sehen, ob
die läuft (Lothar)
- 100nF Abblockkondensator eingebaut. Die Lektion habe ich mir gemerkt …
(Holger, Ingo und Frank)
- 10k Pullup am Reset-Pin (Holger)
- F_CPU hatte und habe ich im Makefile als Compileroption definiert. Ist
das unsauber? (Holger)
- Codefragment eingefügt. (Ingo)
- LED-Vorwiderstand 1k habe ich gelassen, weil die LED sonst sehr
blendet. Ist eine klare, blaue, fließen ca. 3mA. (Ingo)
Hier der resultierende Code:
1
cat main.c
1
#include<avr/io.h>
2
#include<avr/interrupt.h>
3
#include<util/delay.h>
4
5
volatileuint8_tint_flag=0;
6
7
intmain(void){
8
/* enable output pin, set low */
9
DDRB=(1<<PB2)|(1<<PB4);
10
11
/* enable pullup on interrupt pin */
12
PORTB=(1<<PB1);
13
/* allow logic level to rise */
14
_delay_ms(500);
15
16
/* clear INT0 interupt flag if set*/
17
if(GIFR!=0){
18
GIFR=(1<<INTF0);
19
}
20
/* set interrupt enable flag for INT0 extern int. */
21
GIMSK|=(1<<INT0);
22
23
/* reset interrupt signaling variable */
24
//int_flag = 0;
25
26
/* enable interrupts globally */
27
sei();
28
29
while(1){
30
_delay_ms(200);
31
32
if(int_flag){
33
PORTB^=(1<<PB4);
34
int_flag--;
35
}else{
36
PORTB&=~(1<<PB4);
37
}
38
39
/* blink LED to monitor execution */
40
PORTB^=(1<<PB2);
41
}
42
43
return0;
44
}
45
46
ISR(INT0_vect){
47
int_flag=10;
48
}
1
cat Makefile
1
BINARY=klotimer
2
OBJS=main.o
3
4
PREFIX = avr
5
CC = $(PREFIX)-gcc
6
LD = $(PREFIX)-gcc
7
OBJCOPY = $(PREFIX)-objcopy
8
OBJDUMP = $(PREFIX)-objdump
9
# Uncomment this line if you want to use the installed (not local) library.
Jetzt zur allentscheidenden Frage, warum man für einen Klotimer
Interrupts benötigt:
Habe vor einem Monat eine WG mitgegründet. Wir haben ein recht kleines
Bad, man muss nach dem Duschen lüften. Jetzt ist es uns schon ein paar
mal vorgekommen, das wir das Fenster vergessen haben und erst eine
Stunde später in ein klapperkaltes Bad mit rödelnder Heizung kamen.
Der „Klotimer“ soll das verhindern. An Fensterrahmen und Fenster kommen
Kontakte aus Alu-Tape als kruder Taster. Wird das Fenster geöffnet, so
wird der Kontakt unterbrochen, was den ATtiny aus dem Stromsparschlaf
wecken soll. Dieser startet einen 1 Sek. Timer, nach dessen Ablauf er
überprüft, ob der Kontakt immernoch unterbrochen ist. Trifft dies zu,
wird ein 5min Timer gestartet, nachdem ein lauter Alarm ertönt. Wird in
der Zwischenzeit das Fenster wieder geschlossen, bricht der Timer ab und
der ATtiny legt sich wieder schlafen. Das Ganze kommt in das Gehäuse
eines defekten Rauchmelders, wird von einer 3V Lithium-Batterie versorgt
und benutzt die Lärmerzeugungsmaschinerie des Rauchmelders. Mit dem
Stummschaltungsknopf des Rauchmelders kann man den Alarm pausieren.
Der aktuelle Code ist dabei nur um grundlegende Funktionen zu testen,
nicht Versuch einer Implementation.
Sobald das Projekt fertig ist, stelle ich hier den vollständigen
Schaltplan, Code und Fotos ein.
Liebe Grüße,
Lukas
Lukas P. schrieb:> Wird in der Zwischenzeit das Fenster wieder geschlossen, bricht der> Timer ab und der ATtiny legt sich wieder schlafen.
Was du brauchst ist ein simples Monoflop (oder auch zwei). Da hättest du
dann auch keinerlei Probleme mit dem Batterieverbrauch.
Und wenn es unbedingt ein uC sein muss: weil die "5 min." absolut
unkritisch sind, würde ich da allein mit dem Watchdog arbeiten. Der löst
z.B. alle 5 sec. einen Reset aus, du siehst nach dem Kontakt und zählst
in einem Zählerbyte das offene Fenster mit. Wenn dieser Zähler auf 60
ist, schaltest du die Hupe ein (und nach 5 min. wieder aus, sonst hupt
das ding die ganze Nacht, bis morgens um halb 6 der erste wieder nach
Hause torkelt)...
Schönes Projekt.
Nur falls du es noch nicht gelesen haben solltest (Electrical
Characteristics im Datenblatt):
Die 1.2 MHz oder sogar 9.6 MHz sollten für dieses Projekt ein wenig
übertrieben sein, man kann den Tinny intern mit 128kHz betreiben was
noch ein wenig Strom spart. (http://www.engbedded.com/fusecalc/)
Allerdings sollte man vorher kontrollieren ob das Programmiergerät das
noch stemmen kann, die ISP-Frequenz sollte immer 1\4 des Cpu-Taktes
betragen.
Das mit der Aluminiumfolie als Taster ist eine tolle Idee.
Man kann übrigens mit einem einfachen Kondensator, oder einem Transistor
und weiteren einfachen Bauteilen eine Temperaturmessung durchführen, nur
falls die Alufolie sich nicht beweisen sollte.
Lukas P. schrieb:> - F_CPU hatte und habe ich im Makefile als Compileroption definiert. Ist> das unsauber? (Holger)
Ich persönlich schreibe es immer direkt in den Code, das ist einfach
übersichtlicher. Nach einem halben Jahr weiß man meißt nicht mehr was
man dort für Einstellungen hatte, dann ist es auf einen Blick
ersichtlich.
Nutze allerdings auch nur das AtmelStudio, dadurch muß man sich um das
Makefile nicht kümmern.
Lothar M. schrieb:> Lukas P. schrieb:>> Wird in der Zwischenzeit das Fenster wieder geschlossen, bricht der>> Timer ab und der ATtiny legt sich wieder schlafen.> Was du brauchst ist ein simples Monoflop (oder auch zwei). Da hättest du> dann auch keinerlei Probleme mit dem Batterieverbrauch.
Interessante Idee, damit habe ich noch nie gearbeitet. Habe etwas
überlegt und glaube, eine Monoflop&Flipflop-Lösung gefunden zu haben.
Schreibe den Schaltplan die Tage auf und poste ihn, dann kannst Du
kommentieren.
Fürs fertige 'Produkt' würde ich immernoch den uC bevorzugen, weil ich
so nur einen IC verbauen muss und viel flexibler bin. Schön wäre zum
Beispiel ein kurzes 'Pieps' alle 20s, als 'Fenster-offen-Indikator'.
Beim hastigen Verlassen des Bades und Hauses auf dem Weg zur Party merkt
man es dann vllt. grade noch.
> Und wenn es unbedingt ein uC sein muss: weil die "5 min." absolut> unkritisch sind, würde ich da allein mit dem Watchdog arbeiten. Der löst> z.B. alle 5 sec. einen Reset aus, du siehst nach dem Kontakt und zählst> in einem Zählerbyte das offene Fenster mit. Wenn dieser Zähler auf 60> ist, schaltest du die Hupe ein.
Wird das Zählerbyte vom Reset nicht gelöscht? Oder meinst Du im EEPROM?
Werde das mal so und mal mit Timerinterrupts implementieren und den
Verbrauch messen. Sobald ich mir nach Weihnachten ein Speicherscope
gekauft habe, versteht sich. ;) Mutmaße aber, dass es keinen
nenneswerten Unterschied für die Batterielaufzeit machen wird, da der
Piezo-Treiber im zeitlichen Mittel um Größenordnungen mehr Leistung
verbraucht.
Dessen reverse'den Schaltplan gibts auch die Tage.
Holger L. schrieb:> Schönes Projekt.>> Nur falls du es noch nicht gelesen haben solltest (Electrical> Characteristics im Datenblatt):> Die 1.2 MHz oder sogar 9.6 MHz sollten für dieses Projekt ein wenig> übertrieben sein, man kann den Tinny intern mit 128kHz betreiben was> noch ein wenig Strom spart. (http://www.engbedded.com/fusecalc/)> Allerdings sollte man vorher kontrollieren ob das Programmiergerät das> noch stemmen kann, die ISP-Frequenz sollte immer 1\4 des Cpu-Taktes> betragen.
Werde ich ausprobieren. Danke für den Hinweis!
> Das mit der Aluminiumfolie als Taster ist eine tolle Idee.> Man kann übrigens mit einem einfachen Kondensator, oder einem Transistor> und weiteren einfachen Bauteilen eine Temperaturmessung durchführen, nur> falls die Alufolie sich nicht beweisen sollte.
Ok, ist notiert :)
Bei 128kHz brauch der Attiny wirklich sehr wenig Strom. Wenn ich mich
recht erinnere ca. 50uA ( wenn die meiste Peripherie aus ist ).
Ich hatte das für die Solarvögel verwendet. Die laufen schon bei sehr
schwachem Licht los:
http://www.hobby-roboter.de/forum/viewtopic.php?f=5&t=43
Lukas P. schrieb:> Wird das Zählerbyte vom Reset nicht gelöscht? Oder meinst Du im EEPROM?
Der Watchdog kann so programmiert werden das er statt einem Reset einen
ISR-Aufruf durchführt, in dem kann dann eine volatile Variable erhöht
werden.
Lukas P. schrieb:> Mutmaße aber, dass es keinen> nenneswerten Unterschied für die Batterielaufzeit machen wird, da der> Piezo-Treiber im zeitlichen Mittel um Größenordnungen mehr Leistung> verbraucht.
Der muß doch nicht die ganze Zeit über an der Versorgung hängen, den
könnte man mit einem Transistor zuschalten wenn er benötigt wird.
Holger L. schrieb:> Der Watchdog kann so programmiert werden das er statt einem Reset einen> ISR-Aufruf durchführt, in dem kann dann eine volatile Variable erhöht> werden.
Cool!
Holger L. schrieb:>Lukas P. schrieb:>> Mutmaße aber, dass es keinen>> nenneswerten Unterschied für die Batterielaufzeit machen wird, da der>> Piezo-Treiber im zeitlichen Mittel um Größenordnungen mehr Leistung>> verbraucht.> Der muß doch nicht die ganze Zeit über an der Versorgung hängen, den> könnte man mit einem Transistor zuschalten wenn er benötigt wird.
So ist es auch implementiert. Aber wenn er mal piept (und das wird er),
verbraucht er bestimmt auf einen Schlag so viel Energie wie der uC in
zwei Wochen.