Forum: Mikrocontroller und Digitale Elektronik Division mittels Array umgehen - Problem: großer Zahlenbereich


von Marcel (Gast)


Lesenswert?

Moin zusammen,
ich habe gerade ein kleines Projekt, in dem ich einer Zeit zwischen 2 
Impulsen einen Wert zuweise.
Hierbei messe ich die Zeit zwischen 2 Impulsen mittels Timer und habe 
als Ergebnis eine Millisekundenzahl

Die Zeit schwankt von 163 bis 90000 ms.

aktuell rechne ich den Wert mittels Divsion aus:
1
impulse_time= timer.stop + (timer.overfl-1) * 250 + (255 - timer.start);

Das klappt auch alles wunderbar.

Anschließend berechne ich daraus den Wert
1
value = (uint16_t) (1800000/impulse_time);

Mich wurmt es, dass die Divsion fast 500 Cicles braucht, was gerade für 
ein Batteriebetriebenes System, was so schnell wie möglich wieder in den 
Schlaf möchte, echt viel ist.

Jetzt habe ich an eine Tabelle gedacht, wo ich mir dann zu einer 
bestimmten Zahl einen Wert hole, aber 90000-163 = 89837 16Bit-Werte :-D 
Ein bisschen viel.

Habt ihr vielleicht noch ein Möglichkeit parat?

Was mir gerade dazu noch einfällt. Man könnte sich einige Wertepaare 
sparen, da in den unteren Impulszeiten folgendes gilt:

180  10000
...  ...
180  9973
181  9972
...  ...
181  9918

Problem nur, dass diese Abstände immer kleiner werden.



Vielen Dank im Vorraus
Grüße Marcel

von Falk B. (falk)


Lesenswert?

@  Marcel (Gast)

>Mich wurmt es, dass die Divsion fast 500 Cicles braucht, was gerade für
>ein Batteriebetriebenes System, was so schnell wie möglich wieder in den
>Schlaf möchte, echt viel ist.

Bitte? Bei 1 MHz sind das 500us. Wie oft machst du denn diese 
Berechnung?  Wie lange ist dein uC im Sleep Mode?
Ich behaupte einfach mal, dass das nicht mal ansatzweise ein Problem 
ist.
Rechne mal den mittleren Stromverbrauch aus.

MFG
Falk

von user (Gast)


Lesenswert?

Hi

wie wäre es mit dem Aufstellen der Tabelle in umgekehrter Reihenfolge:

bis jetzt hast du ja y = f(x) in der Tabelle gespeichert, das heist für 
jeden x-Wert einen y-Wert, da aber viele y-Werte gleich sind wäre eine 
Idee die Tabelle andersherum zu speichern.

also in deinem Fall:

180  10000
181  9972
182  9930
usw.

und dann in dieser Tabelle mit Teile und Herrsche zu suchen, d.h du hast 
den x-Wert und vergleichst mit dem Wert in der Mitte der Tabelle, wenn 
der wert Größer ist dann machst du das gleiche im oberen Teil sonst im 
unteren, bis du den Wert gefunden hast. Das Brauch dann ld(n) Schritte. 
Du speicherst also Wertebereiche.

von Martin (Gast)


Lesenswert?

...
impulse_time= timer.stop + (timer.overfl-1) * 250 + (255 - timer.start);
...

Ich hätte jetzt 2 Timerwerte erwartet. Warum 3 und warum die 
Multiplikation mir 250?

von Oliver (Gast)


Lesenswert?

Abgesehen davon, daß ich das genauso sehe wie Falk, stellt sich die 
Frage, was du anschliessend mit dem ausgerechneten Wert "value" machst. 
Vielleicht lässt ich ja da die Division vereinfachen oder gar ganz 
vermeiden.

Marcel schrieb:
> Man könnte sich einige Wertepaare
> sparen,

Genaugenommen gibt es 11042-20+1 = 11023 verschiedene Ergebniswerte. Ist 
aber für eine Tabelle immer noch viel.

Wobei die Auflösung bei den unteren Impulszeiten durch die Impulslänge, 
bei den oberen durch die Rundung auf ganze Zahlen im Ergebnis beschränkt 
wird (ganz oben sind es 5s-Schritte). Wenns denn eh nicht so genau sein 
muß, lässt sich da vielleicht ein Faktor 30 reinrechnen, dann kommst du 
mit einer 16-bit-Division aus.

Oliver

von Andreas R. (rebirama)


Lesenswert?

@ Falk Hau doch nicht immer gleich so auf die Pauke. Es scheint für mich 
so, dass du bei jedem Frager vom schlimmsten ausgehst und als ahnungslos 
abstempelst.

@ topic
500µs sind je nach Anwendung wirklich viel Zeit/Strom.

Wenn du mit reduzierter Genauigkeit auskommst, lohnt sich eventuell die 
Interpolation in einer Tabelle mit weniger Einträgen. Die darin 
vorkommende Division kann als schneller Shift implementiert werden.

Für die Linearisierung eines Temperatursensors habe ich das mal gemacht, 
aber das Prinzip lösst sich auch auf eine Division übertragen.

1
int16_t adc_to_temp(uint16_t adc){
2
  /* Calculates temperature from sensor values by interpolating over a lookup table*/
3
  uint8_t i,j;
4
  //table must have 17 entrys!
5
  static int16_t lookup  [] ={150,78, 56, 44, 35, 27, 21, 15,10,5,0,-6,-11,-18,-26,-38,-55};
6
  if ((adc<5) || (adc>1018)) return 150;//prevent shorted/open sensor missbehavior  
7
  i=adc/64;//caclulate entry, which is just below adc value
8
  j=adc%64;//relative position between the two nearest table entrys
9
  int16_t temp = (lookup[i]*(64-j)+lookup[i+1]*j)/64;//interpolate beween two entrys
10
  return temp;
11
}

Dauert 72 Takte incl der Plausibilitätsprüfung. Könnte noch schneller 
werden, wenn du durch 256 statt 64 teilst.

von Karl H. (kbuchegg)


Lesenswert?

> Anschließend berechne ich daraus den Wert
> value = (uint16_t) > (1800000/impulse_time);

Gib doch mal ein paar Datentypen an.
impulse_time ist ein unsigned long?

Da dein maximaler Wert 90000 ist, hast du schon untersucht was es bringt 
bzw. im Ergebnis ausmacht wenn du zb alles vorher durch 2 dividierst.
Durch 2 dividiert sich leicht und bringt deine 90000 auf 45000 runter. 
45000 passt aber in einen unsigned int -> 16 Bit Division anstatt 32 Bit 
Division.

von Stefan E. (sternst)


Lesenswert?

Marcel schrieb:
1
impulse_time= timer.stop + (timer.overfl-1) * 250 + (255 - timer.start);
> Das klappt auch alles wunderbar.

Ich sehe allerdings nicht, wie diese Formel korrekt sein kann. Entweder 
der Timer läuft bei 250 über, oder bei 255. Beides geht ja wohl kaum. 
Und wenn es einfach nur ein durchlaufender 8-Bit Timer ist (was ich 
vermute), sind sogar beide Zahlen falsch.

von Falk B. (falk)


Lesenswert?

@  Andreas R. (rebirama)

>@ Falk Hau doch nicht immer gleich so auf die Pauke. Es scheint für mich
>so, dass du bei jedem Frager vom schlimmsten ausgehst und als ahnungslos
>abstempelst.

Das ist allzuoft auch so :-0
Und was mich in solchen Dingen immer wieder reizt ist, dass die Leute 
nicht nachgedacht haben sondern einfach losplappern, meistens eher 
jammern.
Wenn sich jemand mal Gedanken gemacht, selbst wenn sie falsch sind, und 
das in dem Posting rüberkommt, sieht das ganz anders aus. Und 
dementsprechend auch meine Reaktion. Siehe Netiquette.

