现在的位置: 首页 > FPGA开发 > 正文

IIC总线—-iic master使用VHDL的FPGA实现

2018年03月10日 FPGA开发 ⁄ 共 7004字 ⁄ 字号 IIC总线—-iic master使用VHDL的FPGA实现已关闭评论 ⁄ 阅读 8,818 次

代码参考网上大神程序,加了一些修改 贴出代码:

写了个testbench测试了一下这个模块,仿真时许如下:

iic-tb

iic-tb

iic_tb

iic_tb


library ieee;
use ieee.std_logic_1164.all;

entity iic_master is
generic
(
clock_frequency : positive := 100000000;
baud : positive := 100000
);
port
(
i_clk : in std_logic;
i_rst_n : in std_logic;

i_addr : in std_logic_vector(6 downto 0); --目标地址
i_rw : in std_logic; --'0' 写, '1' 读
i_data_wr : in std_logic_vector(7 downto 0); --要写到slave的数据
o_data_rd : out std_logic_vector(7 downto 0); --从slave中读回的数据

i_trans_en : in std_logic; --启动传输
o_busy : out std_logic; --正在传输
b_ack_error : buffer std_logic; --从机的应答信号

io_sda : inout std_logic; --串行数据
io_scl : inout std_logic --串行时钟

);

end entity iic_master;

architecture behave of iic_master is

type machine is(ready, start, command, slv_ack1, wr, rd, slv_ack2, mstr_ack, stop); --状态

constant divider : integer := (clock_frequency/baud)/4; --1/4 cycle of scl 时钟个数

signal state : machine; --状态机

signal data_clk : std_logic; --data clock for sda
signal data_clk_prev : std_logic; --data clock during previous system clock
signal scl_clk : std_logic; --constantly running internal scl
signal scl_en : std_logic := '0'; --enables internal scl to output
signal sda_int : std_logic := '1'; --internal sda

signal sda_en_n : std_logic; --enables internal sda to output

signal addr_rw : std_logic_vector(7 downto 0); --latched in address and read/write
signal data_tx : std_logic_vector(7 downto 0); --latched in data to write to slave
signal data_rx : std_logic_vector(7 downto 0); --data received from slave

signal bit_cnt : integer range 0 to 7 := 7; --tracks bit number in transaction
signal stretch : std_logic := '0'; --identifies if slave is stretching scl

begin

--时钟产生 - 包括scl和数据更新时钟
process(i_clk, i_rst_n)
variable count : integer range 0 to divider*4; --timing for clock generation
begin
if rising_edge(i_clk) then
if i_rst_n = '0' then
count := 0;
stretch <= '0';
else
data_clk_prev <= data_clk; --store previous value of data clock

if(count = divider*4-1) then --end of timing cycle
count := 0; --reset timer
elsif(stretch = '0') then --clock stretching from slave not detected
count := count + 1; --continue clock generation timing
end if;

case count is
when 0 to divider-1 => --first 1/4 cycle of clocking
scl_clk <= '0';
data_clk <= '0';
when divider to divider*2-1 => --second 1/4 cycle of clocking
scl_clk <= '0';
data_clk <= '1';
when divider*2 to divider*3-1 => --third 1/4 cycle of clocking
scl_clk <= '1'; --release scl
if(io_scl = '0') then --detect if slave is stretching clock
stretch <= '1';
else
stretch <= '0';
end if;
data_clk <= '1';
when others => --last 1/4 cycle of clocking
scl_clk <= '1';
data_clk <= '0';
end case;
end if;
end if;
end process;

--状态机-将数据写到sda当io_scl为地时(data_clk 的上升沿)
process(i_clk, i_rst_n)
begin
if rising_edge(i_clk) then
if i_rst_n = '0' then
state <= ready; --return to initial state
o_busy <= '1'; --indicate not available
scl_en <= '0'; --sets scl high impedance
sda_int <= '1'; --sets sda high impedance
b_ack_error <= '0'; --clear acknowledge error flag
bit_cnt <= 7; --restarts data bit counter
o_data_rd <= "00000000"; --clear data read port
else
if(data_clk = '1' and data_clk_prev = '0') then --data clock rising edge
case state is
when ready =>
if i_trans_en='1' then
o_busy <= '1';
addr_rw <= i_addr & i_rw; --collect requested slave address and command
data_tx <= i_data_wr; --collect requested data to write
state <= start; --go to start bit
else
o_busy <= '0'; --unflag busy
state <= ready; --remain idle
end if;
when start => --start bit of transaction
o_busy <= '1'; --resume busy if continuous mode
sda_int <= addr_rw(bit_cnt); --set first address bit to bus
state <= command; --go to command
when command =>
if bit_cnt=0 then --command transmit finished
sda_int <= '1'; --release sda for slave acknowledge
bit_cnt <= 7; --reset bit counter for "byte" states
state <= slv_ack1; --go to slave acknowledge (command)
else --next clock cycle of command state
bit_cnt <= bit_cnt - 1; --keep track of transaction bits
sda_int <= addr_rw(bit_cnt-1); --write address/command bit to bus
state <= command; --continue with command
end if;
when slv_ack1 => --slave acknowledge bit (command)
if(addr_rw(0) = '0') then --write command
sda_int <= data_tx(bit_cnt); --write first bit of data
state <= wr; --go to write byte
else --read command
sda_int <= '1'; --release sda from incoming data
state <= rd; --go to read byte
end if;
when wr => --write byte of transaction
o_busy <= '1'; --resume busy if continuous mode
if(bit_cnt = 0) then --write byte transmit finished
sda_int <= '1'; --release sda for slave acknowledge
bit_cnt <= 7; --reset bit counter for "byte" states
state <= slv_ack2; --go to slave acknowledge (write)
else --next clock cycle of write state
bit_cnt <= bit_cnt - 1; --keep track of transaction bits
sda_int <= data_tx(bit_cnt-1); --write next bit to bus
state <= wr; --continue writing
end if;
when rd => --read byte of transaction
o_busy <= '1'; --resume busy if continuous mode
if(bit_cnt = 0) then --read byte receive finished
if(i_trans_en = '1' and addr_rw = i_addr & i_rw) then --continuing with another read at same address
sda_int <= '0'; --acknowledge the byte has been received
else --stopping or continuing with a write
sda_int <= '1'; --send a no-acknowledge (before stop or repeated start)
end if;
bit_cnt <= 7; --reset bit counter for "byte" states
o_data_rd <= data_rx; --output received data
state <= mstr_ack; --go to master acknowledge
else --next clock cycle of read state
bit_cnt <= bit_cnt - 1; --keep track of transaction bits
state <= rd; --continue reading
end if;
when slv_ack2 => --slave acknowledge bit (write)
if(i_trans_en = '1') then --continue transaction
o_busy <= '0'; --continue is accepted
addr_rw <= i_addr & i_rw; --collect requested slave address and command
data_tx <= i_data_wr; --collect requested data to write
if(addr_rw = i_addr & i_rw) then --continue transaction with another write
sda_int <= i_data_wr(bit_cnt); --write first bit of data
state <= wr; --go to write byte
else --continue transaction with a read or new slave
state <= start; --go to repeated start
end if;
else --complete transaction
state <= stop; --go to stop bit
end if;
when mstr_ack => --master acknowledge bit after a read
if(i_trans_en = '1') then --continue transaction
o_busy <= '0'; --continue is accepted and data received is available on bus
addr_rw <= i_addr & i_rw; --collect requested slave address and command
data_tx <= i_data_wr; --collect requested data to write
if(addr_rw = i_addr & i_rw) then --continue transaction with another read
sda_int <= '1'; --release sda from incoming data
state <= rd; --go to read byte
else --continue transaction with a write or new slave
state <= start; --repeated start
end if;
else --complete transaction
state <= stop; --go to stop bit
end if;
when stop => --stop bit of transaction
o_busy <= '0'; --unflag busy
state <= ready; --go to idle state
end case;
elsif(data_clk = '0' and data_clk_prev = '1') then --data clock falling edge
case state is
when start =>
if(scl_en = '0') then --starting new transaction
scl_en <= '1'; --enable scl output
b_ack_error <= '0'; --reset acknowledge error output
end if;
when slv_ack1 => --receiving slave acknowledge (command)
if(io_sda /= '0' or b_ack_error = '1') then --no-acknowledge or previous no-acknowledge
b_ack_error <= '1'; --set error output if no-acknowledge
end if;
when rd => --receiving slave data
data_rx(bit_cnt) <= io_sda; --receive current slave data bit
when slv_ack2 => --receiving slave acknowledge (write)
if(io_sda /= '0' or b_ack_error = '1') then --no-acknowledge or previous no-acknowledge
b_ack_error <= '1'; --set error output if no-acknowledge
end if;
when stop =>
scl_en <= '0'; --disable scl
when others =>
null;
end case;
end if;
end if;
end if;
end process;

--set sda output
with state select
sda_en_n <= data_clk_prev when start, --generate start condition
not data_clk_prev when stop, --generate stop condition
sda_int when others; --set to internal sda signal

--set scl and sda outputs
io_scl <= '0' when (scl_en = '1' and scl_clk = '0') else 'Z';
io_sda <= '0' when sda_en_n = '0' else 'Z';


end architecture behave;

×