Liste der Anhänge anzeigen (Anzahl: 1)
Eine bessere R zu T Wandler Routine gesucht
Hallo,
ich habe jetzt meine Heizungssteuerung schon sehr weit. Jetzt ist mir aufgefallen als ich die "richtige" R zu T Wandlerroutine eingebaut habe dass meine CPU Zeit "nachgeht". Daraufhin habe ich einen Ausgang am Anfang eines Timer IRQ gesetzt, und am Ende wieder zurückgesetzt. Das Oszi zeigt mir das folgende:
Anhang 29073
Wie man hoffentlich erkennen kann, sind meine sonstigen Zeitscheiben sehr in Ordnung bis auf diese Eine, welche hier im Screenshot 2.4 ms hat. Mein Timer läuft mit 1 ms. Nach dem schrittweisen auskommentieren bin ich sehr schnell auf der Schuldigen Routine gelandet.
Ich habe eine Tabelle im Flash:
Code:
74 const sensorvalues_flash t_kt81_110[] PROGMEM =
75 { // r t
76 { 490, -55},
77 { 515, -50},
78 { 567, -40},
79 { 624, -30},
80 { 684, -20},
81 { 747, -10},
82 { 815, 0},
83 { 886, 10},
84 { 961, 20},
85 { 1000, 25},
86 { 1040, 30},
87 { 1122, 40},
88 { 1209, 50},
89 { 1299, 60},
90 { 1392, 70},
91 { 1490, 80},
92 { 1591, 90},
93 { 1696, 100},
94 { 1805, 110},
95 { 1915, 120},
96 { 2023, 130},
97 { 2124, 140},
98 { 2211, 150},
99 { 0, 0},
100 };
Diese gehe ich Zeile für Zeile durch bis ich die passende Zeile gefunden habe und interpoliere dann zwischen den Stütztpunkten.
Code:
124 // find smallest distance of r
125 unsigned char smallest_distance_idx = 0;
126 float distance = 100000.0;
127
128 memcpy_P(&tmp, ¤t[idx+0], sizeof(sensorvalues_flash)); t1 = tmp;
129 memcpy_P(&tmp, ¤t[idx+1], sizeof(sensorvalues_flash)); t2 = tmp;
130 do
131 {
132
133 float delta = pow(r-t1.r,2)+pow(t2.r-r,2);
135 if (delta < distance)
136 {
137 distance = delta;
138 smallest_distance_idx = idx;
139 }
140
141 // read next two lines
142 ++idx;
143 memcpy_P(&tmp, ¤t[idx+0], sizeof(sensorvalues_flash)); t1 = tmp;
144 memcpy_P(&tmp, ¤t[idx+1], sizeof(sensorvalues_flash)); t2 = tmp;
145 } while (t2.r > 0);
146
147 // read the best matching lines
148 memcpy_P(&tmp, ¤t[smallest_distance_idx+0], sizeof(sensorvalues_flash)); t1 = tmp;
149 memcpy_P(&tmp, ¤t[smallest_distance_idx+1], sizeof(sensorvalues_flash)); t2 = tmp;
150
151 // interpolate
152 float dr = t2.r-t1.r;
153
154 float f = (r-t1.r)/dr;
155 return t1.t+(t2.t-t1.t)*f;
Meine Randbedingung ist:
- Ich will anhand einer Stütztabelle interpolieren und nicht einfach T=f*R machen. Die Datenblätter zeigen dass es eben keine Gerade ist....
- Ist mein R ausserhalb der Tabelle, will ich den nächst, passenden Eintrag nutzen und linear interpolieren.
Ist hier das zweimalige "pow" so teuer? Ist das memcpy_P so teuer? (teuer, sprich Laufzeit).
Edit: Ersetzte ich das pow() durch abs() lande ich bei 1.4 ms. Besser aber noch nicht gut .... :confused:
Gruß
Georg
Liste der Anhänge anzeigen (Anzahl: 1)
So, nachdem ich gejoggt habe, gemütlich gebadet habe und der Kopf dadurch "freigeblassen" habe, habe ich eine Lösung gefunden. Jetzt bin ich bei 240µs anstatt 2.4 ms und habe auch noch knapp 400 Byte an Flash gespart, da kein abs und pow mehr gelinkt werden muss. Auch denke ich, das der Code einfacher ist. Wieviel der "einfachere" Code, oder besser kleinere Kode ausmacht, und wieviel abs, hab ich nicht gemessen.
Anhang 29077
Peters Hinweis hat mich in die richtige Richtung gelenkt. Es muss die Anzahl der Vergleiche und Abfragen reduziert werden. Wie hab ich das jetzt gemacht? Ich habe mich an qsort und co. erinnert. Binäre Suche [1]. Jetzt brauche ich nur noch log(n)/log(2) Anfragen. Entsprechend weniger memcpy_P und kein abs/pow mehr. Bei meinem 22 Samples brauche ich also nur noch 4.45 Abfragen. Sprich entweder 4 oder 5. Je nach dem wo der Eintrag liegt. Vorher war die Laufzeit O(n) anstatt O(log n).
Hier der Code:
Code:
118 // binary search without recursion
119 unsigned char st = 0;
120 unsigned char en;
121 unsigned char m;
122 switch (s)
123 {
124 case kt81_210: en = sizeof(t_kt81_210)/sizeof(sensorvalues_flash)-2; break; // -2: because start = 0 and endline is a 0/0 which we dont need
125 case kt81_110: en = sizeof(t_kt81_110)/sizeof(sensorvalues_flash)-2; break;
126 default:
127 return 0.0;
128 }
129
130 sensorvalues_flash tmp;
131 while (en-st>1)
132 {
133 m = st + (en-st)/2;
134 memcpy_P(&tmp, ¤t[m], sizeof(sensorvalues_flash)); t1 = tmp;
135 if (r > t1.r) // if we use at sometime a NTC, we need to adjust this
136 {
137 // right side
138 st = m;
139 }
140 else
141 {
142 // left side
143 en = m;
144 }
145 }
146
147 // read the best matching lines
148 memcpy_P(&tmp, ¤t[st+0], sizeof(sensorvalues_flash)); t1 = tmp;
149 memcpy_P(&tmp, ¤t[st+1], sizeof(sensorvalues_flash)); t2 = tmp;
150
151 // interpolate
152 float dr = t2.r-t1.r;
153
154 float f = (r-t1.r)/dr;
155 return t1.t+(t2.t-t1.t)*f;
Nochmals danke für den "Stuppser" :)
@Besserwessi: Klar, ich habe eine Scheduler drin, der die Takte verteilt. Erst lauft die Konvertierung über den SPI (in mehreren Zyklen), dann die Umwandlung im letzten. Hier wird diese Funktion aufgerufen.
[1] http://de.wikipedia.org/wiki/Bin%C3%A4re_Suche