Continue to Site

Welcome to EDAboard.com

Welcome to our site! EDAboard.com is an international Electronics Discussion Forum focused on EDA software, circuits, schematics, books, theory, papers, asic, pld, 8051, DSP, Network, RF, Analog Design, PCB, Service Manuals... and a whole lot more! To participate you need to register. Registration is free. Click here to register now.

read cycle of a bit bang i2c, need some expert advice

Status
Not open for further replies.

JacquesKleynhans

Member level 2
Joined
Jul 3, 2008
Messages
51
Helped
0
Reputation
0
Reaction score
0
Trophy points
1,286
Activity points
1,630
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.

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
 

Status
Not open for further replies.

Part and Inventory Search

Welcome to EDABoard.com

Sponsor

Back
Top