📄 i2c_master.vhd
字号:
clk_cnt_rst <= '0';
scl_out <= '1';
-- check to see if a repeated start or a stop needs to be
-- generated. If so, only hold SCL high for half of the high time
if clk_cnt = HIGH_CNT_2 then
if stop_scl = '1' then
next_scl_state <= SCL_IDLE;
end if;
elsif clk_cnt = HIGH_CNT then
next_scl_state <= SCL_LOW_EDGE;
else
next_scl_state <= SCL_HIGH;
end if;
when others =>
next_scl_state <= SCL_IDLE;
end case;
end process;
scl_generator_regs: process (clock, reset)
begin
if reset = RESET_ACTIVE then
scl_state <= SCL_IDLE;
sda_out_reg <= '1';
stop_scl_reg <= '0';
elsif clock'event and clock='1' then
scl_state <= next_scl_state;
sda_out_reg <= sda_out;
stop_scl_reg <= stop_scl;
end if;
end process;
-- ************************ Clock Counter Implementation ************************
-- The following code implements the counter that divides the sys_clock for
-- creation of SCL. Control lines for this counter are set in SCL state machine
CLKCNT : UPCNT2
port map( data => cnt_zero,
cnt_en => clk_cnt_en,
load => clk_cnt_rst,
reset => reset,
clock => clock,
qout => clk_cnt );
-- ************************ SDA/SCL Sample Registers Process ************************
-- This process samples the incoming SDA and SCL with the system clock
sda_in <= '0' when sda = '0'
else '1';
scl_in <= '0' when scl = '0'
else '1';
start_regs: process(clock,reset)
begin
if reset = RESET_ACTIVE then
start_d1 <= '0';
elsif clock'event and clock = '1' then
start_d1 <= start;
end if;
end process;
-- ************************ START/STOP Detect Process ************************
-- This process detects the start and stop conditions.
-- by using SDA as a clock.
start_det: process(sda, reset, state)
begin
if reset = RESET_ACTIVE or state = HEADER then
detect_start <= '0';
elsif sda'event and sda = '0' then
if scl /= '0' then
detect_start <= '1';
else
detect_start <= '0';
end if;
end if;
end process;
stop_det: process(sda, reset, detect_start)
begin
if reset = RESET_ACTIVE or detect_start = '1' then
detect_stop <= '0';
elsif sda'event and sda /= '0' then
if scl /= '0' then
detect_stop <= '1';
else
detect_stop <= '0';
end if;
end if;
end process;
-- ************************ Generate START/STOP Process ************************
-- This process detects the rising and falling edges of START and sets signals to
-- control generation of start and stop conditions
gen_start_stop: process (clock,reset)
begin
if reset = RESET_ACTIVE then
gen_start <= '0';
gen_stop <= '0';
elsif clock'event and clock = '1' then
if start_d1 = '0' and start = '1' then
-- rising edge of START - generate start condition
gen_start <= '1';
elsif detect_start = '1' then
gen_start <= '0';
end if;
if start_d1 = '1' and start = '0' then
-- falling edge of START - generate stop condition
gen_stop <= '1';
elsif detect_stop = '1' then
gen_stop <= '0';
end if;
end if;
end process;
-- ************************ Main State Machine Process ************************
-- The following processes contain the main I2C state machine.
-- This state machine is clocked on the falling edge of SCL. DETECT_STOP must stay as
-- an asynchronous reset because once STOP has been generated, SCL clock stops.
state_machine_regs: process (scl, reset, detect_stop)
begin
if reset = RESET_ACTIVE or detect_stop = '1' then
state <= IDLE;
sm_stop <= '0';
elsif scl'event and scl = '0' then
state <= next_state;
sm_stop <= sm_stop_com;
end if;
end process;
eot <= eot_com;
state_machine_comb: process (state, detect_start, bit_cnt, sda_in, sda)
begin
-- list defaults
sm_stop_com <= '0';
i2c_err <= '0';
eot_com <= '0';
next_state <= state;
case state is
------------- IDLE STATE -------------
when IDLE =>
if detect_start = '1' then
next_state <= HEADER;
end if;
------------- HEADER STATE -------------
when HEADER =>
if bit_cnt = CNT_DONE then
next_state <= ACK_HEADER;
end if;
------------- ACK_HEADER STATE -------------
when ACK_HEADER =>
-- set EOT bit
eot_com <= '1';
if sda_in = '0' then
-- ack has been received, transmit data
next_state <= XMIT_DATA;
else
-- no ack received, stop and set error bit
next_state <= ERR;
sm_stop_com <= '1';
end if;
------------ XMIT_DATA State --------------
when XMIT_DATA =>
if bit_cnt = CNT_DONE then
-- Wait for acknowledge
next_state <= GET_ACK_DATA;
end if;
------------- GET_ACK_DATA State --------------
when GET_ACK_DATA =>
-- set EOT bit
eot_com <= '1';
if (sda_in = '0') then
next_state <= XMIT_DATA;
else
-- no ack received, generate a stop and go
-- to error state
sm_stop_com <= '1';
next_state <= ERR;
end if;
------------- ERR State --------------
when ERR =>
-- set the error flag and return to IDLE state
i2c_err <= '1';
next_state <= IDLE;
sm_stop_com <= '1';
end case;
end process;
master_sda <= shift_out when (state=HEADER or state=XMIT_DATA)
else '1';
-- ************************ I2C Data Shift Register ************************
I2CSHIFT_REG: SHIFT8
port map (
clock => scl_not,
reset => reset,
data_ld => shift_reg_ld,
data_in => i2cd,
shift_in => sda_in,
shift_en => shift_reg_en,
shift_out => shift_out,
data_out => shift_reg );
i2cdata_reg_ctrl: process(clock, reset)
begin
if reset = RESET_ACTIVE then
shift_reg_en <= '0';
shift_reg_ld <= '0';
elsif clock'event and clock = '1' then
if ((state = HEADER) or (state = XMIT_DATA)) then
shift_reg_en <= '1';
else
shift_reg_en <= '0';
end if;
if ((state = IDLE) or (state = GET_ACK_DATA) or (state = ACK_HEADER)) then
shift_reg_ld <= '1';
else
shift_reg_ld <= '0';
end if;
end if;
end process;
-- ************************ Bit Counter ************************
BITCNT : UPCNT4
port map( data => cnt_start,
cnt_en => bit_cnt_en,
load => bit_cnt_ld,
reset => reset,
clock => scl_not,
qout => bit_cnt );
-- Counter control lines
bit_cnt_en <= '1' when (state = HEADER) or (state = XMIT_DATA)
else '0';
bit_cnt_ld <= '1' when (state = IDLE) or (state = ACK_HEADER)
or (state = GET_ACK_DATA)
else '0';
end behave;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -