Difference between revisions of "FPGA - Rotary Encoder"
From Steak Wiki
Jump to navigationJump to search (Created page with "Reap 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...") |
|||
| Line 2: | Line 2: | ||
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). | 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== | ||
| + | <pre> | ||
| + | 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; | ||
| + | </pre> | ||
| + | |||
| + | ==Rotary.vhd== | ||
| + | <pre> | ||
| + | -------------------------------------- | ||
| + | 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; | ||
| + | |||
| + | |||
| + | |||
| + | |||
| + | |||
| + | |||
| + | |||
| + | </pre> | ||
| + | |||
| + | |||
| + | |||
| + | |||
| + | ==Resources== | ||
| + | * [http://papilio.cc/] While the Papilio is obsolete now, and you should probably start with a newer FPGA, it's still now a bad place to start. | ||
| + | * [http://hamsterworks.co.nz] | ||
Revision as of 05:20, 18 October 2019
Reap 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;