Startproblem mit ATtiny45

Hi
Ich noch mal. Sorry, vergiss meinen Post...., hab grad gesehendas du gar nicht zurück, sondern nach vorn springst.
:eek:
Gruß oldmax
 
Hallo oldmax,

Ich hab nun nicht alles gelesen und daher bin ich mir nicht sicher, ob es bereits bekannt ist,, aber schau dir das mal an:
onTC11:
lds TimeLowByte,0x60 ; Sekunden Lowbyte aus SRAM Adresse 0x60 holen
inc TimeLowByte ; Um 1 erhöhen
brne onTC11

Ups, wo hast denn das her? Steht doch in meinem Code so gar nicht drinnen. :cool:


Übrigends, Lo8 und Hi8 kenn ich so nicht. Ist das nicht einfach nur Low und High ? Ich weiß, das der Compiler nicht alles erfasst und dann so richtig spaßiges Programm auflegt. Aber ich kann mich hier ja auch irren.
Glaub ich dir, dass du das so nicht kennst. Du kennst meine Entwicklungsumgebung nicht. Macht aber nichts. Hast nichts versäumt! Die ist hoffentlich in einigen Tagen sowieso Geschichte.

Jetzt komme ich zum eigentlichen Thema welches mich an deinem Post sehr interessiert.
Nebenbei, ich bin kein Freund von ISR, die den Programmablauf steuern. ISR sollten klein und übersichtlich sein. Allein deine (nicht funktionierende) Zählschleife blockiert mehr als es nutzt. Setz dir in der ISR ein Flag und bearbeite dieses in der Programmschleife. Dann wird auch die zweite ISR eine Chance haben.
Ich gestehe und oute mich. Ich bin ein Freund von Interrupts.
Da schließt sich meine Frage an. Was passiert mit einem Interrupt der eigentlich auftreten würde, während wegen einer anderen Interruptbehandlung die Interrupts mit "cli" gesperrt sind? Geht der einfach verloren oder wird er nach "sei" nachgeholt? Eine Frage die mich brennend interessiert! Sollte er verloren gehen, müsste ich mein grundsätzliches Konzept der Interruptbehandlung überdenken.

Gruß
Manfred

PS: Noch 'ne kleine Anmerkung zum eigentlichen Problem. Wie zu erwarten war, lief ein Controller der gestern Abend einwandfrei arbeitete, heute Abend nicht mehr. Wollte einfach nicht. Da jemand in seinem Post was von thermischem Problem berichtet hatte, machte ich mir den Spaß und hielt etwa 10 Minuten meinen Finger auf den IC um ihn zu erwärmen. Dann musste ich lachen. Er sprang sofort an als ich Spannung anlegte looooooooooooooool

PPS: Hatte ja gestern einen zusätzlichen Controller geflasht. Beide liefen gestern. Heute morgen wollten beide nicht mehr. Hatte dann beide nachgeflasht und sie liefen daraufhin sofort. (Beim flashen wird er sicher erwärmt .............)
 
solange das entsprechende Interruptflag gesetzt bleibt, wartet der Interrupt quasi im Hintergrund. Werden die Interrupts global freigegeben (zB durch das Reti (aber auch ein SEI innerhalb einer ISR hätte diesen Effekt - so könnte (!) man Interrupts schachteln)), treten die wartenden Interrupts in Aktion, wobei die Reihenfolge in der IVT = Ausführungsreihenfolge ist.
Allerdings können Interrupts übersehen werden, wenn derselbe Interrupt mehrfach hintereinander Auftritt - also der 2te request erfolgt, bevor der erste abgearbeitet wurde...

