Forum: Mikrocontroller und Digitale Elektronik Alte Werte in x Schritten auf die neuen Werte aktualisieren


von Kevin Hinrichs (Gast)


Lesenswert?

Hallo,
ich schreibe gerade eine Funktion in der programmier Sprache in c.

Die Daten sind in einem Array mit 16 Elementen und die Werte
sind INT mit einem Wertebereich von 0 bis 29.
Diese Daten werde ca. alle 25ms aktualisiert.

Diese Funktion wird ca. alle 4ms bis 6ms aufgerufen.
In dieser Funktion sollen die alten Daten
sich in x Schritten den neuen Daten nähern.
In dieser Funktion ist die Information bekannt,
ob neue aktualisierte Daten vorhanden sind.
Es ist auch bekannt in wie vielen Schritten sich
die alten zu den aktualisierte Werten nähern soll.
Wichtig ist das die die INT Zahlen ganze Zahlen bleiben.

Ich möchte dies in einem Beispiel beschreiben.

Daten[0].Wert bis Daten[15].Wert bekommen neue aktualisierte Daten.
Alle 15 Daten sind im Wertebereich von 0 bis 29.
Das Beispiel beschreibt die Nährung von dem Array BIN 0
Muss aber für jedes Array bin für sich auch funktionieren.

Der alte Wert in Daten[0] war 5.
Der aktualisierte Wert in Daten[0] ist 18.
Schritte sind 6.

Die Funktion wird nun alle 4ms bis 6ms aufgerufen:
Meine Idee erstmal die Differenz ausrechen:
Die Wäre dann 13.
Aber diese Differenz nun durch 6 Schritte zu teilen bedeutet 2 rest 1.

Schritt 1:
Neuer Wert[0] = Alter Wert[0] + 2;

Schritt 2:
Neuer Wert[0] = Neuer Wert[0] + 2;

Schritt 3:
Neuer Wert[0] = Neuer Wert[0] + 2;

Schritt 4:
Neuer Wert[0] = Neuer Wert[0] + 2;

Schritt 5:
Neuer Wert[0] = Neuer Wert[0] + 2;

Schritt 6:
Neuer Wert[0] = Neuer Wert[0] + 2 + 1;

Die Nähren von dem alten Wert zu dem neuen Wert
soll so gleichmäßig wie möglich erfolgen.
Bei diesem Beispiel war die Differenz positive,
da die neuen Daten größer waren als die alten.
Dies kann aber auch anders sein,
dass die neuen Daten kleiner sind wie die alten Daten.

Zudem bin ich am überlegen ob eine Differenz größer 1 sein muss,
damit eine Nährung an den neuen Wert erfolgt. Dies müsste einstellbar 
sein.

Ich brauche eure Hilfe diesen c Code zu schreiben.

von Stefan F. (Gast)


Lesenswert?

Erinnert mich stark an den Bresenham Algorithmus.

von Kevin Hinrichs (Gast)


Lesenswert?

Stefan F. schrieb:
> Erinnert mich stark an den Bresenham Algorithmus.

Danke für deine Antwort.
Ich habe mir den angeschaut,
aber was muss ich bei diesen Algorithmus anpassen,
damit er meine Anforderung entspricht?
Die Anzahl der Schritte sind variabel und nicht fix.

von Stefan F. (Gast)


Angehängte Dateien:

Lesenswert?

Beim Bresenham geht es doch auch darum, einen Wert zu erreichen während 
die Schrittweise keine glatten Integer Zahlen sind. Schau dir das Bild 
an.

Stell dir vor dass die Y Achse den Wert darstellt. Du willst von Y1 nach 
Y2 kommen und das mit einer gewissen Anzahl von Schritten. Die Schritte 
werden durch die X-Achse repräsentiert, in diesem Fall sind es 18 
Schritte.

Da die Linie hier recht flach verläuft, wird Y bei jedem Schritt um 0 
oder 1 erhöht. Aber das kann in einem anderen Fall auch mehr sein. Der 
eigentlich Knackpunkt ist, dass die Steigung (die pro Schritt addiert 
wird) eine Fließkommazahl ist, kein glatter Integer. Das ist ja auch 
genau dein Problem.

Ohne das jetzt im Detail mathematisch auseinander zu nehmen kannst du 
einfach den C Code aus dem Absatz "Kompakte Variante" verwenden. Den 
habe ich auch schon mehrmals verwendet. So ganz durchblickt habe ich ihn 
selbst nicht, aber er funktioniert.

Deine Eingabeparameter sind:
x0 = 0
x1 = Anzahl der Schritte
y0 = Anfangswert (alter wert)
y1 = Zielwert

Innerhalb der Funktion ist eine while Schleife, die wird genau so oft 
durchlaufen wie "Anzahl der Schritte". Innerhalb der Schleife wird mit 
dem Funktionsaufruf von setPixel() der berechnete Y Wert für jeden 
dieser Schritte ausgegeben. Du müsstest also an dieser Stelle einfach 
setPixel() durch einen Schreibzugriff auf den Zielspeicher (wo der zu 
ändernde Wert liegt) ersetzen.

