Forum: Mikrocontroller und Digitale Elektronik Anfängerprojekt mit Problemen


von Sascha M. (sascha-reloaded)


Lesenswert?

Hallo Allerseits,

ich bin neu hier im Forum und in der mikrocontroller-welt...
Ich hab mir da mal was ausgedacht..^^

Also ich will mit einer Lichtschranke 2 "Lichter" anschalten und danach 
diese Lichter mit jeweils einem Taster ein und aus schalten. Außerdem 
soll später noch die Lichtschranke nach erstmaligem aktivieren inaktiv 
sein...sonst geht ja immer wieder alles an =)

Und wenn ich dann aus dem Haus gehe, will ich draußen einen Taster 
betätigen, mit dem ich Alles aus und die Lichtschranke aktiv schalte.

Ich benutze einen Atmega8 und möchte das Ganze gerne in Assmebler 
machen.

An Alle, denen es in den Fingern juckt zu schreiben, dass man das mit 
ner Hand voll Bistabiler Relais und nen paar Tastern auch machen kann:
Jaja, is schon klar...aber ich möcht nun mal gern Assembler lernen :)

lieben Gruß und danke schonmal
Sascha

PS: Ich Poste hier mal was ich bereits habe...und wäre für jeden 
Vorschlag sehr dankbar...denn bisher gehts garnicht :(

PPS: Alle Komponenten habe ich einzelnd getesetet, ein Materialfehler 
ist auszuschließen. Ich teste zur Zeit mit 4 Tastern und sechs LEDs.


.include "m8def.inc"

.def temp = r16
.def temp2 = r20

.org 0x000
         rjmp main            ; Reset Handler


main:                         ; hier beginnt das Hauptprogramm

     ldi temp, LOW(RAMEND)  ;set RAM
         out SPL, temp
         ldi temp, HIGH(RAMEND)
         out SPH, temp      ;set RAM ende

         ldi temp, 0x00      ;Port D als Eingang
         out DDRD, temp

     ldi temp, 0xFF      ;Port B als Ausgang
         out DDRB, temp

     ldi r17, 0xFF         ;Alles aus
         out PORTB, r17

; lichtschranke an PD0=$01
;Alles Aus an   PD1=$02
;licht1 an    PD2=$04
;licht2 an    PD3=$08
;licht3 an    PD4=$10

lichtschranke:
    ldi temp, PIND      ;Frage Port D ab und lade das Ergebnis in temp
    ANDI temp, $01      ;Prüfe ob Taste 1 gedrückt ist ( Prüfe ob taste 
1 nicht gedrückt ist )
    BRNE lichtein      ;wenn doch, dann spring nach lichtein und mach 
alles an



allesaus:  ;          ist der türtaster zum ausschalten
    ldi temp, PIND      ;Frage Port D ab und lade das Ergebnis in temp
    ANDI temp, $02      ;Prüfe ob Taste 2 gedrückt ist ( Prüfe ob taste 
2 nicht gedrückt ist )
    BRNE lichtaus      ;wenn doch, dann spring nach lichtaus und mach 
alles aus



licht1:
    ldi temp, PIND      ;Frage Port D ab und lade das Ergebnis in temp
    ANDI temp, $04      ;Prüfe ob Taste 3 gedrückt ist ( Prüfe ob taste 
3 nicht gedrückt ist )
    BRNE licht1an      ;wenn doch, dann spring nach licht1an


licht2:
    ldi temp, PIND      ;Frage Port D ab und lade das Ergebnis in temp
    ANDI temp, $08      ;Prüfe ob Taste 4 gedrückt ist ( Prüfe ob taste 
4 nicht gedrückt ist )
    BRNE licht2an      ;wenn doch, dann spring nach licht2an

    rjmp lichtschranke

;----------------------------------------------------------------------- 
------------------------------------------------------------------------ 
------------------------------
; Subroutinen

lichtein:
    cbi PORTB, 2
    cbi PORTB, 3
    cbi PORTB, 4
    ret

lichtaus:
    sbi PORTB, 2
    sbi PORTB, 3
    sbi PORTB, 4
    ret

licht1an:
    eor PORTB, 2
    ret

licht2an:
    eor PORTB, 3
    ret

von Zeusi (Gast)


Lesenswert?

Hallo, was bei dir als main gelabelt ist findest du häufig unter den 
stichpunkt reset, main ist meistens die hauptschleife nach dem reset,..

Du betreibst das ganze im POLLING modus, d.h. du guckst jedesmal ob der 
taster gedrückt worden ist, viele werden dir empfehlen das im Interrupt 
modus laufen zu lassen, d.h. pin INT0 an PULL-DOWN und an TASTER, taster 
gedrückt zieht auf high interrupt löst aus, je nach status kann dann die 
lichtschranke an oder aus geschaltet werden, lichtschranke an INT1, 
interrupt wird ausgelöst wenn pin auf LOW fällt,.. den rest der zeit 
loopt der µC gemütlich in der main oder spart energie im sleep modus,...

die Interrupt vectoren findest du am ende der *.inc und muss oben a la 
.org 0x... zugewissen werden, der rest wird als subroutine getätigt,..

ich hoffe ich konnte helfen,.. sei gegrüüüßt

von Tom (Gast)


Lesenswert?

Ein dicker Hammer ist noch drin.

Du springst in die Subroutinen mit BRNE, und mit RET wieder zurück. Das 
passt nicht zusammen.

Entweder mit RCALL rein und RET raus, oder mit BRNE oder RJMP rein und 
mit RJMP raus.

RET holt die Returnadresse vom Stack, wo in Deinem Fall gar keine ist.

von Sascha M. (sascha-reloaded)


Lesenswert?

ok, vielen dank soweit...
werde das heute abend mal überarbeiten.

btw..ich dachte, ich hab nur zwei interrupts (zumindest nutzbare) zur 
verfügung?
und da sollten noch nen paar mehr sachen angeschlossen werden.

grüzzie
sascha

von Werner B. (Gast)


Lesenswert?

...und viele werden dir dringend davon abraten (wie ich auch) Tasten mit 
Interrupts abzufragen. Prellende Taster (und alle mechanischen Taster 
prellen) erzeugen hier einen ganzen Rattenschwanz von Interrupts. Das 
ist in vielen Umgebugen (nicht nur für Einsteiger) nur schwer zu 
beherrschen. In diesem Fall sollte es zwar keine Rolle spielen, aber man 
gewöhnt sich so schnell an ein gewisses Vorgehen und wird es nur mit 
Mühe wieder los...

Siehe hierzu Entprellung

von Sascha M. (sascha-reloaded)


Lesenswert?

ok, danke für den tip!
habe es schonmal mit tastern und interrupts probiert...hat 
geklappt...war aber auch nur eine led an und aus...

nur wie schauts mit alternativen aus ?
wenn ich keine taster zum testen nehmen soll...was dann ?
auf allen eva boards sind doch auch welche drauf ?
oder geht dein hinweis einfach in die richtung: IMMER entprellen ?

gruß
sascha

von Johannes M. (johnny-m)


Lesenswert?

Ohne Entprellung kannste das in den meisten Fällen vergessen. Die 
sinnvollste Methode ist eigentlich, einen Timer laufen zu lassen, der 
alle paar ms (bei Tastern reicht eine Abfrage alle 10...50 ms i.d.R. 
völlig aus) eine Abfrage der angeschlossenen Taster durchführt 
(Interrupt). Was Zeusi oben schreibt, ist gelinde gesagt ziemlicher 
Unfug. Werner hat da völlig Recht. Taster an externen Interrupts ist 
dummes Zeug.

von Tom (Gast)


Lesenswert?

>Taster an externen Interrupts ist dummes Zeug.

So krass würde ich es nicht formulieren.

Optimal nach meiner Meinung ist: Die Taster lösen einen Interrupt aus, 
dann wird sofort derjenige Interrupt disabled und der Taster gepollt wie 
von Johannes beschrieben.

Vorteil ist, dass vor dem Tastendruck keine CPU-Last besteht. 
Stromverbrauch ist kleiner, zeitkritische Sachen wie AD-Wandlungen und 
serielle Kommunikation laufen problemloser.

von Johannes M. (johnny-m)


Lesenswert?

Tom wrote:
>>Taster an externen Interrupts ist dummes Zeug.
>
> So krass würde ich es nicht formulieren.
Es gibt imho exakt einen sinnvollen Anwendungsfall für Taster an 
externen Interrupts, und das ist das Aufwecken des µC aus einem 
sleep-Modus. Die sleep-Modi machen aber wiederum eigentlich nur bei 
batteriegestützten Anwendungen wirklich Sinn. Und hier scheint es sich 
nicht um eine solche zu handeln.

von Kai S. (Firma: ZeuSWarE GmbH) (zeusosc)


Lesenswert?

(ich =Zeusi, hatte keine lust einzuloggen)

Dennoch Johannes hättest auch nicht unbedingt schreiben müssen es sei 
dummes zeug, den generell benutze ich taster an PCINT des ATtiny2313, 
und der hat ein paar davon, diese sind leicht anzuschliesen genauso 
leicht anzusteuern wie INTn und machen ihre arbeit ohne jegliche 
entprellung mit einen einfache CLI bzw. einfach spezifische IFLAGs 
clearen (öfters in dem man sie setzt).

Grundprinzipiell sind externe Interrupts dafür da das durch eine eXterne 
aKtion der µC beflügelt wird etwas zu tun, und genau das habe ich 
vorgeschlagen, da POLLING zwar manchmal nützlich sein mag (ich nutze es 
ja auch teilweise)aber zum glück in den seltensten fällen, notfalls 
enprelle ich lieber als stetig zu pollen, TOM hat die gründe erwähnt.

Ob batterieanwendung oder nicht: ich bin CO²NTRA! falls es euch was 
sagt,..

seid gegrüüßt und nicht genervt g

von Tom (Gast)


Lesenswert?

So sehe ich das auch, Zeusi.

Eigentlich sind wir doch alle ereignisgesteuert. Telefonklingeln, 
Türklopfen, Hunger, Sex, irgendwann kommt der Interrupt und wir 
beantworten ihn, selbstverständlich nach Prioritäten. Wenn Euch das zu 
abstrakt ist, auch moderne Betriebssysteme sind ereignisgesteuert. 
Mausklick, Tastendruck, Bits aus dem Netzwerk, etc.

Warum nicht auch so die µCs programmieren? Im Reset nur die 
Initialisierung und dann sleep, und für jedes Ereignis einen 
Eventhandler. Das is oft übersichtlicher als die ganzen Statusbits 
reihum abklappern. Geht natürlich auch, jeder wir er/sie will.

von AVRFan (Gast)


Lesenswert?

>Grundprinzipiell sind externe Interrupts dafür da das durch eine eXterne
>aKtion der µC beflügelt wird etwas zu tun

Hört sich richtig an, ist es aber nicht. Externe Interrupts sind dazu 
da, SCHNELLSTMÖGLICH auf ein externes Ereignis reagieren zu können. 
"Schnellstmöglich" bedeutet dabei innerhalb weniger Taktzyklen.  Dafür 
gibt es Anwendungsfälle, aber dazu gehört nicht die Abfrage 
menschbedienter Taster.  Menschen, die solche drücken, nehmen 
Verzögerungszeiten von 20 ms (entsprechen ca. 160000 Taktzyklen) noch 
als unmittelbare Reaktion wahr. Wers nicht glaubt: AUSPROBIEREN.

Fazit: Man schließe Tasten an einen normalen Pin an (Ausnahme: 
Aus-Sleepmodus-Aufwach-Tasten), polle sie in einem sinnvollen Zeitraster 
(grob 1...20 ms), und sorge sich nicht über die dadurch verursachte 
CPU-Last. Wers trotzdem tut, sollte sie mal AUSRECHNEN. Mit F_CPU = 8 
MHz, 50 Samples/s und geschätzten 8 Instruktionen fürs Einlesen plus 
Entscheidung, ob was zu tun ist, komme ich auf eine relative Last von 
0.05 %.

von AVRFan (Gast)


Lesenswert?

>Warum nicht auch so die µCs programmieren? Im Reset nur die
>Initialisierung und dann sleep, und für jedes Ereignis einen Eventhandler.

Weil der µC manchmal auch eine aufwendige Berechnung zu erledigen hat. 
Wenn Du ihn das innerhalb eines Interrupthandlers tun läßt, und die 
Rechnerei dauert 3 ms (d. h. eine "lange" Zeit), dann hast Du das 
Problem, dass sich die Behandlung aller anderen Interrupts um 3 ms 
verzögert (*).   Um dies zu vermeiden, hast Du zwei Möglichkeiten:

a) Du gibst die anderen Interrupts explizit durch Setzen des I-Bits früh 
wieder frei.  Das geht, aber Du würdest damit ein System kreieren, in 
dem prinzipiell ein *** Stacküberlauf durch externe Ereignisse *** 
verursacht werden kann (klar, wieso?).  Würdest Du DAS wollen?

