PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Assembler lernen



Attiny
20.03.2008, 21:42
Hallo,
Ich hätt ma ne frage:
Ich möchte anfangen mit assembler zu programmieren, wie fang ich da am besten an (welches programm ist am geignetsten und wo programmieren lernen oder mit was )?
Danke

Attiny
20.03.2008, 21:44
achso ich habe noch vergessen ich möchte das mit atmel-prozessor machen.

pacer_one
21.03.2008, 10:53
Als Programmierumgebung gibt es AVRStudio und VMLab. Beide nutze ich und finde sie auch gut. Zum 'Brennen' des Atmel nutze ich PonyProg.
Das war es auch schon an Software. Für den Anfang würde man sogar ohne Hardware auskommen, da zB VMLab eine super Testumgebung beinhaltet. Man kann virtuell Displays, Tastenfelder, Analogschieberegler, Oszilloskope und mehr anklemmen.

Ein Buch kann ich dir leider nicht empfehlen, mein erstes Assemblerbuch war auch mein letztes. Zu empfehlen sind die Tutorials auf Mikrocontroller.net.

Also einfach mal reinschauen und loslegen...

HeSt
26.09.2008, 16:37
hallo pacer1,
ich hab gerade angefangen, mich mit den µc zu befassen und mir dazu des lernpaket von franzis gekauft (gibts bei amazon und conrad).
da ist avr studio 4 enthalten.
und möchte nun ein fertiges, mitgeliefertes lernprogramm (zb. led2.asm) mit dem deguger schritt für schritt durchgehen. leider gelingt mir das nicht.
ich werde auch nirgendwo fündig, wei man das bewerkstelligt, auch nicht in der hilfe des avr studio.
lt. pulldown menü müsste es mit "alt" und "+" der zehnertastatur gehen - nichts. auch nicht, wenn ich auf das icon -> (next step) drücke.
was mache ich hier falsch?
der debugger läuft und stellt einen schönen gelben pfeil neben die erste anweisung (rjmp anfang). das wars dann ... :-((
kann mir hier jemand helfen?
lg heinz

Besserwessi
26.09.2008, 17:05
Einzelschritte im Debugger macht man mit der Funktionstaste F11, mit F10 überspringt man eine Befehl. Steht auch im Menue unter DEBUG als "Step into".

oberallgeier
26.09.2008, 20:00
... hab gerade angefangen ... lernpaket von franzis ... da ist avr studio 4 enthalten ...Ja, nut den franzispaket hatte ich auch angefangen - und mir dann die neueste Version des AVRStudio geholt. (https://www.roboternetz.de/phpBB2/zeigebeitrag.php?t=35437&highlight=) Die gibt es als freie Software HIER. (http://sourceforge.net/project/showfiles.php?group_id=68108) Wenn ich mich richtig erinnere, hatte ich anfangs auch gelegentlich Schwierigkeiten mit dem AVRStudio. Ich weiß nicht, ob nicht der GCC-Compiler WinAVR zum ordnungsgemässen Arbeiten auch noch erforderlich ist. Jedenfalls habe ich beide drauf, (wohl auch in der neuesten Version) und bei mir (XP, Pentium ..) läuft alles gut.

Viel Erfolg

Besserwessi
26.09.2008, 20:54
Man braucht GCC/WINAVR nicht zum AVRstudio wenn man in ASM programmieren will. Solange man es nicht braucht, sollte man es auch besser weglassen, denn bei der Kombination WinAvr /AVRstudio funktioniert nicht unbedingt jede Kombination von Versionen.

HeSt
27.09.2008, 09:09
danke für eure antwort.
hab vergessen zu schreiben: hatte es ja auch mit f10 und f11 probiert - funktionierte nicht.
habe heute den pc neu hochgefahren, avr gestartet, programm neu umgewandelt, debuger gestartet - und siehe da, f10 + f11 funktionieren!
musste wahrscheinlich gestern von lauter herumprobieren in einem status gelandet sein, wo sich das avr studio "geweigert" hat, mit den "F"-tasten zu funktionieren.
danke nochmals !!!!

lg heinz

oberallgeier
27.09.2008, 09:37
Hallo HeSt,

Prima, daß Dein Assembler jetzt funktioniert. Ich hatte sehr gerne in Assembler programmiert - nur leider sind eben manche Probleme in Hochsprachen wirklich besser darstellbar. Und ich hatte mich deswegen mühselig in C eingearbeitet und kann mittlerweile einfachere Dinge schon in C schreiben - und die laufen sogar :).


... wo sich das avr studio "geweigert" hat, mit den "F"-tasten zu funktionieren ...Vermutlich war das dann aber eine der vielen Krankheiten des Betriebssystems. Aber das ist ja auch nicht mehr sooo wichtig. Viel Erfolg weiterhin

HeSt
27.09.2008, 18:00
hallo joe,

das "nicht funktionieren" dürfte daran liegen, dass ich einige male in den "power down mode" gehe (sleep). dann lässt das avr nichts mehr mit sich tun.

und dazu gleich eine frage:
wie erzeuge ich interrupts an den eingängen?
int0 oder pinchange?
die entsprechenden pinb anklicken (aktiv setzen) bringt nichts.

schönen abend

oberallgeier
27.09.2008, 18:55
Hi,


... frage: wie erzeuge ich interrupts an den eingängen? int0 oder pinchange? die entsprechenden pinb anklicken (aktiv setzen) bringt nichts ...? ? ? Reale Interrupts im Programm oder im Simulator? Im Simulator hatte ich das nie hinbekommen; das hatte ich dann aufgegeben und lieber gleich im/mit Controller gemacht.

Im Ablauf: 1) ISR (Interrupt Service Routine) schreiben. 2) Interrupt-Initialisierungsroutine schreiben. 2) Interrupt Vector Tabelle schreiben. 3) Port/Pin richtig initialisieren (noch immer Software). 4) Freuen, dass der Interrupt die richtige Aktion auslöst.

Besserwessi
27.09.2008, 19:26
Soweit ich mich noch erinnere war das gar nicht so schwer einen Interrrupt auszulösen. Ist aber doch schon eine ganze Zeit her. Zur Not, wenn sonst nichts geht, könnte man auch die Interrupt flags direkt setzen. Wenn man ein spezielle Signal braucht gibt es noch einen externen Stimulus, ist aber auch nicht ganz einfach.

HeSt
27.09.2008, 21:33
freunde,
verzeiht einem ungeduldigen alten lehrling!
ich rede immer noch vom avr, also vom simulator.
der interrupt lässt sich übrigens schön auslösen, in dem man - so wie ich ja probiert habe - das pinb port bit auf ein setzt.
allerdings muss man dann 2x die f11 taste drücken!! erst dann greift der interrupt und springt in die i-routine.

so, und nun hab ich ein problem mit meinem programm, das ich nicht verstehe:
1. interrupt routine (irr) wird aufgerufen (das SREG wird gesichert)
2. irr macht ein rcall auf eine subroutine (ist ja normal oder?)
3. in der subr gibts unter anderen befehlen 2 schleifen mit rjmp
4. wenn die subr dann auf das ret(urn) kommt, springt das programm nicht in die aufrufende irr zurück (war ja rcall) sondern auf @$0000 - verhält sich also wie ein reset!!!
wie das ???
kann mir das einer erklären?
was mache ich falsch bzw. wo liegt der hund in meinem gedankengang?

oberallgeier
27.09.2008, 22:35
... ich rede immer noch vom avr, also vom simulator ...Vorsicht - "der avr" würde für mich eher nach dem realen Controller klingen als nach dem Simulator.


... problem mit meinem programm ...
1. interrupt routine (irr) wird aufgerufen (das SREG wird gesichert)
2. irr macht ein rcall auf eine subroutine (ist ja normal oder?)
3. in der subr gibts unter anderen befehlen 2 schleifen mit rjmp
4. wenn die subr dann auf das ret(urn) kommt, springt das programm nicht in die aufrufende irr zurück (war ja rcall) sondern auf @$0000 ...Hmmm, das klingt seltsam, und es klingt nicht gut. Also - ich weiß nicht, ob ein rcall in einer ISR sinnig (oder möglich - oder erlaubt) ist - ich hatte das nie gemacht. Ich lebe unter der Philosophie, dass die ISR so schnell wie möglich abzuarbeiten ist.

Nur nebenbei: die ISR wird mit reti abgeschlossen: Return from Interrupt, aber das wird Dir sicher schon klar sein.

Ich habe mal einen funktionierenden Code hervorgeholt, den ich vor Jahrhunderten in Assembler geschrieben hatte und der noch immer in meinem tiny13 als potentionmeter-getriebener Servotester funktioniert. Vielleicht hilft Dir so ein Anschauungsmaterial. Ich muss dazusagen, dass das kein perfekter Code ist - also er hat nicht direkt Vorbildfunktion. Aber der hex-file läuft.


; >>>
; Sicherung vom 21jan08, 13:46 nach Datei servo1_servoplatine-gut.asm
;================================================= ==================================
;========== Beachte: printout aus AVRStudio geht (nur) bis col 85 ==================
;* Target MCU : ATtiny13
;* Target Hardware : Servotester-Platine, Servo auf PB3, Poti auf PB4
;* Target cpu-frequ. : 9,6 MHz, interner Oszillator
;================================================= ==================================
;*Versionsgeschichte:
;====================
;x1599 21jan08 12ff Version für die "Servoplatine" adaptiert
; dazu ISR+adc+servo1-x30-gut.asm abgeändert.
;
;* Aufgabenstellung
;* ISR zum Testen von einem Servo mit der Servotester-Platine
; Servo über getrennte Leitung, max. 1 A, vom Labornetzteil versorgt
; Portbelegung, Schaltung auszugsweise (übernomm. v. srv-adc+pwm-x29b.asm)
;
; Vcc(6V LNT) -----------------------------------------Vcc-Servo1
;
; Vcc -----------tiny13-Vcc
; |
; | tiny13-PB3=pin2----------------+---Signal-Servo1
; | |
; | + - - (poti1->ADC) - - +
; | |
; | tiny13-PB4--------+
; | V
; | +--------+
; +-------------------+ 25klin +----+
; +--------+ |
; |
; GND---------------------------------------+----------GND-Servo1
;
;* ===============================================
; ######### Original war: ISR+adc+servo1-x30-gut.asm
;================================================= ==================================
; Speicherbelegung
;r14 low-Byte der ADC-Wandlung (lsb)
;r15 high-Byte der ADC-Wandlung: Beide ergeben einen Wert zwischen 0...1023
;r16 Mehrzweck, high-Byte in Pausen "pause_an", "pause_aus", "led_ende" u.ä.
;r17 low-Byte in Pausen, s.o.
;
;r18 -- --
;r19 -- --
;r20 -- --
;
;r21 Byte Steuerbyte. Es werden nur Bits ausgewertet
; "Rampe" an-aus, 1=Rampe an, bit 1 = Servo 1 , bit 5 = Servo 2
; Übernahme Rampenwert, 1=Daten wurden noch nicht übernommen vom
; Hauptprogramm in die ISR, 0=ISR hat Daten abgeholt und zwar
; bit 2 = Servo 1 , bit 6 = Servo 2
;r22 1/2 Word lsb Zeitwert Rampe aus auf-ab-Rechnerei im Hauptprogramm
;r23 1/2 Word msb zu r26
;r24 1/2 Word LSB Zeitwert Rampe aus ADC/Poti im Hauptprogramm
;r25 1/2 Word MSB wird vom r14/15 geholt und zurechtgerechnet
;
;r26 1/2 Word LSB GESAMTDAUER Servoimpuls MUSS in r26/27 sein wegen sbiw
;r27 1/2 Word MSB Zeitwert für GESAMTDAUER Servoimpuls, etwa 500 Takte ?
;
;r28 1/2 Word lsb LSB RampenCOUNTER Servo1, wird in ISR runtergezählt
;r29 1/2 Word msb MSB Counter stammt vom ADC
;r30 1/2 Word lsb LSB Auf-ab-COUNTER Servo2, wird in ISR runtergezählt
;r31 1/2 Word msb MSB
;
;================================================= ==================================

