Skalierung von Integerwerten

OP Persönliche Seite #2236639
Hallo Zusammen,

ich möchte Werte aus der Tabelle „sinustabelle[omega]“ mit einem 
Sollwert skalieren. Die Werte liegen zwischen 0 und 1250.
Der Sollwert kommt über parallele Eingänge an PortD. 0-63 entspricht 
0-100%. Da ich im Programm durch 64 teile stimmt das nicht ganz, ist 
aber nicht so schlimm.
Da Werte für eine PWM mit 16KHz berechnet werden sollen, habe ich nur 
62,5µsec Zeit, bis der nächste Wert benötigt wird.
Der folgende Programmteil hat eine Bearbeitungszeit von 33,45 µsec 
@20MHz in der Simulation. Liegt zwar noch im grünen Bereich, aber geht 
das schneller zu berechnen?

Hier ist der entscheidende Teil:
1
hilf= (sinustabelle[omega]*sollwert);
2
hilf=hilf/64;
3
zwischenspeicher_a=(top-hilf);


und hier das ganze Hauptprogramm:
1
int main(void)
2
{
3
char sollwert=0;  // Ausgangssollwert
4
long hilf=0;
5

6
// ******** Initialisierung ****************************************
7
initial();  // Initialisierung Ports etc.
8
// ******** Hauptschleife ******************************  
9
    
10
  while(1)      // 33,45 µsec Bearbeitungszeit @20MHz
11
  {
12

13
sollwert= (PIND & 0x3f);  // Sollwert von 0 bis 63 entspricht 0 bis 100% (na ja, fast)
14

15
if (omega<=160) {   hilf= (sinustabelle[omega]*sollwert);
16
          hilf=hilf/64;
17
          zwischenspeicher_a=(top-hilf); 
18
          }
19
          else zwischenspeicher_a=top;
20

21
if (omega>=160) {  hilf= (sinustabelle[omega]*sollwert);
22
          hilf=hilf/64;
23
          zwischenspeicher_b=(top-hilf);
24
          }
25
          else zwischenspeicher_b=top;
26

27
// ************************************************************** 
28
  } // Ende while(1)Endlosschleife
29
  return 0;
30
}// Ende Hauptprogramm

Grüße
Axel
Angehängte Dateien:
Gast #2236656
bei Faktoren wie 64=2^6 kannst du schieben (hilf = hilf << 6). Aber je 
nach Complier wird das bei festen Faktoren schon optimiert. Ausserdem 
testest Du zwei mal "if (omega<=160)" und "if (omega>=160)"

hilf= (sinustabelle[omega]*sollwert);
hilf=hilf << 6;
zwischenspeicher_a=(top-hilf);

if (omega<=160)
{
  zwischenspeicher_b=top;
}
else
{
  zwischenspeicher_b = zwischenspeicher_a;
  zwischenspeicher_a = top;
}
Gast #2236678
Kleiner Tipp: schau dir das ganze mal im Assemblerfile an.
Da findest Du schnell heraus wo der Compiler Probleme hat.

Was mir aufgefallen ist:
- Sinustabelle ist signed enthält aber nur unsigned Werte die 
Multiplikation von unsigned Werten ist normalerweise etwas kürzer als 
signed

- Wenn du Die Tabelle auf unter 1024 Skalierst (z.B. halbierte Werte) 
kommst du bei der Multiplikation mit unsigned int aus. (in dem Fall nur 
durch 32 Teilen).

- eventuell kannst Du durch tauschen von if + else Zweig für die lange 
Berechnung noch 1-2 Zyklen sparen.

Gruß Anja
Gast #2236690
micha schrieb:
> hilf=hilf << 6;

ist die falsche Richtung: = Multiplikation mit 64.
Das ganze sollte hinterher auch noch das richtige Ergebnis bringen.


Axel Düsendieb schrieb:
> 4,6µsec Bearbeitungszeit

= 92 Takte da habe ich Zweifel ob wirklich eine 32*32 Bit Multiplikation 
gerechet wird.
Da Fehlt noch ein cast auf long vor der Tabelle.

Gruß Anja
Gast #2236701
Anja schrieb:
> micha schrieb:
>> hilf=hilf << 6;
>
> ist die falsche Richtung: = Multiplikation mit 64.
> Das ganze sollte hinterher auch noch das richtige Ergebnis bringen.

ARG, schnell, meeehhhr Kaffee....

