PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Mega16 stürzt manchmal ab



Lenox
29.03.2007, 16:55
Wie es der Titel schon sagt, wenn ich zu viele Sachen gleichzeit ausführe stürt der µC einfach ab.
Über die RS232 kommt dann in etwa sowas (im Terminal sind es kleine Herze):


ìüÿÿÿ
ÿ2ÿÿÿ




8ÿ
ÿ=ÿÿ


Mein Programm ist mitlerweile schon etwas komplexer, ca. 10 Goto's, 2 Timer und ein Interrupt.

Wüsstet ihr wie ich solche Probleme beheben könnte?
Oder liegt es nur an der Software?
Der Mega16 ist über einen 16Mhz Quarz getaktet.

Sorry ist ne etwas dumme Frage, ich könnte den Code auch jetzt postet, aber der ist etwas unübersichtlich.

robocat
29.03.2007, 17:21
springst du evtl mit einem goto aus der ISR oder aus einer subroutine? dann läuft dein stack über, das würde abstürze erklären.

Lenox
29.03.2007, 17:27
Ja das mache ich. Mist.

Wie könnte ich das umgehen?
kann man den Stack einfach erhöhen?
$swstack = 32
$hwstack = 40

Pascal
29.03.2007, 17:38
Ich denke nicht, dass das was bringt. Denn dann tritt das Problem einfach ein klein wenig später auf, eine richtige Lösung ist es nicht.

Lenox
29.03.2007, 17:40
Hm wiso später?
Ist der Stack irgentwann voll oder?

Ich werd mich gleich mal genauer über die Stacks informieren.

Schade das es nicht so geht.

pongi
29.03.2007, 18:27
Naja, der Stack ist eine komplexe Sache. Erstens kann es passieren, dass der Pointer überläuft, also nicht mehr auf die letzte RAM-Adresse sondern zeigt, sondern plötzlich auf 0x00. Danach läuft dein AVR Amok.
Ausserdem kann es bei unachtsamer Benutzung von Routienen und ungünstigen Variablenadressen passieren, dass der Stack eine Variable überschreibt, oder umgekehrt.
Wenn bu bei Interupts komplexere Sachen machen musst, ist es vielleicht ratsam, wenn du die Variable, die du aus der ISR benutzen willst, in ein FIFO ablegst, und eine Control-Varibale einführst, woraus dein Program später weiss, dass da noch eine Variable ist, die noch nicht verarbeitet worden ist. Hoffentlich ist das verständlich, damit kannst du es vermeiden, das unkontrolliert Routienen aufgerufen werden, die normalerweise hintereinander laufen sollten.
Ablauf einmal kurz erklärt:
1. AVR ist in Routine #1, und arbeitet
2. ISR tritt auf, speichert neue Variable, und setzt eine Control-Variable
3. Routine #1 endet
4. Zurück in die Hauptrutine, die anhand der Control Varibale (oder noch besser Control-FIFO) entscheidet, welche Routine jetzt aufgerufen werden soll, und übergibt die Varibale, die von der ISR gespeichert worden ist, an diese weiter
5. AVR arbeitet in Rutine #2

Damit hast du ein kleines OS, was die Aufgaben verteilt. Das kann man dann noch vertiefen, indem man Prioritäten usw einstellt.

MfG

pongi

robocat
29.03.2007, 19:01
in asm springt man einfach mit RETI (interrupt) oder RET (subroutine) zurück. dann läuft auch der stack nicht über. wie das in bascom ist, weiß ich nicht.

Lenox
29.03.2007, 20:01
Danke euch beiden.
Ich denke mal das es in Bascom auch ein Befehl gibt um den Stackpointer zurückzusetzen.

Bei mir ist es in etwas so:

1. Hauptprogramm Endlosschleife ruft Wait_and_get Fkt auf
2. Wait_and_get Fkt emfängt IR und geht in Sub Programm (GoTo).
3. Das Sub Programm wird Endlos ausgeführt uns ruft jedesmal die Wait_and_get auf, um zu prüfen ob ein neues Signal (IR) anliegt

Zusätzlicht läuft noch ein PWM Timer0 der per Interrupt RGB hochzählt
Und halt noch der RC5 Emfang mit Timer2

pongi
29.03.2007, 21:13
Verstehe ich das richtig? Du rufst immer wieder ein Subprogramm auf, das aber nie endet, weil Endlosschleife? Ist nicht besonders gut... Vielleicht liegst ja daran. Sowas kann den Stack ziemlich verwirren...

