Hi,
ich habe ein nicht lösbares Problem (sry schonmal für den langen Text,
muss aber sein da das zum Verständnis gebraucht wird):
Ich nutze einen ATMega168PA mit 20Mhz. Die C's sind 22pf am Quarz.
Dieser ist ein NMRA-DCC Decoder/Bremsgenerator. Sprich er decodiert das
DCC Signal (~20khz) mittels 1x Timer an einem Pin, wertet die Daten aus
etc... Das ist recht viel.
Kurzer Verständniseinschub:
Der Bremsgenerator arbeitet so:
Fährt eine Lok auf den galvanisch getrennten Bremsabschnitt (versorgt
mit meinem Modul), erzeugt dies einen Kurzschluss den ich aber so früh
erkenne und handel, noch bevor die Zentrale die Lasterhöhung bemerkt
(oder gar selbst Überstrom meldet). Wenn mein Modul merkt, dass keine
Spannung mehr anliegt (durch Überbrückung der Lok), dann speist dieses
den Abschnitt mit einem Halt-Befehl oder Langsamfahrtbefehl. Vorteil:
Man braucht keine Booster, Schalter oder sonstiges Zubehör und alle
Funktionen der Lok bleiben erhalten, da das so schnell geht, bevor der
Lokdecoder sich resettet. Außerdem verschleißarm, da nichtmal
Funkensprühen durch kurzschluss sichtbar war (arbeitet mit 24V, Zentrale
kann bis 15A).
Dann erzeugt er nochmals das DCC Signal mittels H-Brücke auf 2 anderen
Pinnen (wieder ~20kHz) und der 2. Timer wird genutzt.
Außerdem für die Zeiten hat er einen ISR-Timer mit OVF-INT mit 1,25khz
und da kommt der 3. TImer zum Einsatz. Außerdem wird mit diesem an 8
Pinnen Soft-PWM erzeugt (sofern Ausgang aktiviert).
Das klappt alles 1a und erstaunlich gut. Jetzt aber das Problem.
Da er ein Bremsgenerator ist, misst er den Stromverbrauch der H-Brücke
(L6201PS). Hier habe ich einen 0,05R Widerstand in Serie zum Source-Pin
der H-Brücke als Sensor. Diese Spannung greife ich (mit 100nF und
Z-Diode für alle Fälle) ab und leite sie auf einen ADC-Pin.
In der Software habe ich den ADC als "Free-Running" konfiguriert (wie
ich das verstanden habe wird nun permanent gewandelt ohne das ich es
immer aktivieren muss.
Den Wert nehme ich entgegen und berechne den Strom. Ich nutze 1,1V int.
Ref.
Jetzt das PROBLEM:
Wenn ich es so mache wie oben klappt alles super ABER ab und an wird ein
Kurzschluss erzeugt, denn im ungünstigsten Moment ist noch der alte Wert
vorhanden und nicht der echte Stromwert. Auf dem Tisch mit Lok hin und
herschieben über die Trennstelle passierte das (bei der hohen Rate) alle
20-30x. Ist nicht viel, aber im Betrieb, wenn es dann mal passiert,
absolut nervig.
Lasse ich bspw. den Timer weg
(decoder.timer.timer_univ[brggen_adc_conversion_timer] löschen und die
Abfrage rausnehmen, sodass die Wandlung permanent passiert), dann ist
die Lokerkennung genauso wie in der Theorie ausgemalt!!! ABER: Dann kann
ich nichts mehr steuern (an dem Modul hängen noch die Signale da das
eine Signalbeeinflussung geben sollte). Ich muss ungelogen 20x auf die
Taste "Signal grün" tippen und wenn ich Glück habe kommt mal ein Befehl
in der richtigen Zeit durch.
Ich bin komplett ratlos was ich machen soll. Es kann doch nicht so krass
lange dauern, bis er die 3 float Befehle abgearbeitet hat, der taktet
mit 20Mhz. Ich weiß, float, und uint32 und größer haben, zumindest bei
mir, immer schon Probleme auf den Mega's gemacht, deswegen vermeide ich
die wo ich kann, aber hier geht es leider nicht. Vll. ist es auch eine
Einstellungssache beim AtmelStudio7?!
Ich würde mich über Hilfe echt freuen!
Nimm doch die ganze Konvertierung mal raus und arbeite mit realen
ADC-Werten. Dem Controller ist es doch Wurscht, ob er mA oder ADC-Ticks
von der H-Brücke liest. Wenn Du es irgendwo ausgeben musst, kannst Du
zur Laufzeit in der Main konvertieren, das kann dann immer noch von
kritischen Interrupten unterbrochen werden.
Marius D. schrieb:> Ich nutze 1,1V int. Ref.
Dann rechne statt mit float in mV. Dadurch kannst du mit Integern
rechnen und bist pfeilschnell. Denn ein kleiner Merkspruch für solche
schmächtigen 8-Bit uC ohne FPU ist "Float = Langsam".
> Es kann doch nicht so krass lange dauern, bis er die 3 float Befehle> abgearbeitet hat
Eins vorneweg: es sind 4 float-Befehle, denn allein das Einlesen des
ADC-Integer-Werts und dessen Umwandeln in einen float ist auch nicht
ohne.
> Es kann doch nicht so krass lange dauern
Aber der Witz ist: sowas kann man im Simulator mal kurz ausmessen
lassen. Oder man misst es mit einem Portpin im echten Leben...
Das Ergebnis ist der Strom in mA als Ganzzahl. Wenn Dir die Auflösung in
dA (ja, Dezi-Ampere, z. B. 54 dA = 5,4 A) reicht, kommst Du sogar mit
16-bit-Variablen aus:
@Edi R. (edi_r)
>Ich würde es so machen:>uint32_t current;>current = 5500UL * ADCW;>current += 128;>current >>= 8;
Was soll der Unsinn? Oder bist du BASCOM-geschädigt? Sowas kann man in C
problemlos in EINE Zuweisung schreiben. Außerdem braucht der OP hier
sicher KEINE 32 Bit Variable. Es reicht, wenn man per Cast oder Suffix
eine 32 Bit RECHNUNG erzeugt. Siehe Festkommaarithmetik. Und wenn
schon, dann bitte richtig rechnen.
Bei 0,05R = 50mOhm sind das 20A/V
1
U = ADCW * U_ref / 1024 = ADCW * 1,1V / 1024
2
I = U / R = U / 0,05R = U * 20A/V = ADCW * 1,1 * 20A/V / 1024 = ADCW * 22 / 1024
Marius D. schrieb:> Es kann doch nicht so krass> lange dauern, bis er die 3 float Befehle abgearbeitet hat,
Das "kann nicht" stützt sich worauf? Erwartung, Erfahrung?
Ist auch ein bisschen unglücklich geschrieben, die 3 Befehle mit 3
Konstanten kann man natürlich vorab berechnen, so dass nur eine
Operation übrigbleibt. Aber ich denke, dass baut der Compiler eh für
dich um, so dass da eher kein Einsparpotential besteht.
Ansonsten ist ja alles gesagt. Solange du nicht irgendeinen Wert
menschlich ablesbar machen willst, brauchst du eigentlich gar keine
Skalierung auf den tatsächlichen Stromwert. Rechne einfach mit dem
nackten ADC-Wert weiter, der entspricht ja nach wie vor dem Strom, halt
in einer sehr unüblichen Einheit.
Falk B. schrieb:> Was soll der Unsinn? Oder bist du BASCOM-geschädigt?
Nö, ich wollte es nur ähnlich schreiben wie der TO. Schließlich soll
er es nachvollziehen können. Du hast aber Recht damit, dass für das
Ergebnis eine 16-Bit-Variable reicht. Die Multiplikation wird aber
trotzdem mit 32 Bit durchgeführt. Bei meinem zweiten Vorschlag mit den
dA nur mit 16 Bit.
Falk B. schrieb:> current = (22000UL * ADCW) >> 10;
Das halte wiederum ich für Unsinn. Das Verschieben der Bytes um 8 Bit
erledigt der Compiler durch ein Verschieben der Bytes. Bei der
Verschiebung um 10 Bit müssen zusätzlich zwei Shifts eingefügt werden.
Marius D. schrieb:> decoder.dcc_current = ADCW; //get adc value ADCW> decoder.dcc_current *= 1.1; //ref voltage> decoder.dcc_current /= 1024; //convert to voltage> decoder.dcc_current /= 0.05; //this is R (I = U/R)
Was willst Du mit dieser elendig langen Rechenorgie, noch dazu mit 2
schnarchlahmen Divisionen.
Du willst doch bestimmt nur auf einen Schwellwert vergleichen. Da kann
man ganz einfach die Schwelle einmalig in ADC-Schritte umrechnen und
erreicht genau das gleiche, bloß 1000-mal schneller.
Edi R. schrieb:> Das Ergebnis ist der Strom in mA als Ganzzahl. Wenn Dir die Auflösung in> dA (ja, Dezi-Ampere, z. B. 54 dA = 5,4 A) reicht, kommst Du sogar mit> 16-bit-Variablen aus:>
1
>uint16_tcurrent;
2
>current=55*ADCW;
3
>current+=128;
4
>current>>=8;
5
>
Viele Antworten in kurzer Zeit. Danke erstmal.
Ich finde die Idee mit den dA am Besten, irgendwie hatte ich ein Brett
vor dem Kopf, vorallem weil der Schwellwert (ist veränderbar via
CV-Programmierung) im EEPROM ebenfalls als dA abgelegt ist. Hätte man
auch sofort so umrechnen können anstatt alles hin und herzuteilen.
Allerdings die Berechnung ist glaube nicht korrekt. Wenn ich einen
ADC-Wert von 120 habe, dann entspricht dies 2,578A.
Mit der obigen Berechnung komme ich auf 26,28dA.
Ich verstehe auch die +128 nicht, wenn ich die nicht habe, dann habe ich
25,78dA dass passt schon besser.
Die korrekte Berechnung müsste doch so lauten:
1
uint16_tcurrent=((55*ADCW)/256);
BTW: Macht es einen Unterschied ob ich um 8 bit schiebe oder teile?
Müsste der Compiler doch eigentlich anpassen das ganze, oder?
@ Marius Dege (2009marius15)
>Ich finde die Idee mit den dA am Besten, irgendwie hatte ich ein Brett>vor dem Kopf, vorallem weil der Schwellwert (ist veränderbar via>CV-Programmierung) im EEPROM ebenfalls als dA abgelegt ist.
Dann reicht sogar eine 8 Bit Variable als Ergebnis. Die Rechung
>Allerdings die Berechnung ist glaube nicht korrekt. Wenn ich einen>ADC-Wert von 120 habe, dann entspricht dies 2,578A.
Stimmt.
>Mit der obigen Berechnung komme ich auf 26,28dA.
Stimmt auch, ist halt gerundet.
>Ich verstehe auch die +128 nicht,
Das ist zum Runden. 128/256 = 0,5. Wenn man die addiert, wird
mathematisch korrekt gerundet. Ist aber akademisch.
>uint16_t current = ((55 * ADCW) / 256);>BTW: Macht es einen Unterschied ob ich um 8 bit schiebe oder teile?>Müsste der Compiler doch eigentlich anpassen das ganze, oder?
Wenn der Compiler was taugt, wird er aus /256 automatisch ein >> 8
machen.
>Was willst Du mit dieser elendig langen Rechenorgie, noch dazu mit 2>schnarchlahmen Divisionen.
geteilt durch 2^10 wird doch bestimmt durch ein shift ersetzt...
GRuß J
@ Jonas Biensack (jibi)
>>Was willst Du mit dieser elendig langen Rechenorgie, noch dazu mit 2>>schnarchlahmen Divisionen.>geteilt durch 2^10 wird doch bestimmt durch ein shift ersetzt...
Bei einer Fließkommadivision? Jaja, theoretisch wäre das möglich, da ja
auch die Fließkommadarstellung zur Basis 2 erfolgt, aber praktisch glaub
ich das eher nicht, schon gar nicht auf dem AVR, wo das alles per
Software emuliert wird. Und wenn schon Fließkomma, dann wäre es eine
Subtraktion des Exponenten um 10 und KEIN Shift . . .!