#include "tn13def.inc"

; Interrupthandler-Tabelle (aus ATTiny13_doc2535.pdf, Seite 42)
; Address Code Labels ; Comments
.org 0x0000 rjmp anfang ; Reset Handler
;.org 0x0003 rjmp isr_x13 ;TIMO_OVF = Timer0 Overflow Handler

.org 0x0006 rjmp isr_ctc ; IRS für Timer0 CompareA Handler
;
;================================================= ==================================
; Deklarationen für den Praeprozessor
; Konstanten ####### evtl. auch keine Konstanten definiert
;
#define a r16 ;Kurzbezeichung für Allzweckregister r16
;
; Anschlussbelegung, vgl. srv-adc+pwm-x29b.asm
#define servo1 3 ;Servo 1 auf Port PB3
#define potiadc 2 ;Poti 1 am Port PB4 geht auf ADC-Kanal 2
;
;Festwerte und div. Daten für adcdat etc ====>
#define adcpsc 7 ; =7 ==> Clock/128, 9,6 MHz werden 75 kHz, vgl. doc, S93
;#define adcdat 3 ;Dummywert vom ADC ###>>> "Nur" 8Bit-Wandlung ! ? !
#define msges 5000 ;Gesamtzeit der Rampe: 5000 ISR-Zyklen sind 20 ms
#define mstest 500 ;Test ergibt mit mstest 500 und tmprs 40: für 2,0 ms
; das heisst: 4 µs Interrupt-Abstand (Fehler +4%)
#define msmin 180 ;Minimalwert für Rampe: 0,5 ms
#define tmrprs 38 ;Preset im Timer-Register (hoffentlich für CTC)
;
;================================================= ==================================
; Hauptprogramm
;================================================= ==================================
;
anfang:
rcall mc_init ;Initialisiere den Mikrocontroller
rcall isr_ini ;Initialisiere die ISR
rcall adcinit ;ADC initialisieren
;
sei ; .. Interrupts allgemein zulassen im Hauptprogramm

anfang2:
rcall adc_hol ;Hole Daten vom ADC in Register low=r14 + high=r15
;
rjmp anfang2
;
;================================================= ==================================
; Prozeduren
;================================================= ==================================
;
adc_hol: ;=== Hole Daten vom ADC, wird erst NACH der Wandlung verlassen
;Nach Wandlung sind die Werte in; L=r14,H=r15
ldi r16,potiadc ;Poti ist auf ADC-Kanal 2. >> Kanal 0 = Pin1=PB5,
; K1(ADC1)=Pin7=PB2, K2=Pin3=PB4 und K3=Pin2=PB3
rcall rdadc ;ADC auslesen
ret ;=====----->>>>>
;
rdadc: ;=== Hole Daten vom ADC in die Register low=r14 + high=r15
out admux,r16 ;vgl.doc2535, S 79
sbi admux,adlar ;Result left adjusted ==> NUR 8-Bit-Wandlung
sbi adcsra,adsc ;Wandlung starten
adrdy: ;=== Warte bis ADC fertig ist ==> ZWEI Wandlngen durchführen
sbic adcsra,adsc ;Skip if bit is cleared => dann ADC fertig
rjmp adrdy ; .. wenn nicht fertig, warten
adrdyb:
sbic adcsra,adsc ;Erst zweite Wandlung bringt korrektes Erbebnis,
rjmp adrdyb ; vgl. doc2535, sonst könnte alter Wert da sein.
; Dies dann, ###===>>> nur WENN der Kanal umgeschaltet wurde.
; in r14,adcl ;Daten einlesen, zuerst Low-Byte - nicht bei adlar
in r15,adch ;vgl. oben ADMUX (out 3), ADSC
ret ;=====----->>>>>
;
;================================================= ==================================
; Initialisierungprozeduren, für µC, ADC und Timer-ISR
;================================================= ==================================
;
mc_init: ;=== Initialisiere Mikrocontroller
; PB0 (servo1) + PB3 (servo2) = Ausgang, PB4 (poti1) = ADC-Eingang
ldi r16,0b00001000 ;Ausgang (1) auf PB3, Eingang (0) auf PB4
out ddrb,r16 ;Datenrichtungsbits setzen, Port ist Ausgang
ldi r16,0b11100111 ;Datenrichtungsbits setzen
out portb,r16 ; und initialisieren
ret ;=====----->>>>>
;
adcinit: ;=== Initialisiere ADC und hole Daten
ldi a,adcpsc ;Setze adpsc0,1,2 => Clock/128, siehe define
out adcsra,a ;Lade prescaler ==> 75 kHZ, siehe define
sbi admux,potiadc ;Wandlung vom Kanal ADC2 = PB4 = potiadc
sbi admux,adlar ;Result left adjusted ==> NUR 8-Bit-Wandlung
sbi adcsra,aden ;AD en-able einschalten
sbi adcsra,adsc ;Wandlung zum Initialisieren starten
;
adrdy_in1: ;=== Warte bis ADC fertig ist ==> ZWEI Wandlngen durchführen
sbic adcsra,adsc ;Skip if bit is cleared => dann ADC fertig
rjmp adrdy_in1 ; .. wenn nicht fertig, warten
adrdy_in2: ;=== Warte bis ADC fertig ist ==> ZWEI Wandlngen durchführen
sbic adcsra,adsc ;Skip if bit is cleared => dann ADC fertig
rjmp adrdy_in2 ; .. wenn nicht fertig, warten
;
ldi r16,0b00000000 ;Dummy-Muster
mov r14,r16 ;Muster nach r14
mov r15,r16 ; .. und nach r15

sbi adcsra,aden ;AD en-able einschalten
sbi adcsra,adsc ;Wandlung zum Initialisieren starten

ret ;=====----->>>>>
;
;
isr_ini: ;=== Initialisierung der ISR (war mal isrinit_x15)
;
;;Timer Register werden belegt
ldi a,(1<<wgm01) ;prescaler ist 1, waveform = CTC
out tccr0a,a ;Register TCCR0 ist für den Prescaler zuständig
ldi a,(1<<cs00) ;prescaler ist 1 => kein Prescaling, Abstand zum
out tccr0b,a ; nächsten Interrupt bestimmt der
; CTC-Prescaler tmrprs
ldi a,(1<<ocie0a) ;Hier Interrupts nach CTC eingeschaltet
out timsk0,a ;Register TIMSK ist dafür zuständig
ldi a,tmrprs ;Hier wird der Timer vorgeladen. Er läuft dann
out TCNT0,a ; .. tmrprs mal durch bevor ein Interrupt auftritt
out ocr0a,a ;Preset tmrprs in OCR0A
; .. Interrupts allgemein zulassen im Hauptprogramm
; ret ;=====----->>>>>
;
;
;================================================= ==================================
; Interrupt-Service-Routine
;================================================= ==================================
;
;
isr_ctc: ;=== Immer wieder Veränderungen
;
sbiw r27:r26,1 ;Pointer von 5000 runterzählen
; für EINEN Servoimpuls von 20 ms
breq i_neu ; .. und Spezialfall "null" ==> Daten umschaufeln
; und Rampe einschalten. WENN keine neuen Daten da
; sind, dann alte Daten nehmen :(
;
; Jetzt wird nachgesehen, ob eine der Rampen ausgeschaltet werden muss. Das steht
; im Portbit ddrb
;
isr_nn: ;=== Zähler der ISR steht nicht auf Null
s1_pran: ;=== Servo1: Prüfe, ob Rampe ist an
in r20,portb
sbrs r20,servo1 ;Ist Rampe von servo1 an?
rjmp i_raus ;Wenn Rampe nicht an ist, dann gleich raus
;
sbiw r29:r28,1 ;Decrementiere Rampenwert
brne i_raus ;weiter, wenn noch nicht Null zu servo2
cbi portb,servo1 ; Sonst Lösche Rampe für Servo1
;
rjmp i_raus

sbiw r29:r28,1
breq i_neu
rjmp i_raus

i_neu: ;Daten neu in Register und port servo1 umschalten
ldi r28,low(msmin)
ldi r29,high(msmin)
; mov r17,r15 ;Es ist adlar gesetzt, das ADCLH ist in r15
ldi r18,0
add r28,r15
adc r29,r18
;Und ADC-Wert gleich nochmal draufaddieren
; aber nur die Hälfte
clc ;Clear carry für anschliessende Division /2
ror r15 ;
add r28,r15
adc r29,r18
;
ldi r26,low(msges) ;Loopgrenzen für Gesamtloop 20 msec
ldi r27,high(msges)
sbi portb,servo1 ; Und setze Rampe für Servo1. FERTIG
;
i_raus:
reti ;=====----->>>>>
;
;================================================= ==================================
; Ende des Quellcodes
;================================================= ==================================

Vielleicht postest Du mal Deinen code? Auf jeden Fall mal viel Erfolg für Dich.

Besserwessi
28.09.2008, 00:20
Das SREG muß man selber retten. In der Regel ist das der Erste befehl nach dem RJMP in der Vektor tabelle.

Es spricht nicht viel gegen rcall in der ISR.

Eine Ursache für das Problem könnte ein nicht initialisierter Stack sein. Wenn der Stack noch irgendwo im Nirwana ist, dann kann der Rücksprung auch schon mal nach 0000 gehen.

HeSt
29.09.2008, 06:59
danke euch beiden für die antworten!

ich rufe aus der ISR deshalb eine SR auf, weil ich diese auch aus anderen SRs aufrufe - ich sie also mehrfach verwende.
und es sollte kein problem dadurch geben - gibt es auch nicht - siehe weiter unten.

der tip mit dem stack war gut!
ich hatte die initialisierung irgendwo abgekupfert und die dürfte wohl nicht für den tiny 13 gedacht sein ...
nach dem init stand die stapeladresse des SPL auf 0 !!
ich hab die stack initialisierung mal raus genommen und siehe da, zumindest der simulator setzt die richtige größe (endadresse) ein.
habe dann sicherheitshlaber das richtige init eingesetzt.
habe dann weiter getestet - nun läufts richtig!!
also war das SPL das problem.

wie poste ich meinen code?
aber das hat sich ja nun erübrigt oder?

übrigens danke für den deinen!
muss mich erst einlesen und verstehen!
hab meinen servotester mit einem 555er gemacht.
funktioniert auch tadellos.

oberallgeier
29.09.2008, 08:51
... wie poste ich meinen code?
aber das hat sich ja nun erübrigt oder? ...Code posten:
Button [C ode] drücken ( danach steht dort [C ode*] ) ,
jetzt den Code eingeben (z.B. copy und paste)
Button [C ode*] drücken.

Oder: Code eingeben, markieren und danach den Button [C ode] drücken.
Anmerkung: Zwischen "C" und "o" muss hier im Text ein Zwischenraum stehen, damit die Schaltfunktion für den Code (an dieser Textstelle im Posting) nicht aktiviert wird.

Deinen Code zu lesen hat sich ja nun erübrigt. Obwohl - wär ja mal interessant . . . . .


... übrigens danke für den deinen!
muss mich erst einlesen und verstehen! ...Ich hatte gerade festgestellt, dass mein Beispiel total schlecht ist. Dieses hier ist vermutlich etwas besser: Stack ist initialisiert, Stackpointer wird in der ISR gesichert und so. Man entwickelt sich halt mit der Zeit *ggggg*.


/* ================================================== ===============================
Sicherung 20dez07 1732 nach Datei ..A1..\minitest\minitest_x10.asm
================================================== =================================
..............
================================================== =================================
*/
/* ================================================== ============================ */
; Deklarationen für den Praeprozessor und Konstanten (s.auch oben Speicherbelegung)

#include <m168def.inc>

#define a r16 ;Kurzbezeichung für Allzweckregister r16
#define ac r17 ;Kurzbezeichung für ISR-Allzweckregister r17
;================================================= ==================================
/* Interrupthandler-Tabelle
Address Code Labels ; Comments */
.org 0x0000 rjmp RESET ;Reset Handler, power-on, brown-out, watchdog
/*.org 0x0001 rjmp xt_int0 ; EXT_INTO IRQO Handler
.org 0x0002 reti ; PCINTO PCINTO Handler
.org 0x0003 reti ; TIMO_OVF ;Timer0 Overflow Handler
.org 0x0004 reti ; EE_RDY ;EEPROM Ready Handler
.org 0x0005 reti ; PITAL_COMP ;Analog Comparator Handler
.org 0x0006 reti ; TIMO_COMPA ;Timer0 CompareA Handler
.org 0x0007 reti ; TIMO_COMPE ;Timer0 CompareE Handler
.org 0x0008 reti ; WATCHDOG ;Watchdog Interrupt Handler
.org 0x0009 reti ; ADC ;ADC Conversion Handler
*/
;================================================= ==================================
; Hauptprogramm
;================================================= ==================================
;
RESET: ;=== Generelle Initialisierung
; Zuerst RAMEND und Stack definieren;
ldi a,RAMEND ;Ram Ende des tiny13
out SPL,a ;Stack Pointer tiny13 setzen
anfang: ;=== Nur ein Dummy-Hauptprogramm
rcall init_mc ;Initialisiere den Mikrocontroller
rcall ini_xti0 ;Initialisiere die ISR für external Interrupt
sei ; .. und Interrupts allgemein zulassen
a2:
rcall tiw ;Tu irgend etwas
rjmp a2
;
;================================================= ==================================
; Prozeduren
;================================================= ==================================
;
tiw: ;=== tuirgendwas ... ein bisschen code muss ja abgearbeitet werden
push a
in a,SREG
push a
push ac ;AUCH das ISR-Allzweckbyte - auf alle Fälle
tiw_pop:
pop ac
pop a
out SREG,a
pop a
ret ;=====----->>>>>
;
;================================================= ==================================
; Initialisierungen für µC und ISR
;================================================= ==================================
;
init_mc: ;=== Initialisiere Mikrocontroller
;ALLE Register initialisieren hier := pull-ups einschalten
ldi a,0b00111100 ;
out ddrb,a ;Datenrichtung "Ausgabe" für alle ports
ldi a,0b00000000
out portb,a ; und alle ports ausschalten = low = sink
ret ;=====----->>>>>
;
ini_xti0: ;=== Initialisiere external Interrupt
;
;(1<<ISC01 | 1<<ISC00)
ldi a,0b00000011 ;Init. extern. Interrupt0 auf rising edge
out MCUCR,a ; d.h. MCUCR ISC00,01 auf 1 (doc2535,S 55)
ldi a,0b01000000 ;(1<<INT0)
out GIMSK,a ; und erlaube diese I´s in GIMSK
ret ;=====----->>>>>
;
;================================================= ==================================
; Interrupt-Service-Routine #######>>>>>
;================================================= ==================================
;
xt_int0: ;=== Markiere Arbeit dieser ISR
push a
in a,SREG
push a
ldi a,0b00001000 ;Bit3 (PB3) einschalten
out portb,a
int0_pop:
pop a
out SREG,a
pop a
reti ;=====----->>>>>
;
;================================================= ==================================
; Ende des Quellcodes
;================================================= ==================================

HeSt
30.09.2008, 08:26
hallo joe,
entschuldige, gibts einen "C" oder "C ode" button?
ich hab hier ein kleines fensterchen "Kurz Antwort" mit 4 buttons drunter, aber keiner davon entspricht dem genannten.
auch nicht im header oder unter jeder antwort werde ich fündig.
tut mir leid, da stehe ich auf der leitung!
gib mir nen schubbs zur seite !!

oberallgeier
30.09.2008, 09:20
1) Du kannst in das Fenster [Kurze Antwort] schreiben. Da gibts den Button (noch) nicht. Wenn Du dann auf [Vorschau] drückst - ahhhhhhhh :).
2) Wenn Du gleich auf [zitat], rechts oben, oder [antwort erstellen], links unten, drückst - dann kommt auch das volle Programm . . . . .
In beiden Fällen kommt der verschwundene C ode Button zum Vorschein :). Nicht zum Lesen gedacht: Die dämliche Organisation der Computer ist so komplex, dass man zum an sich unerwünschten "mal den Button drücken, mal den hier versuchen" animiert wird. Und sich dann wundert, wenn man in der Wüste/im Wald landet.