robocat
29.03.2007, 21:30
bei jedem sprung in eine ISR oder SUB wird die rücksprungadresse (also da, wo das proggi grade war, als gesprungen wurde) auf den stack gepushed. mit RET(I) wird dieser wert wieder vom stack geholt und zurückgesprungen. wird immer weiter gesprungen ohne rücksprung, werden immer neue werte auf den stack geschoben, aber nicht mehr abgeholt. das ergebnis ist, dass auf dem stack früher oder später kein platz mehr ist, und dadurch schlussendlich dein programm abkackt.

deshalb hinter jeden ISR-einsprung ein reti und hinter jedes rcall ein ret (so heissen die befehle in asm, aber das prinzip ist in basic sicher dasselbe).

denke das stimmt so, wenn ich mich irre, tut mich bitte korrigieren.

pongi
29.03.2007, 21:34
Das ist so. Meiner Meinung nach ist das das Problem.

Lenox
29.03.2007, 22:19
Also pack ich einfach mal in jede ISR und jede Call ein RET(I) rein?

Ich hab grad mein Programm etwas verständlicher geschrieben (kommentiert)
Vielleicht versteht man es jetzt



'####################################
'# Projekt IR gesteuertes LED Licht #
'# Copyright 2007 by Stefan Reible #
'####################################

$regfile = "m16def.dat"
$framesize = 32
$swstack = 32
$hwstack = 40
$crystal = 16000000
$baud = 9600

Waitms 50 'Einschwingzeit

'//////////Ports Konfigurieren\\\\\\\\\\
Config Porta = Output 'Multiplexer1
Config Portc = Output 'Multiplexer2
Config Portd.4 = Output 'RGB-Blau
Config Portd.5 = Output 'RGB-Grüen
Config Portd.6 = Output 'RGB-Rot
Config Portd.3 = Input 'TSOP

Porta.4 = 1 'Enable1 active = low
Portc.4 = 1
Enable1 Alias Porta.4
Enable2 Alias Portc.4

Portd.4 = 1 'RGB Led ausschalten
Portd.5 = 1
Portd.6 = 1


'//////////Timer konfigurieren, Vorteiler auf 1\\\\\\\\\\
Config Timer0 = Timer , Prescale = 1

'//////////RC5 konfigurieren mit Timer 2\\\\\\\\\\
Config Rc5 = Pind.3 , Timer = 2

'/////////Sub Deklarieren\\\\\\\\\\\\\
Declare Sub Wait_and_get(byval Ms As Byte)

'//////////Definiere den Interrupthandler\\\\\\\\\\
On Ovf0 Tim0_isr
Enable Interrupts

Enable Timer0 'Timer einschalten


Dim R1 As Byte ' In diese Variablen muss man
Dim G1 As Byte ' im Hauptprogram die gewnschten
Dim B1 As Byte ' Ausgabewerte laden

Dim Z As Word 'Zaehler

Dim Ri1 As Byte 'Hilfsregister
Dim Gi1 As Byte
Dim Bi1 As Byte

'//////////Variablen fr Hauptprogramm\\\\\\\\\\
Dim A As Byte 'Schleifenvariable RGBs

Z = 0

G1 = 0
R1 = 0
B1 = 0

Dim I As Byte 'Schleifenvariable Lauflicht
Dim Ir_address As Byte 'Geraet
Dim Ir_command As Byte 'Befehl

Dim Freilauf As Integer

'//////////Einstellungen\\\\\\\\\\
Freilauf = 10 'Lauflicht Freilaufzeit
'//////////Hauptprogramm <Start>\\\\\\\\\\

Waitms 200
Print "Starte Hauptprogramm"

Standby:
R1 = 0
G1 = 0
B1 = 0
Enable1 = 1
Enable2 = 1
Do
Call Wait_and_get(5) 'Daumchen drehen und auf Befehl warten
Loop

'//////////Hauptprogramm <Ende>\\\\\\\\\\

'//////////Programme <Start>\\\\\\\\\\
'Programm1: RGB LEDs
Programm1:
Enable1 = 1
Enable2 = 1
Do

'Rot
For A = 0 To 254
Incr R1
Waitms 1
Next
Waitms 10
For A = 0 To 254
Decr R1
Waitms 1
Next

Call Wait_and_get(0)

'Grn
For A = 0 To 254
Incr G1
Waitms 1
Next
Waitms 10
For A = 0 To 254
Decr G1
Waitms 1
Next

