Hallo zusammen,
Bin neu auf dem Gebiet der Mikrocontroller, aber soweit klappt alles
ganz gut!
Jetzt bin ich auf ein seltsames Problem gestoßen. Ich benutze einen
Atmel Mega8 und Bascom-Demo Ver. 1.11.8.7
Eine single-Variable (z.B. Temp1) enthält einen Wert ungleich 0.
Nun kommt im Code
Temp2 = Log(temp1) 'Temp2 ist ebenfalls als single deklariert
Compilieren verläuft Fehlerfrei.
Sobald nun der Mega8 an Diese Stelle im Code kommt wird der Controller
in 95% der Fälle neugestartet, nur zu 5% wird die Berechnung korrekt
durchgeführt...
Einen vergleichbaren Wert von Temp1 direkt in das Log()-Argument zu
schreiben funktioniert problemlos (z.B. "Temp2 = Log(1.2)")
Meine Vermutung: Die Variable im Log()-Argument wird von Bascom
automatisch kleingeschrieben, aus "Log(Temp1)" wird automatisch beim
Verlassen der Zeile im BASCOM-Editor "Log(temp1)". Nun wird bei der
Ausführung das Argument als "0" behandelt -> Überlauf -> Absturz. Kann
es daran liegen? Andererseits funktioniert z.B. X = Int(r) problemlos,
trotz ebenfalls kleingeschriebenem "r"?
Bin absolut ratlos, vielleicht kann mir ja einer von Euch weiterhelfen?
Danke und Grüße - Andre
@ Andre (Gast)
>Temp2 = Log(temp1) 'Temp2 ist ebenfalls als single deklariert
Solange temp1 grösser als Null ist, ist das OK.
>Sobald nun der Mega8 an Diese Stelle im Code kommt wird der Controller>in 95% der Fälle neugestartet, nur zu 5% wird die Berechnung korrekt>durchgeführt...
???
Wie prüfst du das? Mit einem Debugger? Vielleicht wird zu 95% temp1
kleiner gleich Null.
>Meine Vermutung: Die Variable im Log()-Argument wird von Bascom>automatisch kleingeschrieben, aus "Log(Temp1)" wird automatisch beim>Verlassen der Zeile im BASCOM-Editor "Log(temp1)".
Sollte egal sein, BASCOM ist nicht Case sensitive.
>Bin absolut ratlos, vielleicht kann mir ja einer von Euch weiterhelfen?
Lass dir mal temp1 auf eiem LCD oder UART ausgeben. Oder poste mal das
vollständige Programm.
MFG
Falk
Hallo Falk,
>>Sobald nun der Mega8 an Diese Stelle im Code kommt wird der Controller>>in 95% der Fälle neugestartet, nur zu 5% wird die Berechnung korrekt>>durchgeführt...>???>Wie prüfst du das? Mit einem Debugger? Vielleicht wird zu 95% temp1>kleiner gleich Null.
Das Programm liest jede Sekunde über den ADC Die Spannung an einem NTC
ein, soll aus der Kanalnummer des ADC Spannung, Wiederstand und
Temperatur berechnen und über UART ausgeben. In der T(R)-Rechnung kommt
Log(R/R_25°C) vor, daran hakt's. Wenn ich die Log()-Zeile auskommentiere
und mir stattdessen das Argument ausgeben lasse klappt alles wunderbar,
Argument ist durchwegs positiv im Bereich um 1,2 (keine chance bei
R/R_25°C was negatives 'rauszubekommen, siehe code).
Sobald ich nun die Log-Zeile mitrechnen lasse bekomme ich quasi immer
nur die "Begrüßung" auf dem UART -> Scheinbar ständiger Neustart des
Programms
>>Bin absolut ratlos, vielleicht kann mir ja einer von Euch weiterhelfen?>Lass dir mal temp1 auf eiem LCD oder UART ausgeben. Oder poste mal das>vollständige Programm.
Hier das ganze Programm:
$regfile = "m8def.dat"
$crystal = 4000000
$baud = 9600
Dim Ad As Word 'Kanalnummer
Dim U As Single 'Spannung
Dim R As Single 'Widerstand
Dim R2 As Integer 'Integer-Widerstand
Dim T As Single 'Temperatur
Dim Temp1 As Single
Dim Temp2 As Single
Dim I As Byte 'Zähler im Timer
I = 0
S = 0
Config Adc = Single , Prescaler = Auto , Reference = Avcc
Start Adc
On Timer0 Ontimer0
Config Timer0 = Timer , Prescale = 1024
Enable Timer0
Enable Interrupts
Print "Begrüßung"
Do
Ad = Getadc(0)
Loop
Ontimer0:
Incr I
If I >= 32 Then
Temp1 = Ad / 1024
U = Temp1 * 5
R = U / 0.000179
R2 = Int(r)
Temp1 = R / 10000
'Hier die kritische Zeile !!!!!!!
'Temp2 = Log(temp1)
'Weitere Berechnung von T, momentan inaktiv
'Temp1 = 0.0002569 * Temp2
'Temp2 = Temp1 + 0.003354
'T = 1 / Temp2
'T = T - 273
Print "Ch= " ; Ad ; " - U= " ; U ; " V - R= " ; R2 ; " Ohm - T= " ;
Temp1 ; " °C"
I = 0
End If
Return
End
Andre (Gast)
>Hier das ganze Programm:
tststs, was soll dass denn mit dem
Ad = Getadc(0)
in der Endlosschleife aber der Rest im Interrupt? Da kann man sich schön
ins Knie schiessen. Stichwort Atomarer Datenzugriff.
Machs gleich richtig wie die grossen Jungs. Nutze eine Variable um einen
neuen Timerüberlauf an des HAUPTPROGRAMM zu signalisieren. Dort läuft
dann das Programm. Hat viele Vorteile und ist sicher.
Etwa so (ohne Gewähr, bin kein BASCOMER)
$regfile = "m8def.dat"
$crystal = 4000000
$baud = 9600
Dim Ad As Word 'Kanalnummer
Dim U As Single 'Spannung
Dim R As Single 'Widerstand
Dim R2 As Integer 'Integer-Widerstand
Dim T As Single 'Temperatur
Dim Temp1 As Single
Dim Temp2 As Single
Dim I As Byte 'Zähler im Timer
Dim Flag as byte ' Merker für Timerüberlauf
I = 0
S = 0
Config Adc = Single , Prescaler = Auto , Reference = Avcc
Start Adc
On Timer0 Ontimer0
Config Timer0 = Timer , Prescale = 1024
Enable Timer0
Enable Interrupts
Print "Begrüßung"
Do
if flag=1 then
flag = 0
Incr I
If I >= 32 Then
Ad = Getadc(0)
Temp1 = Ad / 1024
U = Temp1 * 5
R = U / 0.000179
R2 = Int(r)
Temp1 = R / 10000
'Hier die kritische Zeile !!!!!!!
'Temp2 = Log(temp1)
'Weitere Berechnung von T, momentan inaktiv
'Temp1 = 0.0002569 * Temp2
'Temp2 = Temp1 + 0.003354
'T = 1 / Temp2
'T = T - 273
Print "Ch= " ; Ad ; " - U= " ; U ; " V - R= " ; R2 ; " Ohm - T= " ;
Temp1 ; " °C"
I = 0
End If
end if
Loop
' Interrupt
Ontimer0:
flag =1
Return
End
MFG
Falk
Wenn ich mir diese Berechnung mal so ansehe
Temp1 = Ad / 1024
U = Temp1 * 5
R = U / 0.000179
R2 = Int(r)
Temp1 = R / 10000
'Hier die kritische Zeile !!!!!!!
'Temp2 = Log(temp1)
Temp1 liegt am Anfang im Bereich 0 bis 1
U kommt daher auf 0 bis 5
In der nächsten Zeile wird U dann durch 0.000179
dividiert um R zu erhalten.
Das ist schon mal ziemlich klein.
Wird aber dann nochmal durch 10000 dividiert
um endlich im log zu landen.
Wie du auf einen Wert von 1.2 kommst, kann ich nicht
nachvollziehen. Ich würde eher sagen, dass deine Werte
so klein werden, dass sie mit der SINGLE Präzission
praktisch auf 0 liegen. Und der log von 0 ist nun mal
minus unendlich. Was BASCOM in diesem Fall macht weis
ich nicht. Aber einen Reset schmeissen, würde mir nicht
ungewöhnlich vorkommen.
@ Karl heinz Buchegger:
Nein, daran liegt es leider auch nicht, denn:
>Wenn ich mir diese Berechnung mal so ansehe>Temp1 = Ad / 1024>U = Temp1 * 5>R = U / 0.000179>R2 = Int(r)>Temp1 = R / 10000>Wie du auf einen Wert von 1.2 kommst, kann ich nicht>nachvollziehen.
Temp1 typisch um 0.4
U damit typisch um 2
R damit typisch um 12000 (!!) (Division durch kleine Zahl)
Temp1 damit um 1.2
Log(1.2) scheitert dann, warum auch immer...
Andre wrote:
> @ Karl heinz Buchegger:>> Nein, daran liegt es leider auch nicht, denn:>>>Wenn ich mir diese Berechnung mal so ansehe>>Temp1 = Ad / 1024>>U = Temp1 * 5>>R = U / 0.000179>>R2 = Int(r)>>Temp1 = R / 10000>>>Wie du auf einen Wert von 1.2 kommst, kann ich nicht>>nachvollziehen.>> Temp1 typisch um 0.4> U damit typisch um 2> R damit typisch um 12000 (!!) (Division durch kleine Zahl)
Ah. Da hast du recht.
So weit kann ich mit meinen 10 Fingern nicht zählen :-)
Hallo Falk,
>>Hier das ganze Programm:>tststs, was soll dass denn mit dem>>Ad = Getadc(0)>>in der Endlosschleife aber der Rest im Interrupt? Da kann man sich schön>ins Knie schiessen. Stichwort Atomarer Datenzugriff.
Hmm, verstehe ich leider nicht ganz, ersten Programm überhaupt für
Mikrocontroller und so... g>Machs gleich richtig wie die grossen Jungs. Nutze eine Variable um einen>neuen Timerüberlauf an des HAUPTPROGRAMM zu signalisieren. Dort läuft>dann das Programm. Hat viele Vorteile und ist sicher.>Etwa so (ohne Gewähr, bin kein BASCOMER)
Tatsächlich! So funktioniert es wie es soll! Danke!!!
Aber wieso genau gings denn vorher nicht? Irgend ein Timing-Problem wenn
in der Interrupt-Routine noch gerechnet wird und schon ein neuer
Interrupt ausgelöst wird? dauert die Log()-Berechnung und die anderen
Berechnungen länger als 65ms (4 MHz 1024 Prescale 256 Timer)?
Na ja, danke nochmals jedenfalls!
Andre
@ Andre (Gast)
>>in der Endlosschleife aber der Rest im Interrupt? Da kann man sich schön>>ins Knie schiessen. Stichwort Atomarer Datenzugriff.>Hmm, verstehe ich leider nicht ganz, ersten Programm überhaupt für>Mikrocontroller und so... *g*
Aha. Das Problem is, dass der Interrupt JEDERZEIT auftreten kann. Aber
das Abspeichern von
Ad = Getadc(0)
Dauert MINDESTENS zwei Takte, weil der AVR ein 8 Bit Prozessor ist und
nur 8 Bit in einem Takt kopieren kann. Ad ist aber 16 Bit breit. Dann
kann es passieren, dass ein Byte schon kopiert ist, das danere aber
nicht. Wenn jetzt z.B. in Ad ine 0x00ff drinsteht, die nächste
AD-Wandlung aber 0x0100 ergibt dann kanns knallen. Die CPU kopiert z.B.
erst das untere Byte. Dann steht in Ad ein 0x0000. Wenn GENAU jetzt der
Interrupt zuschlägt wird temp1 zu 0.
Ein weiteres Problem ist der Compiler, egal ob BASCOM oder C. Der kann
aus prinzipiellen Gründen nicht immer erkennen, wenn "parallel" aus dem
Hauptprogramm und einem Interrupt auf Variablen zugegriffen wird. Dort
kann es dann optimieren, was aber fatal ist. Denn dann entstehen zwei
Variablen!
Aber das ist schon die hohe Schule der Programmierung!
>Tatsächlich! So funktioniert es wie es soll! Danke!!!
Schön zu hören.
>Aber wieso genau gings denn vorher nicht? Irgend ein Timing-Problem wenn>in der Interrupt-Routine noch gerechnet wird und schon ein neuer>Interrupt ausgelöst wird?> dauert die Log()-Berechnung und die anderen> Berechnungen länger als 65ms (4 MHz 1024 Prescale 256 Timer)?
Na Vorsicht, du hast auch die UART Ausgabe im interrupt. Sowas macht man
eigentlich nicht. Interruts müssen so kurz wie möglich sein, erst recht
in BASCOM. Die sollten nur fix ihre Daten in Variablen sichern und dem
Hauptprogemm per Flag mitteilen, dass neue Daten anliegen. Das gilt auch
für Assembler und C.
Aber es ist äusserst unwahrscheinlich, dass die 65 ms zu kurz sind, aber
möglich. Das kannst du testen, indem du prüfts, obe am ende des Loop
flag=1 ist. Wenn ja ist zwischendurch wieder ein Interrupt aufgetreten.
Ich tippe aber eher auf das Problem mit atomarem Zugriff.
MfG
Falk