最近编写了一个IIC程序,从机是TCA6416APW芯片,sda,scl 均通过10k上拉电阻接到3.3v电源上。如图1
图1
tca6416的口线通过反相器驱动LED,(只截图两个管脚,其他相同)。sda,scl,reset接到fpga(spartan 3se500)的管脚上。先编写程序驱动led,程序源码如下:
module iic_com(clk,rst_n,reset_led, scl,sda);
input clk; // 30MHz
input rst_n; //复位信号,低有效
output reset_led;
output scl; // 时钟端口
inout sda; // 数据端口
assign reset_led=1;//tca6416复位信号置高
//--------------------------------------------
reg[20:0] cnt_20ms; //20ms计数寄存器
always [url=home.php?mod=space&uid=72445]@[/url] (posedge clk or negedge rst_n)
if(!rst_n) cnt_20ms <= 20'd0;
else if(cnt_20ms == 20'd600000) cnt_20ms <= 20'd0;
else cnt_20ms <= cnt_20ms+1'b1; //不断计数
//---------------------------------------------
//分频部分
reg[2:0] cnt; // cnt=0:scl上升沿cnt=1:scl高电平中间 cnt=2:scl下降沿 cnt=3:scl低电平中间
reg[9:0] cnt_delay; //600循环计数,产生iic所需要的时钟 20us
reg scl_r; //时钟脉冲寄存器
reg ack_rise;
reg scl_dir;
always @ (posedge clk or negedge rst_n)
if(!rst_n) cnt_delay <= 10'd0;
else if(cnt_delay == 10'd599) cnt_delay <=10'd0; //计数到20us为scl的周期
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)
10'd49: cnt <= 3'd1; //cnt=1:scl高电平中间,用于数据采样
10'd299: cnt <= 3'd2; //cnt=2:scl下降沿
10'd314: cnt <= 3'd3; //cnt=3:scl低电平中间,
10'd599: 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)begin
if(!rst_n) begin
scl_r <= 1'b1;
end
else if(cnt==3'd0)
begin
scl_r <= 1'b1; //scl信号上升沿
end
else if(cnt==3'd2) begin
scl_r <= 1'b0; //scl信号下降
end
end
assign scl = scl_dir ? scl_r : 1'bz; //iic所需要的时钟信号
//--------------------------------------------
//配置TCA6416A
`define DEVICE_ADDR 8'b01000010 //TCA6416地址
`define CONFIG_COMMAND 8'b00000110 //P0口配置地址
`define CONFIG_DATA 8'b00000000 //P0/P1口配置数据,配置为输出口
`define OUT_COMMAND 8'b00000010 //P0口输出地址
`define POLARITY_COMMAND 8'b00000100 //p0极性
`define POLARITY_data 8'b00000000 //p0极性
wire[7:0] dis_data0; //故障信息的数据
wire[7:0] dis_data1;
assign dis_data0=8'b00001111;
assign dis_data1=8'b00001111;
reg[7:0] db_r; //在IIC上传送的数据寄存器
//读、写时序
parameter IDLE = 5'd0;
parameter START = 5'd1;
parameter ADDR_DEVICE = 5'd2;
parameter ADDR_DEVICE_ACK = 5'd3;
parameter CONFIG_ADDR = 5'd4;
parameter CONFIG_ADDR_ACK = 5'd5;
parameter CONFIG_DATA0 = 5'd6;
parameter CONFIG_DATA0_ACK = 5'd7;
parameter CONFIG_DATA1 = 5'd8;
parameter CONFIG_DATA1_ACK = 5'd9;
parameter OUT_ADDR = 5'd10;
parameter OUT_ADDR_ACK = 5'd11;
parameter OUT_DATA0 = 5'd12;
parameter OUT_DATA0_ACK = 5'd13;
parameter OUT_DATA1 = 5'd14;
parameter OUT_DATA1_ACK = 5'd15;
parameter POLARITY=5'd16;
parameter POLARITY_ACK=5'd17;
parameter POLARITY_DATA0=5'd18;
parameter POLARITY_DATA0_ACK=5'd19;
parameter POLARITY_DATA1=5'd20;
parameter POLARITY_DATA1_ACK=5'd21;
parameter STOP1 = 5'd22;
parameter STOP2 = 5'd23;
parameter WAIT = 5'd24;
reg[4:0] cstate; //状态寄存器
reg[3:0] num;
reg sda_r; //输出数据寄存器
reg sda_link; //输出sda信号inout方向 1为输出,0为输入
assign sda = sda_link ? sda_r:1'bZ;//三态口
always @ (posedge clk or negedge rst_n) begin
if(!rst_n) begin//复位
ack_rise<=1'b0;
cstate <= IDLE;
sda_link <= 1'b0;
scl_dir<=1'b0;
num <= 4'd0;
db_r <= 8'b00000000; //送器件地址
end
else
case (cstate)
IDLE: begin
scl_dir<=1'b1;
sda_link <= 1'b1; //数据线sda为output
sda_r <= 1'b1;
db_r <= `DEVICE_ADDR; //送器件地址
cstate <= START;
end
START: begin
if(cnt_delay==10'd150)begin //scl为高电平期间
sda_link <= 1'b1; //数据线sda output
sda_r <= 1'b0; //拉低数据sda,产生下降沿
cstate <= ADDR_DEVICE;
num <= 4'd0; //num计数清零
end
else cstate <= START; //等待scl高电平中间位置到来
end
ADDR_DEVICE:begin //发送器件地址
if(`SCL_LOW) begin
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_link <= 1'b1;
num <= num+1'b1;//并行执行
if(num == 4'd8) begin
num <= 4'd0; //num计数清零
sda_link <= 1'b0; //释放总线
cstate <=ADDR_DEVICE_ACK;
end
end
else cstate <= ADDR_DEVICE;//等待
end
ADDR_DEVICE_ACK:begin
if(scl) begin
sda_link <= 1'b0;
if(!sda)begin
cstate <= CONFIG_ADDR; //从机响应
db_r <= `CONFIG_COMMAND;// P0配置地址
end
end
else cstate <= ADDR_DEVICE_ACK; //等待下降沿
end
CONFIG_ADDR:begin
if(`SCL_LOW) begin
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_link <= 1'b1;
num <= num+1'b1;//并行执行
if(num == 4'd8) begin
num <= 4'd0;
//sda_r <= 1'b1;
sda_link <= 1'b0;//释放总线
cstate <=CONFIG_ADDR_ACK;
end
end
else cstate <= CONFIG_ADDR;//等待
end
CONFIG_ADDR_ACK: begin
if(scl) begin
sda_link <= 1'b0;
if(!sda) begin //从机响应
cstate <=CONFIG_DATA0; //写P0配置操作
db_r <= `CONFIG_DATA; //P0口配置数据
end
end
else cstate <= CONFIG_ADDR_ACK; //等待从机响应
end
CONFIG_DATA0: begin
if(`SCL_LOW) begin
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_link <= 1'b1;
num <= num+1'b1;//并行执行
if(num == 4'd8) begin
num <= 4'd0;
//sda_r <= 1'b1;
sda_link <= 1'b0;//释放总线
cstate <=CONFIG_DATA0_ACK;
end
end
else cstate <= CONFIG_DATA0;//等待
end
CONFIG_DATA0_ACK: begin
if(scl) begin
sda_link <= 1'b0;
if(!sda) begin //从机响π藕
cstate <= CONFIG_DATA1; //写P1
db_r <= `CONFIG_DATA; //P1口配置数据
end
end
else cstate <= CONFIG_DATA0_ACK; //等待从机响应
end
CONFIG_DATA1: begin
if(`SCL_LOW) begin
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_link <= 1'b1;
num <= num+1'b1;//并行执行
if(num == 4'd8) begin
num <= 4'd0; //num计数清零
//sda_r <= 1'b1;
sda_link <= 1'b0;//释放总线
cstate <=CONFIG_DATA1_ACK;
end
end
else cstate <= CONFIG_DATA1;//等待
end
CONFIG_DATA1_ACK: begin
if(scl) begin
sda_link <= 1'b0;
if(!sda) begin //从机响应
cstate <= OUT_ADDR; //写P0输出地址
db_r <= `OUT_COMMAND; //P0口配置数据
end
end
else cstate <= CONFIG_DATA1_ACK;
end
OUT_ADDR: begin
if(`SCL_LOW) begin
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
num <= num+1'b1;//并行执行
if(num == 4'd8) begin
ack_rise<=1'b1;
num <= 4'd0; //num计数清零
//sda_r <= 1'b1;
sda_link <= 1'b0;
cstate <=OUT_ADDR_ACK;
end
end
else cstate <= OUT_ADDR;//等待
end
OUT_ADDR_ACK: begin
if(scl) begin
sda_link <= 1'b0;
if(!sda) begin //从机响应
cstate <= OUT_DATA0; //写P0输出数据
db_r <= dis_data0;
end
end
else cstate <= OUT_ADDR_ACK; //等待从机响应
end
OUT_DATA0: begin
if(`SCL_LOW) begin
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_link <= 1'b1;
num <= num+1'b1;//并行执行
if(num == 4'd8) begin
num <= 4'd0; //num计数清零
//sda_r <= 1'b1;
sda_link <= 1'b0;
cstate <=OUT_DATA0_ACK;
end
end
else cstate <= OUT_DATA0;//等待
end
OUT_DATA0_ACK: begin
if(scl) begin
sda_link <= 1'b0;
if(!sda ) begin //从机响应信号
cstate <= OUT_DATA1; //写P1输出数据
db_r <= dis_data1; //P1口数据
end
end
else cstate <= OUT_DATA0_ACK; //等待从机响应
end
OUT_DATA1: begin
if(`SCL_LOW) begin
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_link <= 1'b1;
num <= num+1'b1;//并行执行
if(num == 4'd8) begin
num <= 4'd0; //num计数清零
//sda_r <= 1'b1;
sda_link <= 1'b0;
cstate <=OUT_DATA1_ACK;
end
end
else cstate <= OUT_DATA1;//等待
end
OUT_DATA1_ACK: begin
if(scl) begin
sda_link <= 1'b0;
if(!sda) begin //从机响应信号
db_r <= `POLARITY_COMMAND;
cstate <= POLARITY;
end
end
else cstate <= OUT_DATA1_ACK; //等待从机响应
end
POLARITY: begin
if(`SCL_LOW) begin
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_link <= 1'b1;
num <= num+1'b1;//并行执行
if(num == 4'd8) begin
num <= 4'd0; //num计数清零
//sda_r <= 1'b1;
sda_link <= 1'b0;
cstate <=POLARITY_ACK;
end
end
else cstate <= POLARITY;//等待
end
POLARITY_ACK: begin
if(scl) begin
sda_link <= 1'b0;
ack_rise<=1'b0;
if(!sda) begin //从机响应
cstate <= POLARITY_DATA0; //写P0输出数据
db_r <= `POLARITY_data;
end
end
else cstate <= POLARITY_ACK; //等待从机响应
end
POLARITY_DATA0: begin
if(`SCL_LOW) begin
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_link <= 1'b1;
num <= num+1'b1;//并行执行
if(num == 4'd8) begin
num <= 4'd0; //num计数清零
//sda_r <= 1'b1;
sda_link <= 1'b0;
cstate <=POLARITY_DATA0_ACK;
end
end
else cstate <= POLARITY_DATA0;//等待
end
POLARITY_DATA0_ACK: begin
if(scl) begin
sda_link <= 1'b0;
if(!sda ) begin //从机响应信号
cstate <= POLARITY_DATA1; //写P1输出数据
db_r <= `POLARITY_data; //P1口数据
end
end
else cstate <= POLARITY_DATA0_ACK; //等待从机响应
end
POLARITY_DATA1: begin
if(`SCL_LOW) begin
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_link <= 1'b1;
num <= num+1'b1;//并行执行
if(num == 4'd8) begin
num <= 4'd0; //num计数清零
//sda_r <= 1'b1;
sda_link <= 1'b0;
cstate <=POLARITY_DATA1_ACK;
end
end
else cstate <= POLARITY_DATA1;//等待
end
POLARITY_DATA1_ACK: begin
if(scl) begin
sda_link <= 1'b0
if(!sda) begin //从机响应信号
cstate <= STOP1;
end
end
else cstate <= POLARITY_DATA1_ACK; //等待从机响应
end
STOP1:begin
if(`SCL_LOW) begin
sda_link <= 1'b1;
sda_r <= 1'b0;
cstate <= STOP2;
end
else cstate <= STOP1;
end
STOP2:begin
if(cnt_delay==10'd150)begin
//sda_r <= 1'b1; //scl为高时,sda产生上升沿(结束信号)
sda_link<=1'b0;
scl_dir<=1'b0;
cstate <= WAIT;
end
else cstate <= STOP2;
end
WAIT:begin
if(cnt_20ms==20'd600000) begin
cstate <= IDLE;
end
else cstate <= WAIT;
end
default: cstate <= IDLE;
endcase
end
endmodule
调试运行后,所有led灯均发光,且没有变换,用示波器观测sda.scl的电平,观测波形如图2。
图2(从start到stop)图2放大部分见图3
图3(起始,发送器件地址后应答,发送配置字,应答)
现在问题是,第9个时钟周期未见sda先变高电平后被拉低的过程,且第十个周期的低电平时(即发送 下一字节的第1个周期)均有边沿毛刺。
求各位老师帮我看看,程序哪里有问题,为何会出现毛刺?谢谢。
PS第一次发帖,求关注
|