Hier im Forum kann eine halbe Stunde in der guten FAQ-Stube (https://www.roboternetz.de/phpBB2/faq.php), Schaltfläche siehe oben-mitte, nützlich sein. Man muss ja nicht alles lesen - Hauptsache, man weiss wo man es findet.

Uuuups - war das DER Schubs? Viel Erfolg.

HeSt
30.09.2008, 12:05
danke für den schubs !! ;-)
er hat geholfen :-)

nun mein code, denn erfunktioniert im simulator 1a (da werden die interrupts per mausklick im PINB aktiviert), aber leider nicht mit dem tiny13 :-(.


;************************************************* ******************
;*
;* Programm: GTRiegel
;*
;* Version: 1.0
;*
;*
;* Portzuordnung:
;* PB0 < Schalter Tor zu PinChange Interrupt
;* PB1 < Impuls Tor auf/zu INT0 Interrupt
;* PB2 < Sensor Riegel offen/zu 3 Zustände möglich!!! (ADC)
;* PB3 > Riegel auf
;* PB4 > Riegel zu
;* PB5 > Impuls zur Torelektronik auf/zu
;*
;* PB0 und PB1 sind Interrupt gesteuerte Eingänge ;*
;************************************************* ******************
; Definitionen
;************************************************* ******************

#include "tn13def.inc"

.cseg
.org 0x000 ; Interrupt Vektoren
rjmp MAIN ; Hauptprogrammroutine (Reset)
rjmp IMPLS ; Interrupt0
rjmp CLSLATCH ; PinChangeInterrupt Tor zu
rjmp TMROVF ; TimerOverFlow für Warteschleife
rjmp MAIN ; Hauptprogrammroutine
rjmp MAIN ; Hauptprogrammroutine
rjmp MAIN ; Hauptprogrammroutine
rjmp MAIN ; Hauptprogrammroutine
rjmp MAIN ; Hauptprogrammroutine
rjmp MAIN ; Hauptprogrammroutine

.def TIM1 = R16 ; Timer 1 für Wartezeit
.def TIM2 = R17 ; Timer 2 für Wartezeit
.def TSEC = R18 ; Sekundenregister für Zeitügabe
.def SCNT = R19 ; Sekundenregister für Timer
.def TEMP = R20 ; dient für diverse Operationen
.def SAVR = R21 ; dient zum Sichern des SREG
.def ADCV = R22 ; enthält Wert von ADCH nach AD-Wandlung
.def DIMI = R23 ; "Door In Motion" Indikator
; dient zur Abfrage bei weiterem Impuls
; während Tor in Bewegung
; Bit Bedeutungen:
; Bit2: 1=Tor während Bewegung gestoppt
; Bit1: 0=Bewegung war auf, 1=war zu
; Bit0: 1=in Bewegung, 0=abgeschlossen



;************************************************* ******************
MAIN:
;************************************************* ******************
; Generelle Initialisierungen
;-------------------------------------------------------------------
; Allgemeine Interrupts verhindern
cli

; Arbeitsregister löschen
clr ADCV
clr DIMI

; Stack anlegen
ldi temp,LOW(RAMEND)
out SPL,temp

; Ports initialisieren
ldi temp,0b00111000 ; Register für DDRB in/out laden
out DDRB,temp ; 0 1 2 Eingänge 3 4 5 Ausgänge
ldi temp,0b00000010 ; Register für PORTB PB1 laden
out PORTB,temp ; PB1 PullUp, Ausgänge low

; ADC initialisieren
ldi temp,6 ; Reg. für ADC prescaler laden
out ADCSRA,temp ; 6 (divider 64) sind ca 18kHz
ldi temp,1 ; Reg. für ADMUX PB2 laden
out ADMUX,temp ; ADMUX setzen

; Wait-Timer setzen
ldi temp,3 ; Vorteiler /64
out TCCR0B,temp

; Interrupt Register setzen
ldi temp,0b00110001 ; 0bxxxxxxx1 INT0 jede Level-
; änderung löst Interrupt aus
out MCUCR,temp ; 0bxx11xxxx Sleep-Mode auf
; PowerDown setzen
ldi temp,0b01100000 ; GIMSK Interrupt Mask setzen
out GIMSK,temp ; Int0 und PinChange enable
ldi temp,1 ; PinChange auf PB0 beschränken
out PCMSK,temp

; Initialisierung beendet > enter SLEEP-Modus
rjmp pwrdwn ; dann power down


;************************************************* ******************
;******************** START INTERRUPT ROUTINEN *********************
;************************************************* ******************

;************************************************* ******************
IMPLS: ; OK
;-------------------------------------------------------------------
; Interruptroutine (INT0):
; Diese I-Routine besitz nur den RJUMP Befehl zur Subroutine, da
; diese auch aus dem Programmablauf aufgerufen werden können muss.
; SREG braucht nicht gesichert zu werden, da keine Rückkehr
; erfolgt (rjmp).
;************************************************* ******************
rjmp impuls
reti

;************************************************* ******************
CLSLATCH: ; OK
;-------------------------------------------------------------------
; Diese Interruptroutine wird durch den PINchange (Tor zu) an PB0
; ausgelöst und schließt den Riegel
;-------------------------------------------------------------------
in savr,SREG
cli ; weitere Interrupts verhindern
clr dimi ; Tor in (Soll)Endstellung ZU
sbi portb,4 ; PB 4 ein = Riegel schließen
WCLSLATCH:
rcall readadc
sbrs adcv,0 ; Bit1=1 = Riegel geschlossen
rjmp wclslatch ; neue Abfrage bis geschlossen
cbi portb,4 ; PB 4 aus
rjmp pwrdwn
reti

;************************************************* ******************
TMROVF: ; OK
;-------------------------------------------------------------------
; Diese Interruptroutine wird durch den TimerOVF Interrupt
; aufgerufen
;-------------------------------------------------------------------
in savr,SREG
clr tim1
out TCNT0,tim1 ; Timer zurücksetzen
inc tim2
cpi tim2,75 ; 75 cylces = ca. Sekunde
brlo tmrovfe
clr tim2
inc scnt ; Sekundenzähler um 1 erhöhen
TMROVFE:
out SREG,savr
reti

;******************** ENDE INTERRUPTROUTINEN ***********************



;************************************************* ******************
;*********************** START SUB ROUTINEN ************************
;************************************************* ******************

;************************************************* ******************
IMPULS: ; OK
;-------------------------------------------------------------------
; Fragt Status von Tor und Riegel ab und führt entsprechende
; Routinen aus
;-------------------------------------------------------------------
cli ; weitere Interrupts verhindern
cpi dimi,0 ; DIMI=0, Torbewegung abgeschlossen
breq normrun ; wenn ja, normaler Ablauf
sbrc dimi,2 ; Bit2=0 Tor in Bewegung > skip
rjmp revers
rcall doorimp ; Bewegung stoppen
sbr dimi,2 ; Tor in Bewegung gestoppt
rjmp pwrdwn

REVERS: ; letzte Torrichtung umdrehen
sbrs dimi,1 ; Tor Richtung war ZU > skip
rcall clsdoor ; mit Schließen des Riegels
rcall doorimp ; Tor öffnen
cbr dimi,1 ; Torrichtung = AUF
ldi tsec,30 ; Wartezeit laden
rcall wait
clr dimi ; wenn alles OK, DIMI = 0
rjmp pwrdwn

NORMRUN:
sbis pinb,2 ; Abfrage Riegel zu? > skip
rjmp clsdoor ; Tor + Riegel schließen
rjmp opndoor ; Riegel + Tor öffnen
ret

;************************************************* ******************
READADC: ; OK
;-------------------------------------------------------------------
; Liest den A/D Wandler an PB2 aus ADC 10 Bit Messung
; Da es sich hier um keine digitale „Spannungsmessung“ handelt
; sondern lediglich um eine „Statusabfrage“ reicht hierzu nur die
; Abfrage des Hi-Byte (ADCH):
; 0V = Riegel offen ADCH = 0
; 2V = Riegel „unterwegs“ ADCH = 1
; 5V = Riegel geschlossen ADCH = 3 (oder Abfrage dec. >1)
; Die Messung muss doppelt erfolgen ansonsten wird die vorherige
; Messung eingelesen – Eigenheit des Tiny13
;-------------------------------------------------------------------
sbi ADCSRA,ADEN ; ADC ENable
sbi ADCSRA,ADSC ; ADC Start Conversion

WREADADC1: ; Schleife bis Messung
sbic ADCSRA,ADSC ; abgeschlossen = ADSC=0
rjmp wreadadc1
sbi ADCSRA,ADSC ; ADC Messung 2tes Mal starten



WREADADC2:
sbic ADCSRA,ADSC ; warten bis ADSC = 0
rjmp wreadadc2
in temp,ADCL ; lowbyte einlesen
in adcv,ADCH ; highbyte einlesen
cbi ADCSRA,ADEN ; ADC ausschalten
ret

;************************************************* ******************
CLSDOOR: ; OK
;-------------------------------------------------------------------
; Schickt Tor Impuls zum Schließen, setzt DIMI
;-------------------------------------------------------------------
rcall doorimp
sbr dimi,1 ; Bit1=1 = Torbewegung ZU
rjmp pwrdwn ; sleep bis Interrupt „Tor zu“ (PB0)
ret

;************************************************* ******************
OPNDOOR: ; OK
;-------------------------------------------------------------------
; Öffnet Riegel und Tor
;-------------------------------------------------------------------
sbi portb,3 ; PB 3 ein = Riegel öffnen

WOPEN:
rcall readadc
nop
cpi adcv,0 ; compare (0=offen)
brge wopen ; neue abfrage bis offen
cbi portb,3 ; PB 3 aus
rcall doorimp
ldi dimi,1 ; Torbewegung AUF
ldi tsec,30 ; Wartezeit von 30 Sek. für Wait
rcall wait ; Warteschleife
clr dimi ; DIM Indikator löschen
rjmp pwrdwn
ret

;************************************************* ******************
DOORIMP: ; OK
;-------------------------------------------------------------------
; schickt einen Impuls von etwa 1 Sekunde zur Torelektronik
;-------------------------------------------------------------------
ldi tsec,1 ; Zeit für Impulsspreizung in Sek
sbi portb,5 ; PB 5 ein = Impuls Tor auf
rcall wait ; Impulsspreizung
cbi portb,5 ; PB 5 aus
ret


;************************************************* ******************
WAIT: ; OK
;-------------------------------------------------------------------
; Warteschleife für diverse Zeitverzögerungen
; Wartezeit wird von aufrufender Routine in TSEC übergeben
; Es wird der PinChange Interrupt kurzfristig unterdrückt, da beim
; Öffnen des Tores ungewollt ein PCI ausgelöst wird.
; Der PCI wird vor dem enable wieder gelöscht, damit dieser nicht
; nach der generellen Freigabe der Interrupts ausgeführt wird.
;-------------------------------------------------------------------
in temp,GIMSK
cbr temp,5
out GIMSK,temp ; PCI disable
clr scnt
ldi temp,2
out TIMSK0,temp ; Timer Interrupt freigeben
; Timer läuft .....
sei
LOOP:
cp scnt,tsec ; Sekunden abgelaufen?
brlo loop
clr temp
out TIMSK0,temp ; Timer Interrupt sperren, restliche
; Interrupts bleiben ein
in temp,GIFR
cbr temp,5
out GIFR,temp ; PCI löschen
in temp,GIMSK
sbr temp,5
out GIMSK,temp ; PCI enable
clr scnt
ret

;************************************************* *****************
PWRDWN: ; OK
;-------------------------------------------------------------------
; Beendet alle Funktionen, gibt die Interrupts frei und schaltet ab
; Ein Interrupt an PB1 (INT0) oder an PB0 (PinChange) aktiviert
; den Processor wieder
;-------------------------------------------------------------------
sei ; alle Interrupts frei geben
sleep ; Power Down, warten auf Interrupts

;************************ ENDE SUBROUTINEN *************************

.EXIT


wenn ich ihn in den tiny lade (mittels platine und programm vom franzis lernpaket) scheint er sofort in die "clslatch" routine zu laufen, denn PB4 ist aktiv. wenn ich das signal an PB2 dann gegen Vcc bringe schaltet er ab.
aber - und das ist komisch, kein interrupt bringt ihn aus dem schlaf oder aus der schleife, die ich testweise statt der letzten zeile "pwrdwn" in MAIN probiert habe:

Kreis:
sei
nop
rjmp kreis

hast du eine idee??
wo hab ich meinen gedankenfehler?

schon vorab danke für deine mühe und geduld mit mir !!

oberallgeier
30.09.2008, 12:37
Auf die Schnelle kritisiere ich nicht gerne. Und es fehlt mir aktuell wirklich an Assembler-Erfahrung. ABER: Am Ende des main wird ein rjmp angetreten von dem es kein Zurück gibt . . . . und wenn es ein Zurück gäbe, dann würde IMPLS angesprungen. Denn ich sehe nicht, dass main eine Endlosschleife ist.

Aber wie gesagt - ich muss das mal in Ruhe durchlesen - und dann weiss ich ob ich überhaupt einen sinnvollen Kommentar abgeben kann. Vielleicht gibts erfahrene Assemblernutzer hier?

HeSt
30.09.2008, 12:54
auch die alternative mit der "kreis-schleife" statt dem power down (sleep) ändert nichts an der sache.
hab vergessen zu sagen, dass (auch beim testen am tiny) an
PB0 normal HI liegt (= LO interrupt)
PB1 ein LO impuls kommt
PB2 ein spannungsteiler (2x200k) mit vorwiderstand von 1k liegt, wobei immer eine seite durch einen schalter kurz geschlossen wird)
es hängt also nichts "in der luft".
PB3-PB5 zum testen je 1 led mit 1k gegen GND.