Call Wait_and_get(0)

'Blau
For A = 0 To 254
Incr B1
Waitms 1
Next
Waitms 10
For A = 0 To 254
Decr B1
Waitms 1
Next

Call Wait_and_get(0)

'Blau/Grn
For A = 0 To 254
Incr B1
Incr G1
Waitms 1
Next
Waitms 10
For A = 0 To 254
Decr B1
Decr G1
Waitms 1
Next

Call Wait_and_get(0)

'Blau/Rot
For A = 0 To 254
Incr B1
Incr R1
Waitms 1
Next
Waitms 10
For A = 0 To 254
Decr B1
Decr R1
Waitms 1
Next

Call Wait_and_get(0)

'Grn/Rot
For A = 0 To 254
Incr G1
Incr R1
Waitms 1
Next
Waitms 10
For A = 0 To 254
Decr G1
Decr R1
Waitms 1
Next

Call Wait_and_get(0)

'Grn/Rot/Blau
For A = 0 To 254
Incr G1
Incr R1
Incr B1
Waitms 1
Next
Waitms 10
For A = 0 To 254
Decr G1
Decr R1
Decr B1
Waitms 1
Next
Loop
'Ende Rgb Fadern

'Programm2: Lauflicht
Programm2:
R1 = 0
G1 = 0
B1 = 0
Enable1 = 1
Enable2 = 1

Do

For I = 0 To 15
Porta = I
Call Wait_and_get(0)
Enable1 = 0
Waitms Freilauf
Enable1 = 1
Next I

For I = 0 To 11
Portc = I
Call Wait_and_get(0)
Enable2 = 0
Waitms Freilauf
Enable2 = 1
Next I
Loop
'ende des 2. programms


'Programm3: Lauflicht2
Programm3:
R1 = 0
G1 = 0
B1 = 0
Enable1 = 1
Enable2 = 1

Do

For I = 0 To 15
Porta = I
Enable1 = 0
Call Wait_and_get(0)
Enable1 = 1
Next I

For I = 0 To 11
Portc = I
Enable2 = 0
Call Wait_and_get(0)
Enable2 = 1
Next I
Loop
'ende lauflicht 2

'Dauer
Dauer:
Do
Enable1 = 0
For I = 0 To 15
Porta = I
Waitus 1
Next I
Enable1 = 1

Enable2 = 0
For I = 0 To 12
Portc = I
Waitus 1
Next I
Enable2 = 1
'Call Wait_and_get(0)
Loop
'Dauer

'//////////Standlicht <Start>\\\\\\\\\\
Programm_rot:
R1 = 255
G1 = 0
B1 = 0
Enable1 = 1
Enable2 = 1
Do
Call Wait_and_get(5)
Loop


Programm_gruen:
R1 = 0
G1 = 0
B1 = 255
Enable1 = 1
Enable2 = 1
Do
Call Wait_and_get(5)
Loop

Programm_gelb:
R1 = 255
G1 = 0
B1 = 255
Enable1 = 1
Enable2 = 1
Do
Call Wait_and_get(5)
Loop

Programm_blau:
R1 = 0
G1 = 255
B1 = 0
Enable1 = 1
Enable2 = 1
Do
Call Wait_and_get(5)
Loop

Programm_pink:
R1 = 255
G1 = 255
B1 = 0
Enable1 = 1
Enable2 = 1
Do
Call Wait_and_get(5)
Loop
'//////////Standlicht <Ende>\\\\\\\\\\

'/////////////////Funktion zum RC5 empfangen und warten <Start>\\\\\\\\\\\\\\\
'Sub zum empfangen und warten
Sub Wait_and_get(byval Ms As Integer)
Getrc5(ir_address , Ir_command) 'Empfangen

'Wenn was empfangen dann reagieren
If Ir_address = 0 Then
Ir_command = Ir_command And &B01111111 'entfernt das togglebit
If Ir_command <> 0 Then Print Ir_command 'IR befehl Printen

If Ir_command = 17 Then Goto Programm1 'RGB Fadern
If Ir_command = 18 Then Goto Programm2
If Ir_command = 19 Then Goto Programm3

If Ir_command = 37 Then Freilauf = Freilauf + 10 'Lauflicht langsamer
If Ir_command = 36 And Freilauf > 9 Then Freilauf = Freilauf -10 'Lauflicht schneller

