基于FPGA的I2C读写EEPROM
文章目录
- 前言
- 一、I2C协议
- 1.1 I2C协议简介
- 1.2 物理层
- 1.3 协议层
- 二、EEPROM
- 2.1 型号及硬件规格
- 2.2 各种读写时序
- 三、状态机设计
- 四、项目源码:
- 五、实现效果
- 参考资料
前言
本次项目所用开发板FPGA芯片型号为:EP4CE6F17C8 EEPROM芯片型号为:24LC04B UART串口设置为:波特率115200
无校验位本次项目仅实现了EEPROM的单字节读写。若要实现连续读写可以在下文的EEPROM控制模块设置计数器控制读写字数。
一、I2C协议
1.1 I2C协议简介
I2C总线是Philips公司在八十年代初推出的一种同步、串行、半双工 的总线,主要用于近距离、低速的芯片之间的通信;I2C总线有两根双向的信号线,一根数据线SDA用于收发数据,一根时钟线SCL用于通信双方时钟的同步;I2C总线硬件结构简单,简化了PCB布线,降低了系统成本,提高了系统可靠性,因此在各个领域得到了广泛应用。
I2C是一种多主机多从机总线,通过呼叫应答的方式实现主机与从机间的通信。每一个I2C设备都有一个唯一的7位地址,也就是说同一根线上最多挂载127个I2C从机设备(主机自己占一个地址)。主机有权发起和结束一次通信,从机只能被动呼叫;当总线上有多个主机同时启用总线时,I2C也具备冲突检测和仲裁的功能来防止错误产生。
1.2 物理层
-
I2C支持多主机多从机
-
I2C有两条线,一条SCL串行时钟线,用于数据收发同步;一条SDA串行数据线,用来表示数据
-
每个连接到IIC总线的设备都有一个唯一的地址(ID),主机可以通过地址与不同的从机建立连接
-
I2C 总线通过上拉电阻接到电源。当 IIC 设备空闲时,设备会输出高阻态,当所有设备都空闲,都输出高阻态时,由上拉电阻把 IIC总线拉成高电平。
-
I2C总线有仲裁机制:当多个主机同时发起传输时,触发仲裁机制,最终只给一个主机授权。
-
具有三种传输模式,每种传输模式对应的传输速率不同,具体速率如下:
有些I2C变种还包含了快速+(1Mbit/s)和超高速(5Mbit/s 单向传输)模式。
1.3 协议层
- 空闲状态:I2C协议规定,在空闲状态下SDA数据线和SCL时钟线均处于高电平。
- 开始位:当SCL处于高电平时,SDA数据线被拉低,则认为检测到起始位,一次数据传输开始。
- 数据传输:同时,协议规定在SCL高电平时期SDA数据线必须保持稳定,在SCL低电平时,SDA才允许发生改变。主机进行数据读写时,I2C协议规定,数据传输时先发送寻址字节,即7位从机地址+0/1。其中0表示主机写,1表示主机读。寻址字节发送完成后才是数据字节
- 应答位:I2C协议规定,主机每次向从机传输1字节数据后,需要接收一次从机的应答信号。0为接收成功;1为接受失败,没有响应。
- 停止位:当SCL为高电平时,数据线SDA拉高,则认为检测到停止位,一次数据传输结束。
二、EEPROM
2.1 型号及硬件规格
EEPROM的全称是“电可擦除可编程只读存储器”,即Electrically Erasable Programmable Read-Only Memory。本次项目中使用的EEPROM型号为24LC04B。
由手册可以看出,24LC04B支持的最大时钟频率为400KHz。
由手册得出该型号EEPROM共有两个Block 每个Block的存储容量为256×8bit。
由手册得出,设备地址为1010xxB0共七位(xx为dont care B0为块选择),1为读操作,0为写操作。
2.2 各种读写时序
单字节写
- 先写开始位
- 然后写控制字节,从机接收到发应答信号
- 然后写数据地址,从机接收到发应答信号,如果数据地址是2位的话,就继续写数据地址,从机接收到发应答信号
- 然后写数据,从机接收到发应答信号
- 最后写结束位
页写
- 页节写先开始位
- 然后写控制字节,从机接收到应答信号
- 然后写数据地址,从机接收到应答信号,如果数据地址是2位的话,就继续写数据地址,从机接收到应答信号
- 然后写数据,从机接收到应答信号
- 然后继续写数据,直到写完全部的数据
- 最后是结束位
当前地址读
- 当前地址读先开始位
- 然后写控制字节,从机接收到应答信号,然后读数据,无应答信号
- 最后结束位
随机地址读和顺序读
- 随机读先写开始位
- 然后写控制字节,从机接收到应答信号
- 然后dummuy write 虚写,写数据地址,从机接收到应答信号
- 然后开始位
- 然后读控制字节,从机接收到应答信号
- 然后读数据
- 最后结束位
三、状态机设计
I2C接口模块:
EEPROM控制模块:
四、项目源码:
EEPROM驱动模块:
/**************************************功能介绍***********************************
Date : 2023年8月28日
Author : majiko
Version : 1.0
Description: eeprom驱动模块cmd 0 1开始位 NO YES 写数据 NO YES 读数据 NO YES 停止位 NO YES 应答位 ACK NO_ACK
*********************************************************************************/module eeprom_driver( input wire clk ,input wire rst_n ,input wire [7:0] wr_data ,input wire [4:0] cmd ,input wire cmd_vld ,inout wire i2c_sda ,output reg i2c_scl ,output wire [7:0] rd_data ,output wire rd_data_vld ,output reg done ,output reg rev_ack
);//内部参数定义//命令宏定义`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//状态定义parameter IDLE = 7'b000_0001,START = 7'b000_0010,WR_DATA = 7'b000_0100,R_ACK = 7'b000_1000,STOP = 7'b001_0000,RD_DATA = 7'b010_0000,T_ACK = 7'b100_0000;//i2c速率以及采样点定义parameter SCL_MAX = 50_000_000/100_000; //100K速率parameter SCL_1_4 = SCL_MAX/4;parameter SCL_3_4 = SCL_MAX*3/4;//内部信号定义//状态寄存器reg [6:0] cstate ;reg [6:0] nstate ;reg [7:0] wr_data_r ;//输入数据寄存reg [4:0] cmd_r ;//输入命令寄存reg [7:0] rev_data ; //跳转条件定义wire idle2start ;wire idle2wr_data ;wire idle2rd_data ;wire start2wr_data ;wire wr_data2r_ack ;wire r_ack2stop ;wire r_ack2idle ;wire stop2idle ;wire rd_data2t_ack ;wire t_ack2stop ;wire t_ack2idle ;//bit计数器reg [3:0] cnt_bit ;wire add_cnt_bit ;wire end_cnt_bit ;reg [3:0] bit_max ;//i2c分频计数器reg [8:0] cnt_scl ;wire add_cnt_scl ;wire end_cnt_scl ;//三态门信号定义reg sda_out ;wire sda_in ;reg sda_en ;//****************************************************************
//--数据寄存 命令寄存
//****************************************************************always@(posedge clk or negedge rst_n)beginif(!rst_n)beginwr_data_r <= 1'b0;endelse if(cmd_vld)beginwr_data_r <= wr_data;endelse beginwr_data_r <= wr_data_r;endendalways@(posedge clk or negedge rst_n)beginif(!rst_n)begincmd_r <= 1'b0;endelse if(cmd_vld)begincmd_r <= cmd;endelse begincmd_r <= cmd_r;endend
//****************************************************************
//--读写状态机
//****************************************************************//第一段 状态定义always @(posedge clk or negedge rst_n) beginif(!rst_n)begincstate <= IDLE;endelse begincstate <= nstate;endend//第二段 状态转移规律always@(*)begincase (cstate)IDLE : beginif(idle2start)beginnstate = START;endelse if(idle2wr_data)beginnstate = WR_DATA;endelse if(idle2rd_data)beginnstate = RD_DATA;endelse beginnstate = cstate;endendSTART : beginif(start2wr_data)beginnstate = WR_DATA;endelse beginnstate = cstate;endendWR_DATA : beginif(wr_data2r_ack)beginnstate = R_ACK;endelse beginnstate = cstate;endend R_ACK : beginif(r_ack2stop)beginnstate = STOP;endelse if(r_ack2idle)beginnstate = IDLE;endelse beginnstate = cstate;endendSTOP : beginif(stop2idle)beginnstate = IDLE;endelse beginnstate = cstate;endendRD_DATA : beginif(rd_data2t_ack)beginnstate = T_ACK;endelse beginnstate = cstate;endendT_ACK : beginif(t_ack2stop)beginnstate = STOP;endelse if(t_ack2idle)beginnstate = IDLE;endelse beginnstate = cstate;endend default: nstate = IDLE;endcaseendassign idle2start = (cstate == IDLE) && cmd_vld && (cmd & `START_BIT); assign idle2wr_data = (cstate == IDLE) && cmd_vld && (cmd & `WRITE_BIT); assign idle2rd_data = (cstate == IDLE) && cmd_vld && (cmd & `READ_BIT);assign start2wr_data = (cstate == START ) && end_cnt_bit && (cmd_r & `WRITE_BIT);assign wr_data2r_ack = (cstate == WR_DATA) && end_cnt_bit;assign r_ack2stop = (cstate == R_ACK) && end_cnt_bit && (cmd_r & `STOP_BIT); assign r_ack2idle = (cstate == R_ACK) && end_cnt_bit && !(cmd_r & `STOP_BIT);assign stop2idle = (cstate == STOP ) && end_cnt_bit; assign rd_data2t_ack = (cstate == RD_DATA) && end_cnt_bit;assign t_ack2stop = (cstate == T_ACK) && end_cnt_bit && (cmd_r & `STOP_BIT); assign t_ack2idle = (cstate == T_ACK) && end_cnt_bit && !(cmd_r & `STOP_BIT); //****************************************************************
//--i2c时钟分频
//****************************************************************always @(posedge clk or negedge rst_n)begin if(!rst_n)begincnt_scl <= 'd0;end else if(add_cnt_scl)begin if(end_cnt_scl)begin cnt_scl <= 'd0;endelse begin cnt_scl <= cnt_scl + 1'b1;end endend assign add_cnt_scl = cstate != IDLE;assign end_cnt_scl = add_cnt_scl && cnt_scl == SCL_MAX - 1'b1;//assign i2c_scl = (cnt_scl == (SCL_MAX - 1'b1) >> 1'b1 || stop2idle) ? 1'b1 : 1'b0;always@(posedge clk or negedge rst_n)beginif(!rst_n)begini2c_scl <= 1'b1;endelse if(cnt_scl == (SCL_MAX - 1) >> 1'b1 || stop2idle)begini2c_scl <= 1'b1;endelse if(end_cnt_scl)begini2c_scl <= 1'b0;endelse begini2c_scl <= i2c_scl;endend//****************************************************************
//--bit计数器
//****************************************************************always @(posedge clk or negedge rst_n)begin if(!rst_n)begincnt_bit <= 'd0;end else if(add_cnt_bit)begin if(end_cnt_bit)begin cnt_bit <= 'd0;endelse begin cnt_bit <= cnt_bit + 1'b1;end endend assign add_cnt_bit = end_cnt_scl;assign end_cnt_bit = add_cnt_bit && cnt_bit == bit_max - 1'b1;always @(*)beginif(cstate == WR_DATA || cstate == RD_DATA)beginbit_max = 'd8;endelse beginbit_max = 'd1;endend//****************************************************************
//--三态门控制
//****************************************************************assign sda_in = i2c_sda;assign i2c_sda = sda_en ? sda_out : 1'bz;//使能信号开启always @(posedge clk or negedge rst_n)beginif(!rst_n)beginsda_en <= 1'b0;endelse if(wr_data2r_ack || stop2idle || idle2rd_data)beginsda_en <= 1'b0;endelse if(idle2wr_data || idle2start || rd_data2t_ack || r_ack2stop || start2wr_data)beginsda_en <= 1'b1;endelse beginsda_en <= sda_en;endend
//****************************************************************
//--数据发送
//****************************************************************always@(posedge clk or negedge rst_n)beginif(!rst_n)beginsda_out <= 1'b1;endelse begincase(cstate)IDLE : sda_out <= 1'b1;START : beginif(cnt_scl == SCL_1_4)beginsda_out <= 1'b1;endelse if(cnt_scl == SCL_3_4)beginsda_out <= 1'b0;endelse beginsda_out <= sda_out;endendWR_DATA : beginif(cnt_scl == SCL_1_4)beginsda_out <= wr_data_r[7-cnt_bit];endelse beginsda_out <= sda_out;endendT_ACK : beginif(cnt_scl == SCL_1_4)beginif(cmd & `ACK_BIT)beginsda_out <= `NO_ACK;endelse beginsda_out <= `ACK;endendelse beginsda_out <= sda_out;endendSTOP : beginif(cnt_scl == SCL_1_4)beginsda_out <= 1'b0;endelse if(cnt_scl == SCL_3_4)beginsda_out <= 1'b1;endelse beginsda_out <= sda_out;endenddefault : sda_out <= 1'b1;endcaseendend//****************************************************************
//--数据接收
//****************************************************************always @(posedge clk or negedge rst_n)beginif(!rst_n)beginrev_ack <= 1'b0;rev_data <= 1'b0;endelse begincase(cstate)R_ACK : beginif(cnt_scl == SCL_3_4)beginrev_ack <= sda_in;endelse beginrev_ack <= rev_ack;endendRD_DATA : beginif(cnt_scl == SCL_3_4)beginrev_data[7-cnt_bit] <= sda_in;endelse beginrev_data <= rev_data;endenddefault : ;endcaseendend//****************************************************************
//--接口输出
//****************************************************************always @(posedge clk or negedge rst_n)beginif(!rst_n)begindone <= 1'b0;endelse begindone <= t_ack2idle || r_ack2idle || stop2idle;endend//assign done = (t_ack2idle || r_ack2idle || stop2idle) ? 1'b1 : 1'b0;assign rd_data = (t_ack2idle || t_ack2stop) ? rev_data : 1'b0;assign rd_data_vld = t_ack2idle || t_ack2stop;endmodule
EEPROM控制模块:
/**************************************功能介绍***********************************
Date : 2023年8月30日
Author : majiko
Version : 1.0
Description: EEPROM控制模块
*********************************************************************************/module eeprom_control
#(parameter ADDR_BIT = 8 //从机寄存器地址
)
(input wire clk ,input wire rst_n ,input wire [6:0] device_id ,//i2c从机设备地址input wire wr_req ,input wire rd_req ,input wire [ADDR_BIT - 1:0] reg_addr ,//配置寄存器地址input wire reg_addr_vld ,input wire [7:0] wr_data ,input wire wr_data_vld ,output wire [7:0] rd_data ,output wire rd_data_vld ,output wire ready ,output wire i2c_scl ,inout wire i2c_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'b10000parameter IDLE = 6'b000_001,WR_REQ = 6'b000_010,WR_WAIT = 6'b000_100,RD_REQ = 6'b001_000,RD_WAIT = 6'b010_000,DONE = 6'b100_000;parameter WR_CTRL_BYTE = 8'b1010_0000,RD_CTRL_BYTE = 8'b1010_0001;//内部信号定义//状态寄存器reg [5:0] cstate ;reg [5:0] nstate ;//跳转条件定义wire idle2rd_req ;wire idle2wr_req ;wire wr_req2wr_wait ;wire wr_wait2wr_req ;wire wr_wait2done ;wire rd_req2rd_wait ;wire rd_wait2done ;wire rd_wait2rd_req ;wire done2idle ;wire done ;reg [4:0] cmd ;reg cmd_vld ;reg [7:0] op_wr_data ;reg [7:0] addr_r ;reg [7:0] wr_data_r ;//字节计数器reg [2:0] cnt_byte ;wire add_cnt_byte ;wire end_cnt_byte ;reg [2:0] byte_mawr_addr ;//****************************************************************
//--寄存输入数据
//****************************************************************always @(posedge clk or negedge rst_n)beginif(!rst_n)beginaddr_r <= 1'b0;endelse if(reg_addr_vld)beginaddr_r <= reg_addr;endelse beginaddr_r <= addr_r;endendalways @(posedge clk or negedge rst_n)beginif(!rst_n)beginwr_data_r <= 1'b0;endelse if(wr_data_vld)beginwr_data_r <= wr_data;endelse beginwr_data_r <= wr_data_r;endend//****************************************************************
//--状态机
//****************************************************************always @(posedge clk or negedge rst_n)beginif(!rst_n)begincstate <= IDLE;endelse begincstate <= nstate;endendalways @(*)begincase(cstate)IDLE : beginif(idle2rd_req)beginnstate = RD_REQ;endelse if(idle2wr_req)beginnstate = WR_REQ;endelse beginnstate = cstate;endendWR_REQ : beginif(wr_req2wr_wait)beginnstate = WR_WAIT;endelse beginnstate = cstate;endendWR_WAIT : beginif(wr_wait2done)beginnstate = DONE;endelse if(wr_wait2wr_req)beginnstate = WR_REQ;endelse beginnstate = cstate;endendRD_REQ : beginif(rd_req2rd_wait)beginnstate = RD_WAIT;endelse beginnstate = cstate;endendRD_WAIT : beginif(rd_wait2done)beginnstate = DONE;endelse if(rd_wait2rd_req)beginnstate = RD_REQ;endelse beginnstate = cstate;endendDONE : beginif(done2idle)beginnstate = IDLE;endelse beginnstate = cstate;endenddefault : ;endcaseendassign idle2rd_req = cstate == IDLE && rd_req;
assign idle2wr_req = cstate == IDLE && wr_req;assign wr_req2wr_wait = cstate == WR_REQ && 1'b1;
assign wr_wait2wr_req = cstate == WR_WAIT && done;
assign wr_wait2done = cstate == WR_WAIT && end_cnt_byte;assign rd_req2rd_wait = cstate == RD_REQ && 1'b1;
assign rd_wait2done = cstate == RD_WAIT && end_cnt_byte;
assign rd_wait2rd_req = cstate == RD_WAIT && done;assign done2idle = cstate == DONE && 1'b1;//****************************************************************
//--字节计数器
//****************************************************************always @(posedge clk or negedge rst_n)begin if(!rst_n)begincnt_byte <= 'd0;end else if(add_cnt_byte)begin if(end_cnt_byte)begin cnt_byte <= 'd0;endelse begin cnt_byte <= cnt_byte + 1'b1;end endend assign add_cnt_byte = done;assign end_cnt_byte = add_cnt_byte && cnt_byte == byte_mawr_addr - 1'b1;always @(posedge clk or negedge rst_n) beginif(!rst_n)beginbyte_mawr_addr <= 1'b1;endelse if(wr_req)beginbyte_mawr_addr <= 'd3;endelse if(rd_req)beginbyte_mawr_addr <= 'd4;endelse if(end_cnt_byte)beginbyte_mawr_addr <= 1'b1;endelse beginbyte_mawr_addr <= byte_mawr_addr;endend//****************************************************************
//--接口模块控制
//****************************************************************task TX;input task_cmd_vld;input [4:0] task_cmd;input [7:0] task_wr_data;begincmd_vld = task_cmd_vld;cmd = task_cmd;op_wr_data = task_wr_data;endendtaskalways @(posedge clk or negedge rst_n) beginif(!rst_n)beginTX(0,5'h0,8'h00);endelse begincase(cstate)RD_REQ : case(cnt_byte)0 : TX(1,(`START_BIT | `WRITE_BIT),WR_CTRL_BYTE);//写控制字节1 : TX(1,(`WRITE_BIT),addr_r[7:0]);//写地址2 : TX(1,(`START_BIT | `WRITE_BIT),RD_CTRL_BYTE);//读控制字节3 : TX(1,(`ACK_BIT| `READ_BIT | `STOP_BIT),8'h00);//读数据default : TX(0,cmd,op_wr_data);endcaseWR_REQ : case(cnt_byte)0 : TX(1,(`START_BIT | `WRITE_BIT),WR_CTRL_BYTE);//写控制字节1 : TX(1,(`WRITE_BIT),addr_r[7:0]);//写地址2 : TX(1,(`WRITE_BIT | `STOP_BIT),wr_data_r);//写数据default : TX(0,cmd,op_wr_data);endcasedefault : TX(0,cmd,op_wr_data);endcaseendendeeprom_driver u_eeprom_driver( /*input wire */.clk (clk ),/*input wire */.rst_n (rst_n ),/*input wire [7:0] */.wr_data (op_wr_data ),/*input wire [4:0] */.cmd (cmd ),/*input wire */.cmd_vld (cmd_vld ),/*inout wire */.i2c_sda (i2c_sda ),/*output wire */.i2c_scl (i2c_scl ),/*output wire [7:0] */.rd_data (rd_data ),/*output wire */.rd_data_vld (rd_data_vld ),/*output wire */.done (done ),/*output reg */.rev_ack () );// iic_interface iic_interface_inst(// /* input */.clk (clk ),// /* input */.rst_n (rst_n ),// /* input [4:0] */.cmd (cmd ),// /* input */.cmd_vld (cmd_vld ),// /* output */.done (done ), //操作结束// /* output reg */.rev_ack ( ), //接收到的响应信号// /* input [7:0] */.wr_data (op_wr_data ), //伴随cmd_vld信号一起接收写数据// /* output [7:0] */.rd_data (rd_data ),// /* output */.rd_data_vld (rd_data_vld),// /* output reg */.iic_scl (i2c_scl ),// /* inout */.iic_sda (i2c_sda ) // );assign ready = cstate == IDLE;endmodule
UART RX模块:
//****************************************************************
//--majiko 2023-8-15 UART RX模块
//****************************************************************
module uart_rx
#(parameter BORT = 20'd115200,parameter CLOCK = 50_000_000,parameter CHECK_BIT = "None" //"None" 无校验,"Odd" 奇校验,"Even" 偶校验
)
(input wire clk ,input wire rst_n ,input wire ready ,//准备接受数据信号input wire rx ,output wire rx_data_vld ,//数据有效信号output wire [7:0] rx_data
);//参数定义parameter TIME_BORT = CLOCK/BORT;//状态机状态定义parameter IDLE = 4'b0001,//空闲状态电平默认拉高START = 4'b0010,//起始位赋值DATA = 4'b0100,//数据位赋值CHECK = 4'b1000;//波特率115200 1bit传输时间计数器参数//内部信号定义//状态寄存器reg [3:0] cstate ;reg [3:0] nstate ;//状态跳转条件wire idle2start ;wire start2data ;wire data2idle ;wire data2check ;wire check2idle ;//1Baud时间计数器reg [8:0] cnt_bort ;wire add_cnt_bort ;wire end_cnt_bort ;//比特计数器(复用)reg [2:0] cnt_bit ;wire add_cnt_bit ;wire end_cnt_bit ;reg [3:0] bit_max ;//计数器复用信号reg [7:0] rx_temp ; reg check_bit ;wire check_temp ;reg rx_r1 ;reg rx_r2 ;wire rx_nedge ;//rx同步至时钟域 并检测下降沿always@(posedge clk or negedge rst_n)beginif(!rst_n)beginrx_r1 <= 1'b1;rx_r2 <= 1'b1;endelse beginrx_r1 <= rx;rx_r2 <= rx_r1;endendassign rx_nedge = ~rx_r1 && rx_r2;//三段式状态机//第一段 时序逻辑always @(posedge clk or negedge rst_n) beginif(!rst_n)begincstate <= IDLE;endelse begincstate <= nstate;endend//第二段 组合逻辑always @(*)begincase (cstate)IDLE : beginif(idle2start)beginnstate = START;endelse beginnstate = cstate;endendSTART: beginif(start2data)beginnstate = DATA;endelse beginnstate = cstate;endendDATA : beginif(data2idle)beginnstate = IDLE;endelse if(data2check)beginnstate = CHECK;endelse beginnstate = cstate;endendCHECK: beginif(check2idle)beginnstate = IDLE;endelse beginnstate = cstate;endenddefault : nstate = IDLE;endcaseendassign idle2start = cstate == IDLE && rx_nedge;//检测到开始位,结束空闲状态,开始接收数据assign start2data = cstate == START && end_cnt_bit;assign data2idle = cstate == DATA && end_cnt_bit && CHECK_BIT == "None";assign data2check = cstate == DATA && end_cnt_bit;assign check2idle = cstate == CHECK && end_cnt_bit;//1Baud所需时间always @(posedge clk or negedge rst_n)begin if(!rst_n)begincnt_bort <= 'd0;end else if(add_cnt_bort)begin if(end_cnt_bort)begin cnt_bort <= 'd0;endelse begin cnt_bort <= cnt_bort + 1'b1;end endend assign add_cnt_bort = cstate != IDLE;assign end_cnt_bort = add_cnt_bort && cnt_bort == TIME_BORT - 1'b1;//8bit共需时间always @(posedge clk or negedge rst_n)begin if(!rst_n)begincnt_bit <= 'd0;end else if(add_cnt_bit)begin if(end_cnt_bit)begin cnt_bit <= 'd0;endelse begin cnt_bit <= cnt_bit + 1'b1;end endend assign add_cnt_bit = end_cnt_bort;assign end_cnt_bit = add_cnt_bit && cnt_bit == bit_max - 1'b1;//bit计数器复用always@(*)begincase(cstate)IDLE : bit_max = 1'b0;START : bit_max = 1'b1;DATA : bit_max = 4'd8;//8位数据位CHECK : bit_max = 1'b1;//校验位default : bit_max = 1'b0;endcaseend//状态输出always @(posedge clk or negedge rst_n)beginif(!rst_n)beginrx_temp <= 1'b0;endelse if(cstate == DATA && cnt_bort == (TIME_BORT >> 1))beginrx_temp[cnt_bit] <= rx_r1;endelse beginrx_temp <= rx_temp;endendassign rx_data = rx_temp;//校验位always @(posedge clk or negedge rst_n)beginif(!rst_n)begincheck_bit <= 1'b0;endelse if(cstate == CHECK && cnt_bort == (TIME_BORT >> 1))begincheck_bit <= rx;endelse begincheck_bit <= check_bit;endendassign check_temp = CHECK_BIT == "Odd" ? ~^rx_data : ^rx_data;assign rx_data_vld = CHECK_BIT == "None" ? data2idle : (check2idle && check_temp == check_bit) ? 1: 0;endmodule
UART TX模块:
//****************************************************************
//--majiko 2023-8-15 UART TX模块
//****************************************************************
module uart_tx
#( parameter BORT = 20'd115200,//波特率parameter CLOCK = 50_000_000,//系统时钟参数parameter CHECK_BIT = "None" //"None" 无校验,"Odd" 奇校验,"Even" 偶校验
)
(input wire clk ,input wire rst_n ,input wire [7:0] tx_data ,input wire tx_data_vld ,//数据有效信号output wire ready ,//准备接受数据信号output reg tx
);//参数定义parameter TIME_BORT = CLOCK/BORT;//状态机状态定义parameter IDLE = 5'b00001,//空闲状态电平默认拉高START = 5'b00010,//起始位赋值DATA = 5'b00100,//数据位赋值CHECK = 5'b01000,//奇偶校验位STOP = 5'b10000;//停止位赋值//波特率115200 1bit传输时间计数器参数//内部信号定义//状态寄存器reg [4:0] cstate ;reg [4:0] nstate ;//状态跳转条件wire idle2start ;wire start2data ;wire data2check ;wire data2stop ; wire check2stop ;wire stop2idle ;//1Baud时间计数器reg [8:0] cnt_bort ;wire add_cnt_bort ;wire end_cnt_bort ;//比特计数器(复用)reg [2:0] cnt_bit ;wire add_cnt_bit ;wire end_cnt_bit ;reg [3:0] bit_max ;//计数器复用信号reg [7:0] tx_data_r ;//寄存数据wire check_bit ;//三段式状态机//第一段 时序逻辑always @(posedge clk or negedge rst_n) beginif(!rst_n)begincstate <= IDLE;endelse begincstate <= nstate;endend//第二段 组合逻辑always @(*)begincase (cstate)IDLE : beginif(idle2start)beginnstate <= START;endelse beginnstate <= cstate;endendSTART: beginif(start2data)beginnstate <= DATA;endelse beginnstate <= cstate;endendDATA : beginif(data2check)beginnstate <= CHECK;endelse if(data2stop)beginnstate <= STOP;endelse beginnstate <= cstate;endendCHECK: beginif(check2stop)beginnstate <= STOP;endelse beginnstate <= cstate;endendSTOP : beginif(stop2idle)beginnstate <= IDLE;endelse beginnstate <= cstate;endenddefault : nstate <= IDLE;endcaseendassign idle2start = cstate == IDLE && tx_data_vld;//数据有效信号拉高,结束空闲状态,开始接收数据assign start2data = cstate == START && end_cnt_bit;assign data2check = cstate == DATA && end_cnt_bit;assign data2stop = cstate == DATA && end_cnt_bit && CHECK_BIT == "None"; assign check2stop = cstate == CHECK && end_cnt_bit;assign stop2idle = cstate == STOP && end_cnt_bit;//1Baud所需时间always @(posedge clk or negedge rst_n)begin if(!rst_n)begincnt_bort <= 'd0;end else if(add_cnt_bort)begin if(end_cnt_bort)begin cnt_bort <= 'd0;endelse begin cnt_bort <= cnt_bort + 1'b1;end endend assign add_cnt_bort = cstate != IDLE;assign end_cnt_bort = add_cnt_bort && cnt_bort == TIME_BORT - 1'b1;//不同状态bit数所需时间always @(posedge clk or negedge rst_n)begin if(!rst_n)begincnt_bit <= 'd0;end else if(add_cnt_bit)begin if(end_cnt_bit)begin cnt_bit <= 'd0;endelse begin cnt_bit <= cnt_bit + 1'b1;end endend assign add_cnt_bit = end_cnt_bort;assign end_cnt_bit = add_cnt_bit && cnt_bit == bit_max - 1'b1;//bit计数器复用always@(*)begincase(cstate)IDLE : bit_max = 1'b0;START : bit_max = 1'b1;//1位起始位数据DATA : bit_max = 4'd8;//8位数据位CHECK : bit_max = 1'b1;//1位奇偶校验位STOP : bit_max = 1'b1;//1位停止位数据default : bit_max = 1'b0;endcaseend//状态输出//输入数据寄存always@(posedge clk or negedge rst_n)beginif(!rst_n)begintx_data_r <= 1'b0;endelse if(tx_data_vld)begintx_data_r <= tx_data;endelse begintx_data_r <= tx_data_r;endend//奇偶校验位assign check_bit = CHECK_BIT == "Odd" ? ~^tx_data_r : ^tx_data_r;//数据输出always@(*)begincase(cstate)IDLE : tx = 1'b1;//空闲时为高电平START : tx = 1'b0;//起始位低电平DATA : tx = tx_data_r[cnt_bit];//数据位从低位开始发送CHECK : tx = check_bit;STOP : tx = 1'b1;//停止位高电平default : tx = 1'b1;endcaseendassign ready = cstate == IDLE;endmodule
顶层模块:
module top(input wire clk ,input wire rst_n ,input wire rx ,input wire key_in ,inout wire i2c_sda ,output wire i2c_scl ,output wire tx );wire ready ;wire [7:0] rx_data ;wire [7:0] tx_data ;wire rx_data_vld ;wire tx_data_vld ;wire key_out ;//模块例化eeprom_control u_eeprom_control(/*input wire */.clk (clk ),/*input wire */.rst_n (rst_n ),/*input wire [6:0] */.device_id (7'b1010_000 ),/*input wire */.wr_req (rx_data_vld ),/*input wire */.rd_req (key_out ),/*input wire [ADDR_BIT - 1:0] */.reg_addr (0),/*input wire */.reg_addr_vld (rx_data_vld ),/*input wire [7:0] */.wr_data (rx_data ),/*input wire */.wr_data_vld (rx_data_vld ),/*output wire [7:0] */.rd_data (tx_data ),/*output wire */.rd_data_vld (tx_data_vld ),/*output wire */.ready (),/*output wire */.i2c_scl (i2c_scl ),/*inout wire */.i2c_sda (i2c_sda ));// iic_control u_eeprom_control2// (// /*input wire */.clk (clk ),// /*input wire */.rst_n (rst_n ),// /*input wire [6:0] */.device_id (7'b1010_000 ),// /*input wire */.wr_req (rx_data_vld ),// /*input wire */.rd_req (key_out ),// /*input wire [ADDR_BIT - 1:0] */.addr (0),// /*input wire */.addr_vld (rx_data_vld ),// /*input wire [7:0] */.wr_data (rx_data ),// /*input wire */.wr_data_vld (rx_data_vld ),// /*output wire [7:0] */.rd_data (tx_data ),// /*output wire */.rd_data_vld (tx_data_vld ),// /*output wire */.ready (),// /*output wire */.iic_scl (i2c_scl ),// /*inout wire */.iic_sda (i2c_sda )// );uart_rx u_uart_rx(.clk (clk ),.rst_n (rst_n ),.ready ( ),.rx (rx ),.rx_data_vld (rx_data_vld ),.rx_data (rx_data ) );uart_tx u_uart_tx(.clk (clk ),.rst_n (rst_n ),.tx_data (tx_data ),.tx_data_vld (tx_data_vld ),.ready (ready ),.tx (tx ) );// ctrl u_ctrl (// /*input wire */.clk (clk ),// /*input wire */.rst_n (rst_n ),// /*input wire */.rx_data_vld (rx_data_vld ),// /*input reg [7:0] */.rx_data (rx_data ),// /*input wire */.ready (ready ),// /*output wire [7:0] */.tx_data (),// /*output wire */.tx_data_vld ()// );key_filter#(.WIDTH(1)) u_key_filter (/*input wire */.clk (clk ),/*input wire */.rst_n (rst_n ),/*input wire [WIDTH - 1:0] */.key_in (key_in ),/*output reg [WIDTH - 1:0] */.key_out (key_out ));endmodule
按键消抖模块:
module key_filter#(parameter WIDTH = 4) //参数化按键位宽
(input wire clk ,input wire rst_n ,input wire [WIDTH - 1:0] key_in ,//按键输入信号output reg [WIDTH - 1:0] key_out //输出稳定的脉冲信号
);parameter MAX = 20'd1_000_000;reg [19:0] cnt_delay ; //20ms延时计数寄存器
wire add_cnt_delay ; //开始计数的标志
wire end_cnt_delay ; //结束计数的标志reg [WIDTH - 1:0] key_r0 ; //同步
reg [WIDTH - 1:0] key_r1 ; //打一拍
reg [WIDTH - 1:0] key_r2 ; //打两拍wire [WIDTH - 1:0] nedge ; //下降沿寄存器//同步打拍
always @(posedge clk or negedge rst_n) beginif(!rst_n)beginkey_r0 <= {WIDTH{1'b1}};key_r1 <= {WIDTH{1'b1}};key_r2 <= {WIDTH{1'b1}};endelse beginkey_r0 <= key_in; //同步key_r1 <= key_r0; //寄存一拍key_r2 <= key_r1; //寄存两拍end
end//20ms计数器
always @(posedge clk or negedge rst_n)beginif(!rst_n)begincnt_delay <= 1'b0;endelse if(add_cnt_delay )beginif(nedge)begin //检测到下降沿从0开始计数cnt_delay <= 1'b0;endelse if(cnt_delay == MAX - 1'b1)begincnt_delay <= cnt_delay; //计数计满结束后保持,避免产生多个输出脉冲endelse begincnt_delay <= cnt_delay + 1'b1;endendelse begincnt_delay <= 1'b0;end
endassign nedge = ~key_r1 & key_r2; //下降沿检测
assign add_cnt_delay = 1'b1;
assign end_cnt_delay = add_cnt_delay && cnt_delay == MAX - 1'b1;//key_out脉冲信号赋值
always@(posedge clk or negedge rst_n)beginif(!rst_n)beginkey_out <= 'd0;endelse if(cnt_delay == MAX - 2'd2)begin //计数计满前一个脉冲时产生按键脉冲key_out <= ~key_in;endelse beginkey_out <= 'd0;end
endendmodule
五、实现效果
可以看出,能够正常进行单个字节的收发。
参考资料
https://blog.csdn.net/zhangduang_KHKW/article/details/121953275
相关文章:
基于FPGA的I2C读写EEPROM
文章目录 前言一、I2C协议1.1 I2C协议简介1.2 物理层1.3 协议层 二、EEPROM2.1 型号及硬件规格2.2 各种读写时序 三、状态机设计四、项目源码:五、实现效果参考资料 前言 本次项目所用开发板FPGA芯片型号为:EP4CE6F17C8 EEPROM芯片型号为:24L…...
Viva Employee Communications Communities部署方案
目录 Viva Employee Communications & Communities产品介绍 1. 沟通中心(Communications Center) 2. 新闻和公告(News and Announcements)...
WPF向Avalonia迁移(三、项目结构)
前提: Avalonia版本11.0.0 1.配置文件 1.1 添加配置文件 1.2 读取配置文件 添加System.Configuration.ConfigurationManager using Avalonia.Controls; using System.Configuration;namespace AvaloniaApplication7.Views {public partial class MainWindow : W…...
cvpr24写作模板pdfLaTex编译器注意点小结
文章目录 1 更改作者显示 Anonymous CVPR submission2 \label标签3 换行符// 与换列符&4 \medskip5 首行缩进6 插入图片6.1 单幅图片6.2 并排显示\hfill Reference https://cvpr.thecvf.com/Conferences/2024 1 更改作者显示 Anonymous CVPR submission 这一行开头加上% …...
windows版php扩展包下载
php8有些扩展需自己下载,像redis 看下phpinfo中的PHP Extension Build,确定自己的php版本 windows.php.net - /downloads/pecl/releases/...
计算机竞赛 题目:基于深度学习的中文汉字识别 - 深度学习 卷积神经网络 机器视觉 OCR
文章目录 0 简介1 数据集合2 网络构建3 模型训练4 模型性能评估5 文字预测6 最后 0 简介 🔥 优质竞赛项目系列,今天要分享的是 基于深度学习的中文汉字识别 该项目较为新颖,适合作为竞赛课题方向,学长非常推荐! &a…...
Django跨域访问 nginx转发 开源浏览器
Django跨域访问 https://blog.csdn.net/lonelysnowman/article/details/128086205 nginx转发 https://blog.csdn.net/faye0412/article/details/75200607/ 开源浏览器 https://www.oschina.net/p/chromiumengine 浏览器油猴开发 https://blog.csdn.net/mukes/article/detail…...
Docker Alist 在线网盘部署
文章目录 拉取镜像创建并运行查看容器自动生成的密码在浏览器中进行访问 挂载本地磁盘 拉取镜像 docker pull xhofe/alist-aria2创建并运行 # -v /data/alist:/opt/alist/data 挂载本地目录 docker run -d --restartalways -v /data/alist:/opt/alist/data -p 5244:5244 -e P…...
Jmeter吞吐量控制器使用小结
吞吐量控制器(Throughput Controller)场景: 在同一个线程组里, 有10个并发, 7个做A业务, 3个做B业务,要模拟这种场景,可以通过吞吐量模拟器来实现.。 添加吞吐量控制器 用法1: Percent Executions 在一个线程组内分别建立两个吞吐量控制器, 分别放业务A和业务B 吞吐量控制器采…...
3分钟轻松实现网关网口连接罗克韦尔AB CompactLogix系列PLC
目录 EG网关网口连接罗克韦尔AB CompactLogix系列PLC 一. 准备工作 1.1 在对接前我们需准备如下物品 1.2 EG20网关准备工作 1.3 PLC准备工作 二. EMCP平台设置 2.1 新增EG设备 2.2 远程配置网关 2.3 网关绑定 2.4 通讯参数设置 2.5 创建设备驱动 2.5.1 添加变量 2.…...
vscode刷leetcode使用Cookie登录
1、打开vscode,选择扩展,搜索leetcode,选择第一个,带有中文力扣字样,安装后重启 2、选择这个小球,切换中文版本,切换后,会显示一个打勾 3、选择小球旁边的有箭头的小门࿰…...
每次启动Docker容器指定IP、hosts和端口
每次启动Docker容器指定IP、hosts和端口 1问题描述1解决办法1.1启动容器指定好IP、hostname、端口等信息1.2通过docker-compose启动容器,可以配置extra_hosts属性1.3通过k8s来管理容器,则在可以在创建pod的yaml文件通过hostAliases添加域名IP映射 2问题描…...
PL/SQL增量同步
PL/SQL全量同步_枯河垂钓的博客-CSDN博客 目录 增量同步 增量同步存储过程 ORACEL 独有的 增量更新 merge into 增量同步 逻辑: 用源表的数据更新目标表,数据存在则更新,数据不存在,则插入 实现的逻辑: 1. 首先判断目标表是否有源表的数据 2. 如果有,则用源表的数据更新…...
C++——多态底层原理
虚函数表 先来看这个问题: class Base { public: virtual void Func1() { cout << "Func1()" << endl; } private: int _b 1; }; sizeof(Base)是多少? 答案是:8 因为Base中除了成员变量_b,还有一个虚函数表_vfp…...
asdTools-ReID热力图可视化
文章首发见博客:https://mwhls.top/4869.html。 无图/格式错误/后续更新请见首发页。 更多更新请到mwhls.top查看 欢迎留言提问或批评建议,私信不回。 Github - 开源代码及Readme Blog - 工具介绍 摘要:基于TorchCam实现ReID的热力图可视化的…...
CSS学习笔记
目录 1.CSS简介1.什么是CSS2.为什么使用CSS3.CSS作用 2.基本用法1.CSS语法2.CSS应用方式1. 内部样式2.行内样式3.外部样式1.使用 link标签 链接外部样式文件2.import 指令 导入外部样式文件3.使用举例 3.选择器1.基础选择器1.标签选择器2.类选择器3.ID选择器4.使用举例 2.复杂选…...
linux操作命令
VMware版本: 17.0 ubantu版本:22.04.3 命令: # 查看当前目录文件 ls# 切换路径 []内是路径 cd [snap/] cd ../ #返回上一层# 创建文件 []内是文件名 touch [test.txt]# 创建文件夹 []内是文件夹名 mkdir [myself]# 测试一下网络 ping www.b…...
猜数字游戏(Python)
一、猜数字游戏是一个古老的密码破译类、益智类小游戏,通常由两个人参与,一个人设置一个数字,一个人猜数字,当猜数字的人说出一个数字,由出数字的人告知是否猜中:若猜测的数字大于设置的数字,出…...
可视化模块
目录 可视化送入网络的图片可视化网络层的热力图 可视化送入网络的图片 送入的数据为imgs,其大小为(8,3,256,256),并以2行8列进行展示 import matplotlib.pyplot as plt import numpy as np# 假设你的张量名为 tensor,形状为 (8, 3, 256, 2…...
MyBatis insert标签
<insert id"addWebsite" parameterType"string">insert into website(name)values(#{name}) </insert> 在 WebsiteMapper 接口中定义一个 add() 方法 public int addWebsite(String name); 参数为 Sting 类型的字符串;返回值为 …...
扬尘监测:智能化解决方案让生活更美好
随着工业化和城市化的快速发展,扬尘污染问题越来越受到人们的关注。扬尘不仅影响城市环境,还会对人们的健康造成威胁。为了解决这一问题,扬尘监测成为了一个重要的手段。本文将介绍扬尘监测的现状、重要性以及智能化解决方案,帮助…...
【AI视野·今日NLP 自然语言处理论文速览 第四十五期】Mon, 2 Oct 2023
AI视野今日CS.NLP 自然语言处理论文速览 Mon, 2 Oct 2023 Totally 44 papers 👉上期速览✈更多精彩请移步主页 Daily Computation and Language Papers Efficient Streaming Language Models with Attention Sinks Authors Guangxuan Xiao, Yuandong Tian, Beidi C…...
The little schemer 学习
参考文章: The Little Schemer 阅读笔记-CSDN博客 前言 原子是Scheme的基本元素之一。首先定义了过程atom?,用来判断一个S-表达式是不是原子: (define atom?(lambda (x)(and (not (pair? x)) (not (null? x))))) 这个“pair”实际上…...
yolov5+bytetrack算法在华为NPU上进行端到端开发
自从毕业后开始进入了华为曻腾生态圈,现在越来越多的公司开始走国产化路线了,现在国内做AI芯片的厂商比如:寒武纪、地平线等,虽然我了解的不多,但是相对于瑞芯微这样的AI开发板来说,华为曻腾的生态比瑞芯微…...
【Java-LangChain:使用 ChatGPT API 搭建系统-1】简介
简介 欢迎来到课程《使用 ChatGPT API 搭建系统》 , 旨在指导开发者如何基于 ChatGPT 搭建完整的智能问答系统。 使用 ChatGPT 不仅仅是一个单一的 Prompt 或单一的模型调用,本课程将分享使用 LLM 构建复杂应用的最佳实践。 本课程以构建客服助手为例,…...
BJT晶体管
BJT晶体管也叫双极结型三极管,主要有PNP、NPN型两种,符号如下: 中间的是基极(最薄,用于控制),带箭头的是发射极(自由电子浓度高),剩下的就是集电极࿰…...
ORACLE中SQL运算符的优先级
SQL运算符优先级: 注: 1、可以使用括号改变优先级顺序 2、可以看出OR的优先级最低,算术运算符的优先级最高 另:操作符优先级 * / - 1、乘除的优先级高于加减; 2、同一优先级运算符从左向右执行; 3、括号内的…...
springboot和vue:十一、Axios网络请求的安装引入与使用、跨域问题解决(CORS)
Axios简介与安装 Axios是一个基于promise的网络请求库,作用于node.js和浏览器中Axios在浏览器端使用XMLHttpRequests发送网络请求,并自动完成json数据的转换安装:npm install axios官方文档:https://www.axios-http.cn/ Axios基…...
外汇天眼:真实记录,投资者在盗版MT4平台SCE Group上做交易的经历!
外汇市场是全球最大的金融市场,比起其他市场有着更多天然的优势,但也因为资讯的不对等,导致很多人上当受骗。而在外汇市场上最常见的骗局之一,就是黑平台使用盗版MT4/5交易软件,因为截至目前MT4/5仍是外汇市场交易使用…...
FFmpeg 命令:从入门到精通 | ffmpeg 命令视频录制
FFmpeg 命令:从入门到精通 | ffmpeg 命令视频录制 FFmpeg 命令:从入门到精通 | ffmpeg 命令视频录制安装软件:Screen Capturer Recorder查看可用设备名字音视频录制录制视频(默认参数)录制声音(默认参数&am…...
云服务器安装win系统做网站/谷歌推广公司哪家好
nexus5、nexus6可以刷安卓6.0系统了,想要学习nexus5、nexus6怎么刷安卓6.0系统的朋友可以一起来学习!提示:刷机当然存在风险,并且开发者预览版也很不完善,无法作为主力机使用,刷机前请备份好自己的数据。首…...
营口市网站建设/做搜索引擎优化的企业
1 centos 6.x 安装 MySQL 注意:需要root权限。 yum install -y mysql yum install -y mysql-server yum install -y mysql-devel 2 centos 7.x 安装 MySQL 注意:需要root权限。 1)安装前工作 // 1)检测系统是否已有MySQL rp…...
淘宝网站优化实例/什么是电商
先上软件效果图 代码如下1.根据Url地址得到网页的html源码 1 public static string GetWebContent(string Url)2 {3 string strResult "";4 try5 {6 HttpWebRequest request (HttpWebRequest)WebReq…...
网页在线生成网站/seo每天一贴
问题描述:给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。 输入: [-2,1,-3,4,-1,2,1,-5,4], 输出: 6 解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。 //贪心…...
东莞建设网站推广公司地址/各种推广平台
http://blog.csdn.net/jay14/article/details/54074553 TLB原理 转载于:https://www.cnblogs.com/wangdgy/p/8465574.html...
政府网站官网/seo整站优化一年价格多少
实现的框图如下所示:考虑对32位数据处理核心器件就是一个乘法器。a是输入的32位数据,num是移位的5位数据b是乘法器的64位输出下面是实现各种移位的算法:1、逻辑左移:结果取D2、逻辑右移:num取反加1,结果取C…...