>500µs sind je nach Anwendung wirklich viel Zeit/Strom.

DENKE! RECHNE! REDE!

In der Reihenfolge. Und da der OP mit dem Timer und ICP Zeitdifferenzen 
misst, ist beim AVR sowieso keine riesiges Stromsparpotentiel, weil nur 
der Idle-Mode verfügbar ist.

MfG
Falk

von Andreas R. (rebirama)


Lesenswert?

@ Falk

> DENKE! RECHNE! REDE!
du hast "lesen" vergessen, denn der OP hat nichts von ICP geschrieben 
;-)

Ich gehe davon aus, dass der OP den Timer mit externem 32kHz Uhrenquarz 
betreibt und den Atmel bei Overflow oder Interrupts aus dem Sleep holt. 
Sonst wären die 500µs wirklich nicht so schlimm. Es ist ja nicht mal 
klar, dass es überhaupt ein Atmel ist, da kann der OP wirklich noch 
nachbessern.

Sieht für mich etwas nach einem Tacho im Batteriebetrieb aus, da sollte 
es schon sparsam sein.

die 250 sind hoffentlich ein Tippfehler.

von Detlef _. (detlef_a)


Lesenswert?

für die Berechnung von z.B. 2^18/163 bis 2^18/90000 (man muß dann noch 
mit 1800000/2^18 multiplizieren) bietet sich auch die Iteration

x(k+1)=x(k)*(2-a*x(k))

an, die konvergiert gegen 1/a, leider nicht so schnell, für 1/163 muß 
man 20 Runden oder so rechnen, 1/90000 geht in 5 Runden, wenn man mit a 
als Startwert für x anfängt. Das ganze ist bestimmt auch noch pimpfähig, 
z.B. indem man den Startwert über nen Polynom berechnet oder mal richtig 
googelt.

Wenn der OP mit zeitlichen und technischen Randbedingungen rausrückt, 
kan man da mal kucken. Sein Interesse scheint aber leider erlahmt zu 
sein.

math rulez!

Cheers
Detlef

von Oliver (Gast)


Lesenswert?

Detlef _a schrieb:
> die konvergiert gegen 1/a, leider nicht so schnell

Hm. Kannst du das für 90000 mal vorrechnen (ganzzahlig)?

Oliver

von Detlef _. (detlef_a)


Lesenswert?

>>Hm. Kannst du das für 90000 mal vorrechnen (ganzzahlig)?
Der Konvergenzradius ist 1, das muß man normieren, ich habs in float 
ausprobiert. Für Integerrechnung ist m.E. kein signifikant anderes 
Konvergenzverhalten zu erwarten.

clear all;
a=90000/2^18;
x=a;
for(k=1:30)
 x=x*(2-a*x);
 disp(sprintf('%.8e ',[x x-1/a]))
end;
return

6.46177879e-001 -2.26653323e+000
1.14900277e+000 -1.76370835e+000
1.84474831e+000 -1.06796280e+000
2.52113621e+000 -3.91574897e-001
2.86006912e+000 -5.26419867e-002
2.91175970e+000 -9.51408726e-004
2.91271080e+000 -3.10768397e-007
2.91271111e+000 -3.33066907e-014
2.91271111e+000 4.44089210e-016

Cheers
Detlef

von Oliver (Gast)


Lesenswert?

Detlef _a schrieb:
> Für Integerrechnung ist m.E. kein signifikant anderes
> Konvergenzverhalten zu erwarten.

Das kommt auf deine Definition von signifikant an.

>a=90000/2^18
ergibt ganzzahlig gerechnet 0. Damit wird aus deiner Interation:
>x=x*(2);
und das ist nach einem wie nach 5 Interationsschritten mit Startwert 
x=a=0 immer noch 0.