Ich finde, Du schaufelst in Deinem Programm etwas viel Daten zwischen den Rechenregistern und SRAM hin und her; ich würde ggf mehr Register für irgendwelche Sachen reservieren (Du nutzt ja nur einen Teil der Register). Eventuell statt dem umständlichen inkrementieren von TimeLow/HighByte dann mit ADIW - oder man reserviert ein Rechenregister als 0, und setzt das Inkrement dann zusammen aus:
SUBI lowbyte, -1
ADC highbyte, nullregister
nee... das ist Mist, weil beim SUBI dann fast immer das Carry kommt, oder?

Ich verwende allerdings lieber Zähler die runterzählen - da kann man dann nämlich Subtract with Carry Constant from Register (SBCI) verwenden, mit 0 als Konstante bleibt Rd=Rd-Carry. Außerdem kann man hier ggf Z und C reagieren.

Außerdem ist mir irgendwo ins Auge gefallen, daß Du irgendwo mit LPM was von Flash(Z) liest, und hinterher Z inkrementierst. Meiner Meinung sollte der Controller LPM mit POst increment (LPM rd,Z+) unterstützen...
 
Hallo,
Ich gestehe und oute mich. Ich bin ein Freund von Interrupts.
Da schließt sich meine Frage an. Was passiert mit einem Interrupt der eigentlich auftreten würde, während wegen einer anderen Interruptbehandlung die Interrupts mit "cli" gesperrt sind? Geht der einfach verloren oder wird er nach "sei" nachgeholt? Eine Frage die mich brennend interessiert! Sollte er verloren gehen, müsste ich mein grundsätzliches Konzept der Interruptbehandlung überdenken.

falls ein Interrupt während der Ausführung einer Interrupt-Serviceroutine auftritt, wird das entsprechende Interrupt-Anforderungsflag gesetzt. Nach "reti" setzt der Mikrocontroller wieder das Flag I (global Interrupt enable), was ja bei Einsprung in die ISR gelöscht wurde. Danach wird die ISR des zwischenzeitlich aufgetretenen Interrupts angesprungen. Sind mehrere Interrupts aufgetreten, wird nacheinander abgearbeitet.
Es kann allerdings passieren, dass wenn die Ausführungszeit einer ISR zu lange ist, ein Interrupt-Ereignis mehrfach auftritt, dies aber nicht erkannt wird, es wir nur einmal das Anforderungsflag gesetzt. Bei dir sehe ich da ein Problem, weil es kein Hauptptogramm gibt. Du setzt alles in ISRs. ISR sollten nur zeitkritsche und möglichst kurze Programmteile abarbeiten.

Noch zur Ergänzung: man kann in einer ISR auch manuell interrupts global freigeben (sei), hier muss man aber sehr aufpassen, was für Interrupts freigegeben sind.

Dirk :ciao:

Edit: Beachte auch den Beitrag von LotadaC vor meinem.
 
Hallo ihr beiden,

danke für die Aufklärung. Hab's in etwa so vermutet, war mir aber nicht sicher.
Ich bin gerne bereit von Profis zu lernen. Werde mir das durch den Kopf gehen lassen und die ISR möglichst entlasten.
Auch eure anderen Anregungen werde ich mir anschauen. Allerdings den Zähler runterzählen zu lassen, sehe ich in dem Anwendungsfall kritisch, da hier eine 1 Sekunden Zeitbasis realisiert ist und ich die Sekunden brauche weil danach bestimmte Prozesse abgearbeitet werden. Die im Endausbau variabel einzugebenden Daten werden die Ablaufsteuerung in Sekunden steuern. Hoffe mich verständlich ausgedrückt zu haben. Oder hab ich deine Anregung falsch verstanden?

Gruß
Manfred
 
Sekunde ist Sekunde... ob Du die nun von 0x0000 nach 0xFFFF hochzählst, oder von 0xFFFF nach 0x0000 runter. ADD/SUB etc manipulieren das Carry, INC/DEC nicht - insofern ist ein Mehr-Byte-Zähler damit einfacher zu realisieren. Allerdings untestützen nur die Subtraktionsbefehle die Verwendung von Konstanten (Immediate)...
 