b) Du hältst zeitraubende Aufgaben aus den Interrupts fern.  Dies führt 
auf das bekannte Paradigma, nach dem üblicherweise vorgegangen wird: "1. 
Alles was viel Zeit in Anspruch nimmt in 'Main'; 2. Alles, was 
schnell behandelt werden muss, in die Interrupts, 3. Alle Interrupts 
so kurz wie möglich.

Das Mindeste, was man in Interrupts machen muss, ist grob gesagt die 
Inputs von außen einlesen und in Puffer speichern, und Auszugebendes aus 
den entsprechenden Puffern lesen und ausgeben.

Wie die Erfahrung zeigt, ist b) die eindeutig bessere Alternative. Hier 
nutzt Du die "Außer-Interrupt-Ausführungsebene", anstatt sie wie bei 
Deinem Vorschlag ("leere Main") zu verschenken.

von Tom (Gast)


Lesenswert?

@AVRfan:
Gut, zugegeben, bei langen Berechnungen macht es Sinn, die konsequente 
Interruptprogrammierung in Frage zu stellen. Dies ist aber bei vielen 
Projekten eher die Ausnahme, auch das Projekt des Erstposter scheint 
eher einfach zu sein.

Wie macht es der Mensch oder das Betriebssystem? Verschachteltete 
Interrupts mit Prioritäten. Hunger, Essen, Telefon, Essen wird 
unterbrochen und nach dem Telefon wird weitergegessen. Stackoverflow 
sehe ich nicht so problematisch, denn es gibt ja nur eine begrenzte 
Anzahl Interrupts, und dann würde man halt ein paar mehr Bytes 
freihalten.