Abgesehen davon hat deine Formel je Durchlauf 2 Multiplikationen und 
eine Subtraktion. Macht bei 5 Durchläufen 10 Multiplikationen und 5 
Subtraktionen, zugl. der Schleife. Ob das in signifikant (=überhaupt) 
weniger Zyklen als den o.a. 500 Zyklen zu machen ist, ist sehr fraglich.

Oliver

von Marcel (Gast)


Lesenswert?

Hi,
vielen Dank für eure rege Beteiligung
Vorweg ich benutze den Atmega169P


Falk Brunner schrieb:
> Wie oft machst du denn diese
> Berechnung?

Die Impulse kommen 24 Stunden pro Tag, mal schneller, mal langsamer.
Ganz nach dem Motto Kleinvieh macht auch Mist.

Falk Brunner schrieb:
> Wie lange ist dein uC im Sleep Mode?

Mein Programm ist so strutkruiert, dass die Interrupts Flags setzen. 
Sind alle Flags wieder gecleart, dann schläft er wieder ein.

Martin schrieb:
> Ich hätte jetzt 2 Timerwerte erwartet. Warum 3 und warum die
> Multiplikation mir 250?

Ja das war meine Schuld, hätte ich erklären sollen. Ich benutze nebenbei 
eine RTC, das heißt ich brauche Start UND Stopp ;)

250 ist von mir ein Korrekturterm im "Mittel". Der Grund ist, dass die 
RTC ja nur alle 1s / 255 = 4 ms inkrementiert. Das heißt ich habe immer 
einen Ungenauikeitsfaktor drinn.
Die Berechnung muss nicht allzu genau sein. Sprich eine Streuung von 
+-8ms sind voll O.K!

Falk Brunner schrieb:
> In der Reihenfolge. Und da der OP mit dem Timer und ICP Zeitdifferenzen
> misst, ist beim AVR sowieso keine riesiges Stromsparpotentiel, weil nur
> der Idle-Mode verfügbar ist.

Doch wenn du die RTC nimmst, schon ;)


Oliver schrieb:
> Wenns denn eh nicht so genau sein
> muß, lässt sich da vielleicht ein Faktor 30 reinrechnen, dann kommst du
> mit einer 16-bit-Division aus.
Das verstehe ich nicht ganz. Ist das so schlimm, wenn man mal 8-bit mal 
16-bit Division durchgeführt.

Das mit der "Umkehrfuntionstabelle" nachdem Teile Herrsche-Prinzip ist 
auch echt ne gute Lösung!

von Falk B. (falk)


Lesenswert?

@  Marcel (Gast)

>Die Impulse kommen 24 Stunden pro Tag, mal schneller, mal langsamer.
>Ganz nach dem Motto Kleinvieh macht auch Mist.

ZAHLEN!!!

>Mein Programm ist so strutkruiert, dass die Interrupts Flags setzen.
>Sind alle Flags wieder gecleart, dann schläft er wieder ein.

Apfelmus ist Mus aus Äpfeln. Danach hat aber keiner gefragt. Sondern 
nach ZAHLEN!

>RTC ja nur alle 1s / 255 = 4 ms inkrementiert.

1/256.

> Das heißt ich habe immer
>einen Ungenauikeitsfaktor drinn.

Du hast einen Denkfehler.

>Doch wenn du die RTC nimmst, schon ;)

Dann möchte man wenigstens die Grundrechenarten beherrschen. Und Null 
ist auch eine Zahl.

>Das verstehe ich nicht ganz. Ist das so schlimm, wenn man mal 8-bit mal
>16-bit Division durchgeführt.

Aber über 500 popelige Takte jammern? Ohje.

Der AVR ist ein 8-Bit Prozessor, seine Register sind 8-Bit breit. 
Addieren und Subtrahieren kann er direkt und schnell. Bei breiteren 
Daten muss der das mit mehreren Registern und Operatione zusammenbauen, 
das dauer länger.

MFG
Falk