(Na ja, immerhin zeigt es die richtige Richtung :-) Danke Anja!)
Gast #2236755
Axel Düsendieb schrieb:
> Die Zweifel habe ich auch, da das Programm nicht richtig funktioniert.
> Wie und wo wird der cast gemacht?

ich würde es so machen:

hilf= (((long)sinustabelle[omega])*sollwert)

Ansonsten hast Du ein Problem wenn 1249 * 63 gerechnet wird.

sinustabelle ist ja integer (16 Bit signed) sollwert sollwert char (8bit 
signed). Im Normalfall wird dann eine signed 16 * 16 Bit Multiplikation 
mit 16 Bit Ergebnis aufgerufen. Der Überlauf wird abgeschnitten (oder 
wenn Du Glück hast auf +/-32767 limitiert).

Gruß Anja
OP Persönliche Seite #2236779
Danke an Alle


jetzt klappt es
1
int main(void)
2
{
3
unsigned char sollwert=0;  // Ausgangssollwert
4
unsigned long hilf=0; 
5

6

7
// ******** Initialisierung **********************************
8
initial();  // Initialisierung Ports etc.
9
// ******** Hauptschleife ************************************  
10
    
11
  while(1)      // 6,50 µsec Bearbeitungszeit @20MHz
12
  {
13

14
sollwert= (PIND & 0x3f);  // Sollwert von 0 bis 63 entspricht 0 bis 100% (na ja, fast)
15

16
hilf = ((uint32_t)sinustabelle[omega]*(uint32_t)sollwert);
17
hilf=hilf >> 6;
18

19
if (omega<=160)
20
{
21
  zwischenspeicher_a=(top-hilf);
22
  zwischenspeicher_b=top;
23
}
24
else
25
{
26
  zwischenspeicher_b = (top-hilf);
27
  zwischenspeicher_a = top;
28
}
29

30
// ******************************************************** 
31
  } // Ende while(1)Endlosschleife
32
  return 0;
33
}// Ende Hauptprogramm
Gast #2236793
hilf = ((uint32_t)(sinustabelle[omega]*sollwert));

Wohl eher so ? Sonst gibt es wirklich ne 32 bit MUL...

Oder irre ich mich?

grüße Jonas & sorry für doppelpost
#2236821
Aber was anderes könnte noch was bringen.

  hilf >> 6

ist eine relativ teure Operation, weil der AVR keinen Barrelshifter hat 
und 6 mal Einzelbitshiften bei 32 Bit schon heftig ist.

  hilf >> 8

könnte der Compiler aber durch eine Bytevertauschung implementieren.
Nun kannst du aber nicht einfach durch 256 dividieren, wenn durch 64 
richtig wäre. Es sei denn du gleichst das aus, indem du die Sinuswerte 
in der Tabelle überhaupt als das 4-fache vorlegst. Dann kommt 
mathematisch am Ende alles wieder richtig raus.

Ob das was bringt, müsste man sich im Assemblercode ansehen.
Gast #2236831
Anja:
> - Wenn du Die Tabelle auf unter 1024 Skalierst (z.B. halbierte
> Werte) kommst du bei der Multiplikation mit unsigned int aus.
> (in dem Fall nur durch 32 Teilen).

Genau, jedoch nutzt Du beim Halbieren nicht die vorhandene Dynamik.
Besser: Sinustabelle-Wertebereich 0..1023 und wenn unbedingt nötig, 
danach mit 1,25 multiplizieren (63*1023*1,25/64=1258,769)

  uint16_t hilf;
  hilf = sinustabelle[omega]*sollwert;
  hilf >>=1;  //um genug Platz für *1,25 zu bilden
  hilf += hilf >> 2;  //*1,25
  hilf >>=5;

jetzt bist Du garantiert unter 1 µs.
Gast #2236863
eProfi schrieb:
> jetzt bist Du garantiert unter 1 µs.

Die hätte man auch gehabt wenn man das ganze in Assembler löst.
dort könne man gezielt eine 8Bit * 16 Bit Multiplikation
= 2 MUL-Befehle + 2 ADD-Befehle = 6 Takte verwenden.