Ja sicher. Jedoch wird im Endausbau ein Zeitdiagramm vom Anwender erstellbar sein. Das ist in Sekunden aufgeschlüsselt. Wenn jetzt der Anwender sein Zeitdiagramm von 0 weg gestaltet und mein Zähler von FF runterzählt gibt das wieder umrechenarbeit. Auch wird in einer Konstanten die maximale Laufzeit bis Programmwiederholung oder bis sleep angegeben in Sekunden. Also auch hier wieder keine leichte Vergleichbarkeit.

Bin gerade dabei eure anderen Anregungen zu testen. Durch die Verwendung von "lpm Register,Z+" konnte ich schon ein wenig Code einsparen. Als nächstes werde ich mir die Interrupts vornehmen und schauen was ich davon ins Hauptprogramm schiebe.

Gruß
Manfred

PS: Frage am Rande: Kann man denn über den INT0 den ATtiny aus dem sleep Modus holen? Irgendwie klappt das gerade nicht.

Die Interrup Sprungadresse ist gesetzt:
rjmp onST0 ;2 External Interrupt 0
.
.
.
.
in r16,MCUCR
ori r16,0b00000000
out MCUCR,r16 ; Low level von INT0 generiert einen Interrupt
in r16,GIMSK
ori r16,0b01000000
out GIMSK,r16 ; INT0 wird erlaubt

Was hab ich da noch vergessen?
 
Hi
Ich gestehe und oute mich. Ich bin ein Freund von Interrupts.
Das du gern mit Interrupt arbeitest, ist ja nicht das Problem. Aber man muss da schon unterscheiden, was ist ein Interrupt, was ist eine Programmschleife. Und da hat jeder Programmierer eigene Ansichten und ist (meistens) auch von seinem Stil überzeugt. Deswegen betrachte meine Ausführung nicht als Vorgabe, sondern bestenfalls als Denkanstoß.
Ich halte mich an EVA. Eva ist schon für vieles mißbraucht worden, aber mir gab sie immer eine Richtung. Ich meine Einlesen-Verarbeiten-Ausgeben. Grad bei Assembler ist schnell ein unübersichtliches Programmkonstrukt erstellt. Ich mach es mir da etwas einfacher. Und da du von Sekunden sprichst, ist die Vera<rbeitung in einer ISR meiner Meinung nach völlig unnütz.
Mein Weg wäre :
Eine Handvoll Variablen für Zeitflags und Jobflags sowie Zähler und was sonst noch wichtig ist.
Die Programmschleife rotiert immer, warum auch nicht, und beginnt, Eingänge zu lesen und in einer Variablen abzulegen. Damit bleiben diese an einer Stelle und für den Zyklus gültig. Genauso wird die Ausgabe in einer Variablen abgelegt und erst am Ende der Schleife zugewiesen. Dazwischen erfolgt nun die verarbeitung, indem für die verschiedenen Jobs Bits abgefragt werden.(Job- und Zeitflags)
Wenn du so ein Listing aufstellst, dann gibt es auch Stimmen dagegen: "Wozu dieses Bitgeraffel". Nun, bisher hab ich meine Programme auch nach einem halben Jahr noch pflegen können.....
.... und ich bin schon leicht verkalkt...
Nun zu den Zeiten: Die ISR setzt nur das Zeitflag und gut.In der Regel wird bis zur Zehntelsekunde bei mir in der ISR verschiedene Zeitflags gesetzt, die dann in der Programmschleife ausgewertet werden. z.B. das Flag für eine mSek. Da darf dann natürlich nicht allzuviel Code hinterstehen, aber es gibt durchaus auch dafür Verwendung.
Mit dem Flag der Zehntelsekunde leite ich dann auch schon mal umfangreiche Jobs ein. So auch das Setzen eines Flags für eine halbe Sekunde.
Beispiel
Code:
Chk_Zehntel:
	LDS	Reg_A. Time_Flag
	ANDI	Reg_A, 0b00000001		; Flag für Zehntelsekunde
	BREQ	End_Zehntel
	LDS	Reg_A, Time_Cnt_05
	Dec      Reg_A
	STS	Time_cnt_05
	BRNE	weiter
	LDI	Reg_A, 5
	STS	Time_Cnt_05
	LDS	Reg_A,Time_Flag
	ORI	Reg_A,0b00000010		; Zeitflag halbe Sekunde