Besserwessi
30.09.2008, 18:02
Wenn man die ISRs nicht ordnungsgemäß wieder verläßt, wird der Stack irgenwann überlaufen. Das ist zumindestens bei clslatch der Fall.
Das CLI am Anfang der ISR ist unnötig, das I Flag ist schon gelöscht.

Hinter dem Sleep ist das programm einfach zuende, da muß eigentlich noch ein jump noch oben hin, denn nach einem Timer Interrupt geht es da weiter.

Der AD Wandler wird so weit ich sehr nie eingeschaltet, sondern nur der Teiler gesetzt-

HeSt
30.09.2008, 18:13
so, habe weiter getestet und bin auf einiges drauf gekommen:
egal wie PB5 konfiguriert ist:
als eingang mit einem 10k R gegen GND
oder als ausgang mit einer led+1k an GND
dann geht der tiny in den RESET modus !!!

der int0 funktioniert weder mit + noch mit - flanke.
habe alle varianten im MCUCR gesetzt und probiert.
nur pinchange funktioniert (ausgenommen an PB5 - siehe oben).

das habe ich mit folgendem testprogramm raus gefunden:

; TEST

#include "tn13def.inc"

.cseg
.org 0x000 ; Interrupt Vektoren
rjmp MAIN ; Hauptprogrammroutine (Reset)
rjmp IMPULS ; Interrupt0
rjmp IMPULS ; PinChangeInterrupt Tor zu
rjmp SCHLEIFE ; TimerOverFlow für Warteschleife
rjmp SCHLEIFE ; Hauptprogrammroutine
rjmp SCHLEIFE ; Hauptprogrammroutine
rjmp SCHLEIFE ; Hauptprogrammroutine
rjmp SCHLEIFE ; Hauptprogrammroutine
rjmp SCHLEIFE ; Hauptprogrammroutine
rjmp SCHLEIFE ; Hauptprogrammroutine

.def PORT = R16
.def TEMP = R17

MAIN:
cli
ldi temp,LOW(RAMEND)
out SPL,temp
ldi temp,0b00011000 ; Register für DDRB in/out laden
out DDRB,temp ; 0 1 2 5 Eingänge 3 4 Ausgänge
ldi temp,0b00000010 ; Register für PORTB in/out laden
out PORTB,temp ; Eingang 1 pullup
ldi temp,0b00000011 ; 0b00000010 INT0 N-Flanke
out MCUCR,temp
ldi temp,0b01100000 ; GIMSK Interrupt Mask setzen
out GIMSK,temp ; Int0 und PinChange enable
ldi temp,33 ; PinChange auf PB0 + PB5 beschränken
out PCMSK,temp
sei

SCHLEIFE:
nop
rjmp schleife

IMPULS:
cpi port,0
brne next
ldi port,8 ; mit 8 und 16 PB3 + PB4 ein
NEXT:
out portb,port
lsl port
cpi port,32
brne end
clr port
END:
sei
reti

nur PB3 + 4 liegen mit einer led + 1k an masse, sonst ist der tiny nicht beschalten.
als "schalter" an PB0+PB1 für die interrupts verwende ich einen 1k widerstand gegen Vcc oder Vdd.
wenn ich an PB5 als pin change tippe = reset!

am simulator des avr studio4 funktioniert obiger code tadellos.
warum nicht am tiny??
kann es sein, dass er defekt ist?

kann mir jemand weiter helfen?

HeSt
30.09.2008, 21:41
hallo besserwessi,

danke für deine mühe, dass du dir meinen code angeschaut hast!

ich dachte diese beiden befehle starten den wandler? ist dem nicht so?
sbi ADCSRA,ADEN ; ADC ENable
sbi ADCSRA,ADSC ; ADC Start Conversion

habe in "clslatch" die zeile >rjmp pwrdwn< durch >out SREG,savr< ersetzt, somit wird nun durch das >reti< die routine ordnungsgemäß beendet.

überflüssiges cli entfernt und am ende von "pwrdwn" ein rjmp auf den anfang eingefügt/angehängt.

hast du eine idee zum problem vom vorigen eintrag (INT0 + PB5)?

Besserwessi
30.09.2008, 22:13
Das mit dem ADC einschalten hatte ich übersehen. Ich stelle den Teiler immer gleich beim Einschalten mit ein, vorher braucht man es ja nicht.
Das mit dem AD Wandler könnte funktionieren.

Aus dem PowerDown Sleep mode geht nur pin change oder Int0 mit Level Interrupt. Beim Leven Interrupt muß man auch noch aufpassen das man den von Hand in der ISR Abschalten muß, sonst wird der gleich nochmal aufgerufen. So wie es aussiehr geht bein Tiny13 nur ein Interrupts auf low Signal !

Beim PB5 müßte man die Resetfunktion erst über die Fuses abschalten, bevor man den Pin als normalen IO Pin nutzen kann. Das heißt dann aber das man weder Debugwire noch das normale ISP Interface nutzen kann. Da würde ich eher probieren ob man da die normale Resetfunktion als Teil des Programmablaufts nutzen kann. Immerhin bleiben beim Reset einige Daten (RAM) erhalten. Man kann auch zwischen Signal an Reset und Power on unterscheiden.

HeSt
01.10.2008, 06:44
Beim Leven Interrupt muß man auch noch aufpassen das man den von Hand in der ISR Abschalten muß, sonst wird der gleich nochmal aufgerufen. So wie es aussiehr geht bein Tiny13 nur ein Interrupts auf low Signal !
was meinst du mit "Leven Interrupt" ?

das mit dem nutzen der reset funktion ist eine gute idee.
dazu braucht es aber nur einen impuls und kein solides signal.
lässt sich aber lösen.

danke

oberallgeier
01.10.2008, 09:16
... So wie es aussiehr geht bein Tiny13 nur ein Interrupts auf low Signal ...
The INT0 interrupts can be triggered by a falling or rising edge or a low level.

