Forum Discussion
Altera_Forum
Honored Contributor
12 years agoI just realized that the code given had a bug when encoder moved up and down. This one below fixes this and calculates offset from last pulse output. So it is working as to be expected.
LIBRARY ieee;
USE ieee.std_logic_1164.all;
USE ieee.numeric_std.all;
ENTITY EncoderDecoder IS
GENERIC
(
CNTR_SZ : INTEGER := 32;
COEFF_SZ : INTEGER := 16
);
PORT
(
clk : IN STD_LOGIC;
rstn : IN STD_LOGIC;
clrcnt : IN STD_LOGIC;
encA : IN STD_LOGIC;
encB : IN STD_LOGIC;
rd : IN STD_LOGIC;
coeff : IN STD_LOGIC_VECTOR(COEFF_SZ-1 DOWNTO 0);
ch_coeff : IN STD_LOGIC;
pls : OUT STD_LOGIC;
counter : OUT STD_LOGIC_VECTOR(CNTR_SZ-1 DOWNTO 0);
dbg_cnt : OUT STD_LOGIC_VECTOR(CNTR_SZ-1 DOWNTO 0)
);
END ENTITY;
ARCHITECTURE behv OF EncoderDecoder IS
signal temp_encA : STD_LOGIC_VECTOR(2 DOWNTO 0);
signal temp_encB : STD_LOGIC_VECTOR(2 DOWNTO 0);
signal temp_counter : SIGNED(CNTR_SZ-1 DOWNTO 0);
signal counter_reg : SIGNED(CNTR_SZ-1 DOWNTO 0);
signal sync_encA : STD_LOGIC;
signal sync_encB : STD_LOGIC;
signal rising_encA : STD_LOGIC;
signal falling_encA : STD_LOGIC;
signal rising_encB : STD_LOGIC;
signal falling_encB : STD_LOGIC;
signal coeff_val : UNSIGNED(COEFF_SZ-1 DOWNTO 0);
signal pulse : STD_LOGIC;
signal out_pls : STD_LOGIC;
BEGIN
PROCESS(clk, rstn)
BEGIN
IF(rstn = '0') THEN
coeff_val <= (others => '0');
ELSIF(rising_edge(clk)) THEN
IF(ch_coeff = '1') THEN
coeff_val <= unsigned(coeff);
END IF;
END IF;
END PROCESS;
dbg_cnt <= std_logic_vector(temp_counter);
-- Synchronize encoder A and B inputs and also detect falling and rising edges
-- (1) used as 2-DFF synchronizer output for each signal
-- (2) used to detect rising/falling edges
PROCESS(clk, rstn)
BEGIN
IF(rstn = '0') THEN
temp_encA <= (others=>'0');
temp_encB <= (others=>'0');
ELSIF(rising_edge(clk)) THEN
temp_encA <= temp_encA(1 DOWNTO 0) & encA;
temp_encB <= temp_encB(1 DOWNTO 0) & encB;
END IF;
END PROCESS;
sync_encA <= temp_encA(1);
sync_encB <= temp_encB(1);
rising_encA <= '1' WHEN temp_encA(2) = '0' AND temp_encA(1) = '1' ELSE '0';
rising_encB <= '1' WHEN temp_encB(2) = '0' AND temp_encB(1) = '1' ELSE '0';
falling_encA <= '1' WHEN temp_encA(2) = '1' AND temp_encA(1) = '0' ELSE '0';
falling_encB <= '1' WHEN temp_encB(2) = '1' AND temp_encB(1) = '0' ELSE '0';
-- 4x decoding and detecting pulse rising edge
pulse <= rising_encA XOR rising_encB XOR falling_encA XOR falling_encB;
-- updating counter
-- clrcnt is used to clear the counter back to 0
-- for that we need to use clk and not pulse in the process
-- if clrn is not needed - pulse can be used as clock to update the counter
PROCESS(clk, rstn, clrcnt)
BEGIN
IF(rstn = '0') THEN
temp_counter <= (others=>'0');
ELSIF(rising_edge(clk)) THEN
IF(clrcnt = '1') THEN
temp_counter <= (others => '0');
ELSIF(rising_encB = '1' AND sync_encA = '1') THEN
temp_counter <= temp_counter + 1;
ELSIF(rising_encB = '1' AND sync_encA = '0') THEN
temp_counter <= temp_counter - 1;
ELSIF(falling_encB = '1' AND sync_encA = '0') THEN
temp_counter <= temp_counter + 1;
ELSIF(falling_encB = '1' AND sync_encA = '1') THEN
temp_counter <= temp_counter - 1;
ELSIF(rising_encA = '1' AND sync_encB = '0') THEN
temp_counter <= temp_counter + 1;
ELSIF(rising_encA = '1' AND sync_encB = '1') THEN
temp_counter <= temp_counter - 1;
ELSIF(falling_encA = '1' AND sync_encB = '1') THEN
temp_counter <= temp_counter + 1;
ELSIF(falling_encA = '1' AND sync_encB = '0') THEN
temp_counter <= temp_counter - 1;
ELSE
temp_counter <= temp_counter + 0;
END IF;
END IF;
END PROCESS;
-- Outputs
-- RD pulse latches the counter value to the output
-- so it can be processed as needed elsewhere while counter is still being updated.
PROCESS(clk, rstn, rd)
BEGIN
IF(rstn = '0') THEN
counter <= (others => '0');
counter_reg <= (others => '0');
ELSIF(rising_edge(clk)) THEN
IF(rd = '1') THEN
counter_reg <= temp_counter;
counter <= std_logic_vector(temp_counter);
ELSE
counter <= std_logic_vector(counter_reg);
END IF;
END IF;
END PROCESS;
-- Will output a single pulse when a number of pulses from encoder is reached
-- it can be used for counts to mm conversion when a number of pulses is known (= value should be put into coeff)
-- pulse will be single clk long
PROCESS(clk, pulse, rstn, temp_counter)
VARIABLE cnt : SIGNED(CNTR_SZ-1 DOWNTO 0) := (others => '0');
VARIABLE diff : SIGNED(CNTR_SZ-1 DOWNTO 0) := (others => '0');
BEGIN
IF(rstn = '0') THEN
cnt := temp_counter;
out_pls <= '0';
ELSIF(rising_edge(clk)) THEN
diff := abs(cnt - temp_counter);
IF(clrcnt = '1') THEN
out_pls <= '0';
cnt := temp_counter;
diff := (others => '0');
ELSIF(pulse = '1') THEN
IF(to_integer(diff) >= to_integer(coeff_val)) THEN
out_pls <= '1';
cnt := temp_counter;
ELSE
out_pls <= '0';
END IF;
ELSIF(pulse = '0' AND clrcnt = '0') THEN
out_pls <= '0';
END IF;
END IF;
END PROCESS;
pls <= pulse WHEN (coeff_val = 0 OR coeff_val = 1) ELSE out_pls;
END behv;