---
title: FPGA的inout类型双向端口使用
url: "https://www.fpgalearn.com/9331.html"
kind: article
id: 9331
excerpt: 基于 ZYNQ MINI开发板 一、文档实现功能介绍 本文档实现对如何使用inout双向端口进行学习，通过inout端口，可以实现数据接收和发送，通过…
published_at: 2025-05-26
author: 二牛学FPGA
tags: ECO开发板, fpga入门, FPGA工程
word_count: 3062
source: "https://www.fpgalearn.com"
---

# FPGA的inout类型双向端口使用

> 标签：ECO开发板、fpga入门、FPGA工程
> 基于 ZYNQ MINI开发板 一、文档实现功能介绍 本文档实现对如何使用inout双向端口进行学习，通过inout端口，可以实现数据接收和发送，通过…

> 基于 ZYNQ MINI开发板 一、文档实现功能介绍 本文档实现对如何使用inout双向端口进行学习，通过inout端口，可以实现数据接收和发送，通过…

基于 ZYNQ MINI开发板
## 一、文档实现功能介绍
本文档实现对如何使用inout双向端口进行学习，通过inout端口，可以实现数据接收和发送，通过一根信号线的分时复用。本教程不对IIC时序单独讲解，大家可以看源代码分析学习，本教程仅学习INOUT双向端口。工程新建方法请参考文档《开发软件安装 和介绍/VIVADO简介及软件下新建ZYNQ工程教程》。
## 二、ZYNQ 工程建立
| 起始页（或file->Project->New） | 创建新工程(CreateNew Project) |
| --- | --- |
| 向导起始页面 | 点击 Next- > |
| ProjectName(工程名) | 工程名：fpga_05_pl_iic_eeprom工程路径： (自己选择，不要有中文路径)勾选Create ProjectSubdirectory,点击Next- > |
| AddSource(添加设计源文件) | 点击 Next- > |
| AddExsixtingIP(添加已有的 IP) | 点击 Next- > |
| AddConstraints(添加已有约束文件) | 点击 Next- > |
| Default Part(默认配置，芯片选型) | Family->Zynq-7000Package- >clg400Speed->7010选-1,7020选-27010版本选择目标器件:xc7z010clg400-17020版本选择目标器件:xc7z020clg400-2 点击 Next- > |
| NewProjectSummary(新建工程概况) | 确认工程信息，选型等，点击 Finish 完成 |
## 三、INOUT实现原理
当我们使用一个双向inout信号的时候，既要输入又要输出，怎么办呢？我们在verilog 学习的时候，有一种数值，叫做高阻态。一个比特的高阻态用1’bz表示。高阻态是一种可以   看做芯片内部断开的信号。我们知道，如果一个引脚为高阻态，那么，从外部驱动他，他的值  就取决于外部输入信号。所以，在input输入输出实现中，我们需要对外输出信号，则直接对   这个inout信号赋值，当需要切换为接收，则赋值为高阻态，然后把这个信号引脚的值读过来。 下面展示一个最简单的双向input实现：
reg inout_indicate_reg;//指示当前是输入还是输出
inout bi_derection_singnal;//双向输入输出信号
wire read_value;//从双向输入输出信号读的信号线
wire send_value;//需要发送，放到双向输入输出信号的信号线

//下面这行，当指示信号为1的时候，双向信号连接到发送数据信号线
assign bi_derection_singnal=(inout_indicate_reg==1’b1)?send_value:1’bz;

//下面这行，当指示信号为0的时候，双向信号连接到接收数据信号线
assign read_value=(inout_indicate_reg==1’b0)?sbi_derection_singnal:1’bz;
以上代码仅供学习演示，没有实际运行，仅展示在什么情况下作为输入和输出的赋值  切换。上面可以看到，当作为输入，也就是指示信号判定为0，则赋值为高阻，这样外部信号 就不会受到影响，就可以输进来被内部接收信号使用，当判定信号为1， 则输入输出信号被内 部的发送信号驱动。
## 四、IIC的INOUT端口实例
fpga_05_pl_iic_eeprom的例程提供了一个完整的IIC收发的实例，其中IIC的SDA就是 一个INOUT双向端口。对于IIC通信协议我们不多讲解，大家可以查看24C02数据手册来对照 代码状态机学习。代码整个流程是：通过一个3秒周期计数，来触发状态机的读写。
关于定时触发的代码如下：
```
//3秒周期中，第计数值到达1秒触发一次写操作
assign write_start_flag_wire=(auto_read_write_cnt_reg==CNT_1SEC)?1'b1:1'b0;
//计数值到达3秒之后，触发一次读
assign read_start_flag_wire=(auto_read_write_cnt_reg==CNT_3SEC)?1'b1:1'b0;
always@(posedge clk or negedge rst_n)begin
    if(rst_n=='b0)begin
        auto_read_write_cnt_reg <='d0;
    end
    else begin
        if(auto_read_write_cnt_reg<CNT_3SEC)begin
            auto_read_write_cnt_reg <=auto_read_write_cnt_reg+'d1;
    end
    else begin
        auto_read_write_cnt_reg <='b0;
    end
  end
end
```
代码第64行，表示当SDA使能为1， 也就是需要往外发送，则赋值SDAR寄存器，如果不为1， 则需要设置为高阻，以便外面的信号驱动他，然后被代码读进来外部的值判断。
assign SDA = SDAEN ? SDAR : 1’bz;

细心地朋友阅读了整个代码，可能会发现没有地方使用到了读取SDA值的代码。原则  上IIC的通信中，SDA外部在操作结束会有应答信号需要判断，但是我们FPGA可以不做判断， 也可以做，这里仅提供简单的驱动进行演示，仅限学习双向端口INOUT。
关于整个代码工程大家自行查阅对应例程即可。
## 五、在线调试验证
我们将例程生成bit文件下载到开发版进行在线调试。我们提供的例程请直接使用，因为我们设置了在线调试，大家不要从本教程拷贝代码自己新建工程，因为没有设置在线调试， 不支持抓波形。 关于在线调试的详细教程后面有专门的文档章节来学习，大家在这里请直接使 用例程。例程中，我们使用的管脚绑定如下,我们代码中在状态机转换过程还是用了LED指示当前状态。
![图片](https://admin.shaonianxue.cn/wp-content/uploads/2025/05/image-89.png)
生成bit文件之后我们打开硬件管理器，然后连接开发板 ，点击芯片xc7z020右键选择 program进行烧写，弹出的界面可以看到，烧写进去会有两个文件，上面的是bit文件，下面 的是我们抓取在线调试的文件：
![图片](https://admin.shaonianxue.cn/wp-content/uploads/2025/05/image-90.png)
点击
![图片](https://admin.shaonianxue.cn/wp-content/uploads/2025/05/image-91.png)
符号开始抓取，抓取波形如下：（不要修改任何设置，触发信号我们也设置 好了，更改了可能抓的不是这个波形）
![图片](https://admin.shaonianxue.cn/wp-content/uploads/2025/05/image-92.png)
从上面的抓取可以看到，INOUT信号实际在内部会被逻辑分成两个寄存器 ，一个SDA_IBUF,一个SDA_OBUF ，一个输入和一个输出。只不过共用一个管脚，输入输出到芯片， 都通过这两个BUF。高阻控制的就是输出寄存器 ，设置它输出为高阻态，不影响输入。从上面 看，实际上两个寄存器的值也是不一样的。
