Forum: FPGA, VHDL & Co. ungeradzahlig teilen


von Rolf G. (haegar88)


Lesenswert?

Hallo,
ich stehe vor dem Problem, dass ich von einem Drehgeber 2,671875 Pulse 
pro mm bekomme aber möglichst genau alle mm einen Zähler erhöhen will. 2 
ist falsch, 3 aber auch. Abwechselnd 2 und 3 in geschicktem Wechsel wäre 
am genauesten.. Hat sowas schonmal jemand gemacht? Vielen Dank im 
voraus!
Gruß Haegar

von Tobi (Gast)


Lesenswert?

warum nicht den spiess umkehren?

Rechne aus welche strecke 1 impuls entspricht und rechne damit. Damit 
bis Du relativ genau.

von yalu (Gast)


Lesenswert?

> Abwechselnd 2 und 3 in geschicktem Wechsel wäre am genauesten.. Hat
> sowas schonmal jemand gemacht?

Nein, hätte aber trotzdem einen Vorschlag:
1
#define ZAEHLER 171
2
#define NENNER   64
3
4
unsigned int zaehl(void) {
5
  static unsigned int mm, summe/*=ZAEHLER/2*/;
6
7
  if((summe+=NENNER) >= ZAEHLER) {
8
    summe -= ZAEHLER;
9
    mm++;
10
  }
11
  return mm;
12
}

Die Funktion zaehl wird für jeden Puls aufgerufen und liefert die
ganzzahlige Wegstrecke in mm zurück, wobei weder Multiplikationen noch
Divisionen benötigt werden. Der Umrechnungsfaktor muss als Bruch >1
vorliegen (2,671875=171/64). Je nach Startbedingung und gewünschtem
Rundungsverfahren kann es sinnvoll sein, summe mit ZAEHLER/2 statt mit
0 zu initialisieren.

Das Verfahren entspricht dem Bresenham-Algorithmus zum Zeichnen von
Geraden auf Rasterausgabegeräten:

  http://de.wikipedia.org/wiki/Bresenham-Algorithmus

von yalu (Gast)


Lesenswert?

Ach, ich sehe jetzt erst, dass das das Forum "FPGA, VHDL & Co." ist. Da
hat natürlich C nichts zu suchen. Betrachte den Code also als Pseudocode
und setze ihn in VHDL oder Verilog um. Das Verfahren ist gerade auch für
die Implementierung in Hardware gut geeignet,das es keine komplizierten
Rechenoperationen benötigt.

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Durch 2**n Zahlen kann problemlos geteilt werden. Und weil sich der 
Bruch 171/64 als 171/(2**6) schreiben lässt, kann man das auch so 
machen:
1
library IEEE;
2
use IEEE.STD_LOGIC_1164.ALL;
3
use IEEE.NUMERIC_STD.ALL;
4
5
entity EncScale is
6
    Port ( posin  : in  STD_LOGIC_VECTOR (31 downto 0);
7
           posout : out STD_LOGIC_VECTOR (31 downto 0));
8
end EncScale;
9
10
architecture Behavioral of EncScale is
11
  signal poscnt : integer;
12
  signal posmm  : integer;
13
begin
14
  poscnt <= to_integer(signed(posin));
15
  posmm  <= (poscnt*171)/64; -- 171/64 = 2,671875
16
  posout <= std_logic_vector(to_signed(posmm,32));
17
end Behavioral;
posin enthält die Zählimpulse vom Geber, nur auf diesem Wert wirkt sich 
der Encoder unmittelbar aus. In der Hardware wird nicht geteilt, sondern 
nach der Multiplikation einfach die unteren 6 Bits abgeschnitten. Und 
weil der Multiplizierer ein kombinatrisches Element ist, muß das ganze 
nicht mal getaktet sein ;-)

von Thomas R. (Firma: abaxor engineering) (abaxor)


Lesenswert?

yalu wrote:
>
> Das Verfahren entspricht dem Bresenham-Algorithmus zum Zeichnen von
> Geraden auf Rasterausgabegeräten:
>
>   http://de.wikipedia.org/wiki/Bresenham-Algorithmus

Ein Delta-Sigma-Modulator macht dasselbe. Das interessante daran ist, 
dass beide Verfahren zu Beginn der 1960er Jahre entstanden sind.

Tom

von Gast (Gast)


Lesenswert?

Das ist so wunderam nicht wenn man bedenkt, dass da Patente im Spiel 
waren. Ist so änhlich wie das dem Sigma-Delta-Prinzip hinterliegende 
Single-Slopw-Verfahren, welches es kurz nach der Patentierung auch als 
Dual- und Quadro-Verfahren gab.

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.