Weiter:
	ANDI	Reg_A, 0b11111110		; Zeitflag quittieren
	STS 	Time_Flag, Reg_A
	RCALL weitere_ZeitJobs_01		; weitere Jobs in diesem Zeitraster 
End_Zehntel:
RET

Eine solche Routine wird in der Programmschleife aufgerufen. Klar, wenn es wirklich Zeitkritisch sein sollte, dann kann man das auch etwas intelligenter lösen. Aber i.d.R. sind diese paar µSek. kein Problem.
nun zum Thema Hoch bzw. runterzählen. Wenn du eine Zeit von 23 Sekunden wünscht, dann setzt du hier einen Wert von 230 ( oder du leitest ein Zeitflag von 1 Sekunde ab und setzt dann auf 23 ) Dann das Beispiel:
Code:
Weitere Zeitjobs_01:
	LDS	Reg_A, My_Zeit_Cnt		; Zeitwert bekommt deinen gewünschten Wert
	CMPI	Reg_A, 0			; wenn schon abgelaufen, dann keine weitere Verwendung
						; oder wieder von vorn? oder neuer und anderer Wert ?
	BREQ	End_Job_01
	DEC	REG_A				; sonst runterzählen
	STS	My_Zeit_Cnt, Reg_A		; und abspeichern
	BRNE	End_Job_01			; wenn nun nicht 0 dann Routine verlassen
	;------------------------------------------------------------------------------
	; hier die Aufgabe nach der gewünschten Zeit einbauen
	;------------------------------------------------------------------------------
End_Job_01:
RET

Auf diese Weise erhälst du kleine überschaubare Subroutinen. Wenn du zum Beispiel einen Ausgang für eine bestimmte Zeit setzen willst, dann setzt du den Zeitwert und in der Variablen für die Ausgänge ein Bit. Ist die Zeit abgelaufen, löscht du das Bit.
Da im Programm eine Variable für die Ausgänge wie auch für die Eingänge verwendet wird, kannst du erst einmal völlig ohne Hardware arbeiten. Am Ende der Schleife setzt du dann die Ausgänge. Eine einzige Stelle, die den Aufruf "RCALL Write_IO" hält.
Somit läuft deine Programmschleife wie folgt:
Code:
MainLoop:
;-------------    E   --------------
	RCALL	Read_IO		; Eingänge lesen
	RCALL	Debounce		; entprellen
	RCALL	Event_Flags		; Flanken gültiger Statusänderung der Eingänge
;------------     V    -------------
	RCALL Chk_Event_Flag		; Ereignisauswertung der Eingänge
	RCALL	CHK_Time_Flags	; Auswertung von Zeitereignissen in Time_Flag
	RCALL	CHK_Job_Flags		; Bearbeiten  von Jobs  in Job_Flag
;------------    A    ________
	RCALL Write_IO		; Ausgänge beschreiben
RJMP	MainLoop

ISR_TimeX:
;----------------------------------------------
;    setzt die Zeitflags in Time_Flag
;----------------------------------------------
RETI
Vielleicht hilft dir das weiter
Gruß oldmax
 
PS: Frage am Rande: Kann man denn über den INT0 den ATtiny aus dem sleep Modus holen? Irgendwie klappt das gerade nicht.

Natürlich :)

