|
FPGA模拟I2C时序,配置一个CMOS M芯片,但通讯总是无法建立。
1.示波器得到的信号,如下。从图中可以看出,I2C时序:停止-开始-从机地址(0x80)-SDA拉高阻等待从机应答,但主机却迟迟得不到应答,同时,经过二十多个时钟后,会出现图中一开始的突然拉低,然后进入下一个状态。这个是什么情况?
2.另外,对于上图,还有个问题在于,不管是SCL还是SDA,VPP都在几百mv,这个也不对。以前都用单片机上拉电阻,但对于FPGA应该怎么写呢?
3.以前I2C通讯正常的时候,配置结果总是有不对的情况,SCL和SDA都是通过10cm左右杜邦线,连接FPGA于CMOS模块。这块是不是有什么影响?还是通过调整上拉电阻就可以解决呢?
谢过各位~
原始程序:
`timescale 1ns / 1ps
module iic(
led,led2,
clk,
scl,sda,rst_n,
);
//外部接口
input clk; // 50MHz
input rst_n; //复位信号,低有效
output scl; // I2C时钟端口
inout sda; // I2C的数据端口
//LED test
output wire led, led2;
reg led_r;
reg led_r2;
assign led= led_r;
assign led2= led_r2;
//---------------------------------------------
//分频部分
reg[2:0] cnt; // cnt=0:scl上升沿,cnt=1:scl高电平中间,cnt=2:scl下降沿,cnt=3:scl低电平中间
reg[8:0] cnt_delay; //500循环计数,产生iic所需要的时钟
reg scl_r; //时钟脉冲寄存器
assign scl = scl_r; //产生iic所需要的时钟
always @ (posedge clk or negedge rst_n)
if(!rst_n) cnt_delay <= 9'd0;
else if(cnt_delay == 9'd499) cnt_delay <= 9'd0; //计数到10us为scl的周期,即100KHz
else cnt_delay <= cnt_delay+1'b1; //计算时间
always @ (posedge clk or negedge rst_n) begin
if(!rst_n) cnt <= 3'd5;
else begin
case (cnt_delay)
9'd124: cnt <= 3'd1; //cnt=1:scl高电平中间,用于数据采样
9'd249: cnt <= 3'd2; //cnt=2:scl下降沿
9'd374: cnt <= 3'd3; //cnt=3:scl低电平中间,用于数据变化
9'd499: cnt <= 3'd0; //cnt=0:scl上升沿
default: cnt <= 3'd5;
endcase
end
end
`define SCL_POS (cnt==3'd0) //cnt=0:scl上升沿
`define SCL_HIG (cnt==3'd1) //cnt=1:scl高电平中间,用于数据采样
`define SCL_NEG (cnt==3'd2) //cnt=2:scl下降沿
`define SCL_LOW (cnt==3'd3) //cnt=3:scl低电平中间,用于数据变化
always @ (posedge clk or negedge rst_n)
if(!rst_n) scl_r <= 1'b0;
else if(cnt==3'd0) scl_r <= 1'b1; //scl信号上升沿
else if(cnt==3'd2) scl_r <= 1'b0; //scl信号下降沿
//---------------------------------------------
//需要写入的地址和数据
`define DEVICE_READ 8'b1011_1001 //被寻址器件地址(读操作)
`define DEVICE_WRITE 8'b1000_0000 //被寻址器件地址(写操作)
`define BYTE_ADDR 8'b0101_0000 //写入/读出EEPROM的地址寄存器
`define WRITE_DATA_H 8'b0000_0011 //写入EEPROM的数据
`define WRITE_DATA_L 8'b0000_1111
reg[7:0] db_r; //在IIC上传送的数据寄存器
//---------------------------------------------
//读、写时序
parameter IDLE1 =6'd0;
parameter START1 =6'd1;
parameter SEND_A1 =6'd2;
parameter SLV_ACK1 =6'd3;
parameter SEND_D1 =6'd4;
parameter SLV_ACK2 =6'd5;
parameter SEND_D1_HIGH =6'd6;
parameter SLV_ACK3 =6'd7;
parameter SEND_D1_LOW =6'd8;
parameter SLV_ACK4 =6'd9;
parameter STOP1 =6'd10;
reg[5:0] cstate; //状态寄存器
reg sda_r; //输出数据寄存器
reg sda_link; //输出数据sda信号inout方向方向
reg[3:0] num; //
assign sda = sda_link ? sda_r:1'bz;
always @ (posedge clk or negedge rst_n) begin
if(!rst_n) begin
cstate <= IDLE1;
sda_r <= 1'b1;
sda_link <= 1'b0;
num <= 4'd0;
end
else
case (cstate)
IDLE1:
begin
sda_link <= 1'b1; //数据线sda为input
sda_r <= 1'b1;
db_r <= `DEVICE_WRITE; //送器件地址(写操作)
cstate <= START1;
end
START1:
begin
if(`SCL_HIG)
begin //scl为高电平期间
sda_link <= 1'b1; //数据线sda为output
sda_r <= 1'b0; //拉低数据线sda,产生起始位信号
cstate <= SEND_A1 ;
num <= 4'd0; //num计数清零
end
else
cstate <= START1; //等待scl高电平中间位置到来
end
SEND_A1 : begin
if(`SCL_LOW) begin
if(num == 4'd8) begin
num <= 4'd0; //num计数清零
sda_r <= 1'b1;
//sda置为高阻态(input)
cstate <= SLV_ACK1;
end
else begin
cstate <= SEND_A1 ;
num <= num+1'b1;
case (num)
4'd0: sda_r <= db_r[7];
4'd1: sda_r <= db_r[6];
4'd2: sda_r <= db_r[5];
4'd3: sda_r <= db_r[4];
4'd4: sda_r <= db_r[3];
4'd5: sda_r <= db_r[2];
4'd6: sda_r <= db_r[1];
4'd7: sda_r <= db_r[0];
default: ;
endcase
// sda_r <= db_r[4'd7-num]; //送器件地址,从高位开始
end
end
// else if(`SCL_POS) db_r <= {db_r[6:0],1'b0}; //器件地址左移1bit
else cstate <= SEND_A1 ;
end
SLV_ACK1: begin
//if(`SCL_NEG/*sda==1'b0*/) begin //考虑应答位
if(`SCL_HIG) begin //考虑应答位
sda_link <= 1'b0;
if(sda===1'b0)begin
cstate <= SEND_D1; //从机响应信号
db_r <= `BYTE_ADDR; // 1地址
end
end
else cstate <= SLV_ACK1; //等待从机响应
end
SEND_D1: begin
if(`SCL_LOW) begin
if(num==4'd8) begin
num <= 4'd0; //num计数清零
sda_r <= 1'b1;
sda_link <= 1'b0; //sda置为高阻态(input)
cstate <= SLV_ACK2 ;
end
else begin
sda_link <= 1'b1; //sda作为output
num <= num+1'b1;
case (num)
4'd0: sda_r <= db_r[7];
4'd1: sda_r <= db_r[6];
4'd2: sda_r <= db_r[5];
4'd3: sda_r <= db_r[4];
4'd4: sda_r <= db_r[3];
4'd5: sda_r <= db_r[2];
4'd6: sda_r <= db_r[1];
4'd7: sda_r <= db_r[0];
default: ;
endcase
// sda_r <= db_r[4'd7-num]; //送EEPROM地址(高bit开始)
cstate <= SEND_D1;
end
end
// else if(`SCL_POS) db_r <= {db_r[6:0],1'b0}; //器件地址左移1bit
else cstate <= SEND_D1;
end
SLV_ACK2 : begin
//if(`SCL_NEG/*sda==1'b0*/) begin //考虑应答位
if(`SCL_HIG) begin //考虑应答位
sda_link <= 1'b0;
if(sda===1'b0)
begin //从机响应信号
cstate <= SEND_D1_HIGH; //写操作
db_r <= `WRITE_DATA_H; //写入的数据
end
end
else
cstate <= SLV_ACK2 ; //等待从机响应
end
SEND_D1_HIGH: begin
if(`SCL_LOW) begin
if(num==4'd8) begin
num <= 4'd0; //num计数清零
sda_r <= 1'b1;
sda_link <= 1'b0; //sda置为高阻态(input)
cstate <= SLV_ACK3 ;
end
else begin
sda_link <= 1'b1; //sda作为output
num <= num+1'b1;
case (num)
4'd0: sda_r <= db_r[7];
4'd1: sda_r <= db_r[6];
4'd2: sda_r <= db_r[5];
4'd3: sda_r <= db_r[4];
4'd4: sda_r <= db_r[3];
4'd5: sda_r <= db_r[2];
4'd6: sda_r <= db_r[1];
4'd7: sda_r <= db_r[0];
default: ;
endcase
// sda_r <= db_r[4'd7-num]; //送EEPROM地址(高bit开始)
cstate <= SEND_D1_HIGH;
end
end
// else if(`SCL_POS) db_r <= {db_r[6:0],1'b0}; //器件地址左移1bit
else cstate <= SEND_D1_HIGH;
end
SLV_ACK3 : begin
//if(`SCL_NEG/*sda==1'b0*/) begin //考虑应答位
if(`SCL_HIG) begin //考虑应答位
sda_link <= 1'b0;
if(sda===1'b0)
begin //从机响应信号
cstate <= SEND_D1_LOW; //写操作
db_r <= `WRITE_DATA_L; //写入的数据
end
end
else cstate <= SLV_ACK3 ; //等待从机响应
end
SEND_D1_LOW: begin
if(`SCL_LOW) begin
if(num==4'd8) begin
num <= 4'd0; //num计数清零
sda_r <= 1'b1;
sda_link <= 1'b0; //sda置为高阻态(input)
cstate <= SLV_ACK4 ;
end
else begin
sda_link <= 1'b1; //sda作为output
num <= num+1'b1;
case (num)
4'd0: sda_r <= db_r[7];
4'd1: sda_r <= db_r[6];
4'd2: sda_r <= db_r[5];
4'd3: sda_r <= db_r[4];
4'd4: sda_r <= db_r[3];
4'd5: sda_r <= db_r[2];
4'd6: sda_r <= db_r[1];
4'd7: sda_r <= db_r[0];
default: ;
endcase
// sda_r <= db_r[4'd7-num]; //送EEPROM地址(高bit开始)
cstate <= SEND_D1_LOW;
end
end
// else if(`SCL_POS) db_r <= {db_r[6:0],1'b0}; //器件地址左移1bit
else cstate <= SEND_D1_LOW;
end
SLV_ACK4 : begin
//if(`SCL_NEG/*sda==1'b0*/) begin //考虑应答位
if(`SCL_HIG) begin //考虑应答位
sda_link <= 1'b0;
if(sda===1'b0)
begin //从机响应信号
cstate <= STOP1; //写操作
sda_r <= 1'b1;
//db_r <= `WRITE_DATA; //写入的数据
end
end
else
cstate <= SLV_ACK4 ; //等待从机响应
end
STOP1: begin
if(`SCL_LOW) begin
sda_link <= 1'b1;
sda_r <= 1'b0;
cstate <= STOP1;
end
else if(`SCL_HIG) begin
sda_r <= 1'b1; //scl为高时,sda产生上升沿(结束信号)
cstate <= IDLE1;
led_r<=1'b1;
led_r2<=1'b1;
end
else cstate <= STOP1;
end
endcase
end
endmodule
|
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有帐号?注册
x
阿莫论坛20周年了!感谢大家的支持与爱护!!
曾经有一段真挚的爱情摆在我的面前,我没有珍惜,现在想起来,还好我没有珍惜……
|