-         

Ergebnis 1 bis 6 von 6

Thema: Eine bessere R zu T Wandler Routine gesucht

  1. #1
    Erfahrener Benutzer Begeisterter Techniker Avatar von schorsch_76
    Registriert seit
    25.03.2012
    Ort
    Kurz vor Neuschwanstein
    Alter
    41
    Beiträge
    398

    Eine bessere R zu T Wandler Routine gesucht

    Anzeige

    SMARTPHONES & TABLETS-bis zu 77% RABATT-Kostenlose Lieferung-Aktuell | Cool | Unentbehrlich
    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:

    Klicke auf die Grafik für eine größere Ansicht

Name:	2014-09-20_12:18:42.jpg
Hits:	18
Größe:	10,1 KB
ID:	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, &current[idx+0], sizeof(sensorvalues_flash));    t1 = tmp;
    129     memcpy_P(&tmp, &current[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, &current[idx+0], sizeof(sensorvalues_flash));    t1 = tmp;
    144         memcpy_P(&tmp, &current[idx+1], sizeof(sensorvalues_flash));    t2 = tmp;
    145     } while (t2.r > 0);
    146 
    147     // read the best matching lines
    148     memcpy_P(&tmp, &current[smallest_distance_idx+0], sizeof(sensorvalues_flash));    t1 = tmp;
    149     memcpy_P(&tmp, &current[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 ....

    Gruß
    Georg
    Geändert von schorsch_76 (20.09.2014 um 12:41 Uhr)

  2. #2
    Erfahrener Benutzer Robotik Einstein
    Registriert seit
    27.08.2013
    Ort
    Region Basel
    Alter
    59
    Beiträge
    2.435
    Hallo Geaorg,

    Wo hast du dich dann da verrannt ???

    Also nur so als Idee, ohne Fehlerschutz und nicht wirklich getestet:

    Code:
    unsigned char i = 0;
    
    while ( r <  t_kt81_110[i] )
      {
        ++i;
      }
    
    // Hier muss noch getestet werden ob der Fund innerhalb der Tabelle war, bzw. ob die Temperatur < -55°C ist
    
    
    // read the best matching lines
    memcpy_P(&tmp, &t_kt81_110[i-1], sizeof(sensorvalues_flash));    t1 = tmp;
    memcpy_P(&tmp, &t_kt81_110[i-2], sizeof(sensorvalues_flash));    t2 = tmp;
    
    // interpolate
    float dr = t2.r-t1.r;
     
    float f = (r-t1.r)/dr;
    return t1.t+(t2.t-t1.t)*f;
    MfG Peter(TOO)
    Manchmal frage ich mich, wieso meine Generation Geräte ohne Simulation entwickeln konnte?

  3. #3
    Erfahrener Benutzer Roboter Genie
    Registriert seit
    03.09.2009
    Ort
    Berlin (Mariendorf)
    Beiträge
    938
    Was im Programm gerechnet oder getan werden muss, muss eben abgearbeitet werden.

    In erster Instanz solltest du sortieren, was davon _wirklich_ in der ISR erledigt werden muss und was nicht.
    Wenn diese rechenzeitintensiven Funktionen in dieser Ausführungshäufigkeit unvermeidbar sind, ist höherer Takt oder eine leistungsfähigere CPU unvermeidbar. Bei einer Heizungssteuerung kann ich mir das aber beim besten Willen nicht vorstellen. Die Temperatur (darum dreht es sich doch !?!? )muss ganz bestimmt nicht jede Millisekunde neu bestimmt werden.

    Wenn "pow" eine Exponentialfunktion bedeutet, dann kann sie -je nach Controllertyp und Taktung - schon ziemlich am 1ms-Zeitbudget nagen.
    Wenn abs() statt pow() nur 50% Einsparung bringt, vermute ich noch einen anderen Zeitfresser, vielleicht Pointer-lastige Routinen.

  4. #4
    Erfahrener Benutzer Begeisterter Techniker Avatar von schorsch_76
    Registriert seit
    25.03.2012
    Ort
    Kurz vor Neuschwanstein
    Alter
    41
    Beiträge
    398
    Danke für die Denkanstösse ... Ich werd es ausprobieren und dann meld ich mich wieder

  5. #5
    Erfahrener Benutzer Robotik Visionär
    Registriert seit
    26.11.2005
    Ort
    bei Uelzen (Niedersachsen)
    Beiträge
    7.942
    Die pow() funktion ist ausgesprochen langsam. Da Steckt nämlich zumindest in der generischen Form ein Logarithmus drin versteckt: pow(x,y) = exp(x * ln(y)). In wieweit GCC da ggf bei pow( x,2) noch optimiert weiß ich nicht - möglich wäre es jedenfalls im Prinzip.

    Die Suche muss man nun wirklich nicht mit Fließkomma Zahlen machen - pow erübrigt sich dann. Bei Float sollte auch fabs() deutlich schneller sein als das Quadrat (auch als x*x geschrieben), abs() für integer sowieso.

    Peter(TOO) hat auch schon den richtigen Weg, ganz ohne pow() oder ähnliches angeben. Wenn man es noch schneller haben will, könnte man ggf. äquidistante Stützstellen (bevorzugter Abstand eine Potenz von 2) verwenden (ggf. auch ein paar mehr wenn man mag) - dann wird die Berechnung noch einfacher, weil das suchen und ggf. die Division wegfällt und man kommt ggf. auch ohne Fließkomma aus, wenn man die Temperatur intern etwa in 1/10 Grad handhabt.

    Ein ISR im 1 ms Takt, die Widerstände (KTY) in Temperaturen umrechnet ist auch schon suspekt. So schnell ist der Sensor (und auch die Heizung) bei weitem nicht. Für die Heizungsregelung ist eher eine zuverlässige Messung und ggf. gute Auflösung für die Bildung von Ableitungen wichtig - ein Sekundentakt sollte ausreichen. Für eine Unterdrückung von Störungen sollte man eher den Sensor mehrmals über ein Zeitfenster von 20 ms (oder vielfachen davon) auslesen, und die Werte Aufsummieren / mitteln. Damit unterdrückt man recht effektiv 50 Hz und 100 Hz Störungen und bekommt durch Oversampling auch gleich noch etwas mehr Auflösung.

  6. #6
    Erfahrener Benutzer Begeisterter Techniker Avatar von schorsch_76
    Registriert seit
    25.03.2012
    Ort
    Kurz vor Neuschwanstein
    Alter
    41
    Beiträge
    398
    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.

    Klicke auf die Grafik für eine größere Ansicht

Name:	2014-09-20_19:37:44.jpg
Hits:	3
Größe:	11,6 KB
ID:	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, &current[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, &current[st+0], sizeof(sensorvalues_flash));    t1 = tmp;
    149     memcpy_P(&tmp, &current[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
    Geändert von schorsch_76 (20.09.2014 um 19:51 Uhr)

Ähnliche Themen

  1. Nochmal RFM12, funktionierende Routine gesucht !
    Von Andree-HB im Forum Basic-Programmierung (Bascom-Compiler)
    Antworten: 3
    Letzter Beitrag: 11.01.2008, 19:11
  2. Interruptfreie Routine für I2C gesucht
    Von Dani-Bruchflieger im Forum Basic-Programmierung (Bascom-Compiler)
    Antworten: 4
    Letzter Beitrag: 07.09.2007, 04:39
  3. Wer hat Lust, eine Routine für ein LED-Display zu schreiben?
    Von avr-tools im Forum Basic-Programmierung (Bascom-Compiler)
    Antworten: 15
    Letzter Beitrag: 31.07.2006, 08:14
  4. bessere H-Brücke als 293 gesucht
    Von aaaaamartin im Forum Motoren
    Antworten: 3
    Letzter Beitrag: 03.10.2005, 18:20
  5. [ERLEDIGT] routine für schrittmotor in c-basic gesucht.
    Von im Forum Motoren
    Antworten: 9
    Letzter Beitrag: 10.12.2004, 09:14

Berechtigungen

  • Neue Themen erstellen: Nein
  • Themen beantworten: Nein
  • Anhänge hochladen: Nein
  • Beiträge bearbeiten: Nein
  •