> 技术文档 > FPGA使用IIC协议完成OLED的驱动---附完整工程_fpga的iic驱动oled

FPGA使用IIC协议完成OLED的驱动---附完整工程_fpga的iic驱动oled


本次使用开发环境:

软件:Quartus II 64-Bit

开发板及芯片型号:至芯 --EP4CE6E22C8N 1.2V

完整工程因为会失效的风险请移步到公粽号:发拉不拉电,回复“OLED显示”获取最新网盘链接

协议核心概述

  1. 物理层特性
  2. 双线制:仅需 SCL(时钟线)和 SDA(数据线)两根线,均需外接上拉电阻,空闲时保持高电平
  3. 多主多从:支持多个主机和从机,通过设备地址(通常 7 位)区分不同外设,地址后接 1 位读写标志位(0 写 / 1 读)
  4. 半双工通信:数据线 SDA 同一时刻只能单向传输,由主机控制时序
  5. 关键时序状态
  6. 起始条件(Start):SCL 高电平时,SDA 由高→低跳变,标志通信开始
  7. 停止条件(Stop):SCL 高电平时,SDA 由低→高跳变,标志通信结束
  8. 数据传输:SCL 低电平时更新 SDA 数据,SCL 高电平时保持稳定,数据按高位优先传输
  9. 应答机制(ACK/NACK):每传输 8 位数据后,接收方在第 9 个 SCL 周期拉低 SDA(ACK)或保持高电平(NACK)

  10. 状态机驱动

  11. 通过状态机划分 IIC 操作阶段,典型状态包括:

    空闲态(IDLE)、起始态(START)、写数据(WR_DATA)、读数据(RD_DATA)、应答(ACK)、停止态(STOP)

  12. 示例状态机跳转逻辑:

    Verilogcase(state) IDLE: 检测到读写请求→跳转START; START: 生成起始信号→跳转WR_DATA; WR_DATA: 发送8位数据→跳转ACK; ACK: 检测应答→根据操作类型跳转STOP或继续传输; STOP: 结束通信→返回IDLE;endcase
  13. 时钟分频与时序控制

  14. 将 FPGA 系统时钟(如 50MHz)分频至 IIC 速率(如 100kHz 或 400kHz),需计算分频参数(例如 50MHz/100kHz=500 分频)

  15. 在 SCL 低电平中点(如分频计数器的 65/250 位置)更新 SDA 数据,确保时序稳定

  16. 数据收发逻辑及数据帧结构

  • 写操作:主机依次发送设备地址(+ 写标志)、寄存器地址、数据,每字节后检测 ACK
  • 读操作:主机发送设备地址(+ 写标志)→寄存器地址→重新发送起始信号→设备地址(+ 读标志)→接收数据并发送 ACK/NACK

1. 基础知识

1. I2C(IIC)通讯协议

I2C 通讯协议(Inter - Intergrated Circuit)是由 Philips 公司开发的一种简单、双向二线同步串行总线,只需要两根先就可以在连接于总线上的器件之间传送信息。

I2C 通讯协议和通信接口在很多工程中与广泛的应用,如数据采集领域的串行 AD,图像处理领域的摄像头配置,工业控制领域的 X 射线管配置等等。除此之外,由于 I2C 协议占用的引脚特别少,硬件实现简单,可扩展性强,现在被广泛使用在系统内多个集成电路(IC)间的通讯。同时也通常用于连接低速设备,如传感器、存储器和其他外设。它使用两根线(SCL 和 SDA)来实现双向通信,具有地址定向性和主从模式。