UUuuuups - könnte sein, dass Du das doc nicht hast oder suchen müsstest. Erstens: auf der At mel-doc-Download-site (http://www.atmel.com/dyn/products/datasheets.asp?family_id=607#791) steht zum Tiny13 : Not recommended for new designs. Nichts destotrotz: Hol es Dir (http://www.atmel.com/dyn/resources/prod_documents/doc2535.pdf) - zu jedem Controller gehört seine Dokumentation.

HeSt
01.10.2008, 12:58
ich hab das dokument.
habe auch die verschiedenen varianten des int0 ausprobiert (MCUCR seite 27) - aber der tiny reagiert auf keine einizige variante (- + oder beides).
es wird kein interrupt ausgelöst :-(
im simulator funktionierts tadellos (siehe code test programm) - ist eben SIMULATOR ...

habe mir gerade einen neuen tiny13 besorgt.
werde damit am abend testen ...

Besserwessi
01.10.2008, 17:37
Wenn der Tiny13 im Powerdown mode ist, reagiert er nur auf den Level Interrupt (Input an GND) am Int0 Pin. Die Interrrupts auf Flanken gehen dann nicht. Steht bei der Erklärung der Sleep Modes im Datenblatt.

oberallgeier
01.10.2008, 18:18
... Steht bei der Erklärung der Sleep Modes im Datenblatt ...Oh weh, da habe ich schlecht geraten - vielmehr völlig ungenügend das Datenblatt gelesen - - ich hatte einfach den Abschnitt Interrupt aufgeschlagen. Sorry ihr Beiden.

HeSt
01.10.2008, 18:29
aber nicht doch!
im kapitel 7.1.3 Power-Down Mode seite 31 steht als 3. satz (kopiert):
Only an External Reset, a Watchdog Reset, a Brown-out
Reset, an external level interrupt on INT0, or a pin change interrupt can wake up the MCU.
also kann JEDER dieser interrupts den tiny wieder wecken ...
auch aus dem power down modus.

oberallgeier
01.10.2008, 18:54
Ich glaube, damit steht der Sieger in diesem kleinen Lesewettbewerb eindeutig fest.

Besserwessi
01.10.2008, 19:23
Da steht im datenblatt aber auch die Einschränkung, das Int0 nur als Level Interrupts geht. Mit Pin Change interrupt ist der extra Pin-change Interruts gemeint, der auch die anderen Pins abdecken kann.
Was aber nicht geht ist Int0, wenn die Bits ISC01/ISC00 nicht 0/0 sind, also die Reaktion auf eine oder beide Flanken.

HeSt
01.10.2008, 19:48
Da steht im datenblatt aber auch die Einschränkung, das Int0 nur als Level Interrupts geht.
wo liest du, dass int0 nur als level interrupt geht? seite, kapitel?


Mit Pin Change interrupt ist der extra Pin-change Interruts gemeint, der auch die anderen Pins abdecken kann.
was meinst du mit "extra pin change" ??


Was aber nicht geht ist Int0, wenn die Bits ISC01/ISC00 nicht 0/0 sind, also die Reaktion auf eine oder beide Flanken.
das kann ich auch nicht raus lesen:
ISC01 ISC00 Description
0 0 The low level of INT0 generates an interrupt request.
0 1 Any logical change on INT0 generates an interrupt request.
1 0 The falling edge of INT0 generates an interrupt request.
1 1 The rising edge of INT0 generates an interrupt request.

HeSt
01.10.2008, 19:52
übrigens hab ich das problem mit dem int0 geklärt.
zum laden des codes habe ich das lernprogramm und die platine von franzis verwendet - funktioniert ja, da bei den experimenten diverse codes in den tiny geladen wurden. aber solange nur diese codes lädt.
nur, was ich nicht bedacht habe, es bleibt aufgrund des noch vorhandenen bootladers PB1 für den datenaustausch mit dem pc aktiv ...
daran hatte ich nicht gedacht !!
ich muss also ein anderes programm zum echten "flashen" (mit PB5 > gnd) verwenden, damit ich den bootloader raus bringe und nur mehr mein code drin ist.

Besserwessi
01.10.2008, 20:43
Das mit dem Int0 steht in der Fußnote zu Tabelle 11, und in der Erklärung zu External Interrupts.


Auch wenn da eine Bootloader drauf ist, kann man den Controller in den Powerdown Mode versetzen. Der Bootloader ist normalerweise nur direkt nach dem Reset aktiv. Wenn da kein Signal gefunden wird, wird das normale Programm gestartet ohne das der Bootloader wieder dran kommt. Man hat halt nur weniger Speicher (flash) zur verfügung als ohne Bootloader. Wenn man den Pin PB5 als reset lassen will, und über die Resetfunktion auswerten will, dann hat man natürlich mit den meisten Bootloadern ein Problem.

oberallgeier
01.10.2008, 20:46
... lernprogramm ... platine von franzis ... ... bootloader raus ...Grübelgrübelgrübel - ich habe das "Lernpaket Microcontroller" - und dazu habe ich ein Programm LPMicros und ein weiteres, das LPMicro ISP. Das erste lädt die Experimentierumgebung in den tiny13 - und der versprochene bzw. angeblich enthaltene Bootloader funktionierte bei mir leider nie. Daher hatte ich die selbst geschriebenen Programme als eigenständige Programme geschrieben und mit dem LPMicro ISP in den t13 geflasht. Lief immer bestens.

Das LPMicros verwende ich immer noch und immer wieder. Es ist recht praktisch, damit mal eine LED anzuknipsen um deren Stromaufnahme zu messen, oder ähnliche, einfache Abläufe, die immer wieder mal vorkommen schnell aufzubauen und zu testen.

Sternthaler
02.10.2008, 00:05
Ich grüße euch,

ich bin zwar auch nicht der Asm-Profi, aber ich versuche mal mitzuteilen wo (auch) ich eventuell ein Problem sehe.
Genau wie Besserwessi schon geschrieben hat, sehe ich auch an den gleichen Stellen ein grundsätzliches Problem.

Ich beziehe mich auf den einzigen geposteten Code von dir HeSt aus dem Post an dieser Stelle (https://www.roboternetz.de/phpBB2/zeigebeitrag.php?p=399645#399645).

Wenn ich richtig geschaut habe, landen deine 3 Interrupt-Funktionen (und auch die nicht genutzten über main) alle wieder an einem "rjmp pwrdwn". Nun geht das main so von oben in diese sleep-Funktion.

Jetzt wird also mit dem "sleep" die CPU geparkt, und der erwartete Interrupt sollte seine Arbeit machen und wieder mit dem "rjmp pwrdwn" aus der ISR bzw. den aufgerufenen Funktionen irgendwie wieder, von oben zum "sleep" kommen.

Was aber dann nicht in den ISR-Funktionen ausgeführt wird ist der Befehl "reti".
Genau hier würde ich nun das Problem erwarten, da die AVR's (und wohl auch alle andere CPUs) das I am reti nutzen um einige Bits und Bytes in der Interrupt-Hardware/Register zu richten.

Da ich nun nicht weiß, ob dieses von dir benutzte Schema grundsätztlich funktioniert in dieser CPU, kann ich natürlich auf dem Holzweg sein.
Ich habe auch nicht geprüft, ob diese nicht durchgeführte Aktion vom fehlenden reti, in deinem Programm überhaupt eine Auswirkung hätte.

Meine ASM's hatte ich immer so geschrieben:
main
- init-Code
- sprungmarke: sleep
- jump sprungmarke

ISR
- tu was zu tun ist
- reti

--> reti wird ausgeführt, und im main geht es nach dem sleep mit speed wieder zum sleep. Genau so hatte Besserwessi ja auch schon geschrieben.
--> Ansonsten können nun die ISRs machen was sie wollen, und ich muss nicht darauf achten, dass sie alle wieder mit dem "rjmp pwrdwn" von oben zum sleep kommen. Sonst fehlt ja tatsächlich danach ein weiterer Befehl.


überflüssiges cli entfernt und am ende von "pwrdwn" ein rjmp auf den anfang eingefügt/angehängt.Hast du das, was ich gerade so umständlich beschrieben habe, somit schon erledigt? (Besserwessi hat nur ca. 2% meiner Wortanzahl benötigt ;-))
Wohin springst du, wenn du ".. auf den anfang .." springst?

Ich hoffe, wir kommen gut in den Winter-Sleep und auch wieder raus.
Gruß Sternthaler

HeSt
03.10.2008, 06:55
hallo sternthaler, danke für deine ausführungen.

ich werde leider mit dem tiny13 meine problemstellung in „normaler“ form – also ohne änderung der fuses – nicht realisieren können, da mir der port5 nicht als ein-/ausgang zur verfügung steht sondern nur als reset eingang.
und ich hab leider kein programm mit dem ich fuses ändern kann.
somit baue ich eine „hybrid“ lösung mit zusätzlichen herkömmlichen ic’s.



Man kann auch zwischen Signal an Reset und Power on unterscheiden.
wie kann man das unterscheiden?
ich sehe keine möglichkeit, denn die i/o register werden alle resettet und das kann ich nicht brauchen, denn die ports müssen ihren status behalten um sie auslesen zu können.
und das lese ich nirgendwo raus, dass portb nicht auch auf 0 gestellt wird, samt ddrb.
portb, pinb und ddrb sind doch i/o register, oder irre ich hier?

auszug aus dem datenblatt:
During reset, all I/O Registers are set to their initial values, and the program starts execution from the Reset Vector.

eine frage noch zu den fuses:
ich müsste, um portb5 als „normalen“ ein-/ausgang verwenden zu können, das bit 4, SELFPRGEN Self Programming Enable im hi-byte der fuses umschießen – nachdem ich das programm geladen habe, denn sonst kann ich den tiny ja nicht mehr programmieren/flashen.
sehe ich das richtig?

Besserwessi
03.10.2008, 09:59
Es stimmt, das die IO Register beim Reset neu initialieseirt werde, aber eines davon, MCUSR, enthält genau die Information wodurch der Reset ausgelöst wurde.

Mit einigen Einschränkungen kann man also auch erkennen ob man ein Signal von der Resetleitung bekommen hat. Das geht natürlich nur für eher kurzr low Pulse, denn solange Reset low ist, läuf nichts. Außerdem werden wärend des Resets die anderen IO Ports alle hochohmig, was auch nicht immer paßt. Die Programmierung ist gerade für einen Anfänger alles andere als einfach. Zum Abfrage von Tastern oder Schaltern kann man oft mit relativ wenig externer schaltung auch so 2 oder 3 Tasten an einem Port abfragen. Beim Asuro wird dsa z.B. über den AD Wandler und Widerstände gemacht.

oberallgeier
03.10.2008, 10:50
Hallo HeSt,


... und ich hab leider kein programm mit dem ich fuses ändern kann ...Na ja, das stimmt nicht "in letzter Konsequenz". Das Programm LPmirkros setzt die fuses für die geforderte Umgebung, damit das Experimentierprogramm laufen kann. Das Ganze wird beim Laden des Bootloaders erledigt:

........................http://oberallgeier.ob.funpic.de/t13_fuses_1-1.jpg

Dabei werden folgende fuses gesetzt (Du hast ja mit einem "jungfräulichen" ATtiny13 angefangen):

........................http://oberallgeier.ob.funpic.de/t13_fuses_1-2.jpg

Das Testprogramm mit Bootloader sieht dann im Flash des t13 so aus:



:100000007FC1FFFFFFFFFFFFFFFFFFFFFFFFFFFFBE
:10002000B99AB5D0AAD0D6D1CAD0302F313011F478
:1000300004E6D9D0303119F4C2D0097108BB31318E
:1000400021F4BDD00B71026007BB323131F400E006
:1000500006BF03E80FBD02E003BF303211F406B360
:10006000C2D0303319F402E07ED0BDD0313319F460
:1000700003E079D0B8D0383329F402E07ED0B3D091
:10008000042FB1D0393329F403E077D0ACD0042F5A
:10009000AAD0303411F493D006BF343671F4A0E600
:1000A000B0E07DE378D00D937A95E1F7A0E6B0E07B
:1000B0007DE30D9198D07A95E1F7353681F4A0E68D
:1000C000B0E07FE168D00D9370D00D937A95D1F7B1
:1000D000A0E6B0E07EE30D9186D07A95E1F73A3F55
:1000E00011F439D080D03B3F11F401B77CD03C3FB4
:1000F00019F48FE37CD177D03D3F19F460D08FE3C2
:1001000089D03E3F11F45BD001BF3F3F09F401D0DD
:100110008BCF41B76AE04A5074E141BF1CD0015E09
:10012000003808F00095061720F410D0033008F4CA
:100130000DD059D043957A9581F74A50052F01BFCC
:100140008FE368D050D0042F4ED00895542F0395DC
:10015000602F0A950895B29BFECF00E00395000042
:10016000B299FCCF089507B93D9A369A3699FECFD9
:10017000369A3699FECF05B1089507B9369A369961
:10018000FECF369A3699FECF44B105B1089503E00B
:1001900006B9379A089502E207B9369A3699FECF22
:1001A000369A3699FECF05B1089503E207B9369A1B
:1001B0003699FECF369A3699FECF05B10895B29B97
:1001C000FECF1AE31A95F1F700E028E00695B29900
:1001D000006816E21A95F1F72A95C1F716E21A950A
:1001E000F1F700950895C19A16E21A95F1F728E003
:1001F00000FD03C000C0C19A02C0C19800C016E251
:100200001A95F1F706952A9599F7C19816E21A956D
:10021000F1F70895E199FECF90E09CBB8EBB0DBB3A
:10022000E29AE19A08956CC0FFFFFFFFFFFFFFFF16
:1002F000FFFFFFFFFFFFFFFFFFFFFFFF91CEFFFFAD
:10030000F89468D0B29901C0F9CFB299FECFB99AEA
:1003100009E649D034D0302F393C19F40AD001E035
:1003200042D03A3C09F421D03B3C11F408B3E6CF6B
:10033000F1CF70E124D0F02F22D0E02FC3E0C7BF6F
:10034000E8951DD0002E1BD0102EC1E0C7BFE89548
:10035000E395E395002D27D0012D25D07A9589F7D7
:10036000E052C5E0C7BFE895089509D0F02F07D047
:10037000E02F70E2059117D07A95E1F70895B29BCE
:10038000FECF1AE31A95F1F700E028E00695B2993E
:10039000006816E21A95F1F72A95C1F716E21A9548
:1003A000F1F700950895C19A16E21A95F1F728E041
:1003B00000FD03C000C0C19A02C0C19800C016E28F
:1003C0001A95F1F706952A9599F7C19816E21A95AC
:1003D000F1F708958FE30BD041B7401B453018F07B
:1003E0004C3F08F408958FE302D001BF0895E199CE
:0A03F000FECF8EBBE09A0DB3089516
:00000001FF
..... es hat also zwischen den Adressen :10023000 und :1002FC00 eine "Lücke", danach fängt der Bootloader an. Wenn man ein eigenes Programm hineinlädt, dann (kann) es so aussehen:

:1000000000C00FE304B90FE305B90AEA05B90FEF21
:0E0010001FEF05B1009505B91A95F1F7FACF6B
:00000001FF... bei diesem Progrämmchen "minitest.hex" hatte ich beim Hochladen in den Flash den Speicher gelöscht.

Beide vorstehenden Abbilder des Flashs und die Fuses sind mit den Programmen LPmikros (Experimentierprogramm und Bootloader) und LPmikros ISP auf die Franzisplatine geladen worden und auf einer Experimentierplatine (quick and dirty) mit einem fliegend angebrachten Programmierstecker unter PonyProg ausgelesen worden.

Du hast recht, andere fuses kannst Du mit der Entwicklungsumgebung des Lernpaketes nicht setzen oder löschen. Aber Du kannst sehr viele eigene Programme schreiben (z.B. mit AVRStudio) und mit LPmikros ISP in den t13 laden - ohne dass Du den Bootloader benötigst. Irgendwann willst Du aber vielleicht auch einen Quarz benutzen usf. Daher hatte ich mir nachdem ich fast alle Experimente mit dem Lernpaket durchgearbeitet hatte, eine erste Flex2313 (FL ash und EX perimentierplatine) (https://www.roboternetz.de/phpBB2/zeigebeitrag.php?p=331177#331177) für den tiny2313 selbst gelötet. Die hat den Charme, dass man mit relativ einfach mit ein paar Drahtbrücken auch den t13 flashen und seine fuses ändern kann. Und später hatte ich mehr gelötet *gggg* . . . .

HeSt
04.10.2008, 09:10
hallo freunde,

dank an euch alle, denn ohne euch wäre ich nicht dort, wo ich jetzt bin.
groß ist die freude!
mein erstes progrämmchen läuft zufriedenstellend.
mein spezieller dank gilt besserwessi, denn sein tip mit dem power on reset abfragen hat die problemlösung ohne zusätzliche komponenten oder änderung von fuses möglich gemacht.

einen kleinen schönheitsfehler hat das programm noch:
es funktioniert leider nicht astrein mit dem power down sleep modus.
warum? keine ahnung.
zum aufwecken braucht es auf int0 einen low level.
der erste durchlauf funktioniert perfekt (tor auf - zu - auf).
wenn allerdings das tor nach dem 2. öffnen wieder per impuls (int0) geschlossen werden soll, kommt zum impuls auf pb0 sofort ein hi auf pb4!
ich kann mir das nicht erklären, da es kein reset int auf pb5 gegeben hat, der tiny also nicht in die POR routine (main) laufen dürfte.

ich belasse es halt beim "ewigen kreisen" in der loop mit int0 negativ flanke ohne sleep.
da funktionierts bestens.

hier mein code, falls es jemanden interessiert:

;************************************************* ******************
;*
;* Programm: GTRiegel_PB5
;*
;* Version: 1.0
;*
;* Erstelldatum: 17.09.2008
;*
;* Letztes Änderungsdatum: 04.10.2008
;*
;*
;* Portzuordnung:
;* PB0 > Impuls Torelektronik
;* PB1 < Impuls Tor auf/zu Int0 Interrupt fallend
;* PB2 < Sensor Riegel offen/zu ADC
;* PB3 > Riegel auf
;* PB4 > Riegel zu
;* PB5 < Schalter Tor zu RESET Interrupt
;*
;* PB1 und PB5 sind Interrupt gesteuerte Eingänge
;* PB2 wird per ADC abgefragt
;*
;************************************************* ******************
; Definitionen
;************************************************* ******************

#include "tn13def.inc"

.cseg
.org 0x000 ; Interrupt Vektoren
rjmp MAIN ; Hauptprogrammroutine wird aufgerufen bei
; POR oder ext. Reset an PB5 (-)
rjmp IMPULS ; Interrupt0 an PB1
rjmp MAIN ; PinChangeInterrupt wird disabled
rjmp TMROVF ; TimerOverFlow für Warteschleife
rjmp MAIN ; Hauptprogrammroutine
rjmp MAIN ; Hauptprogrammroutine
rjmp MAIN ; Hauptprogrammroutine
rjmp MAIN ; Hauptprogrammroutine
rjmp MAIN ; Hauptprogrammroutine
rjmp MAIN ; Hauptprogrammroutine

.def TIM1 = R16 ; Timer 1 für Wartezeit
.def TIM2 = R17 ; Timer 2 für Wartezeit
.def TSEC = R18 ; Sekundenregister für Zeitübergabe
.def SCNT = R19 ; Sekundenregister für Timer
.def TEMP = R20 ; dient für diverse Operationen
.def SAVR = R21 ; dient zum Sichern des SREG
.def ADCV = R22 ; enthält Wert von ADCH nach AD-Wandlung

;************************************************* ******************
MAIN:
;-------------------------------------------------------------------
; Generelle Initialisierungen
;-------------------------------------------------------------------
; Stack anlegen
ldi temp,LOW(RAMEND)
out SPL,temp

; Ports initialisieren
ldi temp,0b00011001 ; Register für DDRB in/out laden
out DDRB,temp ; 1,2,5 Eingänge 0,3,4 Ausgänge
sbi PORTB,1 ; PB1 PullUp Ausgänge low

; ADC initialisieren
ldi temp,6 ; Reg. für ADC prescaler laden
out ADCSRA,temp ; 6 (divider 64) sind ca 18kHz
ldi temp,1 ; Reg. für ADMUX PB2 laden
out ADMUX,temp ; ADMUX setzen

; Wait-Timer setzen
ldi temp,3 ; Vorteiler /64
out TCCR0B,temp

; Interrupt Register setzen
; ldi temp,0b00110010 ; 0b001100x0 Sleep-Mode, Int0 Nflanke
; ldi temp,0b00110000 ; 0b00110000 Sleep-Mode, Int0 low level
ldi temp,0b00000010 ; 0b00xx0010 INT0 N-Flanke
out MCUCR,temp
ldi temp,0b01000000 ; Int0 enable, PinChangeInt disable
out GIMSK,temp ; GIMSK setzen

; Initialisierung erledigt, Abfrage ob POR oder PB5 Reset Interrupt
cli ; Interrupts disable
in temp,mcusr ; Statusregister einlesen
sbrc temp,1 ; Abfrage, wenn POR > skip
rjmp clslatch ; Ext. Reset = Riegel schließen
rcall readadc ; Riegelstatus abfragen/setzen
rjmp mloop

CLSLATCH:
sbi PORTB,4 ; PB 4 ein = Riegel schließen

WCLS:
rcall readadc
sbrs adcv,1 ; skip wenn Riegel geschlossen
rjmp wcls
cbi PORTB,4 ; PB 4 aus
ldi temp,64
out GIFR,temp ; Int0 löschen

MLOOP:
;-------------------------------------------------------------------
; Bei SLEEP:
; Beendet alle Funktionen, gibt die Interrupts frei und schaltet ab
; Interrupts an PB1 (INT0) oder Reset an PB5 aktiviert
; den Processor wieder
;-------------------------------------------------------------------
sei ; alle Interrupts frei geben
; sleep ; Power Down, warten auf Interrupts
nop
rjmp mloop
ret

;************************************************* ******************
;******************** START INTERRUPT ROUTINEN *********************
;************************************************* ******************

;************************************************* ******************
IMPULS: ; OK
;-------------------------------------------------------------------
; Fragt Status von Tor und Riegel ab und führt entsprechende
; Routinen aus
;-------------------------------------------------------------------
in savr,SREG
rcall readadc
sbrc adcv,1 ; Bit1=0 = Riegel offen
rjmp opndoor
rcall doorimp ; Tor (+ Riegel) schließen
rjmp endimp

OPNDOOR:
sbi PORTB,3 ; PB 3 ein = Riegel öffnen

WOPN:
rcall readadc
sbrc adcv,1 ; Bit 1=0 wenn offen
rjmp wopn
cbi PORTB,3 ; PB 3 aus
rcall doorimp

ENDIMP:
out SREG,savr
reti

;************************************************* ******************
TMROVF: ; OK
;-------------------------------------------------------------------
; Diese Interruptroutine wird durch den TimerOVF Interrupt
; aufgerufen
;-------------------------------------------------------------------
in savr,SREG
clr tim1
out TCNT0,tim1 ; Timer zurücksetzen
inc tim2
cpi tim2,80 ; 80 cylces = ca. Sekunde
brlo tmrovfe
clr tim2
inc scnt ; Sekundenzähler um 1 erhöhen

TMROVFE:
out SREG,savr
reti

;******************** ENDE INTERRUPTROUTINEN ***********************

;************************************************* ******************
;*********************** START SUB ROUTINEN ************************
;************************************************* ******************

;************************************************* ******************
READADC: ; OK
;-------------------------------------------------------------------
; Liest den A/D Wandler an PB2 aus ADC 10 Bit Messung
; Da es sich hier um keine digitale Spannungsmessung handelt
; sondern lediglich um eine Statusabfrage reicht hierzu nur die
; Abfrage des Hi-Byte (ADCH):
; 0V = Riegel offen ADCH Bit1 = 0
; 5V = Riegel geschlossen ADCH Bit1 = 1
; Die Messung muss doppelt erfolgen ansonsten wird die vorherige
; Messung eingelesen. Eigenheit des Tiny13
;-------------------------------------------------------------------
sbi ADCSRA,ADEN ; ADC ENable
sbi ADCSRA,ADSC ; ADC Start Conversion

WRDADC1: ; Schleife bis Messung
sbic ADCSRA,ADSC ; abgeschlossen = ADSC=0
rjmp wrdadc1
sbi ADCSRA,ADSC ; ADC Messung 2tes Mal starten

WRDADC2:
sbic ADCSRA,ADSC ; warten bis ADSC = 0
rjmp wrdadc2
in adcv,ADCH ; highbyte einlesen
cbi ADCSRA,ADEN ; ADC ausschalten
ret

;************************************************* ******************
DOORIMP: ; OK
;-------------------------------------------------------------------
; schickt einen Impuls von etwa 1 Sekunde zur Torelektronik
;-------------------------------------------------------------------
ldi tsec,1 ; Zeit für Impulsspreizung in Sek
sbi PORTB,0 ; PB0 ein = Impuls Tor auf
rcall wait ; Impulsspreizung
cbi PORTB,0 ; PB0 aus
ret

;************************************************* ******************
WAIT: ; OK
;-------------------------------------------------------------------
; Warteschleife für diverse Zeitverzögerungen
; Wartezeit wird von aufrufender Routine in TSEC übergeben
; Es wird der PinChange Interrupt kurzfristig unterdrückt, da beim
; Öffnen des Tores ungewollt ein PCI ausgelöst wird.
; Der PCI wird, falls gesetzt, vor dem enable wieder gelöscht, damit
; dieser nicht nach der generellen Freigabe der Interrupts
; ausgeführt wird.
;-------------------------------------------------------------------
clr scnt
ldi temp,2
out TIMSK0,temp ; Timer Interrupt freigeben, läuft
sei
WWAIT:
cp scnt,tsec ; Sekunden abgelaufen?
brlo wwait
clr temp
out TIMSK0,temp ; Timer Interrupt sperren, restliche
; Interrupts bleiben ein
ret

;************************ ENDE SUBROUTINEN *************************
.EXIT


@ oberallgeier:
bei der normalen verwendung der lernplatine zum programm laden (LPmikros) darf pb5 nicht auf GND gelegt werden !!
dumme frage: hast du das gemacht? dann gehts nämlich nicht.
bei mir funktioniert alles wie beschrieben ...
nur bei LPmikosISP brauchts die brücke.

lg

oberallgeier
04.10.2008, 09:52
... normalen verwendung der lernplatine ... hast du das gemacht? ...Nein, bestimmt nicht. Mit dem Bootloader habe ich bei den ersten Versuchen keine Programme hochladen können, eigene Programme schiebe ich deswegen immer mit LPmikroISP rauf, die "normale" ? Funktion der Lernplatine mit dem Experimentierprogramm drauf habe ich zum Testen von allerlei kleinsten Aufbauten.

HeSt
07.10.2008, 07:01
hallo joe,

ich hab mir deine screen shots nun lpmikros genauer angeschaut.
wie kommst du zur ansicht, wo die ganzen fuses angezeigt werden?
ich hab das "download" gemacht, aber ich hab nie eine derartige anzeige erhalten.

übrigens bin ich beim eingehenden testen meines (noch etwas verfeinerten) progrämmchens drauf gekommen, dass - weiß der kuckuck warum - nur in der routine "opndoor" der torimpuls (rcall doorimp) unterschiedlich lange dauert.
mal 1 sekunde (wie in tsec übergeben) manchmal aber nur ein ganz kurzer flash (vielleicht 0,3 sek).
woher kann das kommen?
bei allen anderen aufrufen von "doorimp" ist die zeit konstant ca. 1 sekunde.


;************************************************* ******************
;*
;* Programm: GTRiegel_PB5
;*
;* Version: 1.2
;*
;* Erstelldatum: 17.09.2008
;*
;* Letztes Änderungsdatum/-zeit: 06.10.2008 / 20:30
;*
;*
;* Portzuordnung:
;* PB0 > Impuls Torelektronik
;* PB1 < Impuls Tor auf/zu Int0 Interrupt fallend
;* PB2 < Sensor Riegel offen/zu ADC Abfrage
;* PB3 > Riegel auf
;* PB4 > Riegel zu
;* PB5 < Schalter Tor zu RESET Interrupt
;*
;* PB1 und PB5 sind Interrupt gesteuerte Eingänge (-)
;* PB2 wird per ADC abgefragt
;*
;************************************************* ******************
; Definitionen
;************************************************* ******************

#include "tn13def.inc"

.cseg
.org 0x000 ; Interrupt Vektoren
rjmp MAIN ; bei POR oder ext. Reset an PB5 (-)
rjmp IMPULS ; Interrupt0 an PB1
reti ; PinChangeInterrupt disabled
rjmp TMROVF ; TimerOverFlow für WAIT
reti ; EEPROM Ready disabled
reti ; Analog Comparator disabled
reti ; Timer/Counter Compare Match A disabled
reti ; Timer/Counter Compare Match B disabled
reti ; Watchdog Time-out disabled
reti ; ADC Conversion Complete disabled

.def SAV1 = R16 ; dient zum Sichern des SREG
.def SAV2 = R17 ; dient zum Sichern des SREG
.def TIM1 = R18 ; Timer 1 für Wartezeit
.def TIM2 = R19 ; Timer 2 für Wartezeit
.def TSEC = R20 ; Sekundenregister für Zeitübergabe
.def SCNT = R21 ; Sekundenregister für Timer
.def ADCV = R22 ; enthält Wert von ADCH nach AD-Wandlung
.def TMP1 = R23 ; dient für diverse Operationen
.def TMP2 = R24 ; dient für diverse Operationen

;************************************************* ******************
MAIN:
;-------------------------------------------------------------------
; Generelle Initialisierungen
;-------------------------------------------------------------------
; Stack anlegen
ldi tmp1,LOW(RAMEND)
out SPL,tmp1

; Ports initialisieren
ldi tmp1,25 ; 0b00011001 für DDRB in/out laden
out DDRB,tmp1 ; 1,2,5 Eingänge 0,3,4 Ausgänge
sbi PORTB,1 ; PB1 PullUp Ausgänge low

; ADC initialisieren
ldi tmp1,6 ; Reg. für ADC prescaler laden
out ADCSRA,tmp1 ; 6 (divider 64) sind ca 18kHz
ldi tmp1,1 ; Reg. für ADMUX PB2 laden
out ADMUX,tmp1 ; ADMUX setzen

; Wait-Timer Vorteiler setzen
ldi tmp1,3 ; Vorteiler /64
out TCCR0B,tmp1

; Interrupt Register setzen
ldi tmp1,2 ; 0b00000010 PB1 INT0 N-Flanke
out MCUCR,tmp1
ldi tmp1,64 ; Bit6: Int0 enable
out GIMSK,tmp1 ; GIMSK setzen
;-------------------------------------------------------------------
; Ende Initialisierungen
;-------------------------------------------------------------------

; Abfrage ob POR oder Ext. Reset Interrupt (PB5)
cli ; disable Interrupts
in tmp1,mcusr ; Statusregister einlesen
sbrc tmp1,1 ; Abfrage, wenn POR > skip
rjmp clslatch ; Ext.Reset (PB5) = Riegel schließen
rjmp mloop

CLSLATCH:
;-------------------------------------------------------------------
; Dieser Teil schließt den Riegel.
; Beendet wird entweder durch ADC "Riegel zu" oder durch Timeout.
; Zeitbegrenzung zum Schutz des Zahnriemens falls Riegel festsitzt.
;-------------------------------------------------------------------
ldi tmp1,0
out GIMSK,tmp1 ; GIMSK Int0 disable
ldi tsec,5 ; Wartezeit in Sekunden
sbi PORTB,4 ; PB 4 ein = Riegel schließen
rcall strtmr ; Timer starten
CLOOP:
cp scnt,tsec ; Zeit abgelaufen?
brge eloop
rcall readadc
sbrs adcv,1 ; skip wenn Riegel geschlossen
rjmp cloop
ELOOP:
cbi PORTB,4 ; PB 4 aus
rcall stptmr ; Timer stoppen
ldi tmp1,64 ; Bit6 GIFR + GIMSK
in tmp2,GIFR
sbrc tmp2,6 ; Int0 gesetzt? 1=ja > löschen
out GIFR,tmp1 ; Int0 löschen
out GIMSK,tmp1 ; Int0 enable

MLOOP:
;-------------------------------------------------------------------
; Hauptschleife
;-------------------------------------------------------------------
sei ; alle Interrupts frei geben
nop
rjmp mloop
ret

;************************************************* ******************
;******************** START INTERRUPT ROUTINEN *********************
;************************************************* ******************

;************************************************* ******************
IMPULS: ; OK
;-------------------------------------------------------------------
; Fragt Status von Tor und Riegel ab und führt entsprechende
; Routinen aus
;-------------------------------------------------------------------
in sav1,SREG
rcall readadc
cpi adcv,0 ; 0 wenn offen
breq clsdoor ; wenn Tor offen > Torimpuls
rcall opndoor ; sonst Riegel auf, Tor auf
rjmp endimp

CLSDOOR:
rcall doorimp ; Tor (+ Riegel) schließen
rjmp endimp

ENDIMP:
out SREG,sav1
reti

;************************************************* ******************
TMROVF: ; OK
;-------------------------------------------------------------------
; Diese Interruptroutine wird durch den TimerOVF Interrupt
; aufgerufen
;-------------------------------------------------------------------
in sav2,SREG
clr tim1
out TCNT0,tim1 ; Timer zurücksetzen
inc tim2
cpi tim2,80 ; 80 cylces = ca. Sekunde
brlo tmrovfe
clr tim2
inc scnt ; Sekundenzähler um 1 erhöhen

TMROVFE:
out SREG,sav2
reti

;******************** ENDE INTERRUPTROUTINEN ***********************

;************************************************* ******************
;*********************** START SUB ROUTINEN ************************
;************************************************* ******************

;************************************************* ******************
OPNDOOR: ; OK
; Mit Zeitbegrenzung zum Schutz des Zahnriemens falls Riegel
; festsitzt (angefroren).
;-------------------------------------------------------------------
ldi tmp1,0
out GIMSK,tmp1 ; Int0 disable
ldi tsec,5 ; Wartezeit in Sekunden
sbi PORTB,3 ; PB 3 ein = Riegel öffnen
clr tmp2 ; Timeout = 0
rcall strtmr ; Timer starten
STRLP:
cp scnt,tsec ; Zeit abgelaufen?
brge timout
rcall readadc
cpi adcv,0 ; Bit 0=0 wenn offen
breq eopn
rjmp strlp
TIMOUT:
ldi tmp2,16 ; Timeout, Riegel ist blockiert
EOPN:
cbi PORTB,3 ; PB 3 aus
rcall stptmr
sbrs tmp2,4 ; Riegel blockiert, Tor nicht öffnen
rcall doorimp ; Tor öffnen
ldi tmp1,64 ; Bit6 GIFR + GIMSK
in tmp2,GIFR
sbrc tmp2,6 ; Int0 gesetzt? 1=ja > löschen
out GIFR,tmp1 ; Int0 löschen
out GIMSK,tmp1 ; Int0 enable
ret

;************************************************* ******************
READADC: ; OK
;-------------------------------------------------------------------
; Liest den A/D Wandler an PB2 aus ADC 10 Bit Messung
; Da es sich hier um keine digitale Spannungsmessung handelt
; sondern lediglich um eine Statusabfrage reicht hierzu nur die
; Abfrage des Hi-Byte (ADCH):
; 0V = Riegel offen ADCH Bit1 = 0
; 0,5-2,4V = Riegel Zwischenstellung ADCH Bit0 = 1
; 5V = Riegel geschlossen ADCH Bit1 = 1
; Die Messung muss doppelt erfolgen ansonsten wird die vorherige
; Messung eingelesen. Eigenheit des Tiny13
;-------------------------------------------------------------------
sbi ADCSRA,ADEN ; ADC ENable
sbi ADCSRA,ADSC ; ADC Start Conversion

WRDADC1: ; Schleife bis Messung
sbic ADCSRA,ADSC ; abgeschlossen = ADSC=0
rjmp wrdadc1
sbi ADCSRA,ADSC ; ADC Messung 2tes Mal starten

WRDADC2:
sbic ADCSRA,ADSC ; warten bis ADSC = 0
rjmp wrdadc2
in adcv,ADCH ; highbyte einlesen
cbi ADCSRA,ADEN ; ADC ausschalten
ret

;************************************************* ******************
DOORIMP: ; OK
;-------------------------------------------------------------------
; schickt einen Impuls von etwa 1 Sekunde zur Torelektronik
;-------------------------------------------------------------------
ldi tsec,1 ; Zeit für Impulsspreizung in Sek
sbi PORTB,0 ; PB0 ein = Impuls Tor auf
rcall wait ; Warteschleife ...
cbi PORTB,0 ; PB0 aus
ret

;************************************************* ******************
STRTMR: ; OK
;-------------------------------------------------------------------
; Start Timer
;-------------------------------------------------------------------
clr scnt ; Sekundenzähler leeren
ldi tmp1,2
out TIMSK0,tmp1 ; Timer Interrupt freigeben > läuft
sei
ret

;************************************************* ******************
STPTMR: ; OK
;-------------------------------------------------------------------
; Stop Timer
;-------------------------------------------------------------------
clr tmp1
out TIMSK0,tmp1 ; Timer Interrupt sperren
ret

;************************************************* ******************
WAIT: ; OK
;-------------------------------------------------------------------
; Warteschleife für diverse Zeitverzögerungen
; Wartezeit wird von aufrufender Routine in TSEC übergeben
;-------------------------------------------------------------------
rcall strtmr
WWAIT:
cp scnt,tsec ; Zeit abgelaufen?
brlo wwait
rcall stptmr
ret

;************************ ENDE SUBROUTINEN *************************
.EXIT

sonst läuft das programm einwandfrei - dank euch!

oberallgeier
07.10.2008, 09:34
... deine screen shots ... lpmikros genauer angeschaut ... wo die ganzen fuses angezeigt ... hab nie eine derartige anzeige ...Ach je, das war missverständlich. Die fuses (zweites Bild) hatte ich mit PonyProg und (m)einem ISP-Kabel ausgelesen; dazu hatte ich den Controller und die erforderliche Verdrahtung in ein Steckbrett montiert. Sorry, dass ich das nicht hingeschrieben hatte. Das erste Bild kennst Du ja von LPmikros - und das deutet eben an, dass Fuses gesetzt werden. Ebenso können mit LPmikro ISP auf der letzten Registerkarte fuses gesetzt werden, um die Taktrate des internen Oszillators in weiten Grenzen zu verstellen. Mit beiden Programmen kann man die Fuses nicht auslesen. Ich fand das immer sehr unpraktisch, weil z.B. beim Setzen der Taktrate im LPmikro ISP nicht klar ist, wie der aktuelle Zustand des Controllers ist. Aber das Ganze hat seinen Sinn als Werkzeug für Anfänger - da wird man dann eben nicht verwirrt von diesen Dingen.


... in der routine "opndoor" der torimpuls (rcall doorimp) unterschiedlich lange ...Puhhhh - war eine mühevolle Suche (irgendwie lief Dein Code erstmal ne Weile garnicht in meinem Assembler - keine Ahnung warum, aber ich habs hingekriegt).

Vermutlich unbedeutend, aber es fällt auf: Warum steht "cli;" nach "Ende der Initialisierungen" - das ist "sei" doch noch garnicht aktiv ?

Fehlerhaft könnte sein, das Du hast in Deiner Hauptschleife "mloop" als ersten Befehl "sei" stehen hast. Nun gilt aber:

When the AVR exits from an interrupt, it will always return to the main program and execute one more instruction before any pending interrupt is served.
Möglicherweise erzeugt das "nested interrupts" , ohne dass Du es möchtest. Ich hab´s aber nicht ausprobiert. Vielleicht versuchst Du die (nicht sehr hübsche) Methode , dass Du zwei wait-Routinen schreibst - wait1 und wait2 , mit tsec1 und tsec2. Obs hilft, weiss ich aber nicht.

Besserwessi
07.10.2008, 09:56
Da werden verschachtelte Interrrupts ausgeführt. Das ist nicht ganz einfach, aber ich kann auch keinen Fehler finden. Immerhin wird das SREG wohl schon mal getrennt für timer und ext. Interrupt gesichert.
Probleme könnte es geben wenn wärend des wartens ein 2 ter Interrupts an Impuls auftritt, dann geht die sicherung von SREG daneben, denn es wird 2 mal der gleiche Interupt aufgerufen.

HeSt
07.10.2008, 10:53
Vermutlich unbedeutend, aber es fällt auf: Warum steht "cli;" nach "Ende der Initialisierungen" - das ist "sei" doch noch garnicht aktiv ?
das war/ist nur eine vorsichtsmaßnahme.
sollte aber keinen einfluss auf den ablauf haben.

Fehlerhaft könnte sein, das Du hast in Deiner Hauptschleife "mloop" als ersten Befehl "sei" stehen hast.
nun, da kann man vor und nachher noch ein NOP einfügen.
werds probieren und die auswirkung kundtun.

Möglicherweise erzeugt das "nested interrupts" , ohne dass Du es möchtest. Ich hab´s aber nicht ausprobiert. Vielleicht versuchst Du die (nicht sehr hübsche) Methode , dass Du zwei wait-Routinen schreibst - wait1 und wait2 , mit tsec1 und tsec2. Obs hilft, weiss ich aber nicht.
ist eine möglichkeit. werde es (vielleicht in einer museminute) mal probieren.

Immerhin wird das SREG wohl schon mal getrennt für timer und ext. Interrupt gesichert.
Probleme könnte es geben wenn wärend des wartens ein 2 ter Interrupts an Impuls auftritt, dann geht die sicherung von SREG daneben, denn es wird 2 mal der gleiche Interupt aufgerufen.
eben, deshalb habe ich auch 2 getrennte sicherungsregister angelegt.
und zusätzlich fange ich einen interrupt ab indem ich ihn disabled habe - er also nicht "aktiv" werden kann - und, falls während der wartezeit einer auftritt, indem ich ihn INT0-enable und vor rückkehr abfrage und ggf. lösche.
das mache ich deshalb, weil ich das problem ja hatte, was sich allerdings in einer doppelten (mit ganz kurzer unterbrechung, kaum zu sehen) aktivzeit von PB3 auswirkte, aber keinen einfluss auf die imulslänge an PB0 hatte.
aber so lange ich einen impuls bekomme, der ausreicht die torelektronik anzustoßen, soll mir das einstweilen egal sein.
es ist halt nur nicht nachzuvollziehen warum es so ist.

danke jedenfalls für eure mühe, sich durch meinen code zu quälen !!

HeSt
08.10.2008, 07:51
update:
3 NOPs vor und nach dem SEI in MLOOP bringen keine änderung des symptoms.
anfänglich hat es so ausgesehen, als ob damit das problem der kurzen impulse gelöst wäre, aber nach gut 5 durchläufen das selbe verhalten wieder. egal ob ich dann mal durch abschalten wieder von vorne angefangen hab ...

oberallgeier
08.10.2008, 10:31
[Hi, HeSt,

quote="HeSt"]update:
3 NOPs vor und nach dem SEI in MLOOP bringen keine änderung des symptoms.
anfänglich hat es so ausgesehen, als ob damit das problem der kurzen impulse gelöst wäre, aber nach gut 5 durchläufen das selbe verhalten wieder. egal ob ich dann mal durch abschalten wieder von vorne angefangen hab ...[/quote]
-------------------------------------------------------

Schade dass Di nop´s nicht helfen :).

Du verwendest den Sekundentimer SCNT offenbar für mehrere Routinen, kann da ein/der Fehler liegen? Der Wert wird doch möglicherweise von der einen Routinen gerade benutzt, und dann kommt die Interruptgeschicht - die das Wait auch benutzt. Wäre dumm.

Ausserdem wird SCNT im WAIT mit TSEC verglichen, und TSEC kann sowohl den Wert 5 haben - in CLSLATCH und OPNDOOR - als auch den Wert 1 - in DOORIMP. Da werden sich über kurz oder lang die Interrupts sicher in die Quere kommen. Mögliche Abhilfe:
In der ISR vor dem rcall wait ein push tsec machen und vor dem reti ein pop tsec ? ?

Aber bestimmt unschön ist es in STRTMR den Interrupt generell zu erlauben - "sei". Dann bekommst Du (wie schon erwähnt) nested interrupts - eine hübsche Geschichte die mit äusserster Vorsicht programmiert werden sollte/kann/muss. Sonst geht das schief #-o

HeSt
08.10.2008, 15:22
Du verwendest den Sekundentimer SCNT offenbar für mehrere Routinen, kann da ein/der Fehler liegen?
ja, ich verwende den SCNT für mehrere routinen - aber immer nur in EINER aktiv.
zb. OPNDOOR: hier wird TSEC auf 5 sekunden timeout gesetzt. wenn nun der timeout tatsächlich eintritt (oder auch nicht) wird der timer wieder gestoppt, bevor DOORIMP und damit WAIT (mit str und stptmr) aufgerufen wird.
somit sehe ich keine möglichkeit von "nested interrupts" und auch keine doppelverwendung des registers von 2 routinen gleichzeitig.


Ausserdem wird SCNT im WAIT mit TSEC verglichen, und TSEC kann sowohl den Wert 5 haben - in CLSLATCH und OPNDOOR - als auch den Wert 1 - in DOORIMP.
richtig, aber diese routinen laufen NACHEINANDER ab.
die WAIT routine wird nur über die routine DOORIMP aufgerufen. sonst nirgendwo. also auch nichts "nested".


Mögliche Abhilfe:
In der ISR vor dem rcall wait ein push tsec machen und vor dem reti ein pop tsec ? ?
in welcher ISR meinst du? es gibt deren nur 2: IMPULS und TMROVF
und beide haben kein rcall WAIT.
alle timerfunktionen, ob 1 oder 5 sekunden werden abgeschlossen und auch der timer gestoppt, bevor die nächste aufgerufen wird.


Aber bestimmt unschön ist es in STRTMR den Interrupt generell zu erlauben - "sei".
wenn du mir sagst, wie ich interrupts enablen kann OHNE den befehl SEI zu verwenden. soweit ich das datenblatt verstanden habe werden interrupts nur dann abgearbeitet, wenn das "I" bit (7, global interrupt enable) gesetzt ist, sonst wird zwar das interrupt bit des entsprechenden interrupts gesetzt, aber keine ISR ausgeführt. habe ich das falsch verstanden?

halt, aber gerade jetzt fällt mir was ein/auf - ich stoppe zwar den timer aber mache kein CLI !!!
da mag was drin liegen ...

werde es am abend probieren.

aber nochmal: brauche ich das "I" bit on oder nicht zum ausführen der ISR?

noch eine frage ist aufgetaucht, die ich auf die schnelle dem datenblatt nicht zu entnehmen vermag:
was passiert, wenn der timer gestoppt und wieder gestartet wird?
werden seine internen register zurück gesetzt oder läuft er bei dem wert weiter, bei dem er gerade gestoppt wurde?
meine register TIM1 + TIM2 werden ja wieder neu gesetzt - bis auf eines: TIM2 sehe ich gerade!
werde das auch noch berichtigen und testen!!

Besserwessi
08.10.2008, 20:21
Nur wenn das I Bit gesetzt ist werden Interrupts ausgeführt, sonst werden nur die Flags gesetzt und die ISR nach dem Befehl nach dem SEI gestartet.

Beim Neustart des Timers läuft der da weiter wo er aufgehört hat. Timer Stop heißt eigentlich nur Takt auf Null.

HeSt
09.10.2008, 06:35
morgen!
impulsproblem gelöst!
lag nur an TIM2, das bei STRTMR nicht auf 0 gesetzt wurde. #-o
warum sich das allerdings nur in der routine OPNDOOR ausgewirkt hat? :-k
nun läuft alles wirklich astrein.
erste lehrlingsprüfung bestanden :-)