JacquesKleynhans
Member level 2
Hi I wrote some code for i2c and it works great write up to the point where i need to read information.
The following code will start with start command >> address(read) >> command of register of interest >> another start command >> address(write) >> actuall reading of the register >> finally a stop command.
From the code above it can be seen, there are three processes(state machines) running every thing, The first is the main machine , second machine for the scl clock signal and the last is the data. But i think there is an issue with me reading from the inout sda_out pin of the fpga connected to the i2c device. I get ack write up to the point where I have to read the data but then I get only zeros ie the Z bus is pulled low and does not change. I also might be screwing up the the high impedance write from the fpga side so that the slave can control the bus and send data back to the fpga. I have tried this code on a logic analyzer, but yea as I said the sda_out stays low.
Can anyone please have a look to see what im doing wrong.
Kindest Regards
Jacques Kleynhans
The following code will start with start command >> address(read) >> command of register of interest >> another start command >> address(write) >> actuall reading of the register >> finally a stop command.
Code:
-- sccb.vhd
-- Initial testing worked with the pic writing a command and a address, verfied.
--Added the second start and an incremented address command
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
use ieee.NUMERIC_BIT.all;
use ieee.STD_LOGIC_ARITH.all;
entity sccb_write is
port( clk: in std_logic;
reset: in std_logic;
start_pulse: in std_logic;
scl: out std_logic;
sda_out: inout std_logic
);
end sccb_write;
architecture behavioral of sccb_write is
--TYPEDEF DECLARATION
type state_type is
( idle, start_state1, id_address_write,sub_address,start_state2,id_address_read,read, stop_state);
--SIGNAL DECLARATION
signal state_next: state_type;
signal scl_shift_reg: std_logic_vector(17 downto 0);
signal sda_shift_reg: std_logic_vector(8 downto 0) := "000000000";
signal state_counter: std_logic_vector(4 downto 0);
signal sccb_div : std_logic_vector (6 downto 0):= "0000000";
signal sccbclk : std_logic := '0';
signal sccbclk_inv : std_logic := '0';
signal sda : std_logic := '0';
signal z_pulse : std_logic:= '0';
--CONSTANT DECLARATION
constant SCL_start_pattern: std_logic_vector := "111111111111111111";
constant SCL_ida_sccbr_wd_pattern: std_logic_vector := "010101010101010101";
constant SCL_stop_pattern: std_logic_vector := "011111111111111111";
constant SDA_start_pattern: std_logic_vector:= "111111110";
constant SDA_stop_pattern: std_logic_vector := "011111111";
--PROGRAM STARTS HERE
begin
--PROCESS USED TO GENERATE A 100Khz CLOCK SIGNAL FOR THE SCCB SCL
--This clock signal is used on every rising edge thus resulting in clock split to 50khz
process(clk, reset)
begin
if reset = '1' then
sccbclk <= '0';
sccb_div <= "1100011";
elsif rising_edge(clk) then
if sccb_div = "0000000" then
sccbclk <= not(sccbclk);
sccb_div <= "1100011";
else
sccb_div <= sccb_div-1;
end if;
end if;
end process;
sccbclk_inv <= not(sccbclk);
--COUNTER USED TO COUNT FROM 0 TO 17
process(sccbclk, reset)
begin
if (reset = '1') then
state_counter <= (others => '0');
elsif rising_edge(sccbclk) then
state_counter <= state_counter + 1;
if state_counter = "10001" then
state_counter <= (others => '0');
end if;
end if;
end process;
--Process used to generate pulses to set high impedance
-- PROCESS USED TO GENEATE THE NO?DONT CARE BITS
process(sccbclk, reset)
begin
if reset='1' then
z_pulse <= '1';
elsif rising_edge(sccbclk) then
if state_next = id_address_write or state_next = sub_address or state_next = id_address_read then
if state_counter = "1111" or state_counter = "10000" then -- Don't Care bit
z_pulse <= '1';
else
z_pulse <= '0';
end if;
elsif state_next/=idle then -- state_next ‚ idle_main
z_pulse <= '0';
else
z_pulse <= '1';
end if;
end if;
end process;
--MAIN CASE LOOP
process(sccbclk,reset)
begin
if (reset = '1') then
state_next <= idle;
elsif rising_edge(sccbclk) then
case state_next is
when idle =>
if start_pulse = '1' then
state_next <= start_state1;
end if;
when start_state1 =>
if state_counter = "10001" then
state_next <= id_address_write;
end if;
when id_address_write =>
if state_counter = "10001" then
state_next <= sub_address;
end if;
when sub_address =>
if state_counter = "10001" then
state_next <= start_state2;
end if;
when start_state2 =>
if state_counter = "10001" then
state_next <= id_address_read;
end if;
when id_address_read =>
if state_counter = "10001" then
state_next <= read;
end if;
when read =>
if state_counter = "10001" then
state_next <= stop_state;
end if;
when stop_state =>
if state_counter = "10001" then
state_next <= idle;
end if;
end case;
end if;
end process;
--SCL CLOCK LOOP
process(sccbclk, reset)
begin
if (reset = '1') then
scl_shift_reg <= (others => '1');
elsif rising_edge(sccbclk) then
case state_next is
when idle =>
if start_pulse = '1' then
scl_shift_reg <= scl_start_pattern;
end if;
when start_state1 =>
if state_counter = "10001" then
scl_shift_reg <= SCL_ida_sccbr_wd_pattern;
else
scl_shift_reg <= scl_shift_reg(16 downto 0) & '1';
end if;
when id_address_write =>
if state_counter = "10001" then
scl_shift_reg <= SCL_ida_sccbr_wd_pattern;
else
scl_shift_reg <= scl_shift_reg(16 downto 0) & '1';
end if;
when sub_address =>
if state_counter = "10001" then
scl_shift_reg <= SCL_start_pattern;
else
scl_shift_reg <= scl_shift_reg(16 downto 0) & '1';
end if;
when start_state2 =>
if state_counter = "10001" then
scl_shift_reg <= SCL_ida_sccbr_wd_pattern;
else
scl_shift_reg <= scl_shift_reg(16 downto 0) & '1';
end if;
when id_address_read =>
if state_counter = "10001" then
scl_shift_reg <= SCL_ida_sccbr_wd_pattern;
else
scl_shift_reg <= scl_shift_reg(16 downto 0) & '1';
end if;
when read =>
if state_counter = "10001" then
scl_shift_reg <= SCL_stop_pattern;
else
scl_shift_reg <= scl_shift_reg(16 downto 0) & '1';
end if;
when stop_state =>
scl_shift_reg <= scl_shift_reg(16 downto 0) & '1';
end case;
end if;
end process;
--SDA DATA LOOP FOR A WRITE AND READ OPERATION
process(sccbclk, reset)
begin
if (reset = '1') then
sda_shift_reg <= (others => '1');
elsif rising_edge(sccbclk) then
case state_next is
when idle =>
if start_pulse = '1' then
sda_shift_reg <= sda_start_pattern;
end if;
when start_state1 =>
if state_counter(0) = '1' then
if state_counter = "10001" then
sda_shift_reg <= x"50" & '1'; --THE DEVICE SLAVE ADDRESS FOR A WRITE OPERATION x"9A"
else
sda_shift_reg <= sda_shift_reg(7 downto 0) & '1';
end if;
end if;
when id_address_write =>
if state_counter(0) = '1' then
if state_counter = "10001" then
sda_shift_reg <= x"4a" & '1' ; --COMMAND TO READ TeMP x"00"
else
sda_shift_reg <= sda_shift_reg(7 downto 0) & '1';
end if;
end if;
when sub_address =>
if state_counter(0) = '1' then
if state_counter = "10001" then
sda_shift_reg <= sda_start_pattern;
else
sda_shift_reg <= sda_shift_reg(7 downto 0) & '1';
end if;
end if;
when start_state2 =>
if state_counter(0) = '1' then
if state_counter = "10001" then
sda_shift_reg <= x"51" & '1'; --THE DEVICE SLAVE ADDRESS FOR A READ OPERATION x"9B"
else
sda_shift_reg <= sda_shift_reg(7 downto 0) & '1';
end if;
end if;
when id_address_read =>
if state_counter(0) = '1' then
if state_counter = "10001" then
sda_shift_reg <= "11111111" & '1'; --test if i can clock out values
else
sda_shift_reg <= sda_shift_reg(7 downto 0) & '1';
end if;
end if;
when read =>
if state_counter(0) = '1' then
if state_counter = "10001" then
sda_shift_reg <= SDA_stop_pattern;
else
sda_shift_reg <= sda_shift_reg(7 downto 0) & 'Z';
end if;
end if;
when stop_state =>
if state_counter(0) = '1' then
sda_shift_reg <= sda_shift_reg(7 downto 0) & '1';
end if;
end case;
end if;
end process;
sda <= sda_shift_reg(8) when (z_pulse = '0') else 'Z';
sda_out <= '0' when reset = '1' else sda when rising_edge(sccbclk_inv);
scl <= scl_shift_reg(17);
end behavioral;
From the code above it can be seen, there are three processes(state machines) running every thing, The first is the main machine , second machine for the scl clock signal and the last is the data. But i think there is an issue with me reading from the inout sda_out pin of the fpga connected to the i2c device. I get ack write up to the point where I have to read the data but then I get only zeros ie the Z bus is pulled low and does not change. I also might be screwing up the the high impedance write from the fpga side so that the slave can control the bus and send data back to the fpga. I have tried this code on a logic analyzer, but yea as I said the sda_out stays low.
Can anyone please have a look to see what im doing wrong.
Kindest Regards
Jacques Kleynhans