iic协议--Verilog及仿真

阅读: 评论:0

iic协议--Verilog及仿真
1、协议原理:
IIC(Inter-Integrated Circuit),i2c总线由数据线sda和时钟线scl这两条构成的串⾏总线,主机和从机可以在i2c总线上发送和接收数据。scl时钟线作为控制,sda则包含有ack、nack、设备地址字节地址、8bits数据。
起始信号(scl为⾼电平时,sda变成低电平)与结束信号(scl为⾼电平时,sda变成⾼电平)的状态:
IIC单字节写时序有两种:1字节地址段器件单字节写时序、2字节地址段器件单字节写时序。
IIC单字节读时序有两种:1字节地址段器件单节读时序、2字节地址段器件单节读时序
字节地址⾼三位xxx:这⾥使⽤的EEPROM的存储容量只有8192bits(1024bits*8)=210*23=213,所以16位的字节地址就多余了三位。
2、协议代码:
1、这⾥实现的是2字节单次读写。
2、开始和结束时,虽然scl为⾼电平,sda仍要变化;接下来传输字节,scl为低电平,sda才能变化。这⾥采取在scl⾼电平和低电平中线产⽣标志。
3、通过状态机来实现读写。
综合代码:
module IIC_AT24C64(
input sys_clk,
input sys_rst_n,
input iic_en,
input [2:0]cs_bit,//可编程地址
input [12:0]byte_address,//字节地址
input write,
input read,
input [7:0]write_data,
output reg[7:0]read_data,
output reg scl,
inout sda,
output reg done
);
parameter
SYS_CLK=50_000_000,//系统时钟50MHz
SCL_CLK=200_000;//scl时钟200KHz
reg [7:0]scl_cnt;//时钟计数
parameter div_cnt=SYS_CLK/SCL_CLK;
always @(posedge sys_clk or negedge sys_rst_n)begin
if(!sys_rst_n)
scl_cnt<=8'd0;
else if(scl_cnt == div_cnt-1'b1)
scl_cnt<=8'd0;
else
scl_cnt<=scl_cnt+1'b1;
end
//⽣成scl时钟线
always @(posedge sys_clk or negedge sys_rst_n)begin
if(!sys_rst_n)防伪标签识别
scl<=1'b1;
else if(scl_cnt == (div_cnt>>1)-1'b1)
scl<=1'b0;
else if(scl_cnt == div_cnt-1'b1)
scl<=1'b1;
else
scl<=scl;
end
//scl电平中线
reg scl_high_middle;//scl⾼电平中线
reg scl_low_middle;//scl低电平中线
always @(posedge sys_clk or negedge sys_rst_n)begin
scl_high_middle<=1'b0;
油纸电容式套管
scl_low_middle<=1'b0;
end
else if(scl_cnt == (div_cnt>>2))
scl_high_middle<=1'b1;
else if(scl_cnt == (div_cnt>>1)+(div_cnt>>2))
系统升级补丁备份文件scl_low_middle<=1'b1;
else begin
scl_high_middle<=1'b0;
scl_low_middle<=1'b0;
end
end
reg [15:0]state;
parameter
idle=16'd1,//空闲状态
w_or_r_start=16'd2,//设备地址
device_ADDR=16'd3,//发送
ACK1=16'd4,
byte_ADDR_high=16'd5,//字节地址⾼8位
ACK2=16'd6,
byte_ADDR_low=16'd7,//字节地址低8位
ACK3=16'd8,
载体构建w_data=16'd9,//写数据
ACK4=16'd10,
r_start=16'd11,//读开始
device_ADDR_r=16'd12,//设备地址读
ACK5=16'd13,
r_data=16'd14,//读数据
NACK=16'd15,//⾮应答位
stop=16'd16;
reg sda_en;//sda数据线使能
reg sda_reg;//sda数据暂存位
reg [7:0]sda_data_out;//sda数据发给从机暂存
reg [7:0]sda_data_in;//sda数据取之从机暂存
reg [3:0]bit_cnt;//每⼀bit
assign sda=sda_en?sda_reg:1'bz;
//读写标志位
reg w_flag;
reg r_flag;
always @(posedge sys_clk or negedge sys_rst_n)begin
if(!sys_rst_n)begin
state<=idle;
w_flag<=1'b0;
r_flag<=1'b0;
sda_reg<=1'b1;
done<=1'b0;
sda_en<=1'b0;
end
else begin
case(state)
idle:begin
sda_reg<=1'b1;
w_flag<=1'b0;
r_flag<=1'b0;
sda_en<=1'b0;
sda_reg<=1'b1;
done<=1'b0;
if(iic_en && write)begin
w_flag<=1'b1;
sda_en<=1'b1;
sda_reg<=1'b1;
state<=w_or_r_start;
end
else if(iic_en && read)begin
r_flag<=1'b1;
sda_en<=1'b1;
sda_reg<=1'b1;
state<=w_or_r_start;
end
else
state<=idle;
end
w_or_r_start:begin
if(scl_high_middle)begin
sda_reg<=1'b0;
sda_data_out<={4'b1010,cs_bit,1'b0};//在这⾥装好设备地址bit_cnt<=4'd8;
state<=device_ADDR;
end
else begin
sda_reg<=1'b1;
state<=w_or_r_start;
end
end
device_ADDR:begin
if(scl_low_middle)begin
sda_reg<=sda_data_out[7];
sda_data_out<={sda_data_out[6:0],1'b0};//在这⾥发出设备地址。其他的也是在上⼀状态装好值,下⼀个状态发出if(bit_cnt==0)begin
state<=ACK1;
sda_en<=1'b0;
end
else
state<=device_ADDR;
end
else
state<=device_ADDR;
end
ACK1:begin
if(scl_high_middle)begin
if(sda==1'b0)begin
state<=byte_ADDR_high;
sda_data_out<={3'bxxx,byte_address[12:8]};
bit_cnt<=4'd8;
end
else
state<=idle;
end
else
state<=ACK1;
end
byte_ADDR_high:begin
if(scl_low_middle)begin
sda_en<=1'b1;
bit_cnt<=bit_cnt+1'b1;
sda_reg<=sda_data_out[7];
sda_data_out<={sda_data_out[6:0],1'b0};
if(bit_cnt==0)begin
state<=ACK2;
sda_en<=1'b0;
end
else
abs082
state<=byte_ADDR_high;
end
else
state<=byte_ADDR_high;
end
ACK2:begin
if(scl_high_middle)begin
if(sda==1'b0)begin
state<=byte_ADDR_low;
sda_data_out<=byte_address[7:0];
bit_cnt<=4'd8;
end
else
state<=idle;
end
else
state<=ACK2;
end
byte_ADDR_low:begin
if(scl_low_middle)begin
sda_en<=1'b1;
bit_cnt<=bit_cnt-1'b1;
sda_reg<=sda_data_out[7];
sda_data_out<={sda_data_out[6:0],1'b0};
if(bit_cnt==0)begin
state<=ACK3;
sda_en<=1'b0;
end
else
state<=byte_ADDR_low;
end
else
state<=byte_ADDR_low;
end
ACK3:begin
if(scl_high_middle)begin
if(sda==1'b0)begin
if(w_flag)begin
sda_data_out<=write_data;
bit_cnt<=4'd8;
state<=w_data;
end
else if(r_flag)begin
sda_reg<=1'b1;
state<=r_start;
end
end
else
state<=ACK3;
end
end
w_data:begin
if(scl_low_middle)begin
sda_en<=1'b1;
bit_cnt<=bit_cnt-1'b1;
sda_reg<=sda_data_out[7];
sda_data_out<={sda_data_out[6:0],1'b0};
if(bit_cnt==0)begin
state<=ACK4;
sda_en<=1'b0;
end
else
state<=w_data;
end
else
state<=w_data;
end
ACK4:begin
if(scl_high_middle)begin
if(sda==1'b0)
state<=stop;
else
state<=idle;
end
else
state<=ACK4;
end
r_start:begin
if(scl_low_middle)begin
sda_en<=1'b1;
end
else if(scl_high_middle)begin
sda_reg<=1'b0;
state<=device_ADDR_r;
sda_data_out<={4'b1010,cs_bit,1'b1};
bit_cnt<=4'd8;
end
else begin
sda_reg<=1'b1;
state<=r_start;
end
end
device_ADDR_r:begin
if(scl_low_middle)begin
bit_cnt<=bit_cnt-1'b1;
sda_reg<=sda_data_out[7];
sda_data_out<={sda_data_out[6:0],1'b0};
if(bit_cnt==0)begin
state<=ACK5;
sda_en<=1'b0;
end
else
state<=device_ADDR_r;
end
else
state<=device_ADDR_r;
end
ACK5:begin
if(scl_high_middle)begin
if(sda==1'b0)begin
state<=r_data;
sda_en<=1'b0;
bit_cnt<=4'd8;
end
else
state<=idle;
end
else
state<=ACK5;
end
r_data:begin
if(scl_high_middle)begin
sda_data_in<={sda_data_in[6:0],sda};
bit_cnt<=bit_cnt-1'b1;
state<=r_data;
end
else if(scl_low_middle && bit_cnt==0)
state<=NACK;
else
state<=r_data;
end
NACK:begin
read_data<=sda_data_in;
if(scl_high_middle)begin
state<=stop;
sda_reg<=1'b0;  //为什么这⾥要为0?因为你发现其他的应答位都是判断if(sda==1'b0),⽽这⾥由于最后⼀个是主机发出的应答,⽽不是像之前那些是从机发出的。end//也可以看出主机应答的是直接让output sda_reg为0,⽽前⾯的应答是等待从机input sda的值来判断应答位
电力网桥else
state<=NACK;

本文发布于:2023-05-14 09:19:13,感谢您对本站的认可!

本文链接:https://patent.en369.cn/patent/4/99208.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:地址   字节   时钟
留言与评论(共有 0 条评论)
   
验证码:
Copyright ©2019-2022 Comsenz Inc.Powered by © 369专利查询检索平台 豫ICP备2021025688号-20 网站地图