Dazu musst du aber auch erstmal definieren welchen Sleep Mode du nutzen möchtest, da gibt es mehrere, sinnbildlich vom Nickerchen bis hin zum Tiefschlaf. Aber soweit ich weiß ist INT0 auf keinem Controllertyp durch Sleep Modes betroffen.
Auch der ADC oder SPI/TWI kann den Controller wieder aufwecken, genau so wie Counter. Aber dazu lieber erstmal ins Datenblatt schaun, manche Modes nehmen den Takt von den Modulen :)

Ich hab das z. B. so:
Code:
.DEF TEMP1 = R16				; General temporary register
.DEF TEMPI = R19				; General temporary register (ISR only)
.DEF FLAGS = R21				; Applicationwide flags
.EQU fUPUI = 0					; Update UI flag

; Interrupt table
.ORG 0x0000			RJMP OnReset	; Reset / Power on
;...
.ORG ADCCaddr			RJMP OnADC	; ADC Conversion Complete
;...
.ORG INT_VECTORS_SIZE			; End of interrupt table

;
; On reset callback
;
OnReset:
	;RJMP	Main_Init			; Command not required, it's the next label

;
; Controller initialization
;
Main_Init:

	; Enable ADC Noise Reduction mode
	LDI	TEMP1	, (1<<SM0) | (1<<SE)
	OUT	MCUCR	, TEMP1

	; Disable analog comperator
	LDI	TEMP1	, (1<<ACD)
	OUT	ACSRA	, TEMP1

	; Disable other unused components
	LDI	TEMP1	, (1<<PRTIM1) | (1<<PRTIM0) | (1<<PRUSI)
	OUT	PRR	, TEMP1

; hier starte ich noch den adc

	; Execute main application cycle
	;RJMP	Main_App				; Command not required, it's the next label

;
; Main application cycle
;
Main_App:

	WDR						; Reset the watchdog

	; User Interface
	SBRC	FLAGS	, fUPUI				; Check for update UI flag
	RCALL	UI_Update				; If set update UI

	; Clear flags
	CBR		FLAGS	, (1<<fUPUI)

	SLEEP						; Nothing more to do, fall asleep

RJMP Main_App

;
; OnADC complete interrupt service routine
;
OnADC:

	; Backup used registers
	IN	TEMPI	, SREG
	PUSH	TEMPI

; ich kopier hier die Werte noch in den sram und n bissl anderes (schnelles) Zeug wie Channel umschalten

	; Set update UI flag
	SBR	FLAGS	, (1<<fUPUI)

	; Restore used registers
	POP	TEMPI
	OUT	SREG	, TEMPI

RETI

Genau so sollte das auch mit anderen Interrupts gehen, wie INT0
 

Über uns

  • Makerconnect ist ein Forum, welches wir ausschließlich für einen Gedankenaustausch und als Diskussionsplattform für Interessierte bereitstellen, welche sich privat, durch das Studium oder beruflich mit Mikrocontroller- und Kleinstrechnersystemen beschäftigen wollen oder müssen ;-)
  • Dirk
  • Du bist noch kein Mitglied in unserer freundlichen Community? Werde Teil von uns und registriere dich in unserem Forum.
  •  Registriere dich

User Menu

 Kaffeezeit

  • Wir arbeiten hart daran sicherzustellen, dass unser Forum permanent online und schnell erreichbar ist, unsere Forensoftware auf dem aktuellsten Stand ist und der Server regelmäßig gewartet wird. Auch die Themen Datensicherheit und Datenschutz sind uns wichtig und hier sind wir auch ständig aktiv. Alles in allem, sorgen wir uns darum, dass alles Drumherum stimmt :-)

    Dir gefällt das Forum und unsere Arbeit und du möchtest uns unterstützen? Unterstütze uns durch deine Premium-Mitgliedschaft!
    Wir freuen uns auch über eine Spende für unsere Kaffeekasse :-)
    Vielen Dank! :ciao:


     Spende uns! (Paypal)