zoukankan      html  css  js  c++  java
  • IIC

    欲观原文,请君移步

    IIC 简介

    IC(Inter-Integrated Circuit)总线是一种由PHILIPS公司开发的两线式串行总线,用于连接微控制器及其外围设备。I2C总线产生于在80年代,最初为音频和视频设备开发,如今主要在服务器管理中使用,其中包括单个组件状态的通信。例如管理员可对各个组件进行查询,以管理系统的配置或掌握组件的功能状态,如电源和系统风扇。可随时监控内存、硬盘、网络、系统温度等多个参数,增加了系统的安全性,方便了管理。IIC数据传输速率有标准模式(100 kbps)、快速模式(400 kbps)和高速模式(3.4 Mbps),另外一些变种实现了低速模式(10 kbps)和快速+模式(1 Mbps)。

    下图是一个嵌入式系统中处理器仅通过2根线的IIC总线控制多个IIC外设的典型应用图

    特点

    1. 简单性和有效性

    由于接口直接在组件之上,因此 IIC 总线占用的空间非常小,减少了电路板的空间和芯片管脚的数量,降低了互联成本。总线的长度可高达 25 英尺,并且能够以 10Kbps 的最大传输速率支持 40 个组件。

    1. 多主控(multimastering)

    其中任何能够进行发送和接收的设备都可以成为主总线。一个主控能够控制信号的传输和时钟频率。当然,在任何时间点上只能有一个主控。

    IIC 通信协议

    IIC 串行总线一般有两根信号线,一根是双向的数据线 SDA ,另一根是时钟线 SCL ,其时钟信号是由主控器件产生。所有接到 IIC 总线设备上的串行数据 SDA 都接到总线的 SDA 上,各设备的时钟线 SCL 接到总线的 SCL 上。对于并联在一条总线上的每个 IIC 都有唯一的地址。

    IIC 总线在传输数据的过程中一共有三种类型信号,分别为:开始信号结束信号应答信号。这些信号中,起始信号是必需的,结束信号和应答信号,都可以不需要。同时还有空闲状态、数据的有效性、数据传输。

    1. 起始信号

    当时钟线SCL为高期间,数据线SDA由高到低的跳变。

    1. 停止信号

    当时钟线SCL为高期间,数据线SDA由低到高的跳变。

    1. 空闲状态

    当 IIC 总线的数据线 SDA 和时钟线 SCL 两条信号线同时处于高电平时,规定为总线的空闲状态。此时各个器件的输出级场效应管均处在截止状态,即释放总线,由两条信号线各自的上拉电阻把电平拉高。

    1. 应答信号

    发送器每发送一个字节( 8个bit ),就在时钟脉冲 9 期间释放数据线,由接收器反馈一个应答信号。

    • 应答信号为低电平时,规定为有效应答位( ACK ,简称应答位),表示接收器已经成功地接收了该字节;

    • 应答信号为高电平时,规定为非应答位( NACK ),一般表示接收器接收该字节没有成功。

    IIC 总线操作

    对 IIC 总线的操作实际就是主从设备之间的读写操作。大致可分为以下三种操作情况:

    主设备往从设备中写数据

    数据包括从机寄存器地址和需要写入寄存器的数据data

    主设备从从设备中读数据

    数据包括从机寄存器地址和需要从机读数据data

    重复读写数据

    主设备往从设备中写数据,然后重启起始条件,紧接着从从设备中读取数据;或者是主设备从从设备中读数据,然后重启起始条件,紧接着主设备往从设备中写数据。数据传输格式如下

    第三种操作在单个主设备系统中,重复的开启起始条件机制要比用STOP终止传输后又再次开启总线更有效率

    FPGA 程序实现

    物理层源码

    --------------------------------------------------------------------------------
    --
    --   FileName:         i2c_master.vhd
    --   Dependencies:     none
    --   Design Software:  Quartus II 64-bit Version 13.1 Build 162 SJ Full Version
    --
    --   HDL CODE IS PROVIDED "AS IS."  DIGI-KEY EXPRESSLY DISCLAIMS ANY
    --   WARRANTY OF ANY KIND, WHETHER EXPRESS OR IMPLIED, INCLUDING BUT NOT
    --   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
    --   PARTICULAR PURPOSE, OR NON-INFRINGEMENT. IN NO EVENT SHALL DIGI-KEY
    --   BE LIABLE FOR ANY INCIDENTAL, SPECIAL, INDIRECT OR CONSEQUENTIAL
    --   DAMAGES, LOST PROFITS OR LOST DATA, HARM TO YOUR EQUIPMENT, COST OF
    --   PROCUREMENT OF SUBSTITUTE GOODS, TECHNOLOGY OR SERVICES, ANY CLAIMS
    --   BY THIRD PARTIES (INCLUDING BUT NOT LIMITED TO ANY DEFENSE THEREOF),
    --   ANY CLAIMS FOR INDEMNITY OR CONTRIBUTION, OR OTHER SIMILAR COSTS.
    --
    --   Version History
    --   Version 1.0 11/1/2012 Scott Larson
    --     Initial Public Release
    --   Version 2.0 06/20/2014 Scott Larson
    --     Added ability to interface with different slaves in the same transaction
    --     Corrected ack_error bug where ack_error went 'Z' instead of '1' on error
    --     Corrected timing of when ack_error signal clears
    --   Version 2.1 10/21/2014 Scott Larson
    --     Replaced gated clock with clock enable
    --     Adjusted timing of SCL during start and stop conditions
    --   Version 2.2 12/24/2014 Steffen Mauch
    --     fixed bug during stop condition
    --
    --------------------------------------------------------------------------------
    
    LIBRARY ieee;
    USE ieee.std_logic_1164.all;
    USE ieee.std_logic_unsigned.all;
    
    ENTITY i2c_master IS
      GENERIC(
    	input_clk : INTEGER := 50_000_000; --input clock speed from user logic in Hz
    	bus_clk   : INTEGER := 400_000);   --speed the i2c bus (scl) will run at in Hz
      PORT(
    	clk       : IN     STD_LOGIC;                    --system clock
    	reset_n   : IN     STD_LOGIC;                    --active low reset
    	ena       : IN     STD_LOGIC;                    --latch in command
    	addr      : IN     STD_LOGIC_VECTOR(6 DOWNTO 0); --address of target slave
    	rw        : IN     STD_LOGIC;                    --'0' is write, '1' is read
    	data_wr   : IN     STD_LOGIC_VECTOR(7 DOWNTO 0); --data to write to slave
    	busy      : OUT    STD_LOGIC;                    --indicates transaction in progress
    	data_rd   : OUT    STD_LOGIC_VECTOR(7 DOWNTO 0); --data read from slave
    	ack_error : OUT    STD_LOGIC;                    --flag if improper acknowledge from slave
    	sda       : INOUT  STD_LOGIC;                    --serial data output of i2c bus
    	scl       : INOUT  STD_LOGIC);                   --serial clock output of i2c bus
    END i2c_master;
    
    ARCHITECTURE logic OF i2c_master IS
      CONSTANT divider  :  INTEGER := (input_clk/bus_clk)/4; --number of clocks in 1/4 cycle of scl
      TYPE machine IS(ready, start, command, slv_ack1, wr, rd, slv_ack2, mstr_ack, stop); --needed states
      SIGNAL state         : machine;                        --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_ena       : STD_LOGIC := '0';               --enables internal scl to output
      SIGNAL sda_int       : STD_LOGIC := '1';               --internal sda
      SIGNAL sda_ena_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
    
      signal ack_error_int : std_logic;
    BEGIN
    
    ack_error <= ack_error_int;
    
      --generate the timing for the bus clock (scl_clk) and the data clock (data_clk)
      PROCESS(clk, reset_n)
    	VARIABLE count  :  INTEGER RANGE 0 TO divider*4;  --timing for clock generation
      BEGIN
    	IF(reset_n = '0') THEN                --reset asserted
    	  stretch <= '0';
    	  count := 0;
    	ELSIF(clk'EVENT AND clk = '1') THEN
    	  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(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 PROCESS;
    
      --state machine and writing to sda during scl low (data_clk rising edge)
      PROCESS(clk, reset_n)
      BEGIN
    
    	IF(clk'EVENT AND clk = '1') THEN
    
    	IF(reset_n = '0') THEN                 --reset asserted
    	  state <= ready;                      --return to initial state
    	  busy <= '1';                         --indicate not available
    	  scl_ena <= '0';                      --sets scl high impedance
    	  sda_int <= '1';                      --sets sda high impedance
    	  ack_error_int <= '0';                    --clear acknowledge error flag
    	  bit_cnt <= 7;                        --restarts data bit counter
    	  data_rd <= "00000000";               --clear data read port
    
    	  elsIF(data_clk = '1' AND data_clk_prev = '0') THEN  --data clock rising edge
    		CASE state IS
    		  WHEN ready =>                      --idle state
    			 scl_ena <= '0';
    			IF(ena = '1') THEN               --transaction requested
    			  busy <= '1';                   --flag busy
    			  addr_rw <= addr & rw;          --collect requested slave address and command
    			  data_tx <= data_wr;            --collect requested data to write
    			  state <= start;                --go to start bit
    			ELSE                             --remain idle
    			  busy <= '0';                   --unflag busy
    			  state <= ready;                --remain idle
    			END IF;
    		  WHEN start =>                      --start bit of transaction
    			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 =>                    --address and command byte of transaction
    			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
    			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
    			busy <= '1';                     --resume busy if continuous mode
    			IF(bit_cnt = 0) THEN             --read byte receive finished
    			  IF(ena = '1' AND addr_rw = addr & 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
    			  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(ena = '1') THEN               --continue transaction
    			  busy <= '0';                   --continue is accepted
    			  addr_rw <= addr & rw;          --collect requested slave address and command
    			  data_tx <= data_wr;            --collect requested data to write
    			  IF(addr_rw = addr & rw) THEN   --continue transaction with another write
    				sda_int <= 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(ena = '1') THEN               --continue transaction
    			  busy <= '0';                   --continue is accepted and data received is available on bus
    			  addr_rw <= addr & rw;          --collect requested slave address and command
    			  data_tx <= data_wr;            --collect requested data to write
    			  IF(addr_rw = addr & 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
    			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_ena = '0') THEN                  --starting new transaction
    			  scl_ena <= '1';                       --enable scl output
    			  ack_error_int <= '0';                     --reset acknowledge error output
    			END IF;
    		  WHEN slv_ack1 =>                          --receiving slave acknowledge (command)
    			IF(sda /= '0' OR ack_error_int = '1') THEN  --no-acknowledge or previous no-acknowledge
    			  ack_error_int <= '1';                     --set error output if no-acknowledge
    			END IF;
    		  WHEN rd =>                                --receiving slave data
    			data_rx(bit_cnt) <= sda;                --receive current slave data bit
    		  WHEN slv_ack2 =>                          --receiving slave acknowledge (write)
    			IF(sda /= '0' OR ack_error_int = '1') THEN  --no-acknowledge or previous no-acknowledge
    			  ack_error_int <= '1';                     --set error output if no-acknowledge
    			END IF;
    		  WHEN stop =>
    			scl_ena <= '0';                         --disable scl
    		  WHEN OTHERS =>
    			NULL;
    		END CASE;
    	  END IF;
    	END IF;
      END PROCESS;
    
      --set sda output
      WITH state SELECT
    	sda_ena_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
      scl <= '0' WHEN (scl_ena = '1' AND scl_clk = '0') ELSE '1';
      sda <= '0' WHEN sda_ena_n = '0' ELSE 'Z';
    
    END logic;
    

    结果如下图所示

    参考链接

    1. IIC 通讯总结
    https://blog.csdn.net/zuo_an/article/details/89139445?ops_request_misc=&request_id=&biz_id=102&utm_term=IIC&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduweb~default-5-89139445
    
    1. I2C 总线协议图解
    https://www.cnblogs.com/aaronLinux/p/6218660.html
    
    1. IIC总线的原理与Verilog实现
    https://www.cnblogs.com/liujinggang/p/9656358.html
    

    工程源码获取

    获取资料方法一:集赞

    关注小编公众号后,将本文转发至朋友圈,集齐6个赞,截图发送到后台,小编会在24小时之内回复。备注:【领取IIC源码】即可领取资料

    获取资料方法二:转发群

    关注小编公众号后,将本文转发至不低于100人的群(FPGA相关行业),截图发送到后台,小编会在24小时之内回复。备注:【领取IIC源码】即可领取资料

  • 相关阅读:
    Base64
    HBase搭建
    解决Zookeeper无法启动的问题
    docker基础知识之挂载本地目录
    VMware升级到15版本虚拟机黑屏的解决方法
    docker端口映射或启动容器时报错Error response from daemon: driver failed programming external connectivity on endpoint
    MQTT 入门介绍
    OpeTSDB的Configuration配置
    js中ES6的Set的基本用法
    Review-JVM
  • 原文地址:https://www.cnblogs.com/xiguazai/p/13364330.html
Copyright © 2011-2022 走看看