FPGA - Rotary Encoder
From Steak Wiki
Jump to navigationJump to searchReap what you sow. Here's some public domain code for a Spartan 3E, to interface with multiple rotary encoders. It's currently tested up to 8. It uses Block RAM to keep track of a Rotary ID and Value for the rotary (between 0-127). It outputs the rotary values via UART on 57600 baud. Based off of the Papilio and also HamsterNZ's introductory PDF on FPGAs.
I have different versions of this. I've had some issues with UART timing on a Spartan 3E 500, whereas the Papilio's 250 did not have such issues. I'll start with the one that's compatible with the Papilio board (including the pinout / constraints.ucf file).
There is Rotary_Top.vhd, which is the main code, then there are instances of rotaries.
Rotary_Top.vhd
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
use IEEE.NUMERIC_STD.ALL;
-- TODO
--DONE test reading one address in RAM. make sure it is right.
--DONE make sure output on UART is correct. Also bits are
--DONE in right order too. DONE.
--DONE (used counter, not loop) setup a read on 0-99 addresses of RAM . use loop.
--DONE have it output each memory address in uart 3 or 4 times before
--going to the next.
--DONE EASY have it loop through the ram 24/7 EASY
--DONE: set default values of ram to be letters abcabcabcabc
--RAM
-- idea
-- have top level manage all ram and output
-- have output only if cw or ccw
-- if outputs, have that rotaryID(or memory address)
-- be the chosen one to write to.
--UART
-- possible solution #1
-- if a memory address is updated
-- then output uart of new memory address only
-- every rotary change, causes
-- new ram write/ read, and uart output
-- on as needed basis
-- PROBLEMS: having trouble getting just one uart byte out
-- noisy, and timing is off possibly (check timing)
-- possible solution #2
-- loop through all memory constantly
-- output all values constantly
-- any changes will be reflected in micro
-- hopefully fast enough? (should be)(57600 is p fast)
-- any rotaries that update will write to ram
-- problems: if 100 rotaries updating at once,
-- could not be fast enough? maybe? maybe not. maybe it
-- is fast enough to read io, write ram.
-- write RAM would be separate from read ram / UART here
-- but need to adjust write enable flag as needed... maybe
-- two port RAM is better? constantly write on port A
-- constantly read from port b. no need to adjust write enable
-- flag...
-- Yes, you can use dual port RAM. one port always wea high
-- other port always wea low. and they should only interfere
-- if read is during a write. however,
-- depending how fast I read the data, I may be able to get
-- around htis. i could also average the values (say last 3
-- values) from the serial for a given byte, and call that
-- the true value. this way avoid any memory errors.
-- TODO
-- Need to writeup basic documentation and clean these at the
-- very end.
-- look up what a memory latch is
-- output serial as BCD or ascii table bits
-- i.e. instead of using straight hex (one byte each) for
-- a number, convert it to its ascii equivalent (not as fast)
-- (takes more bytes)
-- EDIT: just outputting raw 8 bits
entity rotary_top is
--must map all 100 rotaries here
GENERIC(
RAMADDR : STD_LOGIC_VECTOR(7 downto 0) := x"FF";
ROTARYID : integer := 1
);
PORT( ROT : in STD_LOGIC_VECTOR(1 downto 0); -- semi colons
ROT2 : in STD_LOGIC_VECTOR(1 downto 0); -- here
ROT3 : in STD_LOGIC_VECTOR(1 downto 0);
ROT4 : in STD_LOGIC_VECTOR(1 downto 0);
ROT5 : in STD_LOGIC_VECTOR(1 downto 0);
ROT6 : in STD_LOGIC_VECTOR(1 downto 0);
ROT7 : in STD_LOGIC_VECTOR(1 downto 0);
ROT8 : in STD_LOGIC_VECTOR(1 downto 0);
LED : out STD_LOGIC_VECTOR(7 downto 0); -- commas in
--LEDWATCH : out STD_LOGIC_VECTOR(7 downto 0); --instance
clk : in STD_LOGIC;
--DATAOUTUART : out STD_LOGIC;
TX : out STD_LOGIC_VECTOR(0 downto 0)
--RX : STD_LOGIC
);
end rotary_top;
architecture Behavioral of rotary_top is
COMPONENT rotary
GENERIC(
RAMADDR : STD_LOGIC_VECTOR(7 downto 0) := x"FF";
ROTARYID : STD_LOGIC_VECTOR(7 downto 0) := x"00"
);
PORT(
ROT : IN std_logic_vector(1 downto 0);
clk : IN std_logic;
LED : OUT std_logic_vector(3 downto 0);
-- LEDWATCH : OUT std_logic_vector(7 downto 0);
DATAOUT : OUT STD_LOGIC_VECTOR(15 downto 0)
);
END COMPONENT;
COMPONENT DUALRAM
PORT (
clka : IN STD_LOGIC;
ena : IN STD_LOGIC;
wea : IN STD_LOGIC_VECTOR(0 DOWNTO 0);
addra : IN STD_LOGIC_VECTOR(7 DOWNTO 0);
dina : IN STD_LOGIC_VECTOR(15 DOWNTO 0);
douta : OUT STD_LOGIC_VECTOR(15 DOWNTO 0);
clkb : IN STD_LOGIC;
enb : IN STD_LOGIC;
web : IN STD_LOGIC_VECTOR(0 DOWNTO 0);
addrb : IN STD_LOGIC_VECTOR(7 DOWNTO 0);
dinb : IN STD_LOGIC_VECTOR(15 DOWNTO 0);
doutb : OUT STD_LOGIC_VECTOR(15 DOWNTO 0)
);
END COMPONENT;
signal resultsigtest : STD_LOGIC_VECTOR(7 downto 0) ;
signal RAMADDR_CURRENT : STD_LOGIC_VECTOR(7 downto 0) := "00000000";
-- RAMADDR_0 is start byte (zz (LOWER CASE)) for pic to know when array starts
constant RAMADDR_1 : STD_LOGIC_VECTOR(7 downto 0) := "00000001";
constant ROTARYID_1 : STD_LOGIC_VECTOR(7 downto 0) := "00000001";
constant RAMADDR_2 : STD_LOGIC_VECTOR(7 downto 0) := "00000010";
constant ROTARYID_2 : STD_LOGIC_VECTOR(7 downto 0) := "00000010";
--signal RAMADDR_CURRENT2 : STD_LOGIC_VECTOR(7 downto 0) := "00001000";
constant RAMADDR_3 : STD_LOGIC_VECTOR(7 downto 0) := "00000011";
constant ROTARYID_3 : STD_LOGIC_VECTOR(7 downto 0) := "00000011";
constant RAMADDR_4 : STD_LOGIC_VECTOR(7 downto 0) := "00000100";
constant ROTARYID_4 : STD_LOGIC_VECTOR(7 downto 0) := "00000100";
constant RAMADDR_5 : STD_LOGIC_VECTOR(7 downto 0) := "00000101";
constant ROTARYID_5 : STD_LOGIC_VECTOR(7 downto 0) := "00000101";
constant RAMADDR_6 : STD_LOGIC_VECTOR(7 downto 0) := "00000110";
constant ROTARYID_6 : STD_LOGIC_VECTOR(7 downto 0) := "00000110";
constant RAMADDR_7 : STD_LOGIC_VECTOR(7 downto 0) := "00000111";
constant ROTARYID_7 : STD_LOGIC_VECTOR(7 downto 0) := "00000111";
constant RAMADDR_8 : STD_LOGIC_VECTOR(7 downto 0) := "00001000";
constant ROTARYID_8 : STD_LOGIC_VECTOR(7 downto 0) := "00001000";
-- these must be here or error. must translate output to
-- sig, then to output. can't directly go
-- from output to output
signal ROT1OUT : STD_LOGIC_VECTOR(15 downto 0);
signal ROT2OUT : STD_LOGIC_VECTOR(15 downto 0);
--must be set to zero (or unequal to rot1out) at start
signal ROT1PREV : STD_LOGIC_VECTOR(15 downto 0) := (others => '0');
type t_ROTOUT is array (1 to 100) of std_logic_vector(15 downto 0);
signal r_ROTO : t_ROTOUT := (others => (others => '0'));
signal wea : STD_LOGIC_VECTOR(0 DOWNTO 0) := "0";
signal addra : STD_LOGIC_VECTOR(7 DOWNTO 0) := (others => '0');
signal dina : STD_LOGIC_VECTOR(15 DOWNTO 0) := (others => '0');
signal douta : STD_LOGIC_VECTOR(15 DOWNTO 0) := (others => '0');
signal ena : STD_LOGIC := '0';
signal DINAPROXY : STD_LOGIC_VECTOR(15 DOWNTO 0) := (others => '0');
signal web : STD_LOGIC_VECTOR(0 DOWNTO 0) := "0";
signal addrb : STD_LOGIC_VECTOR(7 DOWNTO 0) := (others => '0');
signal dinb : STD_LOGIC_VECTOR(15 DOWNTO 0) := (others => '0');
signal doutb : STD_LOGIC_VECTOR(15 DOWNTO 0) := (others => '0');
signal enb : STD_LOGIC := '0';
signal ADDRAPROXY : STD_LOGIC_VECTOR(7 DOWNTO 0) := "00000001";
signal ADDRBPROXY : STD_LOGIC_VECTOR(7 DOWNTO 0) := (others => '0');
signal ADDR_RD_COUNTER : STD_LOGIC_VECTOR(7 DOWNTO 0) := (others => '0');
signal WRITE_COUNTER1 : STD_LOGIC_VECTOR(12 downto 0) := (others => '0');
signal WRITE_COUNTER2 : STD_LOGIC_VECTOR(12 downto 0) := (others => '0');
signal COUNTER : STD_LOGIC_VECTOR(29 downto 0) := (others => '0');
signal READOUT : STD_LOGIC_VECTOR(15 downto 0) := (others => '0'); --"1111111010110100"; --default data to test uart
signal BUSY : STD_LOGIC := '0';
signal UART_COUNTER : STD_LOGIC_VECTOR(12 downto 0) := (others => '0');
signal UART_COUNTER2 : STD_LOGIC_VECTOR(12 downto 0) := (others => '0');
signal UART_COUNTER3 : STD_LOGIC_VECTOR(12 downto 0) := (others => '0');
signal RAM_COUNTER : STD_LOGIC_VECTOR(29 downto 0) := (others => '0');
--signal UART_COUNTER_SPACER : STD_LOGIC_VECTOR(12 downto 0) := (others => '0');
--signal UART_COUNTER4 : STD_LOGIC_VECTOR(12 downto 0) := (others => '0');
--signal UART_OFFON : STD_LOGIC := '0';
---variable ROT_INCREASE : integer range 0 to 255 := 1;
signal ROT_INCREASE : STD_LOGIC_VECTOR(7 downto 0) := x"01";
--signal UARTOUT : STD_LOGIC_VECTOR(15 downto 0) := "1111111010110100"; --default data to test uart without reading rotary
--signal UARTOUT : STD_LOGIC_VECTOR(29 downto 0) := "111110101101001111111010110100"; --Y then Z -- WORKS
signal UARTOUT : STD_LOGIC_VECTOR(199 downto 0) := (others => '1');
--debugging uart
--signal UARTOUT : STD_LOGIC_VECTOR(29 downto 0) := "101010101010101010101010101010";
-- must map all 100 instances of rotary module here
--TODO
-- input a value for rotary (i.e. rotary1= 1, as 8 bit value)
-- output that value somewhere else, and put it into uart
-- output the value for rotary plus or minus into
-- a specific 8 bit register. output into uart when called.
-- After uart reads, erase these banks.
-- OR, need to find how to write to RAM. Then I need to
-- programatically write the rotary to a specific ram register
-- then read the registers
begin
Inst_DUALRAM: DUALRAM
PORT MAP (
clka => clk,
ena => ena,
wea => wea,
addra => addra,
dina => dina,
douta => douta,
clkb => clk,
enb => enb,
web => web,
addrb => addrb,
dinb => dinb,
doutb => doutb
);
Inst_ROTARY_1: rotary
GENERIC MAP(
RAMADDR => RAMADDR_1,
ROTARYID => ROTARYID_1
)
PORT MAP(
ROT => ROT,
LED => open,
clk => clk,
DATAOUT => r_ROTO(1)
);
Inst_ROTARY_2: rotary
GENERIC MAP(
RAMADDR => RAMADDR_2,
ROTARYID => ROTARYID_2
)
PORT MAP(
ROT => ROT2,
LED => open,
clk => clk,
DATAOUT => r_ROTO(2)
);
Inst_ROTARY_3: rotary
GENERIC MAP(
RAMADDR => RAMADDR_3,
ROTARYID => ROTARYID_3
)
PORT MAP(
ROT => ROT3,
LED => open,
clk => clk,
DATAOUT => r_ROTO(3)
);
Inst_ROTARY_4: rotary
GENERIC MAP(
RAMADDR => RAMADDR_4,
ROTARYID => ROTARYID_4
)
PORT MAP(
ROT => ROT4,
LED => open,
clk => clk,
DATAOUT => r_ROTO(4)
);
Inst_ROTARY_5: rotary
GENERIC MAP(
RAMADDR => RAMADDR_5,
ROTARYID => ROTARYID_5
)
PORT MAP(
ROT => ROT5,
LED => open,
clk => clk,
DATAOUT => r_ROTO(5)
);
Inst_ROTARY_6: rotary
GENERIC MAP(
RAMADDR => RAMADDR_6,
ROTARYID => ROTARYID_6
)
PORT MAP(
ROT => ROT6,
LED => open,
clk => clk,
DATAOUT => r_ROTO(6)
);
Inst_ROTARY_7: rotary
GENERIC MAP(
RAMADDR => RAMADDR_7,
ROTARYID => ROTARYID_7
)
PORT MAP(
ROT => ROT7,
LED => open,
clk => clk,
DATAOUT => r_ROTO(7)
);
Inst_ROTARY_8: rotary
GENERIC MAP(
RAMADDR => RAMADDR_8,
ROTARYID => ROTARYID_8
)
PORT MAP(
ROT => ROT8,
LED => open,
clk => clk,
DATAOUT => r_ROTO(8)
);
-- LEDWATCH <=READOUT(7 downto 0); --works
-- this means i'm passing rotary values succesfully through
-- to ROT1OUT and the rotary ID as well.
--LEDWATCH <= READOUT(7 downto 0);
--put everything in a process because why not.
--try this out of the clock for starters...
--RAMWRITE: process(r_ROTO(1))
----variable ROT_INCREASE : integer range 0 to 255 := 1;
--begin
--
-- DINAPROXY <= ROT1OUT;
-- ADDRAPROXY <= RAMADDR_1;
--
---- --if rising_edge(clk) then this breaks things
---- ROT_INCREASE <= x"01";
---- --RAMADDR_CURRENT <= std_logic_vector(to_unsigned(ROT_INCREASE, RAMADDR_CURRENT'length));
---- RAMADDR_CURRENT <= ROT_INCREASE;
---- --write to ram
---- dina <= r_ROTO(to_integer(unsigned(ROT_INCREASE)));
---- ADDRAPROXY <= RAMADDR_CURRENT;
---- --ROT_INCREASE := ROT_INCREASE + 1;
---- --end if;
--
--
---- ROT_INCREASE <= x"00";
---- if ROT_INCREASE = x"00" then
---- ROT_INCREASE <= x"01";
---- RAMADDR_CURRENT <= ROT_INCREASE;
---- dina <= r_ROTO(to_integer(unsigned(ROT_INCREASE)));
---- ADDRAPROXY <= RAMADDR_CURRENT;
---- elsif ROT_INCREASE = x"01" then
---- ROT_INCREASE <= x"02";
---- RAMADDR_CURRENT <= ROT_INCREASE;
---- dina <= r_ROTO(to_integer(unsigned(ROT_INCREASE)));
---- ADDRAPROXY <= RAMADDR_CURRENT;
---- elsif ROT_INCREASE = x"02" then
---- ROT_INCREASE <= x"00";
---- end if;
----
---- ROT_INCREASE <= x"00";
---- if ROT_INCREASE = x"00" then
---- ROT_INCREASE <= x"01";
---- RAMADDR_CURRENT <= RAMADDR_1;
---- dina <= r_ROTO(1);
---- ADDRAPROXY <= RAMADDR_CURRENT;
------ elsif ROT_INCREASE = x"01" then
------ ROT_INCREASE <= x"02";
------ RAMADDR_CURRENT <= RAMADDR_2;
------ dina <= r_ROTO(2);
------ ADDRAPROXY <= RAMADDR_CURRENT;
---- elsif ROT_INCREASE = x"01" then
---- ROT_INCREASE <= x"00";
---- end if;
----
--
-- --ROT_INCREASE := ROT_INCREASE + 1;
--
---- for x in 1 to 50 loop
---- ROT_INCREASE := 2;
---- RAMADDR_CURRENT <= std_logic_vector(to_unsigned(ROT_INCREASE, RAMADDR_CURRENT'length));
---- --write to ram
---- dina <= r_ROTO(ROT_INCREASE);
---- ADDRAPROXY <= RAMADDR_CURRENT;
---- --ROT_INCREASE := ROT_INCREASE + 1;
---- end loop;
--
--
-- --end if;
--
-- --maybe just add rot1out to a queue? or variable
-- -- put it in its own process instead of clk?
--
--
-- -- is this if statement needed here? I don't think so.
-- -- I think the process(ROT1OUT) assumes it will run
-- -- should anything change on ROT1OUT. or i could do an if with a explicit change detected i think
-- --if ROT1PREV /= ROT1OUT and BUSY = '0' then --put in fms
-- --if BUSY = '0' then --if some aren't detected, try removing busy...
-- --BUSY <= '1';
--
-- --for loop: upon a change in anything, loop
-- --through all values, and write to block ram
-- --using variables
--
--
-- --ADDRBPROXY <= RAMADDR_CURRENT;
--
---- ROT1PREV <= doutb; -- this needs to be somewhere else
---- READOUT <= doutb; -- RAM reads are all done at once
-- -- in main read loop
-- --BUSY <= '0';
-- --end if;
--
--end process;
--
--ALL RAM writes must be in same processper mikes electiceev forum
--RAMWRITE2: process(r_ROTO(2))
--begin
-- DINAPROXY <= ROT2OUT;
-- ADDRAPROXY <= RAMADDR_2;
--end process;
rotarytop: process (clk)
begin
if rising_edge(clk) then
--if rot1out goes to dinaproxy (which is not connected
--to anything) then nothing happens. I htink it was over
--writing the zs
--this works
if WRITE_COUNTER1 < 100000 then
ADDRAPROXY <= x"8C";
WRITE_COUNTER1 <= WRITE_COUNTER1 + 1;
elsif WRITE_COUNTER1 < 8000000 then
dina <= r_ROTO(to_integer(unsigned(ROT_INCREASE)));
ADDRAPROXY <= ROT_INCREASE;
WRITE_COUNTER1 <= WRITE_COUNTER1 + 1;
elsif WRITE_COUNTER1 = 10000000 then
ROT_INCREASE <= ROT_INCREASE + 1;
WRITE_COUNTER1 <= (others => '0');
else
WRITE_COUNTER1 <= WRITE_COUNTER1 + 1;
end if;
if ROT_INCREASE > x"09" then
ROT_INCREASE <= x"01";
end if;
-- if ROT_INCREASE = x"00" then
-- ROT_INCREASE <= x"01";
-- end if; --NOT NEEDED. Be careful NOT to set ROT_INCREASE
-- TO ZERO ON STARTUP. SET TO x"01".
--this works
-- if WRITE_COUNTER2 < 2 then
-- WRITE_COUNTER1 <= WRITE_COUNTER1 + 1;
-- ADDRAPROXY <= x"8C";
-- ROT_INCREASE <= x"01";
-- elsif WRITE_COUNTER2 < 10 then
-- dina <= r_ROTO(to_integer(unsigned(ROT_INCREASE)));
-- ADDRAPROXY <= ROT_INCREASE;
-- WRITE_COUNTER1 <= WRITE_COUNTER1 + 1;
-- --use elsif here to keep writes exclusive
-- elsif WRITE_COUNTER2 < 12 then
-- ADDRAPROXY <= x"8C";
-- ROT_INCREASE <= x"02";
-- WRITE_COUNTER1 <= WRITE_COUNTER1 + 1;
-- elsif WRITE_COUNTER2 < 25 then
-- dina <= r_ROTO(to_integer(unsigned(ROT_INCREASE)));
-- ADDRAPROXY <= ROT_INCREASE;
-- WRITE_COUNTER1 <= WRITE_COUNTER1 + 1;
-- elsif WRITE_COUNTER2 < 30 then
-- ADDRAPROXY <= x"8C";
-- WRITE_COUNTER1 <= WRITE_COUNTER1 + 1;
-- else
-- WRITE_COUNTER1 <= WRITE_COUNTER1 + 1;
-- end if;
--
-- if WRITE_COUNTER1 > 1000000 then
-- WRITE_COUNTER2 <= WRITE_COUNTER2 + 1;
-- WRITE_COUNTER1 <= (others => '0');
-- end if;
-- if WRITE_COUNTER2 > 30 then
-- WRITE_COUNTER2 <= (others => '0');
-- end if;
-- if WRITE_COUNTER2 = 10 then
-- --write to ram
-- dina <= ROT1OUT;
-- ADDRAPROXY <= RAMADDR_1;
-- --RAMADDR_CURRENT <= RAMADDR_CURRENT + 1;
-- end if;
--
-- if WRITE_COUNTER1 = 500 then
--
-- WRITE_COUNTER2 <= WRITE_COUNTER2 + 1;
-- else
-- WRITE_COUNTER1 <= WRITE_COUNTER1 + 1;
-- end if;
-- ALWAYS --
ena <= '1'; -- ram enable high always
enb <= '1';
wea <= "1"; -- write port, never reads
web <= "0"; -- read port, never writes
addra <= ADDRAPROXY;
--addrb <= ADDRBPROXY;
TX(0) <= UARTOUT(0);
-- DEBUG --
--LEDWATCH <= READOUT(7 downto 0);
--check values are as expected
--LEDWATCH <= ROT1PREV(7 downto 0); --rot1prev WORKING
--LEDWATCH <= ROT1OUT(7 downto 0); --rot1out WORKING only 8
--LEDWATCH <= doutb(7 downto 0);
--LEDWATCH(7 downto 4) <= READOUT(3 downto 0);
-- LED(0) <= r_ROTO(1)(0); --green
-- LED(1) <= r_ROTO(2)(0); --red
-- LED(2) <= ADDRAPROXY(0);
-- LED(3) <= WEA(0);
-- LED(4) <= ROT_INCREASE(0); --green
-- LED(5) <= ROT_INCREASE(1); --red
-- LED(6) <= ROT_INCREASE(2);
-- LED(7) <= ROT_INCREASE(3);
--LED(0) <= r_ROTO(1)(0); --green
--seems to be skipping on two sometimes
--LED <= r_ROTO(2)(7 downto 0);
LED <= r_ROTO(3)(7 downto 0);
--case state is
-- idle state --
-- when state_start =>
-- ROT1PREV starts as zero, state_RAM as soon as ROT1OUT change
---- if ROT1PREV /= ROT1OUT and BUSY = '0' and UARTOFF = '0' then
-- state <= state_RAM;
-- else
-- state <= state_start;
-- end if;
------------- RAM WRITE & READ ------------------
-- if rot1out has a change?
-- put it in its own process instead of clk?
-- if ROT1PREV /= ROT1OUT and BUSY = '0' then --put in fms
--
-- BUSY <= '1';
--
-- dina <= ROT1OUT;
-- addra <= RAMADDR_1;
--
-- RAMADDR_CURRENT <= RAMADDR_1;
-- addrb <= RAMADDR_1;
--
-- ROT1PREV <= doutb;
-- --in here
-- -- then write new data output to uart
-- -- put loops in to keep it to appropriate baud
-- -- use a for loop (of say 1000 times)
-- -- and a counter that counts up, then down
-- -- as delay with an outer for loop
-- -- that shifts bits out once every loop (nested loops)
--
--
-- BUSY <= '0';
-- end if;
-- RAM READ --
-- This will be separate process / module
-- that will read constantly from all memory addresses
-- and output the results to UART pin. need to time correctly.
-- if RAM_COUNTER = 1000000 then
--
---- addrb <= RAMADDR_CURRENT;
---- READOUT <= doutb;
--
-- -- "0000 0000 0000 0000 0000 0000 0000 00"
-- -- xxxxxx8 5 1sxx xxxx x8 5 1s
-- --
---- UARTOUT(0) <= '0';
---- UARTOUT(8 downto 1) <= READOUT(7 downto 0);
---- UARTOUT(15 downto 9) <= (others => '1'); --spacing to allow settling
---- UARTOUT(16) <= '0';
---- UARTOUT(24 downto 17) <= READOUT(15 downto 8);
---- UARTOUT(29 downto 25) <= (others => '1');
-- RAM_COUNTER <= (others => '0');
-- else
-- RAM_COUNTER <= RAM_COUNTER + 1;
--
-- end if;
-- BEWARE
-- When using jumper leads with uart
-- you can't go over like a few inches.
-- or signal fails. BEWARE
-- UART --
-- SHIFTING --
-- We always shift whatever is in the UART register out
-- When we are idling, we fill UART register with 1's (idle high
-- is normal). Every so often (further down), we read an address
-- and output the value into UART register IF uart is on.
-- Otherwise nothing happens.
-- I think timing is wrong on this. need a scope.
--if OUTPUTHIGH = '1' then --this broke things
--if UART_COUNTER = 3333 then --9600
--if UART_COUNTER = 278 then -- 115200 --maybe too fast. maybe not.
--if UART_OFFON = '0' then --this spaces out groups of bytes. not what I'm intending
--uart offon didn't work.
if UART_COUNTER = 555 then -- 57600 -- pretty good.
--shift bit over every 3332 cycles or 9600 bits per second
UARTOUT <= UARTOUT(0) & UARTOUT(199 downto 1);
UART_COUNTER <= (others => '0');
UART_COUNTER3 <= UART_COUNTER3 + 1;
--UART_COUNTER4 <= UART_COUNTER4 + 1; --with uartoffon
else
UART_COUNTER <= UART_COUNTER + 1;
end if;
--end if; --with uart offon
-- --shift one sequence, then stop
-- if UART_COUNTER4 = 1 then
-- UART_OFFON <= '1';
-- end if;
--
-- --idle high while uart_offon is high
-- if UART_OFFON = '1' then
-- UARTOUT <= (others => '1');
-- end if;
-- for 9600 baud, or 3333 cycles
--
--approach 1
-- if UART_COUNTER2 = 99990 then
-- addrb <= RAMADDR_CURRENT;
-- READOUT <= doutb;
-- UARTOUT(0) <= '0';
-- UARTOUT(8 downto 1) <= READOUT(7 downto 0);
-- UARTOUT(15 downto 9) <= (others => '1'); --spacing to allow settling
-- UARTOUT(16) <= '0';
-- UARTOUT(24 downto 17) <= READOUT(15 downto 8);
-- UARTOUT(29 downto 25) <= (others => '1');
-- UART_COUNTER2 <= (others => '0');
-- UART_COUNTER <= (others => '0');
-- else
-- UART_COUNTER2 <= UART_COUNTER2 + 1;
-- end if;
RAM READ --
-- APPROACH 1
-- read all memory addresses, at the speed of if uart counter3
-- equals 90.
-- for i in 0 to 99 loop
-- addrb <= i;
-- READOUT <= doutb;
-- end loop;
-- APPROACH 2
-- use counter
-- if uartcOunter3=90 then
-- addrb <= ADDRCOUNTER (starts at zero)
-- READOUT <= doutb
-- ADDRCOUNTER <= ADDRCOUNTER + 1;
-- if ADDRCOUNTER = 100 then
-- ADDRCOUNTER <= (others to 0)
-- end if
-- end if;
--approach 2 for ram read --read ram for every three txout
if UART_COUNTER3 = 199 then --after one sequence
--if UART_OFFON = '0' then --only do if on
addrb <= ADDR_RD_COUNTER;
READOUT <= doutb;
ADDR_RD_COUNTER <= ADDR_RD_COUNTER + 1;
-- send two bits. one id, one value
--UARTOUT(0) <= '0';
--UARTOUT(8 downto 1) <= READOUT(7 downto 0);
--UARTOUT(15 downto 9) <= (others => '1'); --spacing to allow settling
--UARTOUT(16) <= '0';
--UARTOUT(24 downto 17) <= READOUT(15 downto 8);
--UARTOUT(29 downto 25) <= (others => '1');
-- send RotaryID and Count
UARTOUT(3 downto 0) <= (others => '1'); --idle when high
UARTOUT(4) <= '0'; --start bit
UARTOUT(12 downto 5) <= READOUT(15 downto 8); -- ROTARYID first
UARTOUT(102 downto 13) <= (others => '1'); --idle when high
UARTOUT(103) <= '0'; --start bit
UARTOUT(111 downto 104) <= READOUT(7 downto 0); -- ROTARYCOUNT second
UARTOUT(199 downto 112) <= (others => '1'); --idle when high
UART_COUNTER3 <= (others => '0');
--UART_COUNTER4 <= UART_COUNTER4 + 1;
if ADDR_RD_COUNTER = 100 then
ADDR_RD_COUNTER <= (others => '0');
end if;
--end if; --end uartoffon
end if;
-- COUNTER <= COUNTER +1;
-- if (COUNTER= 100000) then
-- COUNTER <= (others => '0');
-- UART_OFFON <= '0';
-- UART_COUNTER4 <= (others => '0');
-- end if;
-- if (COUNTER(12) = '1') then
-- UART_COUNTER_SPACER <= UART_COUNTER_SPACER +1;
-- end if;
-- if (UART_COUNTER_SPACER = 4) then
-- UART_COUNTER_SPACER <= (others => '0');
-- end if;
end if; -- rising clk end
end process;
Rotary.vhd
--------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
-- todo: IO of rotary needs communication
-- need to store values in signals
-- need to output signals with distinct ID (generic passed
-- holds the ID number)
-- one byte output, with 1 bits for left/right (either 1 or 0)
-- 7 bits for rotary ID, one bit for stop (i.e. normal uart)
--the idea will be. each rotary has a unique ID, and a unique
--signal (ROTSIG1, ROTSIG2) in each instance
--which will output direction bit and id bits.
--there will be a uart to send out bits. Or preferably
--some way to read registers (see hamsterbook) beyond uart.
--there will be a busy flag, which all rotary instances share
--and when a byte is being sent, other rotary instances wait
--until it is cleared. after each sends a byte, it clears
--it. each rotary stores the direction / id in a signal and
--after outputting, it erases the direction. there will be
--another flag to determine if data needs to be sent or not.
--if dataoutputneededflag is 0, then no data is sent.
--signals are specified in top level module and mapped in
--instance. you can map the ports in the instance to signals.
-- Outputs ROTARYCOUNT (7 to 0)
-- and ROTARYID (15 to 8)
-- to DATAOUT
entity rotary is
GENERIC(
RAMADDR : STD_LOGIC_VECTOR(7 downto 0) := "11001100";
ROTARYID : STD_LOGIC_VECTOR(7 downto 0) := "01100110"
);
PORT ( ROT : in STD_LOGIC_VECTOR(1 downto 0);
LED : out STD_LOGIC_VECTOR(3 downto 0);
--LEDWATCH : out STD_LOGIC_VECTOR(7 downto 0);
clk : in STD_LOGIC;
DATAOUT : out STD_LOGIC_VECTOR(15 downto 0)
);
end rotary;
architecture Behavioral of rotary is
-- logic_vector takes double quotes for single num
-- std_logic takes single for single num
signal current : STD_LOGIC_VECTOR(1 downto 0);
signal previous : STD_LOGIC_VECTOR(1 downto 0);
signal target : STD_LOGIC_VECTOR(1 downto 0) := "00";
signal truetarget : STD_LOGIC_VECTOR(1 downto 0) := "00";
signal xortest : STD_LOGIC_VECTOR(1 downto 0);
signal counter : STD_LOGIC_VECTOR(29 downto 0) := (others => '0');
signal shiftreg : STD_LOGIC_VECTOR(9 downto 0) := "1010110100";
signal counter2 : STD_LOGIC_VECTOR(29 downto 0) := (others => '0');
signal ROTARYCOUNT : STD_LOGIC_VECTOR(6 downto 0) := "0000000";
signal counter3 : STD_LOGIC_VECTOR(31 downto 0) := (others => '0');
signal ROTARYMEMWRITE : STD_LOGIC_VECTOR(15 downto 0) := x"0101";
signal counter4 : STD_LOGIC_VECTOR(29 downto 0) := (others => '0');
signal counter5 : STD_LOGIC_VECTOR(7 downto 0) := (others => '0');
signal writeenable : STD_LOGIC_VECTOR(0 downto 0) := (others => '0');
signal memoutput : STD_LOGIC_VECTOR(7 downto 0) := (others => '0');
constant CCWPIN : STD_LOGIC_VECTOR(1 downto 0) := "10";
constant CWPIN : STD_LOGIC_VECTOR(1 downto 0) := "01";
constant PINS : STD_LOGIC_VECTOR(1 downto 0) := "11";
begin
-- hamster page 103 section 20.1 what is rs232
rotarywatch: process (clk)
begin
if rising_edge(clk) then
DATAOUT(6 downto 0) <= ROTARYCOUNT;
DATAOUT(7) <= '0';
DATAOUT(15 downto 8) <= ROTARYID;
--LED(1) <= douta(0); --red
--LED(0) <= wea(0); -- green
--ena <= '1';
--if counter(8) = '1' then
-- wea <= "1" ; -- this works. but lower down make sure it has
-- enough time. EDIT: seems its easier to leave it up
-- and pull it down for reads than vice versa.
-- THIS WORKS
-- note how i had to leave wea to write high occasionally
-- otherwise it will allow enough time to read
--end if;
-- use ledwatch to check addra (CHECKEDOU T)
-- then check dina (CHECKED OUT)
-- then check wea (NOT CHANGING FAST ENOUGH)
-- then check douta etc etc make sure it all lines up
-- douta is not going high, last thing I did last night.
--LEDWATCH(6 downto 0) <= douta(6 downto 0);
--LEDWATCH(6) <= ena;
--LEDWATCH(6) <= douta(0);
--LEDWATCH(7) <= wea(0);
-- how to write to mem?
-- make write enable high
-- specify address
-- output to data in?
-- need to read xapp463 which is the data sheet and
-- copy the pins
--LEDWATCH(7 downto 0) <= counter3(31 downto 24);
--LEDWATCH(7 downto 6) <= previous;
--LEDWATCH(5 downto 4) <= current;
--LEDWATCH(3 downto 2) <= xortest;
--LEDWATCH(1 downto 0) <= target;
--LEDWATCH <= rotarycount;
xortest <= current xor previous; --have it run constnatly
-- so we don't have to worry when debugging whether target
-- equals current
if counter(0) = '1' then
current(0) <= ROT(0);
current(1) <= ROT(1);
if current = target then
if current = truetarget then
--wea <= "1"; --this doesn't work. not enough time see above for better spot for wea
if xortest = CCWPIN then
--LED(2) <= '1';
ROTARYCOUNT <= ROTARYCOUNT-1;
--dina <= rotarycount;
end if;
if xortest = CWPIN then
--LED(3) <= '1'; -- leds are NOT reliable
-- for any fast signals
-- as these missed many
ROTARYCOUNT <= ROTARYCOUNT+1;
-- if write enabled
-- address?
--wea <= "1" ;
--dina <= rotarycount;
end if;
--LED(2) <= '1'; --debug (yellow ch1)
--wea <= "0"; enable this to only read when change
end if;
target <= PINS XOR target; --invert if equal to target (not truetarget)
end if;
previous <= current;
--wea <= "0"; enable this to read at all times
end if;
if counter = 64000000 then --runs every 0.5 or .4 second
--previous <= current; --debug only
counter <= (others => '0');
--target <= PINS XOR target; works
else
counter <= counter+1;
counter2 <= counter2+1;
end if;
if counter2 = 500000000 then
counter2 <= (others => '0');
end if;
end if;
end process;
end Behavioral;