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;