If Ir_command = 38 Then Goto Programm_rot
If Ir_command = 44 Then Goto Programm_gruen
If Ir_command = 46 Then Goto Programm_gelb
If Ir_command = 51 Then Goto Programm_blau
If Ir_command = 47 Then Goto Programm_pink

If Ir_command = 21 Then Goto Dauer
If Ir_command = 32 Then Goto Standby
End If

Waitms Ms 'warten
End Sub
'/////////////////Funktion zum RC5 empfangen und warten <Ende>\\\\\\\\\\\\\\\


'//////////Interupthandler Timer0 <Start>\\\\\\\\\\
Tim0_isr:

If Z = 0 Then 'Gewnschte Ausgabewerte an
Ri1 = R1 'Hilfsregister bergeben
Gi1 = G1
Bi1 = B1
Z = 255
End If

Z = Z - 1

'PWM Kanaele

'RGB LEDs
If Ri1 > 0 Then
Portd.6 = 0
Else
Portd.6 = 1
End If
Decr Ri1
If Ri1 = 255 Then Ri1 = 0

If Gi1 > 0 Then
Portd.5 = 0
Else
Portd.5 = 1
End If
Decr Gi1
If Gi1 = 255 Then Gi1 = 0

If Bi1 > 0 Then
Portd.4 = 0
Else
Portd.4 = 1
End If
Decr Bi1
If Bi1 = 255 Then Bi1 = 0

Return
'//////////Interupthandler Timer0 <Ende>\\\\\\\\\\


EDIT
Aus der ISR geht ich nicht raus.
Nur aus der Funktion (mit Goto)

pongi
30.03.2007, 08:22
Naja, ich kenn mich in Bascom net aus... Und die Zeit hab ich auch nicht, das jetzt zu druchstöbern, aber check mal, ob alle Rutinen die aufgerufen werden, auch einmal enden, und mit return dorthin zurückkehren, von wo sie aufgerufen worden sind.
Nochn ne kurze Verständisfrage zu Bascom: ist CALL = RCALL und GOTO = RJMP?
Falls ja, musst du am Ende aller Funktionen, die mit CALL aufgerufen worden sind, ein Return schreiben, und bei denen, die mit Goto aufgerufen worden sind, entweder ein Return (dann gehen sie nicht dorthin zurück, wo goto stand, sondern dorthin wo die Funktion mit dem Goto aufgerufen worden ist), oder ein goto, damit sie dorthin zurückkehren, wo der letzte goto stand, aber dann ist es gleich besser, diese zwei gotos mit einem CALL und RET zu ersetzen. Generell gilt: RJMP/GOTO + RET nur für Fallunterscheidung, für Subrutinen immer RCALL/CALL + RET.

Bsp:

R1:
...
CALL R2
...
R2:
...
GOTO R3
...
RET --> zurück zu R1, wird nicht erreicht
R3:
...
RET --> wir sind in R1

R1:
...
CALL R2
...
R2:
...
CALL R3
...
RET --> zurück nach R1
R3:
...
RET --> wir sind in R2


War das so eingermaßen vertsändlich? :D

MfG

pongi

Vitis
30.03.2007, 09:27
die Unterprogramme, die per SUB bzw. aus Interrupt
angesprungen werden einfach mit RETURN beenden.
Dann läuft das Programm von der Einsprungstelle aus
weiter.

Lenox
30.03.2007, 12:00
Danke erstmal für die zahlreichen Antworten.
Ja das ist kla, aber sie wird ja über eine Endlosschleife im Hauptprogramm aufgerufen, nicht über ein Interrupt.

'Hauptprogramm
Do
Call Wait_and_get(5)
Loop


'Funktion zum IR Emfang und Programmaufruf
Sub Wait_and_get(byval Ms As Integer)
<emfängt IR>
If Ir_command = 17 Then Goto Programm1
<usw>
End Sub


Programm1:
Do
<Macht irgendwas>
Call Wait_and_get(0)
Loop
Return

Das Hauptprogramm ruft also endlos die Funktion Wait_and_get auf.
Wenn die Fkt ein IR Signal emfängt ruft diese wiederum das passende Programm auf (hier Programm1).

Programm1 wird dann wegen der Do Loop Schleife dauer ausgeführt.
Programm1 hat allerdings die Fkt Wait_and_get implementiert, um zu prüfen ob ein neuer IR Command ankommt (=Programmwechsel).

Die Fkt Wait_and_get ist jetzt mit "End Sub" beendet, und die Programme mit "Return".

Wenn ich jetzt gedoch das Programm ca 10-12 mal wechsel stürzt der µC ab.