statt: setPixel(x0,y0)
neu:   Daten[0]=y0

Und weil du das mit mehreren Daten machen sollst, erweiterst du die 
Funktion um einen Parameter der den Index auf das Daten-Array darstellt 
und ersetzt diese eine Zeile durch:

void line(int x0, int y0, int x1, int y1, int index) {
    ...
    Daten[index]=y0  // statt setPixel(y0,y0)
    ...
}

Danach kannst du die Funktion für alle 16 Elemente aufrufen:
1
for (int index=0; index<16; index++) {
2
  line(0, Daten[index], 6, NeueDaten[index], index)
3
}

Auf deutsch: Für alle 16 Felder im Array, erhöhe den Wert in 
Daten[index] in 6 Schritten bis zum Zielwert der durch NeueDaten[index] 
vorgegeben ist.

Da du immer mit x0=0 beginnst, kannst du den Algorithmus vermutlich noch 
ein bisschen optimieren, wenn du magst. Der funktioniert natürlich auch 
mit 8 Bit Zahlen (int8_t).

Falls das nicht die gewünschte Reihenfolge ergibt, musst du dir den 
Algorithmus halt umschreiben. Das kriege ich jetzt nicht "mal eben 
schnell" im Kopf hin. Vermutlich musst du dann die temporären Variablen 
(dy, dy, err, e2) innerhalb der Funktion durch Arrays ersetzten.

Oder du schreibst das nochmal ganz neu. Die Grundidee hinter dem 
Algorithmus ist folgende: Da die Steigung eine Fließkommazahl ist muss 
sie für jeden Schritt auf Integer abgerundet werden. Soweit war es ja 
klar. Den Fehler merkt man sich. Im nächsten Schritt wird der neue 
Fehler mit dem vorherigen addiert. Wenn das >=1 ergibt, dann muss der 
Wert um +1 mehr erhöht werden. Das ist genau das, was du in deinem 
Eröffnungsbeitrag im Schritt 6 verdeutlicht hast.

Wichtig ist, dass du die vorherigen Fehler aufaddierst bis sie groß 
genug sind, um die Schrittweite um +1 mehr zu rechtfertigen.

Die "Kompakte Variante" auf Wikipedia hat das ganz raffiniert ohne 
Fließkomma-Arithmetik umgesetzt, was ja gerade auf deinem 
Mikrocontroller auch hilfreich ist. Deswegen war der Code auch für mich 
sehr attraktiv, obwohl ich nicht ganz verstanden habe. Aber ich konnte 
ihn anwenden - immerhin.

von Kevin Hinrichs (Gast)


Lesenswert?

Jetzt deine Erklärung dazu hat mir wirklich geholfen.
Die war sehr ausführlich!
Ich setzte mich gleich dran und Untersuche den Algorithmus.
Und binde den in mein Code ein.

Vielen Dank dafür.

von Stefan F. (Gast)


Lesenswert?

Kleine Anregung, wie du den Schreibzugriff (anstelle von setPixel) 
sauberer machen kannst:
1
// Erhoehe dern Wert bis zum Zielwert in n Schritten.
2
void erhoehe(int* wert, int schritte, int zielwert)
3
{
4
    // Hatte keine Lust die Variablen umzubenennen.
5
    // Der Compiler wird das weg optimieren.
6
    x0 = 0;
7
    y0 = *wert;
8
    x1 = schritte;
9
    y1 = zielwert;
10
11
    // Jetzt kommt der Bresenham aus Wikipedia:
12
    int dx =  abs(x1 - x0), sx = x0 < x1 ? 1 : -1;
13
    int dy = -abs(y1 - y0), sy = y0 < y1 ? 1 : -1;
14
    int err = dx + dy, e2; /* error value e_xy */
15
16
    while (1) {
17
        *wert=y0; // statt setPixel(x0, y0);
18
        if (x0 == x1 && y0 == y1) break;
19
        e2 = 2 * err;
20
        if (e2 > dy) { err += dy; x0 += sx; } /* e_xy+e_x > 0 */
21
        if (e2 < dx) { err += dx; y0 += sy; } /* e_xy+e_y < 0 */
22
    }
23
}
Du übergibst anstelle des konkreten Integer Werte einen Zeiger auf den 
Wert. Dann kann der Schreibzugriff über diesen Zeiger stattfinden.
1
for (int index=0; index<16; index++) {
2
  erhoehe( &Daten[index], 6, NeueDaten[index] );
3
}

von Kevin Hinrichs (Gast)


Lesenswert?

Nochmals vielen Dank für deine Hilfe.
Ich konnte dein Beispiel gut übernehmen.
Ich musste nur die while Schleife entfernen,
da die Funktion zyklisch aufgerufen wird.
Weil mit jedem Aufruf ein Schritt gemacht werden muss.

von Stefan F. (Gast)


Lesenswert?

Kevin Hinrichs schrieb:
> Ich musste nur die while Schleife entfernen,
> da die Funktion zyklisch aufgerufen wird.

Das meinte ich mit Umschreiben. Habe mir schon gedacht dass du das 
selbst hin bekommst nachdem die Grundidee des Algorithmus verstanden 
hast.

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.