Der Mensch kommt bei seiner Interruptbearbeitung manchmal auch 
durcheinander, aber es wirkt immer noch intelligenter als ständig 
zwischen Haustür, Telefondisplay, Kühlschrank etc. hin und her zu 
rennen.

Manchmal denke ich, ob die Eventprogrammierung nicht eine neue Epoche 
der Programmiertechnik ist. So wie Objekte (80er Jahre), Funktionen 
(70er) oder Hochsprachen (60er), mit denen sich alte Hasen auch eine 
Weile schwer taten.

Aber vielleicht ist Eventprogrammierung auch nur eine Nischenanwendung 
für µCs mit kurzen Eventhandlern oder komplexe Aufgaben mit aufwendigem 
Interrupthandling.

von Karl H. (kbuchegg)


Lesenswert?

Tom wrote:

> Interrupts mit Prioritäten. Hunger, Essen, Telefon, Essen wird
> unterbrochen und nach dem Telefon wird weitergegessen.

Gegenbeispiel: Dein Postkasten

Einmal am Tag gehst du zum Postkasten und siehst nach ob etwas
drinnen ist. Die 1 Minute die du dafür brauchst tut dir bei 1440
Minuten am Tag nicht weh und ob du jetzt um 10:00 oder 10:20
zum Kasten gehst spiel auch keine grosse Rolle.