von Detlef _. (detlef_a)


Lesenswert?

Oliver schrieb:
> Detlef _a schrieb:
>> Für Integerrechnung ist m.E. kein signifikant anderes
>> Konvergenzverhalten zu erwarten.
>
> Das kommt auf deine Definition von signifikant an.
>

Meiner Erfahrung nach kann man durch geeignete Skalierung in diesem Fall 
ein vergleichbares Konvergenzverhalten erzwingen, also 1/10 mehr 
Iterationen oder so.

>>a=90000/2^18
> ergibt ganzzahlig gerechnet 0. Damit wird aus deiner Interation:
>>x=x*(2);
> und das ist nach einem wie nach 5 Interationsschritten mit Startwert
> x=a=0 immer noch 0.

Olli, mal lesen:  Ich habe das Beispiel in float gerechnet.

> Abgesehen davon hat deine Formel je Durchlauf 2 Multiplikationen und
> eine Subtraktion. Macht bei 5 Durchläufen 10 Multiplikationen und 5
> Subtraktionen, zugl. der Schleife. Ob das in signifikant (=überhaupt)
> weniger Zyklen als den o.a. 500 Zyklen zu machen ist, ist sehr fraglich.
>

Ja, das stimmt. Aber ich erwähnte ja schon das vorhandene Pimppotential. 
Aber da ja der OP jetzt doch alle Zeit der Welt für die Berechnung zu 
haben scheint machts nicht soviel Spaß, da weiter drüber nachzudenken, 
interessant ist es aber schon.

Cheers
Detlef

von Oliver (Gast)


Lesenswert?

Detlef _a schrieb:
> Olli, mal lesen:  Ich habe das Beispiel in float gerechnet.
Das war mir bewusst, und genau das war der Grund meiner Nachfrage.

Detlef _a schrieb:
> Meiner Erfahrung nach kann man durch geeignete Skalierung in diesem Fall
> ein vergleichbares Konvergenzverhalten erzwingen,
und handelt sich damit dann gleich wieder 32bit-Operationen ein.

Das ist sicherlich alles in allem eine nette Lösung, aber hier völlig am 
Thema vorbei.

Oliver

von Detlef _. (detlef_a)


Angehängte Dateien:

Lesenswert?

Was ich für themenrelevant halte und was nicht kannst Du getrost mir 
überlassen.

Beim Lesen Deiner Postings ist mir nen Comic wieder eingefallen, den ich 
schon vergessen hatte( danke dafür), ich hab ihn mal angehängt.

Cheers
Detlef

von Karl H. (kbuchegg)


Lesenswert?

Detlef _a schrieb:

>> und das ist nach einem wie nach 5 Interationsschritten mit Startwert
>> x=a=0 immer noch 0.
>
> Olli, mal lesen:  Ich habe das Beispiel in float gerechnet.

Du musst aber zugeben, dass das Ersetzen einer ulong Division durch 
einen Haufen Floating Point Operationen aller Wahrscheinlichkeit nach 
nicht so das Gelbe vom Ei sein wird.

Es gilt 500 Taktzyklen zu schlagen. Im Moment seh ich noch nicht 
wirklich, wie du das schaffen willst.

von Detlef _. (detlef_a)


Lesenswert?

Karl heinz, da hast Du mich ja geschickt am technischen Ehrgeiz gepackt.

Kucks Du hier:
Beitrag "Integer division, Reciprocal of an integer value, Kehrwert eines integers"

Danke für das schöne Problem, hat Spaß gemacht.

Cheers
Detlef

von Oliver (Gast)


Lesenswert?

Hm. Nah dran, aber noch nicht ganz da.

651 Zyklen bei einer Iteration für die Berechnung von 1/163. Leider auf 
16 bit begrenzt, der ursprüngliche Wertebereich von 163 bis 90000 klappt 
damit nicht.

Aber sonst richtig schönes old-school C ;-)

Oliver

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.