FPGA与I2C总线器件接口电路设计
利用FPGA模拟I2C总线协议对I2C总线接口器件A T24C256 进行读写操作。利用按键输入读写命令和相应的地址、数据,对芯片进行读写操作,读写的数据用数码管显示。
一、I2C总线接口电路设计分析
1. I2C 总线协议
I2C 总线的两根通信线,一根是串行数据线SDA,另一根是串行时钟线SCL。多个符合I2C总线标准的器件都可以通过同一条I2C总线进行通信,而不需要额外的地址译码器。每个连接到总线上的器件都有一个唯一的地址作为识别的标志,都可以发送或接收数据。I2C 总线通信速率受主机控制,标准模式下可达100kbit/s。
一般具有I2C总线的器件其SDA、SCL引脚都为集电极(或漏极)开路结构。因此实际使用时,SDA 和SCL信号线必须加3~10K的上拉电阻。总线空闲时均保持高平。I2C总线接法如图1所示。
图1 I2C总线连接示意图
(1) I2C的主机和从机,发送器和接收器
产生I2C总线时钟信号和起始、停止控制信号的器件,称为主机,被主机寻址的器件称为从机。
任何将数据传送到I2C总线的器件称为发送器,任何从I2C总线接收数据的器件称为接收器。
主机和从机都可作为发送数据器件和接收数据器件。
(2) I2C 总线上数据的有效性:
时钟线SCL为高电平时,数据线SDA的任何电平变化将被看作总线的起始或停止信号;
在数据传送过程中,当时钟线SCL为高电平时,数据线SDA必须保持稳定状态,不允许有跳变;数据线SDA的状态只能在SCL低电平期间才能改变。即进行串行传送数据时,在SCL高电平期间传送位数据,低电平期间准备数据。
(3) 从机地址
I2C总线不需要额外的片选信号或地址译码。多个I2C总线接口器件可连接到一条I2C总线上,它们之间通过地址来区分。主机是主控制器件,只有一个主机的不需要地址。其它器件均为从机,均有器件地址,但必须保证同一条I2C总线上的器件地址不能重复。一般从机地址由7位地址位和1位读写位组成,地址位为高7位,读写位为最低位。读写位为0时,表示主机将向从机写入数据;读写位为1时,表示主机将要从从机读取数据。
(4) I2C 总线的通信时序
I 2
C 总线的通信时序如图2所示。
图2 I2C 总线的通信时序
① 首先主机发送一个起始信号。当时钟线SCL 处于高电平期间,数据线SDA 电平从高到低的跳变形成I 2C 总线的起始信号,启动I 2C 总线。
② 主机逐位发送7位(高位在前,低位在后)从机地址和1位读写控制信号,共8位。需8个时钟。
③ 与传送地址一致的从机发应答信号(ACK )。在第9个时钟周期时将SDA 线拉低表示其已收到一个8位数据。若在第9个时钟周期,SDA 为高电平时为非应答。
④ 开始传送数据,传送数据数量不限。每个字节(8位)后紧跟1个接收器件发出的应答位。若是主机读取从机数据时,从机发送数据,主机发应答位;若是主机写数据到从机时,主机发送数据,从机发应答位。
⑤ 数据传输结束时,主机发送1个停止信号,当时钟线SCL 为高电平时,数据线SDA 由低电平变为高电平时形成终止信号,停止I 2C 总线通信。
(5) 数据传输基本格式如表1。
表1 I2C 总线数据传输基本格式
其中S 、A7~A1、R/W 、P 总是由主机产生;写数据时,ACK 由从机产生,D7~D0由主机产生;读数据时,ACK 由主机产生,D7~D0由从机产生。 2. I2C 总线器件AT24C256
A T24C256 是一个256K 位的串行CMOS 型 E 2PROM , 可存储32768 个字节。该器件通过I 2C 总线接口进行操作,其引脚如图3所示,各引脚功能见表2。
图3 AT24C256引脚图 表
2 AT24C256引脚功能说明
SDA
S 起始 条件
停止 条件
暂停 控制
作为带有I C总线接口的器件,每个A T24C256都有一个7位的从机地址,其高5 位固定为“10100”,接下来的2 位由A T24C256的引脚A1 A0 硬连线输入决定(A1、A0直接接电源VCC或GND),同一I2C总线上最多可以连接4 个A T24C256器件。A T24C256除了有作为从机的地址,其内部还有作为存储单元的编码子地址,其子地址为双字节(16位),从0000H~7FFFH。本设计中只有1 个A T24C256,可将A T24C256的引脚A1、A0直接接地,其硬件电路如图4所示。则该A T24C256作为从机的7位地址为“1010000”。
图4 单个AT24C256连接电路图
3. 对AT24C256的读写过程
(1) 向A T24C256某一存储单元写入1个字节数据,过程如下:
①主机(这里为FPGA控制器)发送一个起始信号,启动发送过程;
②主机发送7 位从机地址(这里为1010000)和1位写控制位(为0);
③从机(这里为A T24C256)发应答位。在主机发送起始信号和从机地址字节后,A T24C256 监视总线并当其地址与发送的从地址相符时,响应一个应答信号。在第9个时钟,将SDA线拉为低电平;
④主机接收到应答位后,发从机子地址高8位(为A T24C256某一存储单元地址)。
⑤从机接收完高8位子地址后,发应答位;
⑥主机接收到应答位后,发从机子地址低8位;
⑦从机接收完低8位子地址后,发应答位;
⑧主机接收到应答位后,发送待8位写入数据;
⑨从机接收完8数据后,发应答位,并开始内部数据的擦写;
⑩主机接收到应答位后,发停止位,结束传送,总线挂起。
SDA上数据传输格式见表3,数据传送时序如图5所示。
表3 向AT24C256写1个数据时总线SDA上数据传输格式
图5 向AT24C256写一个数据时序
(2) 从A T24C256某一存储单元读出1个字节数据,过程如下:
①主机发送一个起始信号,启动发送过程,接着发送7 位从机地址(1010000)和1位写控制位(0);;
②从机检测到起始信号及本身从地址相符时的从机地址后,发应答位。
③主机接收到应答位后,发从机子地址高8位(为A T24C256某一存储单元地址)。
④从机接收完高8位子地址后,发应答位;
⑤主机接收到应答位后,发从机子地址低8位;
⑥从机接收完低8位子地址后,发应答位;
⑦主机接收到应答位后,再发送一个起始信号(称为重复起始信号),接着再发送7 位从机地址(1010000)和1位读控制位(为1);
⑧从机检测到重复起始信号及从机地址后,发应答位,并将子地址对应的存储单元数据发送到总线上。
⑨主机接收到应答位后,接着准备从总线接收数据,从总线接收完8数据后。发非应答位和发停止位,结束传送,总线挂起。
SDA上数据传输格式见表4所示,数据传送时序如图6所示。
表4 从AT24C256上读1个数据时总线SDA上数据传输格式
图6 从AT24C256读一个数据时序
4. FPGA内部电路
模拟I 2
C 总线对A T24C256的读写控制电路基本结构框图如图7所示。
图7 模拟I2C 总线对AT24C256的读写控制框图
(1) I 2
C 总线端口
I 2C 总线端口为三态输出,当使能端有效时,总线输出为低电平;当使能端无效时三态门输出为高
阻,但由于I 2C 总线上有上拉电阻,总线保持在高电平或由总线上从机输出数据决定。总线数据始终能被读入。其结构示意图如图8所示。
图8 I2C 总线端口示意图
(2) 位传输控制模块
位传输模块以“位”为单位产生各种I 2C 协议命令(开始、停止和重复开始)以及进行位数据读写。为了读写到稳定的“位”数据,读写1位数据分为4到5个阶段完成。1位数据传输时序要求如图9所示。这样内部读写时钟频率一般采用5倍于SCL 时钟总线频率。
Sda
Sda Scl_
图9 I2C 协议命令和位数据传输的执行时序
位传输控制电路根据输入的控制命令,将来自字控制模块的一位待写入的数据送到总线上,或从总线上读入一位数据给字控制模块。当完成1位数据传输时产生读写完成标志,并根据数据传输情况产生忙标志和总线仲裁丢失标志。
(3) 字传输控制模块
字节传输模块以字节为单位控制I 2C 总线的数据传输。该模块根据输入控制命令,将存放在发送寄存器中的数据加载到一个移位寄存器,然后逐位发送到位传输模块,再控制位传输模块将数据发送到I 2C 总线上。或控制位传输模块从总线上逐位接收位数据,暂存到移位寄存器,再转换成字节数据送给数据输出。同时给出相关传输标志。
(4) A T24C256读写控制
根据输入控制信号和来自位传输模块的反馈标志,将控制信号加到命令寄存器,给字节传输模块提供控制信号;将输入数据或指定单元地址加载到数据传送寄存器;将从字节模块读取的数据回送到数据接收寄存器。
二、FPGA 硬件系统电路设计(略) 三、I 2
C 总线接口电路VHDL 设计
1. I2C 总线端口
(1) 名称:IIC_IO.vhd
(2) 功能:I 2C 总线双向端口电路描述
SCL SDA
SCL SDA SCL
SDA SCL
SDA
SCL SDA
开始
重复开始
停止
写
读
A B C
(4) VHDL描述
LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;
ENTITY IIC_IO IS
PORT(
Scl_en,sda_en: IN STD_LOGIC;
Sda,Scl: INOUT STD_LOGIC;
Scl_i,sda_i : OUT STD_LOGIC);
END IIC_IO;
ARCHITECTURE one OF IIC_IO IS
BEGIN
Sda_i<=sda;
Scl_i<=scl;
Scl<='0' WHEN scl_en='0' ELSE 'Z';
Sda<='0' WHEN sda_en='0' ELSE 'Z';
END one;
2. 位传输控制模块
(1)名称:bit_txd_rxd.vhd
(2)功能:实现位数据或协议命令的传输
(3)
(4) VHDL描述
LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;
USE IEEE.STD_LOGIC_Arith.ALL;
USE IEEE.STD_LOGIC_Unsigned.ALL;
ENTITY bit_txd_rxd IS
GENERIC (n:INTEGER:=48);--分频系数
PORT(
Clk_sys: IN STD_LOGIC;
Rst,ena: IN STD_LOGIC;
cmd: IN STD_LOGIC_VECTOR(3 DOWNTO 0);
Bit_data_wr: IN STD_LOGIC;--
Scl_i,sda_i : IN STD_LOGIC;
Scl_oen,sda_oen: OUT STD_LOGIC;
Busy,Lose: OUT STD_LOGIC;
Bit_data_rd,Bit_finish: OUT STD_LOGIC);
END bit_txd_rxd;
ARCHITECTURE two OF bit_txd_rxd IS
Type state_t IS (bit_idle,start_a,start_b,start_c,start_d,start_e,stop_a,stop_b,stop_c,stop_d, write_a, write_b, write_c, write_d,read_a, read_b, read_c, read_d);
SIGNAL sta_p: state_t;
CONSTANT n:INTEGER:=48; --产生500KHz的分频系数
SIGNAL en_500k: STD_LOGIC;--500KHz时钟使能信号
SIGNAL Scl_a,Sda_a, Scl_b,Sda_b:STD_LOGIC;--同步SCL和SDA中间信号
SIGNAL scl_edg: STD_LOGIC;--SCL的边沿信号
SIGNAL scl_oen_r, sda_oen_r: STD_LOGIC;--总线使能信号
SIGNAL sda_chk: STD_LOGIC;--写数据时,检查总线信号
SIGNAL dscl_oen,slave_wait: STD_LOGIC;--时钟延迟等待的信号
SIGNAL Sda_S,Sda_P: STD_LOGIC;--启动、停止标志位
SIGNAL Busy_r,Lose_r: STD_LOGIC;--忙标志、丢失标志信号
SIGNAL stop_cmd,stop_cmd_r: STD_LOGIC;--停止命令信号
BEGIN
PROCESS (clk_sys,rst) --同步SCL和SDA的输入信号
BEGIN
IF rst='0' THEN
Scl_a<='1';
Sda_a<='1';
Scl_b<='1';
Sda_b<='1';
ELSIF RISING_EDGE(clk_sys) THEN --暂存SCL、SDA的值
Scl_a<= Scl_i;
Sda_a<= Sda_i;
Scl_b<= Scl_a;
Sda_b<= Sda_a;
END IF;
END PROCESS;
Scl_edg<=scl_a AND( NOT Scl_b);--检测时钟SCL上升沿
PROCESS (clk_sys) --产生数据输出信号,在SCL上升沿时锁存SDA上的数据值
BEGIN
IF RISING_EDGE(clk_sys) THEN
IF scl_edg='1' THEN
Bit_data_rd<=Sda_a;
END IF;
END IF;
END PROCESS;
--从节点未准备好时,下拉SCL延迟周期;当给出的SCL使能为1时,检测SCL总线为0时,则节点未准备就绪,产生等待信号。
PROCESS (clk_sys)
BEGIN
IF RISING_EDGE(clk_sys) THEN
dscl_oen<=scl_oen_r;
END IF;
END PROCESS;
Slave_wait<=dscl_oen AND (NOT scl_a);
PROCESS (clk_sys,rst) --将24M系统时钟分频产生500KHz时钟使能控制信号
V ARIABLE cnt: INTEGER RANGE 0 TO n-1;--时钟分频计数器
BEGIN
IF rst='0' THEN
cnt:=0;
en_500k<='1';
ELSIF RISING_EDGE(clk_sys) THEN
IF clk_cnt IF ena='1' THEN cnt:= cnt+1; en_500k<='0'; END IF; ELSE IF Slave_wait='0' THEN--从节点准备好,给出时钟使能 cnt:=0; en_500k<='1'; ELSE --从节点未准备好,延迟等待 cnt:= cnt; en_500k<='0'; END IF; END IF; END IF; END PROCESS; --生成启动标志和停止标志 --在SCL高电平时,检测SDA的下降沿(起始信号),产生启动标志 --在SCL高电平时,检测SDA的上升沿(停止信号),产生停止标志 PROCESS (clk_sys,rst) BEGIN IF rst='0' THEN Sda_S<='0';--启动标志复位 Sda_P<='0';-- 停止标志复位 ELSIF RISING_EDGE(clk_sys) THEN Sda_S<=(NOT Sda_a ) AND Sda_b AND Scl_a ;--生成启动标志 Sda_P<=Sda_a AND ( NOT Sda_b ) AND Scl_a; --生成停止标志 END IF; END PROCESS; --生成总线忙标志 --检测到启动信号发生,但无停止信号发生时表示总线处于忙状态 PROCESS (clk_sys,rst) BEGIN IF rst='0' THEN Busy_r<='0'; ELSIF RISING_EDGE(clk_sys) THEN Busy_r<=(Sda_S OR busy_r) AND (NOT Sda_P); END IF; END PROCESS; Busy<=busy_r;--忙标志输出 --产生仲裁丢失标志, --当没有停止请求时,检测到停止信号,产生仲裁丢失标志 ---当驱动SDA总线为高时,但检测SDA一直为低,产生仲裁丢失标志 PROCESS (clk_sys,rst) BEGIN IF rst='0' THEN stop_cmd<='0';--停止命令信号 stop_cmd_r <='0'; Lose_r<='0'; ELSIF RISING_EDGE(clk_sys) THEN IF cmd<= "0010" THEN --有停止命令 stop_Cmd<='1'; ELSE stop_Cmd<='0'; END IF; stop_Cmd_r <= stop_Cmd; Lose_r<=(Sda_P AND (NOT stop_Cmd_r )) OR(NOT sda_a AND sda_chk AND sda_oen_r);--丢失标志 END IF; END PROCESS; Lose<=Lose_r; --位传输状态机 Scl_oen<=scl_oen_r; Sda_oen<=Sda_oen_r; PROCESS(clk_sys,rst) BEGIN IF rst='0' THEN Sta_p<=bit_idle;--初始准备状态 bit_finish<='0'; --1位信号发送或接收完成标志 Scl_oen_r<='1';--时钟输出使能 Sda_oen_r <='1';--数据输出使能 Sda_chk<='0';--不检查输出 ELSIF RISING_EDGE(clk_sys) THEN IF Lose_r='1' THEN--数据传输信号丢失 Sta_p<=bit_idle; bit_finish <='0'; Scl_oen_r <='1'; Sda_oen_r <='1'; Sda_chk<='0'; ELSE bit_finish <='0'; IF clk_en='1' THEN CASE sta_p IS WHEN bit_idle=> --准备状态 Scl_oen_r <=scl_oen_r;--保持SCL在同一状态 Sda_oen_r <=sda_oen_r; --保持SDA在同一状态 Sda_chk<='0'; CASE cmd IS --状态命令字 WHEN "0001"=> sta_p<=start_a;--发送起始信号状态 WHEN "0010" => sta_p<=stop_a;-- 发送停止信号状态 WHEN "0100" => sta_p<=write_a;--写入1位数据 WHEN "1000" => sta_p<=read_a; --读出1位数据 WHEN OTHERS=> sta_p<=bit_idle; END CASE; --启动I2C状态,分5个时钟段产生起始信号 WHEN start_a=> sta_p<=start_b; Scl_oen_r <=scl_oen_r; --保持SCL Sda_oen_r <='1'; --SDA处于高 Sda_chk<='0'; --不检查输出 WHEN start_b=> sta_p<=start_c; Scl_oen_r <='1'; Sda_oen_r <='1'; Sda_chk<='0'; WHEN start_c=> sta_p<=start_d; Scl_oen_r <='1'; Sda_oen_r <='0'; Sda_chk<='0'; WHEN start_d=> sta_p<=start_e; Scl_oen_r <='1'; Sda_oen_r <='0'; Sda_chk<='0'; WHEN start_e=> --开始状态5 sta_p<=bit_idle;--回到等待状态 bit_finish <='1';--起始信号传送完成,给出标志 Scl_oen_r <='0'; Sda_oen_r <='0'; Sda_chk<='0'; --停止I2C状态,分4个时钟段产生停止信号 WHEN stop_a=> sta_p<=stop_b; Scl_oen_r <='0'; Sda_oen_r <='0'; Sda_chk<='0'; WHEN stop_b=> sta_p<=stop_c; Scl_oen_r <='1'; Sda_oen_r <='0'; Sda_chk<='0'; WHEN stop_c=> sta_p<=stop_d; Scl_oen_r <='1'; Sda_oen_r <='0'; Sda_chk<='0'; WHEN stop_d=> sta_p<=bit_idle; bit_finish <='1';--停止信号传送完成,给出标志 Scl_oen_r <='1'; Sda_oen_r <='1'; Sda_chk<='0'; --读状态,分4个时钟段读1位信号 WHEN read_a=> sta_p<= read_b; Scl_oen_r <='0'; -- SCL处于低 Sda_oen_r <='1'; --SDA由从器件决定 Sda_chk<='0'; WHEN read_b=> sta_p<= read_c; Scl_oen_r <='1'; Sda_oen_r <='1'; Sda_chk<='0'; WHEN read_c=> sta_p<= read_d; Scl_oen_r <='1'; Sda_oen_r <='1'; Sda_chk<='0'; WHEN read_d=> sta_p<=bit_idle; bit_finish <='1';--读完1位数据,给出标志 Scl_oen_r <='0'; Sda_oen_r <='1'; Sda_chk<='0'; --写状态,分4个时钟段写1位信号 WHEN write_a=> sta_p<= write_b; Scl_oen_r <='0'; -- SCL处于低 Sda_oen_r <=bit_data_wr; --输入数据 Sda_chk<='0'; WHEN write_b=> sta_p<= write_c; Scl_oen_r <='1'; -- Sda_oen_r <=bit_data_wr; Sda_chk<='1';-- WHEN write_c=> --写状态3 sta_p<= write_d; Scl_oen_r <='1'; -- Sda_oen_r <= bit_data_wr; Sda_chk<='1'; WHEN write_d=> --写状态4 sta_p<=bit_idle; bit_finish <='1';--写完1位信号,给出标志 Scl_oen_r <='0'; Sda_oen_r <= bit_data_wr; Sda_chk<='0'; WHEN OTHERS=>null; END CASE; END IF; END IF; END IF; END PROCESS; END two; 3. 字传输控制模块 (1)名称:byte_txd_rxd.vhd (2)功能:实现字节数据的传输控制。 (3) (4) VHDL描述 LIBRARY IEEE; USE IEEE.STD_LOGIC_1164.ALL; USE IEEE.STD_LOGIC_Arith.ALL; USE IEEE.STD_LOGIC_Unsigned.ALL; ENTITY byte_txd_rxd IS PORT( clk_sys: IN STD_LOGIC; rst: IN STD_LOGIC; ack_in: IN STD_LOGIC; data_txd: IN STD_LOGIC_VECTOR(7 DOWNTO 0); start,stop,write,read: IN STD_LOGIC; lose: IN STD_LOGIC; bit_data_rd:IN STD_LOGIC;--从位控制模块读入的一位数据 bit_finish: IN STD_LOGIC;--1位传送完成标志 cmd: OUT STD_LOGIC_VECTOR(3 DOWNTO 0); bit_data_wr:OUT STD_LOGIC;--待写出的位数据 ack_finish,ack_out: OUT STD_LOGIC; data_rxd: OUT STD_LOGIC_VECTOR(7 DOWNTO 0)); END byte_txd_rxd; ARCHITECTURE three OF byte_txd_rxd IS Type state_byte IS (byte_idle,byte_start,byte_stop, byte_write,byte_read,ack_wr,ack_rd); SIGNAL sta_c: state_byte; SIGNAL ack_finish_r:STD_LOGIC;--完成1字节数据传输标志 SIGNAL en: STD_LOGIC;--使能信号 SIGNAL ld_en,shift_en:STD_LOGIC;--加载数据使能、移位数据使能 SIGNAL shift_reg: STD_LOGIC_VECTOR(7 DOWNTO 0);--数据移位寄存器SIGNAL cnt_rw: S TD_LOGIC_VECTOR(2 DOWNTO 0);--移位次数计数器SIGNAL cnt_done: STD_LOGIC;--完成1个字节数据移位完成标志 BEGIN ack_finish<=ack_finish_r; en<=(read OR write OR stop) AND (NOT Ack_finish_r);--操作使能 PROCESS (clk_sys,rst) --生成移位寄存器内容 BEGIN IF rst='0' THEN shift_reg<=(OTHERS=>'0'); ELSIF RISING_EDGE(clk_sys) THEN IF ld_en='1' THEN --写数据时 shift_reg<=data_txd;--待发送数据加载到移位寄存器 ELSIF shift_en='1' THEN--读数据时 shift_reg<=shift_reg(6 DOWNTO 0)& bit_data_rd;--移位读入数据 END IF; END IF; END PROCESS; PROCESS (clk_sys,rst) --进行读写位计数 BEGIN IF rst='0' THEN cnt_rw<="000"; ELSIF RISING_EDGE(clk_sys) THEN IF Ld_en='1' THEN --加载新数据时 cnt_rw<="111";--设置计数初值为8次 ELSIF shift_en='1' THEN --移位1次 cnt_rw<=cnt_rw-1;--减1计数 END IF; END IF; END PROCESS; cnt_done<='1' WHEN cnt_rw="000" ELSE '0';--读写完1个字节(计数8次),给出标志--字节传送状态控制 PROCESS (clk_sys,rst) BEGIN IF rst='0' THEN cmd<="0000";--I2C总线处于空闲状态命令 bit_data_wr <='0'; --待写出的位数据 ld_en<='0';--禁止加载数据 shift_en<='0';--禁止移位 Sta_c<=Byte_idle;--初始准备状态 sck_finish_r<='0';-- 应答完成标志置0 sck_out<='0';--应答输出置0 ELSIF RISING_EDGE(clk_sys) THEN IF Lose='1' THEN--数据传输信号丢失 cmd<="0000"; bit_data_wr <='0'; ld_en<='0'; shift_en<='0'; sta_c<=Byte_idle; ack_finish_r<='0'; ack_out<='0'; ELSE -- bit_data_wr<=shift_reg(7); --取移位寄存器的最高位作为待写出位 shift_en<='0';--禁止移位 ld_en<='0';-- 禁止加载 ack_finish_r<='0'; -- 应答完成标志置0 CASE sta_c IS WHEN Byte_idle=>--空闲状态 IF en='1' THEN--发生读/写/停止命令且无应答完成标志 ld_en<='1'; --重新加载写入数据初值,复位传送计数器 IF start='1' THEN sta_c<=Byte_start;--起始状态 cmd<= "0001";--启动位模块发送起始位 ELSIF read='1' THEN sta_c<=Byte_read;--读状态 cmd<= "1000"; --启动位模块读取1位数据ELSIF write='1' THEN sta_c<=Byte_write;--写状态 cmd<="0100" ; --启动位模块写入1位数据ELSE sta_c<=Byte_stop;-- --停止状态 cmd<= "0010"; --启动位模块发送停止位 ack_finish_r<='1';--产生传送完成标志 END IF; END IF; WHEN Byte_start=> IF bit_finish='1' THEN --起始位发送完成标志 ld_en<='1'; --重新加载初值 IF read='1' THEN sta_c<=Byte_read; cmd<="1000"; ELSE sta_c<=Byte_write; cmd<="0100"; END IF; END IF; WHEN Byte_write=> IF bit_finish ='1' THEN --写完1位标志 IF cnt_done='1' THEN--写完8位标志 sta_c<=ack_rd; --读应答状态 Cmd<="1000";-- 准备读应答位 ELSE sta_c<=Byte_write;--保持写状态 cmd<= "0100";--准备继续写下1位 shift_en<='1'; --移位,准备下1位数据 END IF; END IF; WHEN Byte_read=> IF bit_finish ='1' THEN --读完1位标志 shift_en<='1'; --移位,存储新读入的1位数据 IF cnt_done='1' THEN--读完8位标志 sta_c<=ack_wr; --写应答状态 cmd<= "0100";-- 准备写应答位 bit_data_wr<=ack_in;--给出待发送的应答信号ELSE sta_c<=Byte_read;--保持读状态 Cmd<= "1000";-- 准备继续读下1位 END IF; END IF; WHEN ack_rd=>--读应答状态 IF bit_finish ='1' THEN --应答位读完成标志 ack_out<=bit_data_rd;--输出应答信号 ack_finish_r<='1'; --给出应答完成标志 data_rxd<=shift_reg; IF stop='1' THEN sta_c<=Byte_stop; cmd<= "0010";--发停止位 ELSE sta_c<=Byte_idle; --空闲状态 cmd<="0000";--等待 END IF; END IF; WHEN ack_wr=>--写应答状态 IF bit_finish ='1' THEN --应答位写完成标志 bit_data_wr<='1';--待写入的位信号为高 ack_finish_r<='1'; --给出应答完成标志 IF stop='1' THEN sta_c<=Byte_stop; cmd<= "0010";--发停止位 ELSE sta_c<=Byte_idle; --空闲状态 cmd<="0000";--等待 END IF; ELSE bit_data_wr<=ack_in; END IF; WHEN Byte_stop=>--停止状态 IF bit_finish='1' THEN --停止位发送完成标志 sta_c<=Byte_idle; Cmd<="0000";--等待 END IF; END CASE; END IF; END IF; END PROCESS; END three; 4. AT24C256读写控制 (1) 名称:A T24C256_wr.vhd (2) 功能:通过I2C总线对A T24C256指定单元进行读写。 (4) VHDL描述 LIBRARY IEEE; USE IEEE.STD_LOGIC_1164.ALL; USE IEEE.STD_LOGIC_Arith.ALL; USE IEEE.STD_LOGIC_Unsigned.ALL; ENTITY A T24C256_rw IS PORT( clk_sys: IN STD_LOGIC;--24MHz系统时钟 rst,ena, clk_ena: IN STD_LOGIC;--复位,使能信号 rw: IN STD_LOGIC; slave_addr: STD_LOGIC_VECTOR(6 DOWNTO 0); sub_addr:STD_LOGIC_VECTOR(15 DOWNTO 0); data_in: IN STD_LOGIC_VECTOR(7 DOWNTO 0); ack_finish,ack_flag: IN STD_LOGIC; data_rxd: IN STD_LOGIC_VECTOR(7 DOWNTO 0); data_wr: OUT STD_LOGIC_VECTOR(7 DOWNTO 0); start,stop,write,read:OUT STD_LOGIC; data_out: OUT STD_LOGIC_VECTOR(7 DOWNTO 0); rtx_f: OUT STD_LOGIC); END A T24C256_rw; ARCHITECTURE four OF A T24C256_rw IS Type state_at IS (S0,S1,S2,S3,S4,S5,S6); SIGNAL sta_n: state_at; SIGNAL cmd_reg: STD_LOGIC_VECTOR(3 DOWNTO 0);--命令寄存器SIGNAL txd_reg: STD_LOGIC_VECTOR(7 DOWNTO 0);--数据发送寄存器SIGNAL rxd_reg: STD_LOGIC_VECTOR(7 DOWNTO 0);--数据接收寄存器BEGIN start<=cmd_reg(0); stop <=cmd_reg(1); write <=cmd_reg(2); read <=cmd_reg(3); data_wr <=txd_reg; data_out<=rxd_reg; PROCESS(rst,clk_sys) BEGIN IF rst='0' THEN cmd_reg<="0000";--命令寄存器清零 txd_reg<="00000000";--数据传输寄存器清零 Sta_n<=S0; ELSIF RISING_EDGE(clk_sys) THEN CASE sta_n IS WHEN S0=> IF ena='1' AND clk_ena='1' THEN Sta_n<=S1; Cmd_reg<="0101";--给出启动命令,准备写从机地址 txd_reg <=slave_addr&'0'; --给出从机地址,最低位为0 ELSE Sta_n<=S0; cmd_reg <="0000"; END IF; WHEN S1=> IF ack_finish='1' THEN--应答完成,1个字节传送完成 IF ack_Flag='1' THEN cmd_reg <="0010";--非应答,发停止命令 rtx_f<='0'; --传送失败 Sta_n<=S0; ELSE --应答 cmd_reg <="0100" ; --准备写从机子地址高8位 txd_reg<=sub_addr(15 DOWNTO 8) ; --给出从机子地址高8位 Sta_n<=S2; END IF; ELSE Sta_n<=S1; END IF; WHEN S2=> IF ack_finish='1' THEN IF ack_Flag='1' THEN cmd_reg <="0010";--非应答,发停止命令 rtx_f<='0'; --传送失败 Sta_n<=S0; ELSE --应答 cmd_reg <="0100" ; --准备写从机子地址低8位 txd_reg<=sub_addr(7 DOWNTO 0); --给出从机子地址低8位 Sta_n<=S3; END IF; ELSE Sta_n<=S2; END IF; WHEN S3=> IF ack_finish='1' THEN IF ack_Flag='1' THEN cmd_reg <="0010";--非应答,发停止命令 rtx_f<='0'; --传送失败 Sta_n<=S0; ELSE --应答 IF rw='0' THEN --向从机写1个数据 cmd_reg <="0100" ; --准备写数据 txd_reg<=data_in ; --给出待写数据值 Sta_n<=S4; ELSE --从从机读1个数据 cmd_reg <="0101";--给出重新启动命令,准备写从机地址 txd_reg <=slave_addr & '1'; --给出从机地址,最低位为1 Sta_n<=S5; END IF; END IF; ELSE Sta_n<=S3; END IF; WHEN S4=> IF ack_finish='1' THEN cmd_reg <="0010";--发停止命令,结束传送 rtx_f<='1'; --传送成功 Sta_n<=S0; ELSE Sta_n<=S4; END IF; WHEN S5=> IF ack_finish='1' THEN IF ack_Flag='1' THEN cmd_reg <="0010";--发停止命令,结束传送 rtx_f<='0'; --接收失败