Ob dein µC 50 mal in der Sekunde die Tasten abscannt oder nicht,
macht in der verbleibenden Rechenzeit so gut wie keinen
Unterschied. Dafür kriegst du aber:
* Entprellung
* Autorepeat
* Unterscheidung zw. langem und kurzen Tastendruck

von AVRFan (Gast)


Lesenswert?

>Gut, zugegeben, bei langen Berechnungen macht es Sinn, die konsequente
>Interruptprogrammierung in Frage zu stellen. Dies ist aber bei vielen
>Projekten eher die Ausnahme,

Bei vielen aber auch nicht. Es genügt, bei einer Regelungsanwendung mit 
Fließkommazahlen und Funktionen wie Wurzel, x/y, Logarithmus etc. zu 
operieren.  Dann hat der µC schon zu tun.

>Wie macht es der Mensch oder das Betriebssystem? Verschachteltete
>Interrupts mit Prioritäten. Hunger, Essen, Telefon, Essen wird
>unterbrochen und nach dem Telefon wird weitergegessen.

Wie ich schon sagte, ist von verschachtelten Interrupts abzuraten.  Wie 
die Erfahrung lehrt, führt das zu einem schwer kontrollierbaren Design 
nebst Stack-Overflow-Gefahr, bringt aber ansonsten keine Vorteile.

