NTCTABLE 0.1
----------------------------------------
NTCTABLE ist ein Programm zum Genierieren von sogenannten Loopup
Tabellen fuer NTC Temperatursensoren. NTC's haben nichtlineare
Kennlinien die das Auswerten dieser Sensoren erschweren.
Eine mathematische Formel (die einen natuerlichen Logarithmus enthaellt)
liegt zum Umrechnen fuer NTC's vor, jedoch ist der Speicherbedarf
relativ gross, sodass ein Berechnen in einem kleinen Controller nicht in
Frage kommt.
Eine Moeglichkeit NTC's auszuwerten, ist es, Abschnitte zwischen
bestimmten Punkten ihrer Kennlinie als linear anzusehen, und Werte
zwischen 2 Punkten zu interpolieren.
NTCTABLE generiert eine solche Tabelle und stellt die Auswertefunktion
der Tabelle zur Verfuegung, hierbei wird angenommen, dass ein NTC
Widerstand folgendermassen verschaltet ist:
1
+U
2
^
3
|
4
|
5
+-+
6
| | Pullup Widerstand
7
| |
8
+-+
9
|
10
o-------> zum ADC
11
|
12
+-+
13
| | NTC
14
| |
15
+-+
16
|
17
|
18
---
NTCTABLE ist ein Kommandozeilenprogramm fuer Linux und erwartet
mindestens folgende Parameter:
1
-r value | R25 Widerstandswert NTC
2
-R value | Popupwiderstand gegen Referenzspannung
3
-b value | Betawert NTC (B 25/85)
Optional koennen weitere Parameter angegeben werden:
1
-a | Sourcedatei fuer AVR-Conroller erstellen (Tabelle wird im "PROGMEM" abgelegt)
2
-l | Lookup-Table mit 32 Punkten (statt 16), erzielt etwas bessere Genauigkeit
Die Ausgabe von NTCTABLE erfolgt auf dem Standardausgabegeraet (in aller
Regel der Monitor) und kann mittels Pipe umgeleitet werden.
Bsp.:
Datei ntc10k.c erstellen für:
https://www.ebay.de/itm/20PCS-10K-5-Thermistor-Resistor-NTC-MF52AT-10K-10K-Ohms-B-3950-1/252632407824?hash=item3ad2109f10:g:AMIAAOSwyttZyS3u
NTC-Sensor 10K Ohm, B(eta)wert 3950, Pullupwiderstand 4,7 kOhm
Ausgabe fuer ATMEL-AVR Controller
NTC Kennlinie in 32 Stuetzpunkte unterteilt
ntctable -r 10000 -R 4700 -b 3950 -a -l > ntc10k.c
------------------------------------
Auch wenn die Kommandozeile nur 16 oder 32 Stützpunkte unterstützt,
könnten im Programm die Stützpunkte für höhere Genauigkeit erhöht
werden. Hier ist zu beachten, dass die Stützpunkte immer eine 2er Potenz
sein muß, weil hierdurch ein Zugriff auf die Tabelle und ein
Interpolieren sehr erleichtert wird (das Adressieren eines Wertes
innerhalb der Tabelle erfolgt nur durch Schiebeoperationen).
------------------------------------
ntctable wird übersetzt mit:
gcc -lm ntctable.c -o ntctable
Irgendwann einmal hatte ich genau eine solche Seite benutzt (vllt. war
es auch diese) und hatte nur noch die Tabelle, leider.
Also habe ich mir das rückwarts angesehen, wie ein solcher Generator
wohl funktionieren müßte.
Also habe ich das in C geschrieben und kann das jetzt (mein
Programmierrechner ist meist offline) auf der Konsole generieren, sehe
die Abweichung und kann das auch gleich als Code für AVR generieren.
Hätte ich nicht geschrieben, wenn ich die Seite wieder gefunden hätte
(grundsätzlich habe ich das für die Codesammlung für ATtiny44 - auch
hier bei Projekte & Code gemacht, damit dort alles in einem "Paket" ist)
Sinnvollerweise ist der Festwiderstand gleich gross wie der NTC
Widerstand bei der Temperatur, die interessiert.
Alternativ kann man auch ein Polynom fitten.
Jetzt ist G. schrieb:> Sinnvollerweise ist der Festwiderstand gleich gross wie der NTC> Widerstand bei der Temperatur, die interessiert.
Wenn du einen möglichst linearen Verlauf der Teilerspannung über der
Temperatur haben willst, also zur Temperaturmessung, dann gibt es einen
optimalen Widerstandswert.
Bei einem 1k KTY81 habe ich den mal zu etwa 2k5 bestimmt.
Wenn du nur auf eine feste Solltemperatur regeln willst, dann magst du
recht haben. Wahrscheinlich (hab es nicht nachgerechnet) ist dann die
Empfindlichkeit am größten.
Jetzt ist G. schrieb:> Sinnvollerweise ist der Festwiderstand gleich gross wie der NTC> Widerstand bei der Temperatur, die interessiert.
... und deshalb kann der Pullupwiderstand eben auch variieren und muß
nicht zwangsläufig den Widerstandswert von R25 haben.
Pullup macht man immer dann gerne genausogroß, wenn man in etwa
Zimmertemperaturen messen mag und über den am NTC abfallenden
Spannungswert die Temperatur ermittelt (Linearisierung der Kennlinie).
Bei der Lookup Tabelle wird aber der Widerstandswert ermittelt und
daraus die Temperatur errechnet.
Im C Programm für den PC sind Funktionen erhalten, mit denen man zu
jeder Temperatur die Abweichung feststellen kann und man so mit dem
Pullup "spielen" kann um zu sehen, wo der größte Fehler entstehen wird.
Hallo zusammen,
ich möchte diesen alten Thread nochmal ausgraben. Einen neuen zu
eröffnen finde ich nicht sinnvoll, da die Fragestellung hier gut
platziert ist.
Ich beschäftige mich auch grad mit dem Thema NTC und Look-Up-Table.
Auf der Seite
https://preis-ing.de/extras/alle-berechnungen-im-schnellzugriff/automatisches-erzeugen-einer-ntc-tabelle/?points=32&unit=0.1&resolution=10+Bit&circuit=pulldown&resistor=10000&r25=10000&beta=3480&tmin=10&tmax=80
gibt es einen LUT-Generator, der auch gut funktioniert. Ich benötige die
Werte aber in K. Daher möchte ich den Quellcode modifizieren. Bekomme
ich aber hin.
Mich interessiert die Berechnung des NTC-Widerstandes R(T) (=resistance
im Java-Quellcode der Seite):
resistor bzw. Rp ist der Pull-Down-Widerstand
n die Auflösung des ADB in Bit
max_adc = 2^n
ADC bzw. adc_v der gewandelte ADC-Wert
Es gibt zwei Gleichungen, wie der Widerstand aus dem ADC-Wert berechnet
wird (s. Anhang):
GL. I : Die Herleitung von mir aus der elektrotechnischen Betrachtung
GL. II : Aus dem Java-Quellcode der Seite. Für mich nicht
nachvollziehbar und herleitbar.
Das Rechenergebnis der beiden Gleichungen sind in dem Tabellenausschnitt
im Anhang zu sehen. Die Widerstandswerte sind identisch. Jedoch um eine
Zeile (1 LSB) versetzt.
Den Rest des Java-Codes ist nachvollziehbar und identisch mit meinen
Betrachtungen Zur Berechnung des Widerstandes für die
Pull-Up-Konfiguration sowie die Temperaturberechnung aus R(T).
Kann mit jemand vielleicht erklären, wie Gleichung II zustande kommt?
Vielen Dank
Ralph S. schrieb:> Eine mathematische Formel (die einen natuerlichen Logarithmus enthaellt)> liegt zum Umrechnen fuer NTC's vor, jedoch ist der Speicherbedarf> relativ gross, sodass ein Berechnen in einem kleinen Controller nicht in> Frage kommt.
Blödsinn: das macht man zur Compilezeit, da rechnet der µC gar nichts.
Wilhelm M. schrieb:> Blödsinn: das macht man zur Compilezeit, da rechnet der µC gar nichts.
Wenn du Berechnungen zur Compilezeit vornimmst und diese Werte in einem
konstanten Array ablegst, dann hast du genau das, was hier vorliegt:
Eine Lookup-Table.
Oben erkläre ich haarklein, warum ich die Berechnungen NICHT zur
Laufzeit vornehmen möchte.
Du zitierst mich hier:
Wilhelm M. schrieb:> sodass ein Berechnen in einem kleinen Controller nicht in>> Frage kommt.
und gibst mir gleichzeitig Recht während du "blödsinn" zu mir schreibst.
Ralph S. schrieb:> Wenn du Berechnungen zur Compilezeit vornimmst und diese Werte in einem> konstanten Array ablegst, dann hast du genau das, was hier vorliegt:>> Eine Lookup-Table.
Sage ich doch. LUTs generiert man zur Compile-Zeit.
> Oben erkläre ich haarklein, warum ich die Berechnungen NICHT zur> Laufzeit vornehmen möchte.
Genau: LUTs generiert man zur Compile-Zeit.
Irgendwie sprichst Du wirr.
Du berechnest die LUT mit einem Hilfsprogramm außerhalb der µC und
generierst ein C-File, in dem die Werte hardcoded drin stehen.
Ich spreche dagegen davon, kein extra Tool zur Generierung des C-Files
zu benutzen, sondern die LUT im µC-Code, aber zur Compile_zeit dieses
Codes zur erzeugen. Vorteil: man hat alles an einer Stelle und braucht
keine extra Tool mit extra Code.
... mach mal!
Wenn der Präprozessorxode dann halbwegs übersichtlich ist würd ich das
übernehmen, ansonsten erstell ich das mit dem Miniprogramm. Unterm
Strich zählt für mich die generierte Codegröße. Aber (keine Irinie,
keine Beleidigung): ich würde gerne einen Code von dir sehen...
Wenn man die 2 Schritte nicht haben möchte, könnte man auch ein Script
schreiben dass einem beim Aufruf des Makes einen Header generiert...
Bei CPP könnte man sich auch noch constexpr überlegen. Dann hat man
wirklich alles zusammen im Code stehen.
Ralph S. schrieb:> ... mach mal!
So auf die Schnelle ... man kann es wohl noch etwas hübscher gestalten,
doch dazu wäre ggf. mehr zusätzliche Dinge nötig. In meinem eigenen Code
wäre nur noch eine Lambda-Expression (x-Zeilen) und die Meta-Funktion
zur Erzeugung des PGM-Arrays (1 Zeile wie unten), der Rest ist natürlich
allgemeingültig in einem Header.
Liefert beides denselben Code (Deine Variante ist V1).
Ok, stimmt nicht ganz: Deine ist etwas durch den Funktionsaufruf länger,
Du könntest Sie aber auch als static inline in einer Header-Datei
packen.
BTW: ich würde mich nicht darauf verlassen, dass adc_value immer im
zugelassenen Wertebereich ist. Also: entweder maskieren (wie bei mir)
oder zusichern oder einen uint_t<10> verwenden.
Allerdings sind die Daten der LUT bei meinem Code nicht wirklich
sinnvoll. Deine Berechnung komplett zu übernehmen, war mit jetzt zu
mühsam.
Ich weiß schon, welche Kommentare jetzt kommen:
1) Es soll doch C sein.
2) Soo viel Code.
3) Unlesbar.
Dazu:
1) Die meisten der hier vorgestellten µC-Programme kann man wohl als C
oder C++ übersetzen. Sollte das wegen der strengeren UB-Regeln von C++
mal nicht möglich sein, kann man natürlich das Programm auch gemischt
aus C/C++ zusammen setzen.
2) Man schreibt sich einmal(!) ein template zu Generierung von
Pgm-Tabellen, packt das in einen Header und gut ist. Hier ist eben nur
alles zu sehen, inkl. dem Closure zur Compile-Zeit-Berechnung.
3) Das kommt auf das Auge des Betrachters an.
Mit dem Boilerplate-Code erhält man eben ganz klare Sicherheits- und
Bequemlichkeitsvorteile: die Größe der LUT ist einfach parametrierbar
wie auch etwa die signifikanten Bits des Adc: ändert man an einer
Schraube, so bleibt alles weitere richtig und sinnvoll.
1
#include<mcu/avr.h>
2
#include<array>
3
#include<cmath>
4
5
//#define V1
6
7
#ifdef V1
8
constintPROGMEMntctable[]={
9
1597,1305,1013,851,735,643,565,495,
10
430,368,306,242,175,98,4,-133,
11
-270
12
};
13
14
// Diese Funktion ist unsicher (index out-of-range)
Wilhelm M. schrieb:> Wilhelm M. schrieb:>> Ralph S. schrieb:>>> ... mach mal!>>>> So auf die Schnelle ...>> Ups, das war zu schnell.
Aller guten Dinge sind drei (vergiss den vorigen Beitrag ;-) ):
1
#include<mcu/avr.h>
2
#include<array>
3
#include<cmath>
4
5
//#define V1
6
7
#ifdef V1
8
constintPROGMEMntctable[]={
9
1597,1305,1013,851,735,643,565,495,
10
430,368,306,242,175,98,4,-133,
11
-270
12
};
13
14
// Diese Funktion ist unsicher (index out-of-range)
Als erstes (wirklich nur als Anmerkung):
Ich trete nicht in einen Wettstreit der Programmierer ein oder bin ein
Verfechter der "wahren Lehre" des Programmierens. 1000 Wege führen nach
Rom (das Rom in Italien und nicht das read only memory : - ) ).
Es gibt für die meisten Programmieraufgaben viele Lösungen und es ist
erstaunlich wie kreativ manche Problemlösungen angegangen werden (von
naserümpfend bis genial).
@wilhelm
Deine gehört zu den interessanten.
Wilhelm M. schrieb:> Ich weiß schon, welche Kommentare jetzt kommen:>> 1) Es soll doch C sein.> 2) Soo viel Code.> 3) Unlesbar.
zu 1.
Es ist fast egal ob das C oder C++ ist (auch wenn ich auf einem kleinen
Mikrocontroller C++ als nicht so angenehm empfinde). Allerdings
funktioniert der Generator auch mit dem SDCC der kein C++ kann (für
MCS-51, STM8 und für PIC interessant).
zu 2.
Wenn etwas gut ist, funktioniert und das erzeugte Binary klein ist, ist
die Sourcecodelänge relativ uninteressant.
zu 3.
Der Quellcode wird hier durch die Zeilenumbrüche durch die Darstellung
von mikrocontroller.net schlechter lesbar. In meinem Codeeditor wird das
besser.
Jeder kommt mit dem Code, den er selbst erarbeitet hat am besten
zurecht. Unterm Strich zählt die Handhabbarkeit für denjenigen der es
benutzt, der Vorteil deiner Lösung ist, dass es kein weiteres
zusätzliches Tool (wie bspw. den Generator) benötigt.
----------------------------------------------------
Für Temperaturerfassungen werde ich weiterhin den Generator verwenden,
allerdings behalte ich deinen Lösungsansatz im Kopf da ich glaube, dass
er für andere Aufgabenstellungen eine elegante Lösung sein kann.
Ralph S. schrieb:> Wilhelm M. schrieb:>> Ich weiß schon, welche Kommentare jetzt kommen:>>>> 1) Es soll doch C sein.>> 2) Soo viel Code.>> 3) Unlesbar.>> zu 1.>> Es ist fast egal ob das C oder C++ ist (auch wenn ich auf einem kleinen> Mikrocontroller C++ als nicht so angenehm empfinde).
Wie Du sicher weißt, ist es bei mir umgekehrt ;-)
> Allerdings> funktioniert der Generator auch mit dem SDCC der kein C++ kann (für> MCS-51, STM8 und für PIC interessant).
Wobei Dein Generator ja nicht auf dem µC laufen soll.
Für meine C++-Lösung ist natürlich ein C++-Cross-Compiler für den
Ziel-µC erforderlich. Da ich allerdings auch Zugriff auf den EDG habe,
und der als Target auch C hat, kann ich via C++ -> EDG --> C --> SDCC ->
µC-Code auch Code für µC erzeugen, für die es keinen C++-Cross-Compiler
gibt.
> zu 2.>> Wenn etwas gut ist, funktioniert und das erzeugte Binary klein ist, ist> die Sourcecodelänge relativ uninteressant.
Jein: je weniger Code man zu lesen hat, desto besser. Frei nach dem
Motto: nur eine gelöschte Codezeile ist eine gute Codezeile.
Da helfen die Abstraktionsmöglichkeiten von C++ allerdings enorm.
> zu 3.
...
> Jeder kommt mit dem Code, den er selbst erarbeitet hat am besten> zurecht. Unterm Strich zählt die Handhabbarkeit für denjenigen der es> benutzt, der Vorteil deiner Lösung ist, dass es kein weiteres> zusätzliches Tool (wie bspw. den Generator) benötigt.
Erstes das, wie Du sagst. Und die Flexibiltät bzw. Sicherheit durch
Generizität (ist für mich der entscheidende Punkt).
> Für Temperaturerfassungen werde ich weiterhin den Generator verwenden,> allerdings behalte ich deinen Lösungsansatz im Kopf da ich glaube, dass> er für andere Aufgabenstellungen eine elegante Lösung sein kann.
Ich benutze das gerade auf den kleinen µC sehr gerne, etwa für meine
BLDC-Controller, um dort LUTs für Cosinus mit den unterschiedlichsten
Periodendauern abzulegen. Flash ist ja meistens genug vorhanden.