Forum: Mikrocontroller und Digitale Elektronik Problem mit BASCOM und Log()-Funktion


von Andre (Gast)


Lesenswert?

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

von Falk B. (falk)


Lesenswert?

@ 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

von Andre (Gast)


Lesenswert?

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

von Falk B. (falk)


Lesenswert?

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

von Karl H. (kbuchegg)


Lesenswert?

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.

von Andre (Gast)


Lesenswert?

@  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...

von Karl H. (kbuchegg)


Lesenswert?

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 :-)

von Andre (Gast)


Lesenswert?

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

von Falk B. (falk)


Lesenswert?

@ 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

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.