>Stackoverflow ehe ich nicht so problematisch, denn es gibt ja nur eine
>begrenzte Anzahl Interrupts, und dann würde man halt ein paar mehr Bytes
>freihalten.

Hast Du verstanden wie Interrupts funktionieren?  Bei verschachtelten 
Interrupts wird sofort mit der Ausführung auch DESSELBEN Interrupts auf 
der "nächstunteren Ebene" begonnen, sobald er von dem entsprechenden 
externen Ereignis ausgelöst wird.  Simulier es mal im AVRStudio. Also: 
Ereignis E am Pin --> Programm sichert PC auf Stack und springt in 
Interrupthandler --> I-Bit wird gesetzt --> µC rechnet --> Ereignis E 
tritt erneut ein --> Programm sichert PC auf Stack und springt in 
denselben Interrupthandler (!) --> I-Bit wird gesetzt --> µC beginnt die 
auf der nächsthöheren Ebene schon halbfertige Rechnung von neuem --> 
Ereignis E tritt abermals ein --> ... [Kette irgendwann zu ende] ... --> 
Programm springt nach fertiger Rechung in denselben Interrupthandler auf 
nächsthöherer Ebene zurück --> muss dort  erst noch 
fertigrechnen........

>Manchmal denke ich, ob die Eventprogrammierung nicht eine neue Epoche
>der Programmiertechnik ist.

Nein.  Du darfst "Event" nicht mit Interrupt gleichsetzten.  Interrupts 
gehören zur Hardware des µCs (oder PCs), Events zum Betriebssystem. Es 
läuft so ab: Das Betriebssystem läßt seine 12 (Beispielwert) laufenden 
Prozesse vom Scheduler gesteuert in bestimmten Zeitabschnitten rundum 
nacheinander rechnen.  Einer der 12 Prozesse ist der 
"Tasten-Abfrage"-Prozess.  In ihm werden die zugehörigen Tasten z. B. 
alle 20 ms gepollt, und auf eventuelle Flanken (nicht gedrückt --> 
gedrückt, oder umgekehrt) getestet.  Sobald eine Flanke erkannt wird, 
wird DANN das entsprechende Event ("OnKeyPress") ausgelöst (= Nachricht 
in Warteschlange gelegt, die von anderen Prozessen ausgelesen wird, und 
darüber zum Aufruf einer passenden Behandlungsfunktion = "Eventhandler" 
führt).  Wie Du siehst, wurde das Tastenflanken-Event *** nicht von 
einem Interrupt verursacht (jedenfalls nicht direkt) ***.  Das ist der 
entscheidende Unterschied.

>Aber vielleicht ist Eventprogrammierung auch nur eine Nischenanwendung
>für µCs mit kurzen Eventhandlern oder komplexe Aufgaben mit aufwendigem
>Interrupthandling.

Im Gegenteil, eventbasierte Programme sind eine sehr natürliche und 
weitverbreitete Angelegenheit, auch auf PCs.

von Tom (Gast)


Lesenswert?

>Hast Du verstanden wie Interrupts funktionieren?  Bei verschachtelten
>Interrupts wird sofort mit der Ausführung auch DESSELBEN Interrupts auf
>der "nächstunteren Ebene" begonnen, sobald er von dem entsprechenden
>externen Ereignis ausgelöst wird.

In der Atmel-Doku steht:
The I-bit is cleared by hardware after an interrupt has occurred, and is 
set by the RETI instruction to enable subsequent interrupts.
anders würde z.B. ein Level-gesteuerter Interrupt keinen Sinn machen.

OK, wenn man in der Int-Routine das I-Bit setzt, dann kann der gleiche 
Int wiederkommen, bei Level gesteuerten auch ziemlich oft. Das meinst Du 
wohl mit "bei verschachtelten ...". Dann muss man halt vor dem SEI den 
auslösenden Int explizit verbieten, z.B. GIMSK/INT0.

Stack-Overflows mache ich meistens so:
- Rekursive Funktionen
- Mist auf den Stack-Pointer geschrieben
- zuviel gepusht

Vielen Dank auch für Deine ausführlichen Erläuterungen bzgl. Events und 
so - das muss ich erst mal verdauen.

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.