优点:

  • 多设备支持:I2C 支持多个设备连接到同一总线上,每个设备都有唯一的地址。
  • 简单:I2C 协议相对简单,易于实现和调试。
  • 低功耗:在空闲状态时,显示高阻态,I2C 总线上的器件可以进入低功耗模式,节省能量。(空闲状态下,IIC 设备端口处于高阻态,所以 IIC 总线空闲时刻也就是高电平
  • 仲裁:总线上可以连接多个从机,也可以连接多个主机,当多个主机同时使用总线时,为了防止数据发生冲突,会使用仲裁的方式来决定哪个设备使用总线
  • 速度:IIC 具有 3 个传输模式,在标准模式下,传输速度为 100kb/s。在快速模式下,速度为 400kb/s。最后是高速模式,速度可以达到 3.4MB/s。当前的大多数 IIC 设备仅支持标准模式和快速模式。

缺点:

  • 速度较慢:I2C 通信速度较低,适用于低速设备。
  • 受限制:I2C 的总线长度和设备数量受到限制,过长的总线可能导致通信问题。
  • 冲突:当多个设备尝试同时发送数据时,可能会发生冲突,需要额外的冲突检测和处理机制。

应用案例:

就其应用而言,连接方面,I2C 在需要简单且经济的通信环境中表现出色。它尤其擅长在小型传感器、LCD 屏幕和 RTC(实时时钟)模块中使用。此外,I2C 由于其在紧凑电路中的效率,在温度控制设备、电池管理系统和 LED 控制器中很有用。但是,在需要快速或长距离数据传输的项目中,最好选择其他协议。

读写等详细的通信过程可参考下文:

老宇哥带你玩转 ESP32:07 I2C 协议,看这一篇就够了 (点击阅读)

 2. I2C 物理层

I2C_SCL:串行时钟线,用于同步通讯数据;

I2C_SDA: 双向串行数据线,传输通讯数据;

3. I2C 协议层

在这个示意图中,包含 4 个状态:

1: 总线空闲状态

2: 起始信号

3: 数据读写状态

4: 停止信号

在 SCL 为高电平的时候,进行数据的写入,低电平的时候,进行数据的更新。

当我们的从机设备正确接受到我们发送的信号的时候,会把 SDA 数据线拉低,称为响应位,表示向主机发送一个单比特的应答信号。

为什么数据在传输过程中不会被误判为停止信号?

IIC 将 SCL 处于高时 SDA 拉低的动作作为开始信号,SCL 处于高时 SDA 拉高的动作作为结束信号,这里我存在疑惑,iic 协议的各种状态时根据这两根线的时序进行判断的,scl 时时钟信号,再空闲时一直保持高电平,再准备开始信号时 sda 从高电平拉低后,准备发送地址信号,此时 scl 开始时钟震荡,那么会不会出现 scl 在震荡到高电平时,sda 正好发送的一个高电平,此时是不是也同样满足停止信号呐?

在这里有一个误区,首先只有在 scl 时钟信号为低电平时,sda 才会进行更新数据,可以理解为 scl 为高电平时,sda 的状态才是有效的,那么我们的开始和停止状态并不是单一的在 scl 和 sda 都为高时,而是 scl 为高,sda 从高电平变换为低电平时为开始,从低电平变换为高电平时为停止状态。在正常的数据传输过程中,scl 在高电平时,sda 禁止变换电平,在开始和停止状态中,时变换电平的。所以,我们的数据传输过程中,数据电平和时钟电平的状态是不会影响我们开始和停止状态的,下面是两个示意图,讲述的内容是一致的。

我们怎么检测这个开始或者停止信号呐?

相信大家在学习 FPGA 的时候会常常遇到一些工程中会对信号进行打拍的处理,这个的目的是将信号与其他的信号进行对齐和防止亚稳态,每次打一个拍后信号会延迟一个时钟周期。当我们将需要检测的信号对齐之后,我们开始对信号的上升沿或者下降沿进行检测,使用时序逻辑,以系统时钟为标准,如果打拍的两个信号前后不一样,那么就是检测到了变化沿,例如 reg1 在这个时钟下为高电平,reg2 为低电平,那么我们的信号则是下降沿。

关于器件的地址的内容

器件地址被定义为 7Bit 的数据,器件厂商在出厂的时候就将地址设置好了,用户不能进行自己更改,但是有的厂商如上图所示,其中高 4 位是固定的,但是后 3 位没有被写入,这样的情况是允许用户自行设置的。

可以在这里看到,根据上面的两行图像,可以确定这个芯片的 I2C 地址为 10100_000,因为 A0、A1、A2 都是低电平。

最后一位为读写控制位,0 表示写入,1 表示读取。

举例:如果要让这个芯片处于读取状态,使用 I2C 协议的地址则为:10100_000_1

4. 数据传输流程

I²C 协议中判断数据接收设备的基本步骤:

  1. 地址分配:每个连接到 I²C 总线的设备都有一个唯一的地址。这个地址在设备制造时被设定,并且通常通过硬件方式设置。
  2. 开始条件:在数据传输开始之前,主机设备会发送一个开始条件,这通常涉及到 SDA(数据线)和 SCL(时钟线)的特定电平变化。
  3. 发送地址:主机设备随后会在 SDA 线上发送目标设备的 7 位地址,加上一个额外的读 / 写位(R/W 位)。这个位指示了接下来的操作是读取(1)还是写入(0)。
  4. 确认应答:当地址和 R/W 位被发送后,目标设备需要在下一个时钟脉冲的开始时将 SDA 线拉低,以表示它已经接收到了地址并且准备好进行通信。这个过程称为应答(ACK)。
  5. 数据传输:一旦接收到应答,主机设备就会开始发送数据。如果是写入操作,主机发送数据;如果是读取操作,从设备发送数据。
  6. 非应答(NACK):如果目标设备不是预期的接收者,或者由于其他原因无法接收数据,它会在下一个时钟脉冲的开始时不拉低 SDA 线,表示非应答。
  7. 停止条件:数据传输完成后,主机设备会发送一个停止条件,这涉及到 SDA 和 SCL 线的特定电平变化,以结束数据传输。
  8. 多主机环境:在多主机环境中,如果有多个主机尝试同时控制总线,它们会使用仲裁机制来决定哪个主机获得控制权。仲裁是基于发送的数据位,如果两个主机尝试发送不同的位,发送 0 的主机会放弃控制权。

2. IIC 驱动 7Pin 0.96 寸 OLED 显示屏实例

1. 硬件修改

在驱动显示屏时,显示屏默认使用 SPI 协议驱动,要改为 IIC 协议驱动需要将硬件电路进行简单的修改,具体就是:

  1. 将背面 R3 电阻移动到 R4 的 位置

  2. 短接 R8 电阻

改造好之后,不能象原生的 IIC 屏幕那样接 4 根线即可,必须将 7 个管脚都要接线,否则可能没有任何显示。管脚处理:

1、CS 脚接地。

2、DC 脚的处理:在 IIC 通信中 DC 的高低电平是用来选择 IIC 通信地址的;当 DC 接地时 IIC 从机地址为:0x78, 当 DC 接高电平时 IIC 地址为 0x7A; 测试程序中所用的为 0x78; 通常直接将 DC 接地

3、关于 RES 的处理。RES 这个脚是 OLED 屏的复位脚;大家在用 OLED 屏的时候会发现;所

有 OLED 本身都会有一个复位脚;因为 OLED 在被操作之前需要在将寄存作一次复位;然后才能对期进行初始货操作;否则 OLED 可能会出现水稳定的情况。RES 处理方案:  

1> 简单的验证办法:将 RES 接电源正;这样可以把屏点亮;但是会不稳定,在快速测试时可以这么操作

2> 将 RES 脚与开发板的复位脚连接;通过开发板的复位来对 OLED 进行复位

3> 通过一个 IO 脚来对 OLED 进行复位,这个操作放在对屏初始化之前;先将 RES 拉低延迟 200ms 左右;然后再拉高一直处于高电平状态

4> 通过一个 RC 复位电路来控制 RES

4、D0 为 IIC 时钟线或者 SCK 为时钟线,

5、D1 为 IIC 数据线或者 SDA 为数据线;

6、GND 为接地线,

7、VDD 为电源线;

2.OLED 上的驱动部分:

OLED 屏幕有三种刷新方式分别为页地址模式,水平地址模式和垂直地址模式,。 水平地址模式和垂直地址模式可以在一页 (一列) 写完后自动换页 (列) 所以在水平地址寻址或者垂直地址寻址模式下,只要源源不断的发送数据即可

页地址模式

水平地址模式

垂直地址模式

3. OLED 关键命令介绍

0xAE/0xAF: 对应着开启 OLED 显示和关闭 OLED 显示 0x20-0x22: 对应着上面的三种 OLED 数据存储模式,默认为 0x22,模式一 0x00-0x0F: 设置列地址的低四位,默认为 0x00, 0x10-0x1F: 设置列地址的高四位,默认为 0x10, 0xB0-0xB7: 设置 page,第四位表示 page。

IIC 数据格式 :OLED 地址 + 命令 / 数据 + 值。

OLED 地址,就是 IIC 协议中的从机地址,我这里是 0x78。

命令 / 数据中,0x00 表示接下来的值代表命令,0x40 表示接下的值表示数据,存入 GRMA。

,具体的命令或者数据

always@(*)begin case(Init_index) \'d0: Init_data_reg <= {8\'h78,8\'h00,8\'hAE}; //OLED地址 + 命令 + 值。** \'d1: Init_data_reg <= {8\'h78,8\'h00,8\'h00}; \'d2: Init_data_reg <= {8\'h78,8\'h00,8\'h10}; \'d3: Init_data_reg <= {8\'h78,8\'h00,8\'h40}; \'d4: Init_data_reg <= {8\'h78,8\'h00,8\'hB0}; \'d5: Init_data_reg <= {8\'h78,8\'h00,8\'h81}; \'d6: Init_data_reg <= {8\'h78,8\'h00,8\'hFF}; \'d7: Init_data_reg <= {8\'h78,8\'h00,8\'hA1}; \'d8: Init_data_reg <= {8\'h78,8\'h00,8\'hA6}; \'d9: Init_data_reg <= {8\'h78,8\'h00,8\'hA8}; \'d10: Init_data_reg <= {8\'h78,8\'h00,8\'h3F}; \'d11: Init_data_reg <= {8\'h78,8\'h00,8\'hC8}; \'d12: Init_data_reg <= {8\'h78,8\'h00,8\'hD3}; \'d13: Init_data_reg <= {8\'h78,8\'h00,8\'h00}; \'d14: Init_data_reg <= {8\'h78,8\'h00,8\'hD5}; \'d15: Init_data_reg <= {8\'h78,8\'h00,8\'h80}; \'d16: Init_data_reg <= {8\'h78,8\'h00,8\'hD8}; \'d17: Init_data_reg <= {8\'h78,8\'h00,8\'h05}; \'d18: Init_data_reg <= {8\'h78,8\'h00,8\'hD9}; \'d19: Init_data_reg <= {8\'h78,8\'h00,8\'hF1}; \'d20: Init_data_reg <= {8\'h78,8\'h00,8\'hDA}; \'d21: Init_data_reg <= {8\'h78,8\'h00,8\'h12}; \'d22: Init_data_reg <= {8\'h78,8\'h00,8\'hDB}; \'d23: Init_data_reg <= {8\'h78,8\'h00,8\'h30}; \'d24: Init_data_reg <= {8\'h78,8\'h00,8\'h8D}; \'d25: Init_data_reg <= {8\'h78,8\'h00,8\'h14}; \'d26: Init_data_reg <= {8\'h78,8\'h00,8\'hAF}; default: Init_data_reg <= {8\'h78,8\'h00,8\'hAE}; endcaseend

3. IIC 驱动 4Pin OLED 显示器显示 DHT11 温湿度数据

3.1 字模设计

3.2 程序设计

程序如下

module main( input  sys_clk, input  rst_n, inout  dht11, output OLED_SCL, inout  OLED_SDA);wire dht11_done;reg dht11_req;wire[7:0] tempH;wire[7:0] tempL; wire[7:0] humidityH;wire[7:0] humidityL;localparam S_DELAY = \'d55_000_000;reg[35:0] delay;always@(posedge sys_clk or negedge rst_n)begin if(rst_n == 1\'b0) delay <= \'d0; else if(dht11_done == 1\'b1) delay <= \'d0; else if(delay == S_DELAY) delay <= delay; else delay <= delay + 1\'b1;endalways@(posedge sys_clk or negedge rst_n)begin if(rst_n == 1\'b0)  dht11_req <= 1\'b0; else if(delay == S_DELAY) dht11_req <= 1\'b1; else dht11_req <= 1\'b0;endDHT11 DHT11HP( .sys_clk (sys_clk), .rst_n (rst_n), .dht11_req (dht11_req), //dht11数据采集请求 .dht11_done (dht11_done), //dht11数据采集结束 .dht11_error (), //dht11数据采集正确与否判断 1为错误 .tempH (tempH), //温度数据整数 .tempL (tempL), //温度数据小数 .humidityH (humidityH), //温度数据整数 .humidityL (humidityL), //温度数据小数 .dht11 (dht11));OLED_Top OLED_TopHP( .sys_clk (sys_clk), .rst_n (rst_n), .dht11_done (dht11_done), .tempH (tempH), //温度数据整数 .tempL (tempL), //温度数据小数 .humidityH (humidityH), //温度数据整数 .humidityL (humidityL), //温度数据小数 //OLED IIC .OLED_SCL (OLED_SCL), .OLED_SDA (OLED_SDA));endmodule 

IIC_Driver 模块如下:

`timescale 1ns/1psmodule IIC_Driver( input sys_clk,  /*系统时钟*/ input rst_n, /*系统复位*/ output IICSCL, /*IIC 时钟输出*/ inout IICSDA, /*IIC 数据线*/ input[15:0] IICSlave,  /*从机 8bit的寄存器地址 + 8bit的从机地址*/ input IICWriteReq, /*IIC写寄存器请求*/ output IICWriteDone, /*IIC写寄存器完成*/ input[7:0] IICWriteData, /*IIC发送数据 8bit的数据*/ input IICReadReq, /*IIC读寄存器请求*/ output IICReadDone, /*IIC读寄存器完成*/ output[7:0] IICReadData /*IIC读取数据*/);/*IIC 状态*/localparam IIC_IDLE = 6\'b000_001; /*空闲态*/localparam IIC_START = 6\'b000_010; /*起始态*/localparam IIC_WRDATA = 6\'b000_100; /*写数据态*/localparam IIC_RDDATA = 6\'b001_000; /*读数据态*/localparam IIC_ACK = 6\'b010_000; /*应答态*/localparam IIC_STOP = 6\'b100_000; /*停止态*/localparam IIC_Pre = \'d100; /*iiC分频*/reg[5:0] state , next_state;reg[21:0] IICCnt;  /*IIC计数器*/reg[3:0] IICBitCnt;  /*IIC数据发送个数计数*/reg[1:0] IICACKStopCnt; /*IIC ack stop应答计数*/reg[2:0] IICSendBytes; /*IIC 发送字节计数*/ reg[15:0] IICSlaveReg;  /*从机地址+寄存器数据*/reg[7:0] IICReadDataReg; /*读取到的数据*/reg IICWriteReqReg;reg iictx; /*iic发送数据引脚*/reg iicCLK; /*iic时钟信号引脚*/assign IICSDA = (state == IIC_RDDATA || (state == IIC_ACK)) ? 1\'bz : iictx; /*iic为读数据或者应答的时候,输出为高阻态*/assign IICSCL = iicCLK;assign IICReadData = IICReadDataReg;assign IICReadDone = (state != next_state && state == IIC_STOP) ? 1\'b1 : 1\'b0;assign IICWriteDone = (state != next_state && state == IIC_STOP) ? 1\'b1 : 1\'b0;always@(posedge sys_clk or negedge rst_n)begin if(rst_n == 1\'b0) IICWriteReqReg <= 1\'b0; else if(IICWriteDone == 1\'b1) IICWriteReqReg <= 1\'b0; else if(IICWriteReq == 1\'b1) IICWriteReqReg <= 1\'b1; else IICWriteReqReg <= IICWriteReqReg;endalways @(posedge sys_clk or negedge rst_n) begin if(rst_n == 1\'b0) state <= IIC_IDLE; else state <= next_state; end/*状态机*/always @(*)begin case(state) IIC_IDLE: if(IICWriteReq == 1\'b1 || IICReadReq == 1\'b1) next_state <= IIC_START; else next_state <= IIC_IDLE; IIC_START: if(IICCnt == (IIC_Pre * \'d2)) next_state <= IIC_WRDATA; else next_state <= IIC_START; IIC_WRDATA: if(IICBitCnt == \'d8 /*&& IICCnt == IIC_Pre /4 */&& iicCLK == 1\'b0) next_state <= IIC_ACK; else next_state <= IIC_WRDATA; IIC_RDDATA: if(IICBitCnt == \'d8 && IICCnt == IIC_Pre /4 && iicCLK == 1\'b0) next_state <= IIC_ACK; else next_state <= IIC_RDDATA; IIC_ACK: if(IICACKStopCnt == \'d1 /*&& IICCnt == IIC_Pre /4 */&& iicCLK == 1\'b0) if(IICSendBytes == \'d2)  if(/*IICWriteReq*/IICWriteReqReg == 1\'b1) /*三个字节发送完成,进入停止态*/  next_state <= IIC_STOP; else  next_state <= IIC_RDDATA; else if(IICSendBytes == \'d2 && IICReadReq == 1\'b1) next_state <= IIC_START; else if(IICSendBytes == \'d4) next_state <= IIC_STOP; else next_state <= IIC_WRDATA; else next_state <= IIC_ACK; IIC_STOP: if(IICACKStopCnt == \'d1 && IICCnt == IIC_Pre/4 && iicCLK == 1\'b1) next_state <= IIC_IDLE; else next_state <= IIC_STOP; default: next_state <= IIC_IDLE; endcaseend/*IIC 发送字节计数*/always@(posedge sys_clk or negedge rst_n)begin if(rst_n == 1\'b0) IICSendBytes <= \'d0; else if(state == IIC_IDLE) IICSendBytes <= \'d0; else if(state == IIC_ACK) if(next_state != state) IICSendBytes <= IICSendBytes + 1\'b1; else IICSendBytes <= IICSendBytes; else IICSendBytes <= IICSendBytes;end/*IIC分频计数*/always@(posedge sys_clk or negedge rst_n)begin if(rst_n == 1\'b0) IICCnt <= \'d0; else if(IICCnt == IIC_Pre && state != IIC_START) IICCnt <= \'d0; else if(IICCnt == (IIC_Pre *\'d2) && state != IIC_START) IICCnt <= \'d0; else if(state != next_state) IICCnt <= \'d0; else if(state == IIC_START) IICCnt <= IICCnt + 1\'b1; else if(state == IIC_WRDATA ) IICCnt <= IICCnt + 1\'b1; else if(state == IIC_RDDATA) IICCnt <= IICCnt + 1\'b1; else if(state == IIC_ACK) IICCnt <= IICCnt + 1\'b1; else if(state == IIC_STOP) IICCnt <= IICCnt + 1\'b1;end/*IIC发送bit计数*/always@(posedge sys_clk or negedge rst_n)begin if(rst_n == 1\'b0) IICBitCnt <= \'d0; else if(state == IIC_IDLE || state == IIC_ACK) IICBitCnt <= \'d0; else if(state == IIC_WRDATA && IICCnt == (IIC_Pre /2)) if(iicCLK == 1\'b1) IICBitCnt <= IICBitCnt + 1\'b1; else IICBitCnt <= IICBitCnt; else if(state == IIC_RDDATA && IICCnt == (IIC_Pre /2)) if(iicCLK == 1\'b1) IICBitCnt <= IICBitCnt + 1\'b1; else IICBitCnt <= IICBitCnt; else IICBitCnt <= IICBitCnt;end/*IIC ack stop应答计数*/always@(posedge sys_clk or negedge rst_n)begin if(rst_n == 1\'b0) IICACKStopCnt <= \'d0; else if(state != next_state) IICACKStopCnt <= \'d0; else if((state == IIC_ACK || state == IIC_STOP) && IICCnt == (IIC_Pre /2)) if(iicCLK == 1\'b1) IICACKStopCnt <= IICACKStopCnt + 1\'b1; else IICACKStopCnt <= IICACKStopCnt; else if(state == IIC_ACK || state == IIC_STOP) IICACKStopCnt <= IICACKStopCnt; else IICACKStopCnt <= \'d0;end/*IIC从机信息控制*/always@(posedge sys_clk or negedge rst_n)begin if(rst_n == 1\'b0) IICSlaveReg <= \'d0; else if((IICWriteReq == 1\'b1 || IICReadReq == 1\'b1) && state == IIC_IDLE) /*请求来时,保存信息*/ IICSlaveReg <= IICSlave; else if(state == IIC_ACK && state != next_state) /*每发送完成一字节,就调换数据,始终发送的是低8位*/ if(IICSendBytes == \'d2) IICSlaveReg <= {IICSlaveReg[7:0],IICSlaveReg[15:8]} + 1\'b1; else IICSlaveReg <= {IICSlaveReg[7:0],IICSlaveReg[15:8]}; else IICSlaveReg <= IICSlaveReg;end/*IIC 时钟控制*/always@(posedge sys_clk or negedge rst_n)begin if(rst_n == 1\'b0) iicCLK <= 1\'b1; else if(state == IIC_START && IICCnt == (IIC_Pre*2)) /*开始结束时,拉低时钟线*/ iicCLK  IIC_Pre) iicCLK <= 1\'b1; else if(state == IIC_START && IICCnt == IIC_Pre) iicCLK <= iicCLK; else if(state == IIC_WRDATA && IICCnt == IIC_Pre) /*发送数据,依次取反时钟线*/ iicCLK <= ~iicCLK; else if(state == IIC_RDDATA && IICCnt == IIC_Pre) /*接收数据,依次取反时钟线*/ iicCLK <= ~iicCLK; else if(state == IIC_ACK && IICCnt == IIC_Pre) iicCLK <= ~iicCLK; else if(state == IIC_STOP && IICCnt == IIC_Pre) /*在停止态时,IICCnt时,直接拉高iicCLK*/ iicCLK <= 1\'b1;end/*iic 发送控制*/always@(posedge sys_clk or negedge rst_n)begin if(rst_n == 1\'b0)  iictx <= 1\'b1; else if(state == IIC_START && IICCnt == (IIC_Pre/2)) /*开始,iic数据线拉低*/ iictx <= 1\'b0; else if(state == IIC_START && IICCnt == IIC_Pre/4) /*开始,iic数据线拉低*/ iictx <= 1\'b1; else if(state == IIC_WRDATA && IICCnt == IIC_Pre / 2) /*iic发送数据*/ if(iicCLK == 1\'b0 && IICSendBytes == \'d2 && (IICWriteReq == 1\'b1 || IICWriteReqReg == 1\'b1)) iictx <= IICWriteData[\'d7-IICBitCnt]; else if(iicCLK == 1\'b0) iictx <= IICSlaveReg[\'d7-IICBitCnt]; else iictx <= iictx; else if(state == IIC_ACK) iictx <= 1\'b0; else if(state == IIC_STOP && IICCnt == (IIC_Pre)) if(iicCLK == 1\'b1) iictx <= 1\'b1; else iictx <= iictx; else if(state == IIC_IDLE) iictx <= 1\'b1;end/*iic 读取数据控制*/always@(posedge sys_clk or negedge rst_n)begin if(rst_n == 1\'b0) IICReadDataReg <= \'d0; else if(state == IIC_RDDATA && IICCnt == IIC_Pre / 2) if(iicCLK == 1\'b1) IICReadDataReg <= {IICReadDataReg[6:0],IICSDA}; else IICReadDataReg <= IICReadDataReg; else IICReadDataReg <= IICReadDataReg;endendmodule

DHT11 模块如下:

/*dht11温湿度数据获取模块*/module DHT11( input  sys_clk, input  rst_n, input  dht11_req, //dht11数据采集请求 output dht11_done, //dht11数据采集结束 output dht11_error, //dht11数据采集正确与否判断 1为错误 output[7:0] tempH, //温度数据整数 output[7:0] tempL, //温度数据小数 output[7:0] humidityH, //温度数据整数 output[7:0] humidityL, //温度数据小数 inout  dht11);//时钟为50MHZ,20nslocalparam TIME18ms = \'d1000_099; //开始态的拉低18ms,900_000个时钟周期,这里适当的延长了拉低时间。localparam TIME35us = \'d1_750; //数据传输过程中,数据0拉高的出现localparam S_IDLE = \'d0; //空闲态localparam S_START_FPGA = \'d1; //FPGA请求采集数据开始localparam S_START_DHT11 = \'d2; //DHT11开始请求应答localparam S_DATA = \'d3; //数据传输localparam S_STOP = \'d4; //数据结束localparam S_DOEN = \'d5; //数据采集完成reg[2:0] state , next_state;reg[22:0] DHT11_Cnt; //计时器reg[5:0] DHT11Bit_Cnt; // 接收dht11传输bit数计数reg[39:0] dht11_data;reg dht11_d0 , dht11_d1;wire dht11_negedge; //检测dht11的上下边沿 assign dht11_negedge = (~dht11_d0) & dht11_d1;assign dht11 = (state == S_START_FPGA && (DHT11_Cnt <= TIME18ms)) ? 1\'b0 : 1\'bz;assign dht11_done = (state == S_DOEN) ? 1\'b1 : 1\'b0;assign tempH = dht11_data[23:16];assign tempL = dht11_data[15:8];assign humidityH = dht11_data[39:32];assign humidityL = dht11_data[31:24];always@(posedge sys_clk or negedge rst_n)begin if(rst_n == 1\'b0) begin dht11_d0 <= 1\'b1; dht11_d1 <= 1\'b1; end else begin dht11_d0 <= dht11; dht11_d1 <= dht11_d0; endendalways@(posedge sys_clk or negedge rst_n)begin if(rst_n == 1\'b0) state <= S_IDLE; else state <= next_state;endalways@(*)begin case(state) S_IDLE: if(dht11_req == 1\'b1) //数据采集请求过来进入开始态 next_state <= S_START_FPGA; else next_state = TIME18ms) && dht11_negedge == 1\'b1) //FPGA请求结束结束 next_state <= S_START_DHT11; else next_state  TIME35us) && dht11_negedge == 1\'b1) //延时一段时间后,通过判断dht11总线的下降沿,是否结束响应 next_state <= S_DATA; else next_state <= S_START_DHT11; S_DATA: if(DHT11Bit_Cnt == \'d39 && dht11_negedge == 1\'b1) //接收到40bit数据后,进入停止态 next_state <= S_STOP; else next_state <= S_DATA; S_STOP: if(DHT11_Cnt == TIME35us + TIME35us) //数据传输完成后,等待总线拉低50us,这里是70us next_state <= S_DOEN; else next_state <= S_STOP; S_DOEN: next_state <= S_IDLE; default: next_state <= S_IDLE; endcaseend/*计数模块*/always@(posedge sys_clk or negedge rst_n)begin if(rst_n == 1\'b0) DHT11_Cnt <= \'d0; else if(state != next_state) //状态变换的时候,计时器置0 DHT11_Cnt <= \'d0; else if(state == S_START_FPGA)  DHT11_Cnt <= DHT11_Cnt + 1\'b1; else if(state == S_START_DHT11) DHT11_Cnt <= DHT11_Cnt + 1\'b1; else if(state == S_DATA && dht11_negedge == 1\'b0) //数据的时候,只需要在高电平的时候计数 DHT11_Cnt <= DHT11_Cnt + 1\'b1; else if(state == S_STOP) DHT11_Cnt <= DHT11_Cnt + 1\'b1; else DHT11_Cnt <= \'d0; end/*接收数据存储*/always@(posedge sys_clk or negedge rst_n)begin if(rst_n == 1\'b0) dht11_data <= \'d0; else if(state == S_DATA) if((DHT11_Cnt <= (TIME35us + \'d2500)) && dht11_negedge == 1\'b1) //\'d3000为低电平时间,高电平持续时间低于35us认为是数据0 dht11_data <= {dht11_data[38:0],1\'b0}; else if(dht11_negedge == 1\'b1) dht11_data <= {dht11_data[38:0],1\'b1}; else dht11_data <= dht11_data; else dht11_data <= dht11_data;end/*接收bit计数*/always@(posedge sys_clk or negedge rst_n)begin if(rst_n == 1\'b0) DHT11Bit_Cnt <= \'d0; else if(state == S_DATA && dht11_negedge == 1\'b1) DHT11Bit_Cnt <= DHT11Bit_Cnt + 1\'b1; else if(state == S_DOEN) //结束后,bit计数清零 DHT11Bit_Cnt <= \'d0; else DHT11Bit_Cnt <= DHT11Bit_Cnt;endendmodule 

下面是 oled 的程序

//中文16*16 数字和字母8*16module OLED_FontData( input  sys_clk, input  rst_n, input  font_row, input[5:0] font_sel, input[8:0] index, output reg[7:0] data);reg[7:0] data0[15:0]; //Freg[7:0] data1[15:0]; //Greg[7:0] data2[15:0]; //Preg[7:0] data3[15:0]; //Areg[7:0] data4[31:0]; //之reg[7:0] data5[31:0]; //旅reg[7:0] data6[31:0]; //温reg[7:0] data7[31:0]; //度reg[7:0] data8[31:0]; //湿reg[7:0] data9[31:0]; //度reg[7:0] data10[15:0]; //.reg[7:0] data11[31:0]; //℃reg[7:0] data12[15:0]; //Rreg[7:0] data13[15:0]; //Halways@(posedge sys_clk or negedge rst_n)begin if(rst_n == \'d0) data <= \'d0; else if(font_sel == \'d0) data <= data0[index + \'d8 * font_row]; else if(font_sel == \'d1) data <= data1[index + \'d8 * font_row]; else if(font_sel == \'d2) data <= data2[index + \'d8 * font_row]; else if(font_sel == \'d3) data <= data3[index + \'d8 * font_row]; else if(font_sel == \'d4) data <= data4[index + \'d16 * font_row]; else if(font_sel == \'d5) data <= data5[index + \'d16 * font_row]; else if(font_sel == \'d6) data <= data6[index + \'d16 * font_row]; else if(font_sel == \'d7) data <= data7[index + \'d16 * font_row]; else if(font_sel == \'d8) data <= data8[index + \'d16 * font_row]; else if(font_sel == \'d9) data <= data9[index + \'d16 * font_row]; else if(font_sel == \'d10) data <= data10[index + \'d8 * font_row]; else if(font_sel == \'d11) data <= data11[index + \'d16 * font_row]; else if(font_sel == \'d12) data <= data12[index + \'d8 * font_row]; else if(font_sel == \'d13) data <= data13[index + \'d8 * font_row]; else if(font_sel == \'d14) data <= data10[index + \'d8 * font_row]; else data <= data;endalways@(posedge sys_clk or negedge rst_n)begin if(rst_n == 1\'b0) begin data0[0] = 8\'h08; data0[1] = 8\'hF8; data0[2] = 8\'h88; data0[3] = 8\'h88; data0[4] = 8\'hE8; data0[5] = 8\'h08; data0[6] = 8\'h10; data0[7] = 8\'h00; data0[8] = 8\'h20; data0[9] = 8\'h3F; data0[10] = 8\'h20; data0[11] = 8\'h00; data0[12] = 8\'h03; data0[13] = 8\'h00; data0[14] = 8\'h00; data0[15] = 8\'h00; endendalways@(posedge sys_clk or negedge rst_n)begin if(rst_n == 1\'b0) begin data1[0] = 8\'h08; data1[1] = 8\'hF8; data1[2] = 8\'h08; data1[3] = 8\'h08; data1[4] = 8\'h08; data1[5] = 8\'h08; data1[6] = 8\'hF0; data1[7] = 8\'h00; data1[8] = 8\'h20; data1[9] = 8\'h3F; data1[10] = 8\'h21; data1[11] = 8\'h01; data1[12] = 8\'h01; data1[13] = 8\'h01; data1[14] = 8\'h00; data1[15] = 8\'h00; endendalways@(posedge sys_clk or negedge rst_n)begin if(rst_n == 1\'b0) begin data2[0] = 8\'hC0; data2[1] = 8\'h30; data2[2] = 8\'h08; data2[3] = 8\'h08; data2[4] = 8\'h08; data2[5] = 8\'h38; data2[6] = 8\'h00; data2[7] = 8\'h00; data2[8] = 8\'h07; data2[9] = 8\'h18; data2[10] = 8\'h20; data2[11] = 8\'h20; data2[12] = 8\'h22; data2[13] = 8\'h1E; data2[14] = 8\'h02; data2[15] = 8\'h00; endendalways@(posedge sys_clk or negedge rst_n)begin if(rst_n == 1\'b0) begin data3[0] = 8\'h00; data3[1] = 8\'h00; data3[2] = 8\'hC0; data3[3] = 8\'h38; data3[4] = 8\'hE0; data3[5] = 8\'h00; data3[6] = 8\'h00; data3[7] = 8\'h00; data3[8] = 8\'h20; data3[9] = 8\'h3C; data3[10] = 8\'h23; data3[11] = 8\'h02; data3[12] = 8\'h02; data3[13] = 8\'h27; data3[14] = 8\'h38; data3[15] = 8\'h20; endendalways@(posedge sys_clk or negedge rst_n)begin if(rst_n == 1\'b0) begin data4[0] = 8\'h10; data4[1] = 8\'h0C; data4[2] = 8\'h04; data4[3] = 8\'h84; data4[4] = 8\'h14; data4[5] = 8\'h64; data4[6] = 8\'h05; data4[7] = 8\'h06; data4[8] = 8\'hF4; data4[9] = 8\'h04; data4[10] = 8\'h04; data4[11] = 8\'h04; data4[12] = 8\'h04; data4[13] = 8\'h14; data4[14] = 8\'h0C; data4[15] = 8\'h00; data4[16] = 8\'h04; data4[17] = 8\'h84; data4[18] = 8\'h84; data4[19] = 8\'h44; data4[20] = 8\'h47; data4[21] = 8\'h24; data4[22] = 8\'h14; data4[23] = 8\'h0C; data4[24] = 8\'h07; data4[25] = 8\'h0C; data4[26] = 8\'h14; data4[27] = 8\'h24; data4[28] = 8\'h44; data4[29] = 8\'h84; data4[30] = 8\'h04; data4[31] = 8\'h00; endendalways@(posedge sys_clk or negedge rst_n)begin if(rst_n == 1\'b0) begin data5[0] = 8\'h02; data5[1] = 8\'hFA; data5[2] = 8\'h82; data5[3] = 8\'h82; data5[4] = 8\'hFE; data5[5] = 8\'h80; data5[6] = 8\'h40; data5[7] = 8\'h20; data5[8] = 8\'h50; data5[9] = 8\'h4C; data5[10] = 8\'h43; data5[11] = 8\'h4C; data5[12] = 8\'h50; data5[13] = 8\'h20; data5[14] = 8\'h40; data5[15] = 8\'h00; data5[16] = 8\'h08; data5[17] = 8\'h18; data5[18] = 8\'h48; data5[19] = 8\'h84; data5[20] = 8\'h44; data5[21] = 8\'h3F; data5[22] = 8\'h40; data5[23] = 8\'h44; data5[24] = 8\'h58; data5[25] = 8\'h41; data5[26] = 8\'h4E; data5[27] = 8\'h60; data5[28] = 8\'h58; data5[29] = 8\'h47; data5[30] = 8\'h40; data5[31] = 8\'h00; endendalways@(posedge sys_clk or negedge rst_n)begin if(rst_n == 1\'b0) begin data6[0] = 8\'h10; data6[1] = 8\'h60; data6[2] = 8\'h02; data6[3] = 8\'h8C; data6[4] = 8\'h00; data6[5] = 8\'h00; data6[6] = 8\'hFE; data6[7] = 8\'h92; data6[8] = 8\'h92; data6[9] = 8\'h92; data6[10] = 8\'h92; data6[11] = 8\'h92; data6[12] = 8\'hFE; data6[13] = 8\'h00; data6[14] = 8\'h00; data6[15] = 8\'h00; data6[16] = 8\'h04; data6[17] = 8\'h04; data6[18] = 8\'h7E; data6[19] = 8\'h01; data6[20] = 8\'h40; data6[21] = 8\'h7E; data6[22] = 8\'h42; data6[23] = 8\'h42; data6[24] = 8\'h7E; data6[25] = 8\'h42; data6[26] = 8\'h7E; data6[27] = 8\'h42; data6[28] = 8\'h42; data6[29] = 8\'h7E; data6[30] = 8\'h40; data6[31] = 8\'h00; endendalways@(posedge sys_clk or negedge rst_n)begin if(rst_n == 1\'b0) begin data7[0] = 8\'h00; data7[1] = 8\'h00; data7[2] = 8\'hFC; data7[3] = 8\'h24; data7[4] = 8\'h24; data7[5] = 8\'h24; data7[6] = 8\'hFC; data7[7] = 8\'h25; data7[8] = 8\'h26; data7[9] = 8\'h24; data7[10] = 8\'hFC; data7[11] = 8\'h24; data7[12] = 8\'h24; data7[13] = 8\'h24; data7[14] = 8\'h04; data7[15] = 8\'h00; data7[16] = 8\'h40; data7[17] = 8\'h30; data7[18] = 8\'h8F; data7[19] = 8\'h80; data7[20] = 8\'h84; data7[21] = 8\'h4C; data7[22] = 8\'h55; data7[23] = 8\'h25; data7[24] = 8\'h25; data7[25] = 8\'h25; data7[26] = 8\'h55; data7[27] = 8\'h4C; data7[28] = 8\'h80; data7[29] = 8\'h80; data7[30] = 8\'h80; data7[31] = 8\'h00; endendalways@(posedge sys_clk or negedge rst_n)begin if(rst_n == 1\'b0) begin data8[0] = 8\'h10; data8[1] = 8\'h60; data8[2] = 8\'h02; data8[3] = 8\'h8C; data8[4] = 8\'h00; data8[5] = 8\'hFE; data8[6] = 8\'h92; data8[7] = 8\'h92; data8[8] = 8\'h92; data8[9] = 8\'h92; data8[10] = 8\'h92; data8[11] = 8\'h92; data8[12] = 8\'hFE; data8[13] = 8\'h00; data8[14] = 8\'h00; data8[15] = 8\'h00; data8[16] = 8\'h04; data8[17] = 8\'h04; data8[18] = 8\'h7E; data8[19] = 8\'h01; data8[20] = 8\'h44; data8[21] = 8\'h48; data8[22] = 8\'h50; data8[23] = 8\'h7F; data8[24] = 8\'h40; data8[25] = 8\'h40; data8[26] = 8\'h7F; data8[27] = 8\'h50; data8[28] = 8\'h48; data8[29] = 8\'h44; data8[30] = 8\'h40; data8[31] = 8\'h00; endendalways@(posedge sys_clk or negedge rst_n)begin if(rst_n == 1\'b0) begin data9[0] = 8\'h00; data9[1] = 8\'h00; data9[2] = 8\'hFC; data9[3] = 8\'h24; data9[4] = 8\'h24; data9[5] = 8\'h24; data9[6] = 8\'hFC; data9[7] = 8\'h25; data9[8] = 8\'h26; data9[9] = 8\'h24; data9[10] = 8\'hFC; data9[11] = 8\'h24; data9[12] = 8\'h24; data9[13] = 8\'h24; data9[14] = 8\'h04; data9[15] = 8\'h00; data9[16] = 8\'h40; data9[17] = 8\'h30; data9[18] = 8\'h8F; data9[19] = 8\'h80; data9[20] = 8\'h84; data9[21] = 8\'h4C; data9[22] = 8\'h55; data9[23] = 8\'h25; data9[24] = 8\'h25; data9[25] = 8\'h25; data9[26] = 8\'h55; data9[27] = 8\'h4C; data9[28] = 8\'h80; data9[29] = 8\'h80; data9[30] = 8\'h80; data9[31] = 8\'h00; endendalways@(posedge sys_clk or negedge rst_n)begin if(rst_n == 1\'b0) begin data10[0] = 8\'h00; data10[1] = 8\'h00; data10[2] = 8\'h00; data10[3] = 8\'h00; data10[4] = 8\'h00; data10[5] = 8\'h00; data10[6] = 8\'h00; data10[7] = 8\'h00; data10[8] = 8\'h00; data10[9] = 8\'h30; data10[10] = 8\'h30; data10[11] = 8\'h00; data10[12] = 8\'h00; data10[13] = 8\'h00; data10[14] = 8\'h00; data10[15] = 8\'h00; endendalways@(posedge sys_clk or negedge rst_n)begin if(rst_n == 1\'b0) begin data11[0] = 8\'h06; data11[1] = 8\'h09; data11[2] = 8\'h09; data11[3] = 8\'hE6; data11[4] = 8\'hF8; data11[5] = 8\'h0C; data11[6] = 8\'h04; data11[7] = 8\'h02; data11[8] = 8\'h02; data11[9] = 8\'h02; data11[10] = 8\'h02; data11[11] = 8\'h02; data11[12] = 8\'h04; data11[13] = 8\'h1E; data11[14] = 8\'h00; data11[15] = 8\'h00; data11[16] = 8\'h00; data11[17] = 8\'h00; data11[18] = 8\'h00; data11[19] = 8\'h07; data11[20] = 8\'h1F; data11[21] = 8\'h30; data11[22] = 8\'h20; data11[23] = 8\'h40; data11[24] = 8\'h40; data11[25] = 8\'h40; data11[26] = 8\'h40; data11[27] = 8\'h40; data11[28] = 8\'h20; data11[29] = 8\'h10; data11[30] = 8\'h00; data11[31] = 8\'h00; endendalways@(posedge sys_clk or negedge rst_n)begin if(rst_n == 1\'b0) begin data12[0] = 8\'h08; data12[1] = 8\'hF8; data12[2] = 8\'h88; data12[3] = 8\'h88; data12[4] = 8\'h88; data12[5] = 8\'h88; data12[6] = 8\'h70; data12[7] = 8\'h00; data12[8] = 8\'h20; data12[9] = 8\'h3F; data12[10] = 8\'h20; data12[11] = 8\'h00; data12[12] = 8\'h03; data12[13] = 8\'h0C; data12[14] = 8\'h30; data12[15] = 8\'h20; endendalways@(posedge sys_clk or negedge rst_n)begin if(rst_n == 1\'b0) begin data13[0] = 8\'h08; data13[1] = 8\'hF8; data13[2] = 8\'h08; data13[3] = 8\'h00; data13[4] = 8\'h00; data13[5] = 8\'h08; data13[6] = 8\'hF8; data13[7] = 8\'h08; data13[8] = 8\'h20; data13[9] = 8\'h3F; data13[10] = 8\'h21; data13[11] = 8\'h01; data13[12] = 8\'h01; data13[13] = 8\'h21; data13[14] = 8\'h3F; data13[15] = 8\'h20; endendendmodule 

下面是 OLED 模块初始化程序

//oled_init 初始化模块module OLED_Init( input  sys_clk, input  rst_n, input  init_req,  //初始化请求 input  write_done, //一组初始化数据完成信号 output  init_finish, //初始化完成输出 output[23:0] Init_data //初始化的数据);localparam RST_T  = 1\'b0;  //复位有效reg[23:0] Init_data_reg;reg[4:0] Init_index;assign Init_data = Init_data_reg;assign init_finish = (Init_index >= \'d26 && write_done == 1\'b1) ? 1\'b1 : 1\'b0;//初始化完成信号always@(posedge sys_clk or negedge rst_n)begin if(rst_n == RST_T) Init_index <= \'d0; else if(Init_index == \'d26 && write_done == 1\'b1 ) Init_index <= \'d0; else if(write_done == 1\'b1 && init_req == 1\'b1) Init_index <= Init_index + 1\'b1; else Init_index <= Init_index;endalways@(*)begin case(Init_index) \'d0: Init_data_reg <= {8\'h78,8\'h00,8\'hAE}; \'d1: Init_data_reg <= {8\'h78,8\'h00,8\'h00}; \'d2: Init_data_reg <= {8\'h78,8\'h00,8\'h10}; \'d3: Init_data_reg <= {8\'h78,8\'h00,8\'h40}; \'d4: Init_data_reg <= {8\'h78,8\'h00,8\'hB0}; \'d5: Init_data_reg <= {8\'h78,8\'h00,8\'h81}; \'d6: Init_data_reg <= {8\'h78,8\'h00,8\'hFF}; \'d7: Init_data_reg <= {8\'h78,8\'h00,8\'hA1}; \'d8: Init_data_reg <= {8\'h78,8\'h00,8\'hA6}; \'d9: Init_data_reg <= {8\'h78,8\'h00,8\'hA8}; \'d10: Init_data_reg <= {8\'h78,8\'h00,8\'h3F}; \'d11: Init_data_reg <= {8\'h78,8\'h00,8\'hC8}; \'d12: Init_data_reg <= {8\'h78,8\'h00,8\'hD3}; \'d13: Init_data_reg <= {8\'h78,8\'h00,8\'h00}; \'d14: Init_data_reg <= {8\'h78,8\'h00,8\'hD5}; \'d15: Init_data_reg <= {8\'h78,8\'h00,8\'h80}; \'d16: Init_data_reg <= {8\'h78,8\'h00,8\'hD8}; \'d17: Init_data_reg <= {8\'h78,8\'h00,8\'h05}; \'d18: Init_data_reg <= {8\'h78,8\'h00,8\'hD9}; \'d19: Init_data_reg <= {8\'h78,8\'h00,8\'hF1}; \'d20: Init_data_reg <= {8\'h78,8\'h00,8\'hDA}; \'d21: Init_data_reg <= {8\'h78,8\'h00,8\'h12}; \'d22: Init_data_reg <= {8\'h78,8\'h00,8\'hDB}; \'d23: Init_data_reg <= {8\'h78,8\'h00,8\'h30}; \'d24: Init_data_reg <= {8\'h78,8\'h00,8\'h8D}; \'d25: Init_data_reg <= {8\'h78,8\'h00,8\'h14}; \'d26: Init_data_reg <= {8\'h78,8\'h00,8\'hAF}; default: Init_data_reg <= {8\'h78,8\'h00,8\'hAE}; endcaseendendmodule

OLED_NumData 的模块:

/*数字数据0-9*/module OLED_NumData( input  sys_clk, input  rst_n, input  font_row, input[4:0] font_sel, input[4:0] index, output reg[7:0] data);/*0-9*/reg[7:0] data0[15:0];reg[7:0] data1[15:0];reg[7:0] data2[15:0];reg[7:0] data3[15:0];reg[7:0] data4[15:0];reg[7:0] data5[15:0];reg[7:0] data6[15:0];reg[7:0] data7[15:0];reg[7:0] data8[15:0];reg[7:0] data9[15:0];always@(posedge sys_clk or negedge rst_n)begin if(rst_n == 1\'b0) data <= \'d0; else if(font_sel == \'d0) data <= data0[index + \'d8 * font_row]; else if(font_sel == \'d1) data <= data1[index + \'d8 * font_row]; else if(font_sel == \'d2) data <= data2[index + \'d8 * font_row]; else if(font_sel == \'d3) data <= data3[index + \'d8 * font_row]; else if(font_sel == \'d4) data <= data4[index + \'d8 * font_row]; else if(font_sel == \'d5) data <= data5[index + \'d8 * font_row]; else if(font_sel == \'d6) data <= data6[index + \'d8 * font_row]; else if(font_sel == \'d7) data <= data7[index + \'d8 * font_row]; else if(font_sel == \'d8) data <= data8[index + \'d8 * font_row]; else if(font_sel == \'d9) data <= data9[index + \'d8 * font_row];endalways@(posedge sys_clk or negedge rst_n)begin if(rst_n == 1\'b0) begin data0[0] = 8\'h00; data0[1] = 8\'hE0; data0[2] = 8\'h10; data0[3] = 8\'h08; data0[4] = 8\'h08; data0[5] = 8\'h10; data0[6] = 8\'hE0; data0[7] = 8\'h00; data0[8] = 8\'h00; data0[9] = 8\'h0F; data0[10] = 8\'h10; data0[11] = 8\'h20; data0[12] = 8\'h20; data0[13] = 8\'h10; data0[14] = 8\'h0F; data0[15] = 8\'h00; endendalways@(posedge sys_clk or negedge rst_n)begin if(rst_n == 1\'b0) begin data1[0] = 8\'h00; data1[1] = 8\'h00; data1[2] = 8\'h10; data1[3] = 8\'h10; data1[4] = 8\'hF8; data1[5] = 8\'h00; data1[6] = 8\'h00; data1[7] = 8\'h00; data1[8] = 8\'h00; data1[9] = 8\'h00; data1[10] = 8\'h20; data1[11] = 8\'h20; data1[12] = 8\'h3F; data1[13] = 8\'h20; data1[14] = 8\'h20; data1[15] = 8\'h00; endendalways@(posedge sys_clk or negedge rst_n)begin if(rst_n == 1\'b0) begin data2[0] = 8\'h00; data2[1] = 8\'h70; data2[2] = 8\'h08; data2[3] = 8\'h08; data2[4] = 8\'h08; data2[5] = 8\'h08; data2[6] = 8\'hF0; data2[7] = 8\'h00; data2[8] = 8\'h00; data2[9] = 8\'h30; data2[10] = 8\'h28; data2[11] = 8\'h24; data2[12] = 8\'h22; data2[13] = 8\'h21; data2[14] = 8\'h30; data2[15] = 8\'h00; endendalways@(posedge sys_clk or negedge rst_n)begin if(rst_n == 1\'b0) begin data3[0] = 8\'h00; data3[1] = 8\'h30; data3[2] = 8\'h08; data3[3] = 8\'h08; data3[4] = 8\'h08; data3[5] = 8\'h88; data3[6] = 8\'h70; data3[7] = 8\'h00; data3[8] = 8\'h00; data3[9] = 8\'h18; data3[10] = 8\'h20; data3[11] = 8\'h21; data3[12] = 8\'h21; data3[13] = 8\'h22; data3[14] = 8\'h1C; data3[15] = 8\'h00; endendalways@(posedge sys_clk or negedge rst_n)begin if(rst_n == 1\'b0) begin data4[0] = 8\'h00; data4[1] = 8\'h00; data4[2] = 8\'h80; data4[3] = 8\'h40; data4[4] = 8\'h30; data4[5] = 8\'hF8; data4[6] = 8\'h00; data4[7] = 8\'h00; data4[8] = 8\'h00; data4[9] = 8\'h06; data4[10] = 8\'h05; data4[11] = 8\'h24; data4[12] = 8\'h24; data4[13] = 8\'h3F; data4[14] = 8\'h24; data4[15] = 8\'h24; endendalways@(posedge sys_clk or negedge rst_n)begin if(rst_n == 1\'b0) begin data5[0] = 8\'h00; data5[1] = 8\'hF8; data5[2] = 8\'h88; data5[3] = 8\'h88; data5[4] = 8\'h88; data5[5] = 8\'h08; data5[6] = 8\'h08; data5[7] = 8\'h00; data5[8] = 8\'h00; data5[9] = 8\'h19; data5[10] = 8\'h20; data5[11] = 8\'h20; data5[12] = 8\'h20; data5[13] = 8\'h11; data5[14] = 8\'h0E; data5[15] = 8\'h00; endendalways@(posedge sys_clk or negedge rst_n)begin if(rst_n == 1\'b0) begin data6[0] = 8\'h00; data6[1] = 8\'hE0; data6[2] = 8\'h10; data6[3] = 8\'h88; data6[4] = 8\'h88; data6[5] = 8\'h90; data6[6] = 8\'h00; data6[7] = 8\'h00; data6[8] = 8\'h00; data6[9] = 8\'h0F; data6[10] = 8\'h11; data6[11] = 8\'h20; data6[12] = 8\'h20; data6[13] = 8\'h20; data6[14] = 8\'h1F; data6[15] = 8\'h00; endendalways@(posedge sys_clk or negedge rst_n)begin if(rst_n == 1\'b0) begin data7[0] = 8\'h00; data7[1] = 8\'h18; data7[2] = 8\'h08; data7[3] = 8\'h08; data7[4] = 8\'h88; data7[5] = 8\'h68; data7[6] = 8\'h18; data7[7] = 8\'h00; data7[8] = 8\'h00; data7[9] = 8\'h00; data7[10] = 8\'h00; data7[11] = 8\'h3E; data7[12] = 8\'h01; data7[13] = 8\'h00; data7[14] = 8\'h00; data7[15] = 8\'h00; endendalways@(posedge sys_clk or negedge rst_n)begin if(rst_n == 1\'b0) begin data8[0] = 8\'h00; data8[1] = 8\'h70; data8[2] = 8\'h88; data8[3] = 8\'h08; data8[4] = 8\'h08; data8[5] = 8\'h88; data8[6] = 8\'h70; data8[7] = 8\'h00; data8[8] = 8\'h00; data8[9] = 8\'h1C; data8[10] = 8\'h22; data8[11] = 8\'h21; data8[12] = 8\'h21; data8[13] = 8\'h22; data8[14] = 8\'h1C; data8[15] = 8\'h00; endendalways@(posedge sys_clk or negedge rst_n)begin if(rst_n == 1\'b0) begin data9[0] = 8\'h00; data9[1] = 8\'hF0; data9[2] = 8\'h08; data9[3] = 8\'h08; data9[4] = 8\'h08; data9[5] = 8\'h10; data9[6] = 8\'hE0; data9[7] = 8\'h00; data9[8] = 8\'h00; data9[9] = 8\'h01; data9[10] = 8\'h12; data9[11] = 8\'h22; data9[12] = 8\'h22; data9[13] = 8\'h11; data9[14] = 8\'h0F; data9[15] = 8\'h00; endendendmodule 

OLED 刷新模块:

//刷新模块,将oled屏幕全部变为黑色module OLED_Refresh( input  sys_clk, input  rst_n, input  refresh_req, //初始化请求 input  write_done, //一组初始化数据完成信号 output  refresh_finish, //初始化完成输出 output[23:0] refresh_data //初始化的数据);reg[23:0] refresh_data_reg;reg[10:0] refresh_index;reg[2:0] page;assign refresh_data = refresh_data_reg;assign refresh_finish = (page == \'d7 && refresh_index == \'d130 && write_done == 1\'b1) ? 1\'b1 : 1\'b0;always@(posedge sys_clk or negedge rst_n)begin if(rst_n == 1\'b0) refresh_index <= \'d0; else if(refresh_index == \'d130 && write_done == 1\'b1) refresh_index <= \'d0; else if(write_done == 1\'b1) refresh_index <= refresh_index + 1\'b1; else refresh_index <= refresh_index;endalways@(posedge sys_clk or negedge rst_n)begin if(rst_n == 1\'b0) page <= \'d0; else if(refresh_index == \'d130 && write_done == 1\'b1) page <= page + 1\'b1; else page <= page;endalways@(*)begin case(refresh_index) \'d0: refresh_data_reg <= {8\'h78,8\'h00,8\'hB0 + page}; \'d1: refresh_data_reg <= {8\'h78,8\'h00,8\'h00}; \'d2: refresh_data_reg <= {8\'h78,8\'h00,8\'h10}; default: refresh_data_reg <= {8\'h78,8\'h40,8\'h00}; endcaseendendmodule 

OLED_SelData 模块

module OLED_SelData( input  sys_clk, input  rst_n, input  init_req, input[23:0] init_data, input  refresh_req, input[23:0] refresh_data, input  showfont_req, input[23:0] showfont_data, input  showdata_req, input[23:0] showdata_data, output IICWriteReq, output[23:0] IICWriteData);reg IICWriteReqReg;reg[23:0] IICWriteDataReg; assign IICWriteReq = init_req | showfont_req | refresh_req | showdata_req;assign IICWriteData = (init_req == 1\'b1) ? init_data : (refresh_req == 1\'b1) ? refresh_data : (showfont_req == 1\'b1) ? showfont_data : showdata_data;endmodule 

OLED_ShowData 模块:

module OLED_ShowData( input  sys_clk, input  rst_n, input  dht11_done, input[7:0] tempH, input[7:0] tempL, input[7:0] humidityH, input[7:0] humidityL, input  ShowData_req, //字符显示请求 input  write_done, //iic一组数据写完成 output[23:0] ShowData_Data, //字符显示数据 output ShowData_finish //字符显示完成);////reg[3:0] tempHH; //tempH的高位//reg[3:0] tempHL; //tempH的低位//reg[3:0] tempLH;//reg[3:0] tempLL;////reg[3:0] humidityHH;//reg[3:0] humidityHL;//reg[3:0] humidityLH;//reg[3:0] humidityLL;reg[7:0] tempHREG;reg[7:0] tempLREG;reg[7:0] humidityHREG;reg[7:0] humidityLREG;reg[4:0] font;reg[4:0] font_sel;reg[4:0] font_index;reg font_row;reg[7:0] show_x;reg[3:0] show_y;reg[23:0] showfont_data_reg;wire onefont_finish;wire[7:0] fontdata;assign onefont_finish = (font_row == 1\'b1 && font_index == \'d10 && write_done == 1\'b1) ? 1\'b1 : 1\'b0;assign ShowData_finish = (onefont_finish == 1\'b1 && font_sel == \'d7) ? 1\'b1 : 1\'b0;assign ShowData_Data = showfont_data_reg;always@(*)begin case(font_index) \'d0: showfont_data_reg <= {8\'h78,8\'h00,8\'hB0 + show_y + font_row}; \'d1: showfont_data_reg <= {8\'h78,8\'h00,8\'h00 + show_x[3:0]}; \'d2: showfont_data_reg <= {8\'h78,8\'h00,8\'h10 + show_x[7:4]}; default: showfont_data_reg <= {8\'h78,8\'h40,fontdata}; //fontdata endcaseendalways@(posedge sys_clk or negedge rst_n)begin if(rst_n == 1\'b0) font_index <= \'d0; else if(write_done == 1\'b1 && font_index == \'d10) font_index <= \'d0; else if(write_done == 1\'b1 && ShowData_req == 1\'b1) font_index <= font_index + 1\'b1; else font_index <= font_index;endalways@(posedge sys_clk or negedge rst_n)begin if(rst_n == 1\'b0) font_row <= 1\'b0; else if(onefont_finish == 1\'b1) font_row <= 1\'b0; else if(write_done == 1\'b1 && font_index == \'d10) font_row <= 1\'b1; else font_row <= font_row;endalways@(posedge sys_clk or negedge rst_n)begin if(rst_n == 1\'b0) font_sel <= \'d0; else if(ShowData_finish == 1\'b1) font_sel <= \'d0; else if(onefont_finish == 1\'b1) font_sel <= font_sel + 1\'b1; else font_sel <= font_sel;endalways@(posedge sys_clk or negedge rst_n)begin if(rst_n == 1\'b0) begin show_x <= \'d0; show_y <= \'d0; end else if(font_sel == \'d0) begin show_x <= \'d54; show_y <= \'d3; end else if(font_sel == \'d1) begin show_x <= \'d62; show_y <= \'d3; end else if(font_sel == \'d2) begin show_x <= \'d79; show_y <= \'d3; end else if(font_sel == \'d3) begin show_x <= \'d88; show_y <= \'d3; end else if(font_sel == \'d4) begin show_x <= \'d54; show_y <= \'d5; end else if(font_sel == \'d5) begin show_x <= \'d62; show_y <= \'d5; end else if(font_sel == \'d6) begin show_x <= \'d79; show_y <= \'d5; end else if(font_sel == \'d7) begin show_x <= \'d88; show_y <= \'d5; end else begin show_x <= \'d0; show_y <= \'d0; endendalways@(posedge sys_clk or negedge rst_n)begin if(rst_n == 1\'b0) font <= \'d0; else if(font_sel == \'d0) font <= tempHREG / 10; else if(font_sel == \'d1) font <= tempHREG % 10; else if(font_sel == \'d2) font <= tempLREG / 10; else if(font_sel == \'d3) font <= tempLREG % 10; else if(font_sel == \'d4) font <= humidityHREG / 10; else if(font_sel == \'d5) font <= humidityHREG % 10; else if(font_sel == \'d6) font <= humidityLREG / 10; else if(font_sel == \'d7) font <= humidityLREG % 10; else font <= font; endalways@(posedge sys_clk or negedge rst_n)begin if(rst_n == 1\'b0) tempHREG <= \'d0; else if(dht11_done == 1\'b1) tempHREG <= tempH; else tempHREG <= tempHREG;endalways@(posedge sys_clk or negedge rst_n)begin if(rst_n == 1\'b0) tempLREG <= \'d0; else if(dht11_done == 1\'b1) tempLREG <= tempL; else tempLREG <= tempLREG;endalways@(posedge sys_clk or negedge rst_n)begin if(rst_n == 1\'b0) humidityHREG <= \'d0; else if(dht11_done == 1\'b1) humidityHREG <= humidityH; else humidityHREG <= humidityHREG;endalways@(posedge sys_clk or negedge rst_n)begin if(rst_n == 1\'b0) humidityLREG <= \'d0; else if(dht11_done == 1\'b1) humidityLREG <= humidityL; else humidityLREG <= humidityLREG;endOLED_NumData OLED_NumDataHP( .sys_clk (sys_clk), .rst_n (rst_n), .font_row (font_row), .font_sel (font), .index (font_index - \'d3), .data  (fontdata));endmodule 

OLED_ShowFont 模块:

module OLED_ShowFont( input  sys_clk, input  rst_n, input  ShowFont_req, //字符显示请求 input  write_done, //iic一组数据写完成 output[23:0] ShowFont_Data, //字符显示数据 output ShowFont_finish //字符显示完成);reg[8:0] showfont_index;reg[23:0] showfont_data_reg;wire[7:0] fontdata;reg[5:0] font_index; //当前显示第几个字符reg[1:0] font_size; //1 :16*16 0 : 8*16reg[7:0] show_x;reg[3:0] show_y;reg font_row;wire onefont_finish;assign onefont_finish = ((showfont_index == (\'d10 + \'d8 * font_size)) && (font_row == 1\'b1) && write_done == 1\'b1) ? 1\'b1 : 1\'b0;assign ShowFont_finish = (onefont_finish == 1\'b1 && font_index == \'d14) ? 1\'b1 : 1\'b0;assign ShowFont_Data = showfont_data_reg;always@(posedge sys_clk or negedge rst_n)begin if(rst_n == 1\'b0) showfont_index <= \'d0; else if(onefont_finish == 1\'b1) showfont_index <= \'d0; else if(font_size == \'d0 && showfont_index == \'d10 && write_done == 1\'b1) showfont_index <= \'d0; else if(font_size == \'d1 && showfont_index == \'d18 && write_done == 1\'b1) showfont_index <= \'d0; else if(write_done == 1\'b1 && ShowFont_req == 1\'b1) showfont_index <= showfont_index + 1\'b1; else showfont_index <= showfont_index;endalways@(*)begin case(showfont_index) \'d0: showfont_data_reg <= {8\'h78,8\'h00,8\'hB0 + show_y + font_row}; \'d1: showfont_data_reg <= {8\'h78,8\'h00,8\'h00 + show_x[3:0]}; \'d2: showfont_data_reg <= {8\'h78,8\'h00,8\'h10 + show_x[7:4]}; default: showfont_data_reg <= {8\'h78,8\'h40,fontdata}; //fontdata endcaseendalways@(posedge sys_clk or negedge rst_n)begin if(rst_n == 1\'b0) font_row <= 1\'b0; else if(onefont_finish == 1\'b1) font_row <= 1\'b0; else if(font_size == \'d0 && showfont_index == \'d10 && write_done == 1\'b1) font_row <= 1\'b1; else if(font_size == \'d1 && showfont_index == \'d18 && write_done == 1\'b1) font_row <= 1\'b1; else font_row <= font_row;endalways@(posedge sys_clk or negedge rst_n)begin if(rst_n == 1\'b0) font_size <= \'d0; else if(font_index <= \'d3) font_size <= \'d0; else if(font_index == \'d10 || font_index == \'d12 || font_index == \'d13 || font_index == \'d14) font_size <= \'d0; else font_size <= \'d1;endalways@(posedge sys_clk or negedge rst_n)begin if(rst_n == 1\'b0) font_index <= \'d0; else if(ShowFont_finish == 1\'b1) font_index <= \'d0; else if(onefont_finish == 1\'b1) font_index <= font_index + 1\'b1; else font_index <= font_index;endalways@(posedge sys_clk or negedge rst_n)begin if(rst_n == 1\'b0) //F begin show_x <= \'d8; show_y <= \'d0; end else if(onefont_finish == 1\'b1 && font_index == \'d0) //P begin show_x <= \'d24; show_y <= \'d0; end else if(onefont_finish == 1\'b1 && font_index == \'d1) //G begin show_x <= \'d40; show_y <= \'d0; end else if(onefont_finish == 1\'b1 && font_index == \'d2) //A begin show_x <= \'d56; show_y <= \'d0; end else if(onefont_finish == 1\'b1 && font_index == \'d3) //之 begin show_x <= \'d77; show_y <= \'d0; end else if(onefont_finish == 1\'b1 && font_index == \'d4) //旅 begin show_x <= \'d100; show_y <= \'d0; end else if(onefont_finish == 1\'b1 && font_index == \'d5) //温 begin show_x <= \'d10; show_y <= \'d3; end else if(onefont_finish == 1\'b1 && font_index == \'d6) //度 begin show_x <= \'d30; show_y <= \'d3; end else if(onefont_finish == 1\'b1 && font_index == \'d7) //湿 begin show_x <= \'d10; show_y <= \'d5; end else if(onefont_finish == 1\'b1 && font_index == \'d8) //度 begin show_x <= \'d30; show_y <= \'d5; end else if(onefont_finish == 1\'b1 && font_index == \'d9) //. begin show_x <= \'d70; show_y <= \'d3; end else if(onefont_finish == 1\'b1 && font_index == \'d10) //℃ begin show_x <= \'d100; show_y <= \'d3; end else if(onefont_finish == 1\'b1 && font_index == \'d11) //R begin show_x <= \'d100; show_y <= \'d5; end else if(onefont_finish == 1\'b1 && font_index == \'d12) //H begin show_x <= \'d108; show_y <= \'d5; end else if(onefont_finish == 1\'b1 && font_index == \'d13) //. begin show_x <= \'d70; show_y <= \'d5; end else begin show_x <= show_x; show_y <= show_y; endendOLED_FontData OLED_FontData_HP( .sys_clk (sys_clk), .rst_n (rst_n), .font_row (font_row), .font_sel (font_index), .index (showfont_index - \'d3), .data  (fontdata));endmodule 

OLED_Top.v 模块:

//OLED顶层模块module OLED_Top( input  sys_clk, input  rst_n, //DHT11数值显示 input  dht11_done, input[7:0] tempH, input[7:0] tempL, input[7:0] humidityH, input[7:0] humidityL, //OLED IIC output OLED_SCL, inout  OLED_SDA);localparam OLED_INIT = \'d0; //初始化localparam OLED_Refresh = \'d1; //刷新,将oled全部写0localparam OLED_ShowFont = \'d2; //显示字符localparam OLED_IDLE = \'d3; //空闲localparam OLED_ShowData = \'d4; //显示数据reg[4:0] state , next_state;//IIC相关信号wire IICWriteReq;wire[23:0] IICWriteData;wire IICWriteDone;//初始化相关信号wire init_finish;wire[23:0] Init_data;wire init_req;//refresh相关信号wire refresh_finish;wire[23:0] refresh_data;wire  refresh_req;//字符显示相关信号wire showfont_finish;wire[23:0] showfont_data;wire showfont_req;//显示数据相关信号wire showdata_finish;wire[23:0] showdata_data;wire showdata_req;assign init_req = (state == OLED_INIT) ? 1\'b1 : 1\'b0;assign refresh_req = (state == OLED_Refresh) ? 1\'b1 : 1\'b0;assign showfont_req = (state == OLED_ShowFont) ? 1\'b1 : 1\'b0;assign showdata_req = (state == OLED_ShowData) ? 1\'b1 : 1\'b0;always@(posedge sys_clk or negedge rst_n)begin if(rst_n == 1\'b0) state <= OLED_INIT; else state <= next_state;endalways@(*)begin case(state) OLED_INIT: if(init_finish == 1\'b1) next_state <= OLED_Refresh; else next_state <= OLED_INIT; OLED_Refresh: if(refresh_finish == 1\'b1) next_state <= OLED_ShowFont; else next_state <= OLED_Refresh; OLED_ShowFont: if(showfont_finish == 1\'b1) next_state <= OLED_IDLE; else next_state <= OLED_ShowFont; OLED_IDLE: if(dht11_done == 1\'b1) next_state <= OLED_ShowData; else next_state <= OLED_IDLE; OLED_ShowData: if(showdata_finish == 1\'b1) next_state <= OLED_IDLE; else next_state <= OLED_ShowData; default: next_state <= OLED_INIT; endcaseendOLED_Init OLED_InitHP( .sys_clk (sys_clk), .rst_n  (rst_n), .init_req  (init_req), //初始化请求 .write_done (IICWriteDone), //一组初始化数据完成信号 .init_finish (init_finish), //初始化完成输出 .Init_data (Init_data)//初始化的数据);OLED_Refresh( .sys_clk (sys_clk), .rst_n  (rst_n), .refresh_req (refresh_req),  //初始化请求 .write_done (IICWriteDone), //一组初始化数据完成信号 .refresh_finish (refresh_finish),  //初始化完成输出 .refresh_data (refresh_data) //初始化的数据);OLED_ShowFont OLED_ShowFont_HP( .sys_clk (sys_clk), .rst_n (rst_n), .ShowFont_req (showfont_req), //字符显示请求 .write_done (IICWriteDone), //iic一组数据写完成 .ShowFont_Data (showfont_data), //字符显示数据 .ShowFont_finish(showfont_finish) //字符显示完成);OLED_ShowData OLED_ShowDataHP( .sys_clk (sys_clk), .rst_n (rst_n), .dht11_done (dht11_done), .tempH (tempH), //温度数据整数 .tempL (tempL), //温度数据小数 .humidityH (humidityH), //温度数据整数 .humidityL (humidityL), //温度数据小数 .ShowData_req (showdata_req), //字符显示请求 .write_done (IICWriteDone), //iic一组数据写完成 .ShowData_Data (showdata_data), //字符显示数据 .ShowData_finish (showdata_finish) //字符显示完成);//数据选择OLED_SelData OLED_SelDataHP( .sys_clk (sys_clk), .rst_n (rst_n), .init_req (init_req), .init_data (Init_data), .refresh_req (refresh_req), .refresh_data (refresh_data), .showfont_req (showfont_req), .showfont_data (showfont_data), .showdata_req (showdata_req), .showdata_data (showdata_data), .IICWriteReq (IICWriteReq), .IICWriteData (IICWriteData));IIC_Driver IIC_DriverHP_OLED( .sys_clk (sys_clk),  /*系统时钟*/ .rst_n  (rst_n), /*系统复位*/ .IICSCL (OLED_SCL), /*IIC 时钟输出*/ .IICSDA (OLED_SDA), /*IIC 数据线*/ .IICSlave  ({IICWriteData[15:8],IICWriteData[23:16]}),  /*从机 8bit的寄存器地址 + 8bit的从机地址*/ .IICWriteReq (IICWriteReq), /*IIC写寄存器请求*/ .IICWriteDone (IICWriteDone), /*IIC写寄存器完成*/ .IICWriteData (IICWriteData[7:0]), /*IIC发送数据 8bit的数据*/ .IICReadReq (1\'b0), /*IIC读寄存器请求*/ .IICReadDone (), /*IIC读寄存器完成*/ .IICReadData () /*IIC读取数据*/);endmodule 

参考链接

[1] STM32 7 针 0.96 寸 OLED 显示屏(硬件 SPI+DMA)无需内核响应 超高刷新率!_0.96oled 速率 - CSDN 博客

[2] 7 脚 spi OLED 屏幕改造成 IIC 屏幕_7 针 spioled 改 4 针 iic-CSDN 博客

[3] FPGA 之旅设计 99 例之第九例 ----- 驱动 0.96 寸 OLED 屏_fpga oled 12864-CSDN 博客

[4] IIC 通讯协议手册

[5] 一次讲透 I2C 总线时序图,单片机 IIC 通信协议底层【洋桃电子大百科 P28】_哔哩哔哩_bilibili

免费财务软件