打印
[verilog]

xilinx verilog 编写IIC程序,从机TCA6416APW,求高手指导

[复制链接]
2259|1
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
最近编写了一个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第一次发帖,求关注



相关帖子

沙发
wangliang724|  楼主 | 2013-9-12 21:18 | 只看该作者
正确结果应该是LED灯亮8个,灭8个,因为发送的数据是disdata0/disdata1=00001111

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

1

主题

2

帖子

0

粉丝