Karl Heinz Buchegger schrieb:
> Es sei denn du gleichst das aus, indem du die Sinuswerte
> in der Tabelle überhaupt als das 4-fache vorlegst.
oder wenn man "sollwert" um 2 Bits nach links schiebt
Gast #2236877
Axel Düsendieb schrieb:
> ich möchte Werte aus der Tabelle „sinustabelle[omega]“ mit einem
> Sollwert skalieren. Die Werte liegen zwischen 0 und 1250.
> Der Sollwert kommt über parallele Eingänge an PortD. 0-63 entspricht
> 0-100%. Da ich im Programm durch 64 teile stimmt das nicht ganz, ist
> aber nicht so schlimm.
Muss bei der Skalierung mit dem Faktor 0 wirklich bei jedem Wert 0 
rauskommen? Eventuell mit Faktor 1 anfangen, dann stimmts auch mit der 
Division. Ansonsten
eProfi schrieb:
> Sinustabelle-Wertebereich 0..1023
Da reichen dann 16Bit.
1
hilf = sinustabelle[omega]*(sollwert+1);
und mögliche Verfeinerungen für die Geschwindigkeit.
(Vielleicht bekommt man das hin, wie ich es in Assembler direkt machen 
würde: 2 Bits nach links schieben/ rollen, und dann die oberen 16Bit 
verwenden?)
Gast #2236899
Wenn Du Ausgangswerte von 0 .. 1249 willst, skaliere die Sinustabelle 
auf 0..1015 oder 0..1016
63*1015*1,25/64=1248,92578
63*1016*1,25/64=1250,15625
Um genau auf 1249 zu kommen, müsstest Du 1015 nehmen und (auf)runden.


Oder 1023 lassen und die *1,25 korrigieren auf 1,241291563.
//32224+ 8056  - 251   =40029  /32 = 1250
  hilf+=hilf>>2-hilf>>7;

Alternativ:
  hilf2=hilf>>8;  //geht schnell, da nur das untere Byte genommen wird
//32224+ 8056  -125    -125     =40030   /32 = 1250
  hilf+=hilf>>2-hilf2-hilf2;

Rest wie oben gepostet.
Gast #2236949
Axel Düsendieb schrieb:
> Da Werte für eine PWM mit 16KHz berechnet werden sollen, habe ich nur
> 62,5µsec Zeit, bis der nächste Wert benötigt wird.
Muss das wirklich so sein? Also: Kaum hat sich OCR1x mit dem neuen Wert 
angefreundet, zack!, kommt schon der nächste!
Lässt sich an den absoluten Zahlen für OCR1x und der PWM-Frequenz 'noch 
was machen'? Ich würde die 1023 (max. Ergebnis) gleich als Topwert 
nehmen.
OP Persönliche Seite #2237407
Karl Heinz Buchegger schrieb:
> Nun kannst du aber nicht einfach durch 256 dividieren, wenn durch 64
> richtig wäre. Es sei denn du gleichst das aus, indem du die Sinuswerte
> in der Tabelle überhaupt als das 4-fache vorlegst. Dann kommt
> mathematisch am Ende alles wieder richtig raus.

Hallo Karl Heinz,
gute Idee, werde ich gleich mal probieren.

Axel

Ralf schrieb:
>> Sinustabelle-Wertebereich 0..1023
> Da reichen dann 16Bit.

Stimmt schon, aber dann bin ich bei 10 Bit PWM mit fast 20KHz. Ich 
glaube, ich kann mit dem Erreichten schon gut leben.

Danke noch mal an Alle.

Axel
Gast #2238058
Ralf schrieb:
> Muss das wirklich so sein? Also: Kaum hat sich OCR1x mit dem neuen Wert
> angefreundet, zack!, kommt schon der nächste!
Oh Mann, konnte glatt nicht wieder einschlafen, als mir diese Nacht 
eingefallen ist, was das soll :-(

Antwort schreiben

Eine eigene Frage? Dieser Thread ist über 15 Jahre alt. Stell sie in einem eigenen Thread – dort lesen mehr Leute mit, und sie wird öfter und schneller beantwortet.

Neuen Thread starten

Eine Lösung, Ergänzung oder Rückmeldung zu diesem Thread? Dann antworte hier.

Formatierung
  • [c]C-Code[/c]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] – Link zu einem Artikel
  • Verweis auf einen anderen Beitrag: Rechtsklick auf dessen Beitrags-ID (#8078628), „Adresse kopieren“, in den Text einfügen

Mehr zur Formatierung

Längeren Sourcecode als Anhang, nicht in den Text. JPG nur für Fotos und Scans; Zeichnungen und Screenshots als PNG oder GIF. Siehe Bildformate.

Schon registriert? Anmelden

Wird nicht angezeigt. Wir schicken dir einen Link; sobald du damit bestätigst, erscheint dein Beitrag.

Mit dem Abschicken legst du einen kostenlosen Account an und erkennst die Nutzungsbedingungen und die Datenschutzerklärung an.