赞
踩
本次所用开发板FPGA芯片型号为:EP4CE6F17C8 EEPROM
芯片型号为:24LC04B UART串口设置为:
波特率11520,无校验位。
本次仅实现了EEPROM的单字节读写。若要实现连续读写可以在下文的EEPROM控制模块设置计数器控制读写字数。
一、I2C协议
1.1 I2C协议简介
①IIC:(Inter-Integrated Circuit)即集成电路总线,是一种两线 式串行总线,由 PHILIPS 公司开发,用于连接微控制器及其外围设备。 多用于主机和从机在数据量不大且传输距离短的场合下的主从通信。
物理层的特点:
(1) 它是一个支持多设备的总线(支持多主机多从机)。
(2) IIC 总线只使用两条总线线路,一条双向串行数据线(SDA) 一条串行时钟线(SCL)。数据线即用来表示数据,时钟线用于数据收发同步。
(3) 每个连接到 IIC 总线的设备都有一个独立的地址,主机可以利用设备独立地址访问不同设备。
(4) IIC 总线通过上拉电阻接到电源。当 IIC 设备空闲时,设备会输出高阻态,当所有设备都空闲,都输出高阻态时,由上拉电阻把 IIC总线拉成高电平。
(5) IIC 总线具有仲裁机制。当多个主机同时发起传输时,触发仲裁机制,最终只给一个主机授权。
(6)具有三种传输模式:
1.3 IIC 协议层:
① IIC 协议空闲状态
② IIC 协议的起始信号
③ IIC 协议的数据传输(数据读写)状态(应答信号(ACK))
④ IIC 协议的停止信号
图中标注的①②③④表示 IIC 协议的 4 个状态,
分别为“总线空闲状态”、“起始信号”、“数据读/写状态”和“停止信号”。
空闲状态:图中标注①表示“总线空闲状态”,在此状态下串口时钟信号SCL和串行数据信号 SDA均保持高电平(都为 1),此时无IIC设备工作。
起始位:
图中标注②表示“起始信号”,在 IIC 总线处于“空闲状态”时, 时钟信号线 SCL 为高电平时,数据信号线 SDA 被拉低(由高电平变为低电平),出现下降沿,表示产生了一个起始信号。起始信号是由主机(本实验中为 FPGA)主动建立的,在建立该信号之前IIC总线必须处于空闲状态。
数据传输:
同时,协议规定在SCL高电平时期SDA数据线必须保持稳定,在SCL低电平时,SDA才允许发生改变。主机进行数据读写时,I2C协议规定,数据传输时先发送寻址字节,即7位从机地址+0/1。其中0表示主机写,1表示主机读。寻址字节发送完成后才是数据字节。
应答位:
I2C协议规定,主机每次向从机传输1字节数据后,需要接收一次从机的应答信号。0为接收成功;1为接受失败,没有响应。
停止位:
当SCL为高电平时,数据线SDA拉高,则认为检测到停止位,一次数据传输结束。
注意:SCL高电平数据必须保持,SCL低电平数据才能改变。
二、EEPROM
2.1 基本信息
EEPROM的全称是“电可擦除可编程只读存储器”,即Electrically Erasable Programmable Read-Only Memory。本次项目中使用的EEPROM型号为24LC04B。
24LC04B 的存储容量为 4Kbit,其内部有两个 Block,每个Block中有256个字节(一个字节为 8bit)。其读写操作都是以字节(8bit)为基本单位。24LC04B EEPROM存储芯片的器件地址包括厂商设置的高4 位 1010和用户需自主设置的低3位A0、A1、A2。
在IIC主从设备通讯时,主机在发送了起始信号后,接着会向从机发送控制命令。控制命令长度为1个字节,它的高7位为上文讲解的IIC设备的器件地址,最低位为读写控制位。EEPROM储存芯片控制命令格式示意图,具体见下图:
三、IIC协议读写EEPROM
EEPROM的写操作有:
①单字节写(Byte WRITE) ②页写(Page WRITE)
EEPROM的读操作有:
①当前地址读(Current Address READ)
②随机地址读(RANDOM READ)
③顺序地址读(SEQUENTIAL READ)
3.1 EEPROM单字节写(Byte WRITE)操作时序
单字节写操作:在数据信号线SDA上,发起始 -> 控制字节,从机接收到发应答信号 -> 写数据地址,从机接收到发应答信号,如果数据地址是2位的话,就继续写数据地址,从机接收到发应答信号 -> 写数据,从机接收到发应答信号 -> 最后发停止位。
3.2 EEPROM页写操作时序
页写(PAGE WRITE)操作:一次写入16个字节(每字节为8bit)数据。
在数据信号线SDA上,发起始位 -> 写控制字节,从机接收到应答信号 -> 写数据地址,从机接收到应答信号,如果数据地址是2位的话,就继续写数据地址,从机接收到应答信号 -> 写数据,从机接收到应答信号 -> 继续写数据,直到写完全部的数据 -> 最后发停止位。
当前地址读(Curren Address READ)操作:在数据线SDA上,发起始位 -> 写控制字节,从机接收到应答信号,然后读数据,无应答信号(发No ACK) -> 发停止位。
随机地制读(RANDOM READ)操作:在数据信号线SDA上,发起始位 -> 写控制字节,从机接收到应答信号 -> 再发起始位 -> 读控制字节,从机接收到应答信号 -> 接收读数据 -> 发无应答(No ACK)-> 最后发停止位。
3.5 EEPROM 顺序地址读(SEQUENTIAL READ)操作时序
顺序地址读(RANDOM READ)操作:在数据信号线SDA上,发起始位 -> 写控制字节,从机接收到应答信号 -> 虚写(dummuy write),写数据地址,从机接收到应答信号 -> 再发开始位 -> 读控制字节,从机接收到应答信号 -> 然后接收读数据 -> 发No ACK -> 最后发结束位
顺序地址读实质就是在随机地制读之后
四、状态机
4.1 IIC接口模块
五、程序
5.1、IIC接口模块
- /**************************************功能介绍***********************************
- Date : 2024年5月20日16:16:29
- Author :
- Version : 1.0
- Description: I2C接口模块
-
- 接口命令列表(cmd):
- 0 1
- bit0(起始位) NO YES
- bit1(写数据) NO YES
- bit2(读数据) NO YES
- bit3(停止位) NO YES
- bit4(响应位) ACK NO ACK
-
- *********************************************************************************/
-
- //---------<模块及端口声名>------------------------------------------------------
- module I2C_driver(
- input clk ,
- input rst_n ,
-
- input [7:0] wr_data ,//要写进的数据
- input [4:0] cmd ,//命令信号
- input cmd_vld ,//命令信号有效信号
-
- output [7:0] rd_data ,//读出的数据
- output rd_data_vld,
-
- output reg rev_ack ,//记响应位为ack还是no ack
-
- output done ,//表示写/读完8bit的数据
-
- output reg scl ,
- inout sda
- );
- //---------<参数定义>---------------------------------------------------------
- //状态机参数定义
- localparam IDLE = 7'b0000001,//空闲状态
- START = 7'b0000010,//起始位
- WR_DATA = 7'b0000100,//写状态(包括写控制字节,写地址和写数据)
- RD_DATA = 7'b0001000,//读状态
- R_ACK = 7'b0010000,//收ACK(由EEPROM发送给FPGA)
- T_ACK = 7'b0100000,//发ACK(由FPGA发送给EEPROM)考虑为ACK还是NO ACK
- STOP = 7'b1000000;//结束位
- parameter T = 100_000,//速率 100k,400k,3.4M
- SCL_MAX = 50_000_000 / T;//速率为100k时,计1bit需要计数500
- parameter SCL_LOW_HALF = (SCL_MAX * 1 / 4) - 1,
- SCL_HIGH_HALF = (SCL_MAX * 3 / 4) - 1;
-
- `define START_BIT 5'b00001
- `define WRITE_BIT 5'b00010
- `define READ_BIT 5'b00100
- `define STOP_BIT 5'b01000
- `define ACK_BIT 5'b10000
-
- `define ACK 0
- `define NO_ACK 1
- //---------<内部信号定义>-----------------------------------------------------
- reg [6:0] cstate ;//现态
- reg [6:0] nstate ;//次态
-
- reg [4:0] cmd_r ;//cmd打一拍
- reg [7:0] wr_data_r ;
- reg [7:0] rd_data_r ;
-
- reg sda_out ;
- reg OE ;//三态门使能信号
- wire sda_in ;
-
- reg [8:0] cnt_bit ;//计1bit(IIC工作时钟)
- wire add_cnt_bit ;
- wire end_cnt_bit ;
-
- reg [3:0] cnt_num ;//计8bit
- wire add_cnt_num ;
- wire end_cnt_num ;
- reg [3:0] num ;
-
- //状态转移条件
- wire IDLE_START ;
- wire START_WR_DATA ;
- wire WR_DATA_R_ACK ;
- wire R_ACK_IDLE ;
- wire IDLE_WR_DATA ;
- wire R_ACK_STOP ;
- wire STOP_IDLE ;
-
- wire IDLE_RD_DATA ;
- wire RD_DATA_T_ACK ;
- wire T_ACK_IDLE ;
- wire T_ACK_STOP ;
-
- //寄存wr_data_r和cmd_r
- always @(posedge clk or negedge rst_n) begin
- if (!rst_n) begin
- wr_data_r <= 'd0;
- cmd_r <= 'd0;
- end
- else if (cmd_vld) begin
- wr_data_r <= wr_data;
- cmd_r <= cmd;
- end
- end
- /****************************************************************
- 计数器
- ****************************************************************/
- //计1bit(IIC工作时钟)
- always @(posedge clk or negedge rst_n)begin
- if(!rst_n)begin
- cnt_bit <= 'd0;
- end
- else if(add_cnt_bit)begin
- if(end_cnt_bit)begin
- cnt_bit <= 'd0;
- end
- else begin
- cnt_bit <= cnt_bit + 1'd1;
- end
- end
- end
-
- assign add_cnt_bit = cstate != IDLE;
- assign end_cnt_bit = add_cnt_bit && cnt_bit == SCL_MAX - 1'd1;
- //IIC_SCL
- always @(posedge clk or negedge rst_n) begin
- if (!rst_n) begin
- scl <= 'd1;
- end
- else if (cnt_bit == (SCL_MAX - 1 ) >> 1 || STOP_IDLE) begin
- scl <= 'd1;
- end
- else if (end_cnt_bit) begin
- scl <= 'd0;
- end
- end
- //8bit计数器
- always @(posedge clk or negedge rst_n)begin
- if(!rst_n)begin
- cnt_num <= 'd0;
- end
- else if(add_cnt_num)begin
- if(end_cnt_num)begin
- cnt_num <= 'd0;
- end
- else begin
- cnt_num <= cnt_num + 1'd1;
- end
- end
- end
-
- assign add_cnt_num = end_cnt_bit;
- assign end_cnt_num = add_cnt_num && cnt_num == num - 1;
- //考察计bit数最大值
- always @(*) begin
- case (cstate)
- IDLE : num = 1;
- START : num = 1;
- WR_DATA : num = 8;
- RD_DATA : num = 8;
- R_ACK : num = 1;
- T_ACK : num = 1;
- STOP : num = 1;
- default : num = 1;
- endcase
- end
- /****************************************************************
- 状态机
- ****************************************************************/
- //第一段:时序逻辑描述状态转移
- always @(posedge clk or negedge rst_n)begin
- if(!rst_n)begin
- cstate <= IDLE;
- end
- else begin
- cstate <= nstate;
- end
- end
- //第二段:组合逻辑描述状态转移规律和状态转移条件
- always @(*) begin
- case(cstate)
- IDLE : begin
- if (IDLE_START) begin
- nstate = START;
- end
- else if (IDLE_WR_DATA) begin
- nstate = WR_DATA;
- end
- else if (IDLE_RD_DATA) begin
- nstate = RD_DATA;
- end
- else begin
- nstate = cstate;
- end
- end
- START : begin
- if (START_WR_DATA) begin
- nstate = WR_DATA;
- end
- else begin
- nstate = cstate;
- end
- end
- WR_DATA : begin
- if (WR_DATA_R_ACK) begin
- nstate = R_ACK;
- end
- else begin
- nstate = cstate;
- end
- end
- RD_DATA : begin
- if (RD_DATA_T_ACK) begin
- nstate = T_ACK;
- end
- else begin
- nstate = cstate;
- end
- end
- R_ACK : begin
- if (R_ACK_STOP) begin
- nstate = STOP;
- end
- else if (R_ACK_IDLE) begin
- nstate = IDLE;
- end
- else begin
- nstate = cstate;
- end
- end
- T_ACK : begin
- if (T_ACK_STOP) begin
- nstate = STOP;
- end
- else if (T_ACK_IDLE) begin
- nstate = IDLE;
- end
- else begin
- nstate = cstate;
- end
- end
- STOP : begin
- if (STOP_IDLE) begin
- nstate = IDLE;
- end
- else begin
- nstate = cstate;
- end
- end
- default : nstate = cstate;
- endcase
- end
-
- assign IDLE_START = (cstate == IDLE) && cmd_vld && (cmd & `START_BIT) ;//存在起始位
- assign START_WR_DATA = (cstate == START) && end_cnt_num && (cmd_r & `WRITE_BIT) ;//计1bit起始位并写数据有效
- assign WR_DATA_R_ACK = (cstate == WR_DATA) && end_cnt_num ;//计8bit数据位
- assign R_ACK_IDLE = (cstate == R_ACK) && end_cnt_num && !(cmd_r & `STOP_BIT) ;//计1bit响应位,没有停止位
- assign IDLE_WR_DATA = (cstate == IDLE) && cmd_vld && (cmd & `WRITE_BIT) ;//写数据有效
- assign R_ACK_STOP = (cstate == R_ACK) && end_cnt_num && (cmd_r & `STOP_BIT) ;//计1bit响应位,有停止位
- assign STOP_IDLE = (cstate == STOP) && end_cnt_num ;//计1bit停止位
- assign IDLE_RD_DATA = (cstate == IDLE) && cmd_vld && (cmd & `READ_BIT) ;//读数据有效
- assign RD_DATA_T_ACK = (cstate == RD_DATA) && end_cnt_num ;//计8bit数据位
- assign T_ACK_IDLE = (cstate == T_ACK) && end_cnt_num && !(cmd_r & `STOP_BIT) ;//计1bit响应位,没有停止位
- assign T_ACK_STOP = (cstate == T_ACK) && end_cnt_num && (cmd_r & `STOP_BIT) ;//计1bit响应位,有停止位
- /****************************************************************
- 三态门
- ****************************************************************/
- assign sda = OE ? sda_out : 1'bz;
- assign sda_in = sda;
- //考察OE
- always @(posedge clk or negedge rst_n) begin
- if (!rst_n) begin
- OE <= 'b0;
- end
- else if (IDLE_START || START_WR_DATA || IDLE_WR_DATA || R_ACK_STOP || RD_DATA_T_ACK) begin//FPGA发送,EEPROM接收
- OE <= 'b1;
- end
- else if (IDLE_RD_DATA || WR_DATA_R_ACK || STOP_IDLE) begin//FPGA接收,EEPROM发送
- OE <= 'b0;
- end
- end
- /****************************************************************
- 输出
- ****************************************************************/
- //考察输出
- always @(posedge clk or negedge rst_n) begin
- if (!rst_n) begin
- sda_out <= 1;
- end
- else begin
- case (cstate)
- IDLE :sda_out <= 1;
- START :begin//起始位scl一直为高电平,检测sda需要由高电平变低电平
- if (cnt_bit == SCL_LOW_HALF) begin
- sda_out <= 'b1;
- end
- else if (cnt_bit == SCL_HIGH_HALF) begin
- sda_out <= 'b0;
- end
- end
- WR_DATA :begin//高电平保持,低电平值变化采样
- if (cnt_bit == SCL_LOW_HALF) begin
- sda_out <= wr_data_r[7 - cnt_num];//高位先发
- end
- end
- T_ACK :begin
- if (cnt_bit == SCL_LOW_HALF) begin
- if (cmd & `ACK_BIT) begin//命令响应位为1:NO ACK
- sda_out <= `NO_ACK;
- end
- else begin
- sda_out <= `ACK;//命令响应位为0:ACK
- end
- end
- end
- STOP :begin//检测sda需要由低电平变高电平
- if (cnt_bit == SCL_LOW_HALF) begin
- sda_out <= 'b0;
- end
- else if (cnt_bit == SCL_HIGH_HALF) begin
- sda_out <= 'b1;
- end
- end
- default: sda_out <= 'b1;
- endcase
- end
- end
- /****************************************************************
- ACK
- ****************************************************************/
- //数据接收(考察EEPROM发送数据给FPGA的情况)
- always @(posedge clk or negedge rst_n) begin
- if (!rst_n) begin
- rev_ack <= 0;
- rd_data_r <= 8'b0;
- end
- else begin
- case (cstate)
- RD_DATA:begin
- if (cnt_bit == SCL_HIGH_HALF) begin//高电平采样
- rd_data_r[7-cnt_num] <= sda_in;
- end
- end
- R_ACK :begin
- if (cnt_bit == SCL_HIGH_HALF) begin
- rev_ack <= sda_in;
- end
- end
- default:;
- endcase
- end
- end
-
- assign done = R_ACK_IDLE || T_ACK_IDLE || STOP_IDLE;
- assign rd_data = rd_data_r;
- assign rd_data_vld = T_ACK_IDLE || T_ACK_STOP;
- endmodule
5.2 IIC控制模块
- /**************************************功能介绍***********************************
- Date : 2024年5月20日16:19:44
- Author :
- Version : 1.0
- Description: I2C控制模块
- *********************************************************************************/
-
- //---------<模块及端口声名>------------------------------------------------------
- module I2C_control #(
- parameter ADDR_BIT = 8 //从机寄存器地址的位宽
- )(
- input clk ,
- input rst_n ,
- input wr_req ,//写使能
- input rd_req ,//读使能
- input [6:0] device_id ,//从机设备ID
- input [ADDR_BIT - 1:0] reg_addr ,//读写地址
- input reg_addr_vld,
- input [7:0] wr_data ,
- input wr_data_vld ,
-
- output [7:0] rd_data ,
- output rd_data_vld ,
- output ready ,
- output scl ,
-
- inout sda
- );
- //---------<参数定义>---------------------------------------------------------
- //接口模块控制命令
- `define START_BIT 5'b00001
- `define WRITE_BIT 5'b00010
- `define READ_BIT 5'b00100
- `define STOP_BIT 5'b01000
- `define ACK_BIT 5'b10000
- //状态机参数定义
- localparam IDLE = 6'b000001,//
- WR_REQ = 6'b000010,//
- WR_WAIT = 6'b000100,//
- RD_REQ = 6'b001000,//
- RD_WAIT = 6'b010000,//
- DONE = 6'b100000;//
- localparam WR_CTRL_BYTE = 8'b1010_0000;
- localparam RD_CTRL_BYTE = 8'b1010_0001;
- //---------<内部信号定义>-----------------------------------------------------
- reg [5:0] cstate ;//现态
- reg [5:0] nstate ;//次态
-
- wire IDLE_WR_REQ ;
- wire IDLE_RD_REQ ;
- wire WR_REQ_WR_WAIT ;
- wire RD_REQ_RD_WAIT ;
- wire WR_WAIT_WR_REQ ;
- wire WR_WAIT_DONE ;
- wire RD_WAIT_RD_REQ ;
- wire RD_WAIT_DONE ;
- wire DONE_IDLE ;
- reg [2:0] num ;//字节计数器最大值(写3字节,读4字节)
- wire done ;
- reg [4:0] cmd ;
- reg cmd_vld ;
- reg [7:0] op_wr_data ;
- reg [15:0] addr_r ;
- reg [7:0] wr_data_r ;
- reg [2:0] cnt_byte ;
- wire add_cnt_byte ;
- wire end_cnt_byte ;
- //同步数据打拍
- reg wr_req_r;
- reg rd_req_r;
- always @(posedge clk or negedge rst_n) begin
- if (!rst_n) begin
- wr_req_r <=0;
- rd_req_r <= 0;
- end
- else begin
- wr_req_r <= wr_req;
- rd_req_r <= rd_req;
- end
- end
-
- //寄存
- always @(posedge clk or negedge rst_n) begin
- if (!rst_n) begin
- addr_r <= 'd0;
- end
- else if (reg_addr_vld) begin
- addr_r <= reg_addr;
- end
- end
-
- always @(posedge clk or negedge rst_n) begin
- if (!rst_n) begin
- wr_data_r <= 'd0;
- end
- else if (wr_req) begin
- wr_data_r <= wr_data;
- end
- end
- //字节计数器
- always @(posedge clk or negedge rst_n)begin
- if(!rst_n)begin
- cnt_byte <= 'd0;
- end
- else if(add_cnt_byte)begin
- if(end_cnt_byte)begin
- cnt_byte <= 'd0;
- end
- else begin
- cnt_byte <= cnt_byte + 1'd1;
- end
- end
- end
-
- assign add_cnt_byte = done;
- assign end_cnt_byte = add_cnt_byte && cnt_byte == num - 1;
-
- //考察字节计数器最大值(写3字节,读4字节)
- always @(posedge clk or negedge rst_n) begin
- if (!rst_n) begin
- num <= 1;
- end
- else if (wr_req) begin
- num <= 4;
- end
- else if (rd_req) begin
- num <= 5;
- end
- else if (end_cnt_byte) begin
- num <= 1;
- end
- end
-
- assign IDLE_WR_REQ = (cstate == IDLE) && wr_req_r;//写使能拉高
- assign IDLE_RD_REQ = (cstate == IDLE) && rd_req_r;//读使能拉高
- assign WR_REQ_WR_WAIT = (cstate == WR_REQ) && 1;
- assign RD_REQ_RD_WAIT = (cstate == RD_REQ) && 1;
- assign WR_WAIT_WR_REQ = (cstate == WR_WAIT) && done;//写完1byte
- assign WR_WAIT_DONE = (cstate == WR_WAIT) && end_cnt_byte;//写完3byte
- assign RD_WAIT_RD_REQ = (cstate == RD_WAIT) && done;//读完1byte
- assign RD_WAIT_DONE = (cstate == RD_WAIT) && end_cnt_byte;//读完4byte
- assign DONE_IDLE = (cstate == DONE) && 1;
-
- //第一段:时序逻辑描述状态转移
- always @(posedge clk or negedge rst_n)begin
- if(!rst_n)begin
- cstate <= IDLE;
- end
- else begin
- cstate <= nstate;
- end
- end
-
- //第二段:组合逻辑描述状态转移规律和状态转移条件
- always @(*) begin
- case(cstate)
- IDLE :begin
- if (IDLE_WR_REQ) begin
- nstate = WR_REQ;
- end
- else if (IDLE_RD_REQ) begin
- nstate = RD_REQ;
- end
- else begin
- nstate = cstate;
- end
- end
- WR_REQ :begin
- if (WR_REQ_WR_WAIT) begin
- nstate = WR_WAIT;
- end
- else begin
- nstate = cstate;
- end
- end
- WR_WAIT :begin
- if (WR_WAIT_DONE) begin
- nstate = DONE;
- end
- else if (WR_WAIT_WR_REQ) begin
- nstate = WR_REQ;
- end
- else begin
- nstate = cstate;
- end
- end
- RD_REQ :begin
- if (RD_REQ_RD_WAIT) begin
- nstate = RD_WAIT;
- end
- else begin
- nstate = cstate;
- end
- end
- RD_WAIT :begin
- if (RD_WAIT_DONE) begin
- nstate = DONE;
- end
- else if (RD_WAIT_RD_REQ) begin
- nstate = RD_REQ;
- end
- else begin
- nstate = cstate;
- end
- end
- DONE :begin
- if (DONE_IDLE) begin
- nstate = IDLE;
- end
- else begin
- nstate = cstate;
- end
- end
- default : nstate = cstate;
- endcase
- end
-
- always @(posedge clk or negedge rst_n) begin
- if (!rst_n) begin
- TX(0,4'h0,8'h00);
- end
- else begin
- case (cstate)
- RD_REQ:begin
- case (cnt_byte)
- 0 : TX(1,(`START_BIT | `WRITE_BIT),WR_CTRL_BYTE);
- 1 : TX(1,(`WRITE_BIT ),addr_r[15:8]);
- 2 : TX(1,(`WRITE_BIT ),addr_r[7:0] );
- 3 : TX(1,(`START_BIT | `WRITE_BIT),RD_CTRL_BYTE);
- 4 : TX(1,(`READ_BIT | `STOP_BIT ),8'h00 );
- default: TX(0,cmd,op_wr_data);
- endcase
- end
- WR_REQ:begin
- case (cnt_byte)
- 0 : TX(1,(`START_BIT | `WRITE_BIT),WR_CTRL_BYTE);
- 1 : TX(1,(`WRITE_BIT ),addr_r[15:8]);
- 2 : TX(1,(`WRITE_BIT ),addr_r[7:0] );
- 3 : TX(1,(`WRITE_BIT | `STOP_BIT ),wr_data_r );
- default: TX(0,cmd,op_wr_data);
- endcase
- end
- default: TX(0,cmd,op_wr_data);
- endcase
- end
- end
- I2C_driver u_I2C_driver(
- .clk (clk),
- .rst_n (rst_n),
- .wr_data (op_wr_data),
- .cmd (cmd),
- .cmd_vld (cmd_vld),
- .rd_data (rd_data),
- .rd_data_vld (rd_data_vld),
- .done (done),
- .scl (scl),
- .sda (sda)
- );
- task TX;
- input task_cmd_vld ;
- input [3:0] task_cmd ;
- input [7:0] task_wr_data ;
- begin
- cmd_vld = task_cmd_vld ;
- cmd = task_cmd ;
- op_wr_data = task_wr_data ;
- end
- endtask
- assign ready = cstate == IDLE;
- endmodule
5.3按键消抖模块(可以不用)
- /**************************************功能介绍***********************************
- Date : 2024年5月20日16:33:32
- Author :
- Version : 1.0
- Description: 四位按键消抖模块
- *********************************************************************************/
-
- //---------<模块及端口声名>------------------------------------------------------
- module key_debounce #(parameter WIDTH = 1,
- parameter DELAY_20MS = 1000_000
- )(
- input clk ,
- input rst_n ,
- input [WIDTH-1:0] key_in ,
- output [WIDTH-1:0] key_out
- );
- //---------<参数定义>---------------------------------------------------------
-
- reg [WIDTH-1:0] key_out_r;
- //状态机参数定义
- localparam IDLE = 4'b0001,//空闲状态
- FILETER_DOWN = 4'b0010,//按键按下抖动状态
- HOLD_DOWN = 4'b0100,//按下稳定按下状态
- FILTER_UP = 4'b1000;//按键释放抖动状态
-
- //---------<内部信号定义>-----------------------------------------------------
- reg [3:0] cstate ;//现态
- reg [3:0] nstate ;//次态
-
- reg [WIDTH-1:0] key_r0 ;//同步打拍
- reg [WIDTH-1:0] key_r1 ;
- reg [WIDTH-1:0] key_r2 ;
- wire [WIDTH-1:0] n_edge ;//下降沿
- wire [WIDTH-1:0] p_edge ;//上升沿
-
- reg [19:0] cnt_20ms ;//20ms计数器
- wire add_cnt_20ms;
- wire end_cnt_20ms;
-
- //状态转移条件定义
- wire idle2filter_down ;
- wire fiter_down2hold_down ;
- wire hold_down2filter_up ;
- wire filter_up2idle ;
-
- //****************************************************************
- //--cstate
- //****************************************************************
- //第一段:时序逻辑描述状态转移
- always @(posedge clk or negedge rst_n)begin
- if(!rst_n)begin
- cstate <= IDLE;//复位的初始状态
- end
- else begin
- cstate <= nstate;
- end
- end
-
- //第二段:组合逻辑描述状态转移规律和状态转移条件
- always @(*) begin
- case(cstate)
- IDLE : begin
- if(idle2filter_down)begin
- nstate = FILETER_DOWN;
- end
- else begin
- nstate = cstate;
- // state_n = IDLE;
- end
- end
- FILETER_DOWN : begin
- if(fiter_down2hold_down)begin
- nstate = HOLD_DOWN;
- end
- else begin
- nstate = cstate;
- end
- end
- HOLD_DOWN : begin
- if(hold_down2filter_up)begin
- nstate = FILTER_UP;
- end
- else begin
- nstate = cstate;
- end
- end
- FILTER_UP : begin
- if(filter_up2idle)begin
- nstate = IDLE;
- end
- else begin
- nstate = cstate;
- end
- end
- default : nstate = IDLE;
- endcase
- end
-
- assign idle2filter_down = cstate == IDLE && n_edge;
- assign fiter_down2hold_down = cstate == FILETER_DOWN && end_cnt_20ms;
- assign hold_down2filter_up = cstate == HOLD_DOWN && p_edge;
- assign filter_up2idle = cstate == FILTER_UP && end_cnt_20ms;
-
- //****************************************************************
- //--n_edge、p_edge
- //****************************************************************
- always @(posedge clk or negedge rst_n)begin
- if(!rst_n)begin
- key_r0 <= {WIDTH{1'b1}};
- key_r1 <= {WIDTH{1'b1}};
- key_r2 <= {WIDTH{1'b1}};
- end
- else begin
- key_r0 <= key_in;
- key_r1 <= key_r0;
- key_r2 <= key_r1;
- end
- end
-
- assign n_edge = ~key_r1 & key_r2;
- assign p_edge = ~key_r2 & key_r1;
-
- //****************************************************************
- //--cnt_20ms
- //****************************************************************
- always @(posedge clk or negedge rst_n)begin
- if(!rst_n)begin
- cnt_20ms <= 'd0;
- end
- else if(add_cnt_20ms)begin
- if(end_cnt_20ms)begin
- cnt_20ms <= 'd0;
- end
- else begin
- cnt_20ms <= cnt_20ms + 1'b1;
- end
- end
- end
-
- assign add_cnt_20ms = cstate == FILETER_DOWN || cstate == FILTER_UP;
- assign end_cnt_20ms = add_cnt_20ms && cnt_20ms == DELAY_20MS - 1;
-
- //****************************************************************
- //--key_out
- //****************************************************************
- always @(posedge clk or negedge rst_n)begin
- if(!rst_n)begin
- key_out_r <= 'd0;
- end
- else if(hold_down2filter_up)begin
- key_out_r <= ~key_r2;
- end
- else begin
- key_out_r <= 'd0;
- end
- end
-
- assign key_out = key_out_r;
- endmodule
5.4串口接收模块(非必要)
- /**************************************功能介绍***********************************
- Date : 22024年5月20日16:38:29
- Author :
- Version : 1.0
- Description: FPGA收上位机发来的数据【1bit(波形)变8bit】
- *********************************************************************************/
-
- //---------<模块及端口声名>------------------------------------------------------
- module uart_rx(
- input clk ,
- input rst_n ,
- input rx ,
- output rx_data_vld,
- output [7:0] rx_data
- );
- //---------<参数定义>---------------------------------------------------------
- parameter MAX_BPS = 115200;
- parameter CLOCK = 50_000_000;
- parameter MAX_1bit = CLOCK/MAX_BPS;//1bit要计434次
- parameter CHECK_BIT = "None";//None无校验,Odd奇校验,Even偶校验
- //状态机参数定义
- localparam IDLE = 'b0001,//空闲状态
- START = 'b0010,//起始位
- DATA = 'b0100,//数据位
- CHECK = 'b1000;
- //---------<内部信号定义>-----------------------------------------------------
- reg [3:0] cstate ;//现态
- reg [3:0] nstate ;//次态
-
- wire IDLE_START;
- wire START_DATA;
- wire DATA_IDLE;
- wire DATA_CHECK;
- wire CHECK_IDLE;
-
- reg [8:0] cnt_baud ;//波特计数器,波特率115200
- wire add_cnt_baud ;
- wire end_cnt_baud ;
-
- reg [2:0] cnt_bit ;//bit计数器,起始位1bit,数据位8bit,结束位1bit
- wire add_cnt_bit ;
- wire end_cnt_bit ;
-
- reg [3:0] bit_max;//bit最大值,复用需要考察每个状态的bit值
-
- reg [7:0] rx_temp;
- reg rx_check;
- wire check_val;
-
- reg rx_r1;
- reg rx_r2;
- wire rx_nege;
-
- //打两拍
- always @(posedge clk or negedge rst_n) begin
- if (!rst_n) begin
- rx_r1 <= 1;
- rx_r2 <= 1;
- end
- else begin
- rx_r1 <= rx;
- rx_r2 <= rx_r1;
- end
- end
-
- assign rx_nege = ~rx_r1 && rx_r2;
-
- //计434次
- always @(posedge clk or negedge rst_n)begin
- if(!rst_n)begin
- cnt_baud <= 'd0;
- end
- else if(add_cnt_baud)begin
- if(end_cnt_baud)begin
- cnt_baud <= 'd0;
- end
- else begin
- cnt_baud <= cnt_baud + 1'd1;
- end
- end
- end
-
- assign add_cnt_baud = cstate != IDLE;
- assign end_cnt_baud = add_cnt_baud && cnt_baud == MAX_1bit - 1'd1;
-
- //bit计数器
- always @(posedge clk or negedge rst_n)begin
- if(!rst_n)begin
- cnt_bit <= 'd0;
- end
- else if(add_cnt_bit)begin
- if(end_cnt_bit)begin
- cnt_bit <= 'd0;
- end
- else begin
- cnt_bit <= cnt_bit + 1'd1;
- end
- end
- end
-
- assign add_cnt_bit = end_cnt_baud;
- assign end_cnt_bit = add_cnt_bit && cnt_bit == bit_max -1'd1;
-
- //计数器复用
- always @(*)begin
- case (cstate)
- IDLE :bit_max = 'd0;
- START:bit_max = 'd1;//起始位1bit
- DATA :bit_max = 'd8;//数据位8bit
- CHECK:bit_max = 'd1;
- default: bit_max = 'd0;
- endcase
- end
- assign IDLE_START = (cstate == IDLE) && rx_nege;//识别到起始位0
- assign START_DATA = (cstate == START) && end_cnt_bit;//计1bit数据
- assign DATA_IDLE = (cstate == DATA) && end_cnt_bit && CHECK_BIT == "None";//计8bit数据
- assign DATA_CHECK = (cstate == DATA) && end_cnt_bit;
- assign CHECK_IDLE = (cstate == CHECK) && end_cnt_bit;
- //第一段:时序逻辑描述状态转移
- always @(posedge clk or negedge rst_n)begin
- if(!rst_n)begin
- cstate <= IDLE;
- end
- else begin
- cstate <= nstate;
- end
- end
-
- //第二段:组合逻辑描述状态转移规律和状态转移条件
- always @(*) begin
- case(cstate)
- IDLE :begin
- if (IDLE_START) begin
- nstate = START;
- end
- else begin
- nstate = cstate;
- end
- end
- START :begin
- if (START_DATA) begin
- nstate = DATA;
- end
- else begin
- nstate = cstate;
- end
- end
- DATA :begin
- if (DATA_IDLE) begin
- nstate = IDLE;
- end
- else if (DATA_CHECK) begin
- nstate = CHECK;
- end
- else begin
- nstate = cstate;
- end
- end
- CHECK:begin
- if (CHECK_IDLE) begin
- nstate = IDLE;
- end
- else begin
- nstate = cstate;
- end
- end
- default : nstate = IDLE;
- endcase
- end
- //接受校验位
- always @(posedge clk or negedge rst_n) begin
- if (!rst_n) begin
- rx_check <= 0;
- end
- else if (cstate == CHECK && cnt_baud == MAX_1bit >>1) begin
- rx_check <= rx_r1;
- end
- end
- //计算校验位
- assign check_val = (CHECK_BIT == "Odd") ? ~^rx_temp : ^rx_temp;
- //第三段:描述输出,时序逻辑或组合逻辑皆可
- always @(posedge clk or negedge rst_n) begin
- if (!rst_n) begin
- rx_temp <= 0;
- end
- else if (cstate == DATA && cnt_baud == MAX_1bit >> 1) begin//电平中间值采样,边沿采样容易出错
- rx_temp[cnt_bit] <= rx_r1;
- end
- else begin
- rx_temp <= rx_temp;
- end
- end
- assign rx_data = rx_temp;
- assign rx_data_vld = (CHECK_BIT == "None") ? DATA_IDLE
- :(CHECK_IDLE && (check_val == rx_check)) ? 1
- : 0;
-
- endmodule
5.5串口发送模块(非必要)
- /**************************************功能介绍***********************************
- Date : 2024年5月20日16:40:06
- Author :
- Version : 2.0
- Description: FPGA向上位机发送数据【8bit变1bit(波形)】
- *********************************************************************************/
-
- //---------<模块及端口声名>------------------------------------------------------
- module uart_tx(
- input clk ,
- input rst_n ,
- input [7:0] tx_data ,
- input tx_data_vld,
- output ready ,
- output reg tx
- );
- //---------<参数定义>---------------------------------------------------------
- parameter MAX_BPS = 115200;
- parameter CLOCK = 50_000_000;
- parameter MAX_1bit = CLOCK/MAX_BPS;//1bit要计434次
- parameter CHECK_BIT = "None";//None无校验,Odd奇校验,Even偶校验
- //状态机参数定义
- localparam IDLE = 'b00001,//空闲状态
- START = 'b00010,//起始位
- DATA = 'b00100,//数据位
- CHECK = 'b01000,//校验位
- STOP = 'b10000;//停止位
- //---------<内部信号定义>-----------------------------------------------------
- reg [4:0] cstate ;//现态
- reg [4:0] nstate ;//次态
-
- wire IDLE_START;
- wire START_DATA;
- wire DATA_CHECK;
- wire CHECK_STOP;
- wire STOP_IDLE;
- reg [8:0] cnt_baud ;//波特计数器,波特率115200
- wire add_cnt_baud ;
- wire end_cnt_baud ;
- reg [2:0] cnt_bit ;//bit计数器,起始位1bit,数据位8bit,结束位1bit
- wire add_cnt_bit ;
- wire end_cnt_bit ;
-
- reg [3:0] bit_max;//bit最大值,复用需要考察每个状态的bit值
- reg [7:0] tx_data_r;
-
- wire check_val;
-
- //计434次
- always @(posedge clk or negedge rst_n)begin
- if(!rst_n)begin
- cnt_baud <= 'd0;
- end
- else if(add_cnt_baud)begin
- if(end_cnt_baud)begin
- cnt_baud <= 'd0;
- end
- else begin
- cnt_baud <= cnt_baud + 1'd1;
- end
- end
- end
-
- assign add_cnt_baud = cstate != IDLE;
- assign end_cnt_baud = add_cnt_baud && cnt_baud == MAX_1bit - 1'd1;
-
- //bit计数器
- always @(posedge clk or negedge rst_n)begin
- if(!rst_n)begin
- cnt_bit <= 'd0;
- end
- else if(add_cnt_bit)begin
- if(end_cnt_bit)begin
- cnt_bit <= 'd0;
- end
- else begin
- cnt_bit <= cnt_bit + 1'd1;
- end
- end
- end
-
- assign add_cnt_bit = end_cnt_baud;
- assign end_cnt_bit = add_cnt_bit && cnt_bit == bit_max -1'd1;
-
- //计数器复用
- always @(*)begin
- case (cstate)
- IDLE :bit_max = 'd0;
- START:bit_max = 'd1;//起始位1bit
- DATA :bit_max = 'd8;//数据位7bit
- CHECK:bit_max = 'd1;//校验位1bit
- STOP :bit_max = 'd1;//结束位1bit
- default: bit_max = 'd0;
- endcase
- end
- assign IDLE_START = (cstate == IDLE) && tx_data_vld;//考察到开始传输信号
- assign START_DATA = (cstate == START) && end_cnt_bit;//计1bit数据
- assign DATA_STOP = (cstate == DATA) && end_cnt_bit && CHECK_BIT == "None";
- assign DATA_CHECK = (cstate == DATA) && end_cnt_bit;//计8bit数据
- assign CHECK_STOP = (cstate ==CHECK) && end_cnt_bit;//计1bit数据
- assign STOP_IDLE = (cstate == STOP) && end_cnt_bit;//计1bit数据
- //第一段:时序逻辑描述状态转移
- always @(posedge clk or negedge rst_n)begin
- if(!rst_n)begin
- cstate <= IDLE;
- end
- else begin
- cstate <= nstate;
- end
- end
-
- //第二段:组合逻辑描述状态转移规律和状态转移条件
- always @(*) begin
- case(cstate)
- IDLE :begin
- if (IDLE_START) begin
- nstate = START;
- end
- else begin
- nstate = cstate;
- end
- end
- START :begin
- if (START_DATA) begin
- nstate = DATA;
- end
- else begin
- nstate = cstate;
- end
- end
- DATA :begin
- if (DATA_CHECK) begin
- nstate = CHECK;
- end
- else if (DATA_STOP) begin
- nstate = STOP;
- end
- else begin
- nstate = cstate;
- end
- end
- CHECK :begin
- if (CHECK_STOP) begin
- nstate = STOP;
- end
- else begin
- nstate = cstate;
- end
- end
- STOP :begin
- if (STOP_IDLE) begin
- nstate = IDLE;
- end
- else begin
- nstate = cstate;
- end
- end
- default : nstate = cstate;
- endcase
- end
- //寄存一拍
- always @(posedge clk or negedge rst_n) begin
- if (!rst_n) begin
- tx_data_r <= 'd0;
- end
- else if (tx_data_vld) begin
- tx_data_r <= tx_data;
- end
- else begin
- tx_data_r <= tx_data_r;
- end
- end
-
- assign check_val = (CHECK_BIT == "Odd") ? ~^tx_data_r : ^tx_data_r;
-
- //第三段:描述输出,时序逻辑或组合逻辑皆可
- always @(*)begin
- case (cstate)
- IDLE : tx = 1'b1;
- START: tx = 1'b0;//起始位为0
- DATA : tx = tx_data_r[cnt_bit];
- CHECK: tx = check_val;
- STOP : tx = 1'b1;//结束位为1
- default: tx = 1'b1;
- endcase
- end
-
- assign ready = cstate == IDLE;//当状态为IDLE时,表示tx端可以接收数据
-
-
-
- endmodule
5.6顶层模块:
- /**************************************功能介绍***********************************
- Date : 2024年5月20日16:54:32
- Author :
- Version :
- Description:
- *********************************************************************************/
-
- //---------<模块及端口声名>------------------------------------------------------
- module top(
- input clk ,
- input rst_n ,
- input key_in ,
- input rx ,
- output tx ,
- output scl ,
-
- inout sda
- );
- //---------<参数定义>---------------------------------------------------------
- wire rd_req;
- wire wr_req;
- wire [7:0] wr_data;
- wire [7:0] rd_data;
- wire rd_data_vld;
- //---------<内部信号定义>-----------------------------------------------------
- key_debounce u_key_debounce(
- .clk (clk),
- .rst_n (rst_n),
- .key_in (key_in),
- .key_out(rd_req)
- );
-
- uart_rx u_uart_rx(
- .clk (clk),
- .rst_n (rst_n),
- .rx (rx),
- .rx_data_vld (wr_req),
- .rx_data (wr_data)
- );
-
- uart_tx u_uart_tx(
- .clk (clk),
- .rst_n (rst_n),
- .tx_data (rd_data),
- .tx_data_vld(rd_data_vld),
- .ready (),
- .tx (tx)
- );
-
- I2C_control u_I2C_control(
- .clk (clk),
- .rst_n (rst_n),
- .wr_req (wr_req),
- .rd_req (rd_req),
- .device_id (7'b1010000),
- .reg_addr (8'h03),
- .reg_addr_vld(1'b1),
- .wr_data (wr_data),
- .wr_data_vld (wr_req),
- .rd_data (rd_data),
- .rd_data_vld (rd_data_vld),
- .ready (),
- .scl (scl),
- .sda (sda)
- );
- endmodule
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。