Mit dem hier gezeigten Code wird ein Fernbedienungsempfänger für das RC-5 Protokoll von Philips im einem FPGA oder einem CPLD realisiert. Bei geeigneter Wahl des Taktes (100kHz) kann das Ganze sogar in einem XC9536 untergebracht werden.
Zuerst wird auf eine steigende Flanke am RC-5 Eingang gewartet. Von dort aus wird dann 1/4 Bitzeit abgewartet, und anschliessend jedes Bit nach 3/4 der Bitzeit abgetastet (cnt = 0). Der Wert cnt = 0 markiert also den Abtastzeitpunkt an 3/4 eines RC-5-Bits. Bei cnt = 1/4 der RC-5 Bitzeit (1778us/4) sollte demnach der Bitanfang, und bei cnt = 3/4 Bitzeit (1778us*3/4) die Bitmitte sein.
Durch die Biphase-Codierung (Manchester-Codierung) ist die Übertragung extrem stabil gegenüber Taktfrequenzschwankungen, denn mit jeder Flanke am RC5-Eingang kann der Empfänger neu synchronisiert werden. Die Neusynchonisierung erfolgt beim Erkennen einer Flanke des RC-5 Signals. Ist der Zähler kleiner als die Hälfte der Bitzeit, ist diese Flanke ein Bitanfang, deshalb wird der Zähler cnt auf 1/4 der Bitzeit korrigiert. Ist der Zähler cnt bereits über die Hälfte einer Bitzeit hinausgelaufen, markiert diese Flanke die Bitmitte, der Zähler cnt muß auf 3/4 der Bitzeit gesetzt werden.
Im folgenden wird ein Schieberegister verwendet, in das die empfangenen Bits nacheinander eingeschoben werden.
library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.NUMERIC_STD.ALL; entity RC5_RX is Port ( clk : in STD_LOGIC; rc5 : in STD_LOGIC; busy : out STD_LOGIC; toggle : out STD_LOGIC; addr : out STD_LOGIC_VECTOR (4 downto 0); cmd : out STD_LOGIC_VECTOR (5 downto 0)); end RC5_RX; architecture Behavioral of RC5_RX is signal rc5sr : std_logic_vector (2 downto 0) := "000"; --constant oscclk : integer := 50000000; -- 50MHz z.B. FPGA constant oscclk : integer := 100000; -- 100kHz im CPLD constant bittime : integer := 1778; -- us constant clkdiv : integer := (oscclk/(1000000/bittime))-1; signal cnt : integer range 0 to clkdiv; signal bitcnt : integer range 0 to 15 := 15; signal bits : std_logic_vector (11 downto 0) := (others=>'0'); begin rc5sr <= rc5sr(1 downto 0) & rc5 when rising_edge(clk); process begin wait until rising_edge(clk); if (cnt<clkdiv) then cnt <= cnt+1; if (rc5sr(2) /= rc5sr(1)) then -- Flankenwechsel --> Resync if (cnt < clkdiv/2) then -- eine Abweichung bis +-20% wird korrekt ausgewertet: cnt <= clkdiv/4; -- weniger als halbe Bitdauer --> auf "viertel Bit" korrigieren else cnt <= 3*clkdiv/4; -- mehr als halbe Bitdauer --> auf "dreiviertel Bit" korrigieren end if; end if; else cnt <= 0; if (bitcnt<14) then -- 14 Bits eintakten, Startbits werden durchgeschoben bits <= bits(10 downto 0) & rc5sr(2); end if; if (bitcnt<15) then -- Übertragung aktiv bitcnt <= bitcnt+1; end if; end if; if (bitcnt=15) then -- Übertragung beendet busy <= '0'; if (rc5sr(2 downto 1) = "01") then -- Startbit erkannt --> cnt <= 3*clkdiv/4; -- nur 1/4 einer Bitzeit abwarten bitcnt <= 0; busy <= '1'; end if; end if; end process; toggle <= bits(11); addr <= bits(10 downto 6); cmd <= bits(5 downto 0); end Behavioral;
Mit Erkennen des Startbits wird das Signal busy aktiv. Nach der Übertragung wird busy wieder deaktiviert. Dieser Ablauf ist im Screenshot der Simulation schön zu sehen:
Hier noch ein Ausschnitt der Aufsynchronisation mit einer fehlerhaften Taktfrequenz von 120kHz statt der vorgesehenen 100kHz. Der Zähler cnt läuft bei 100kHz von 0..176. An der steigenden Flanke des RC-5 Signals ist er wegen der zu hohen Taktfrequenz bereits auf 164 hochgelaufen. Nach der Flankenerkennung über das Schieberegister rc5sr wird der Zähler auf 3/4 der Bitzeit, also auf 132 korrigiert. Das passiert bei jedem Bit, und deshalb ist dieses Protokoll extrem tolerant gegenüber unpassenden Frequenzen. RS232 lässt z.B. nur eine max. Toleranz von 3% zu.
Alternativ kann statt des Schieberegisters eine Umsetzung über einen Multiplexer durchgeführt werden. Allerdings ist hier die Bit-Ordnung während der Übertragung genau anders herum, wie sie letztlich in den zu übergebenden Datenworten benötigt wird: das erste Bit ist das MSB. Weil der Zähler bitcnt aber bei 0 losläuft müsste eine Umrechnung auf die entsprechende Bitposition erfolgen. Alternativ könnte auch der Zähler beim Zählerstand 14 loslaufen, was aber aber nicht sehr intuitiv ist.
Daher verwende ich einen VHDL-Trick mit der Bitreihenfolge: wird einem x(0 to 3) Vektor der Wert "1100" zugewiesen, und danach dieser x(0 to 3) Vektor einem y(3 downto 0) Vektor, dann hat der y(3 downto 0) Vektor den Wert "1100". Es wird also die Bitpositionen (x'left an y'left ... x'right an y'right) zugewiesen. Aber, und das ist der Trick, jetzt hat y(3) den Wert von x(0). Die indizierten Bits wurden getauscht
library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.NUMERIC_STD.ALL; entity RC5_RX is Port ( clk : in STD_LOGIC; rc5 : in STD_LOGIC; busy : out STD_LOGIC; toggle : out STD_LOGIC; addr : out STD_LOGIC_VECTOR (4 downto 0); cmd : out STD_LOGIC_VECTOR (5 downto 0)); end RC5_RX; architecture Behavioral of RC5_RX is signal rc5sr : std_logic_vector (2 downto 0) := "000"; constant oscclk : integer := 100000; -- 100kHz constant bittime : integer := 1778; -- us constant clkdiv : integer := (oscclk/(1000000/bittime))-1; signal cnt : integer range 0 to clkdiv; signal bitcnt : integer range 0 to 15 := 15; signal bits : std_logic_vector (0 to 13) := (others=>'0'); -- Achtung Bitreihenfolge!!! begin rc5sr <= rc5sr(1 downto 0) & rc5 when rising_edge(clk); process begin wait until rising_edge(clk); if (cnt<clkdiv) then cnt <= cnt+1; if (rc5sr(2) /= rc5sr(1)) then -- Flankenwechsel --> Resync if (cnt < clkdiv/2) then -- Abweichung bis+-20% cnt <= clkdiv/4; -- wird korrekt ausgewertet else cnt <= 3*clkdiv/4; end if; end if; else cnt <= 0; if (bitcnt<14) then -- Bits über MUX zuweisen bits(bitcnt) <= rc5sr(2); end if; if (bitcnt<15) then -- Übertragung aktiv bitcnt <= bitcnt+1; end if; end if; if (bitcnt=15) then -- Übertragung beendet busy <= '0'; if (rc5sr(2 downto 1) = "01") then -- Startbit erkannt --> cnt <= 3*clkdiv/4; -- nur 1/4 einer Bitzeit abwarten bitcnt <= 0; busy <= '1'; end if; end if; end process; toggle <= bits(2); addr <= bits(3 to 7); cmd <= bits(8 to 13); end Behavioral;
In der Waveform der Simulation ist zu erkennen, dass die hektische Umschalterei (gut zu sehen am Signal toggle) während der Übertragung entfällt. Allerdings sind auch hier erst bei inaktivem busy Signal die Daten gültig: