958|1

35

帖子

0

TA的资源

一粒金砂(中级)

楼主
 

【Sipeed Tang Primer 25K】评测:4、I2C搞起来 [复制链接]

 

//声明,定义了输入输出端口。clk是时钟信号,start是开始信号,DCn用于区分数据和命令,Data是要传输的8位数据。输出包括busy表示模块是否忙碌,scl和sda分别是I2C协议的时钟线和数据线。// 

module I2C(
input clk,                     // 时钟输入
input start,                   // 开始信号
input DCn,                     // 1 -> 数据 / 0 -> 命令
input [7:0]Data,               // 8位数据
output reg busy=0,             // I2C忙碌状态
output reg scl=1,              // 串行时钟
output reg sda=1);             // 串行数据

// 定义一些参数,用于表示状态机的各个状态和等待时间。// 

parameter IDEL  = 0;
parameter START = 1;
parameter ADDR  = 2;
parameter CBYTE = 3;
parameter DATA  = 4;
parameter STOP  = 5;
parameter T_WAIT= 6; 

// 定义一些寄存器用于存储状态、计数器、延迟、从设备地址、控制字节和数据。// 

reg DCn_r=0;
reg [2:0]state=0;
reg [3:0]i=0;
reg [3:0]step=0;
reg [12:0]delay=1;
reg [7:0]slave= 8'b01111000;   // 从设备地址
reg [7:0]cbyte= 8'b10000000;   // 命令的控制字节
reg [7:0]dbyte= 8'b01000000;   // 数据的控制字节
reg [7:0]data=  0;

//通过有限状态机(FSM)的方式实现了I2C通信协议。在每个状态中,根据子状态(step)的不同,控制时钟线(scl)和数据线(sda)的电平,以及处理延迟和计数器,从而实现了I2C协议的各个阶段:开始、地址发送、控制字节发送、数据发送和停止。//

always @(posedge clk)
begin
    // 时钟上升沿触发
    if(delay != 1)                 // 如果延迟不为1
    begin
        delay <= delay - 1;        // 延迟计数器减1
    end 
    else begin                     // 如果延迟为1
        case(state)                // 根据当前状态执行相应操作
        IDEL:begin                // 空闲状态
            scl <= 1;              // 保持时钟线高
            sda <= 1;              // 保持数据线高
            if(start)              // 如果接收到开始信号
            begin
                DCn_r <= DCn;      // 读取数据/命令标志
                data <= Data;      // 读取数据
                busy <= 1;         // 设置忙碌标志
                state <= START;    // 转移到开始状态
                step <= 0;         // 子状态设置为0
            end
        end
        
        START:begin                // 开始状态
            case(step)
            0:begin
                sda <= 0;          // 数据线拉低,表示开始条件
                delay <= T_WAIT;   // 等待一定周期
                step <= step + 1;  // 转到下一个子状态
            end
            1:begin
                scl <= 0;          // 时钟线拉低,准备发送数据
                state <= ADDR;     // 转移到地址发送状态
                step <= 0;         // 子状态设置为0
            end
            endcase
        end

        ADDR:begin                 // 地址发送状态
            case(step)
            0:begin
                if(i < 8)          // 如果还没有发送完8位地址
                begin
                    scl <= 0;      // 时钟线拉低
                    step <= 1;     // 转到下一个子状态
                end 
                else if(i == 8)    // 如果地址发送完毕
                begin
                    scl <= 0;      // 时钟线拉低
                    sda <= 0;      // 数据线拉低,准备接收应答位
                    delay <= T_WAIT;// 等待一定周期
                    i <= i + 1;    // 计数器加1
                    step <= 2;     // 转到下一个子状态
                end
            end
            1:begin
                sda <= slave[7 - i]; // 发送地址位
                delay <= T_WAIT - 1; // 等待一定周期
                i <= i + 1;          // 计数器加1
                step <= 2;           // 转到下一个子状态
            end
            2:begin
                if(i < 9)            // 如果还没有接收完应答位
                begin
                    scl <= 1;        // 时钟线拉高,锁存数据
                    delay <= T_WAIT; // 等待一定周期
                    step <= 0;       // 转到下一个子状态
                end 
                else begin
                    scl <= 1;        // 时钟线拉高
                    delay <= T_WAIT; // 等待一定周期
                    step <= 3;       // 转到下一个子状态
                end
            end
            3:begin
                scl <= 0;            // 时钟线拉低
                sda <= 0;            // 数据线拉低
                delay <= T_WAIT;     // 等待一定周期
                step <= 4;           // 转到下一个子状态
            end
            4:begin
                step <= 0;           // 子状态设置为0
                i <= 0;              // 计数器清零
                state <= CBYTE;      // 转移到控制字节发送状态
            end
            endcase
        end
        
        CBYTE:begin                // 控制字节发送状态
            case(step)
            0:begin
                if(i < 8)          // 如果还没有发送完8位控制字节
                begin
                    scl <= 0;      // 时钟线拉低
                    step <= 1;     // 转到下一个子状态
                end 
                else if(i == 8)    // 如果控制字节发送完毕
                begin
                    scl <= 0;      // 时钟线拉低
                    sda <= 0;      // 数据线拉低,准备接收应答位
                    delay <= T_WAIT;// 等待一定周期
                    i <= i + 1;    // 计数器加1
                    step <= 2;     // 转到下一个子状态
                end
            end
            1:begin
                if(DCn_r)          // 如果是数据
                begin
                    sda <= dbyte[7 - i]; // 发送数据控制字节
                end 
                else begin
                    sda <= cbyte[7 - i]; // 发送命令控制字节
                end
                delay <= T_WAIT - 1; // 等待一定周期
                i <= i + 1;          // 计数器加1
                step <= 2;           // 转到下一个子状态
            end
            2:begin
                if(i < 9)            // 如果还没有接收完应答位
                begin
                    scl <= 1;        // 时钟线拉高,锁存数据
                    delay <= T_WAIT; // 等待一定周期
                    step <= 0;       // 转到下一个子状态
                end 
                else begin
                    scl <= 1;        // 时钟线拉高
                    delay <= T_WAIT; // 等待一定周期
                    step <= 3;       // 转到下一个子状态
                end
            end
            3:begin
                scl <= 0;            // 时钟线拉低
                sda <= 0;            // 数据线拉低
                delay <= T_WAIT;     // 等待一定周期
                step <= 4;           // 转到下一个子状态
            end
            4:begin
                step <= 0;           // 子状态设置为0
                i <= 0;              // 计数器清零
                state <= DATA;       // 转移到数据发送状态
            end
            endcase
        end
        
        DATA:begin                 // 数据发送状态
            case(step)
            0:begin
                if(i < 8)          // 如果还没有发送完8位数据
                begin
                    scl <= 0;      // 时钟线拉低
                    step <= 1;     // 转到下一个子状态
                end 
                else if(i == 8)    // 如果数据发送完毕
                begin
                    scl <= 0;      // 时钟线拉低
                    sda <= 0;      // 数据线拉低,准备接收应答位
                    delay <= T_WAIT;// 等待一定周期
                    i <= i + 1;    // 计数器加1
                    step <= 2;     // 转到下一个子状态
                end
            end
            1:begin
                sda <= data[7 - i]; // 发送数据位
                delay <= T_WAIT - 1; // 等待一定周期
                i <= i + 1;          // 计数器加1
                step <= 2;           // 转到下一个子状态
            end
            2:begin
                if(i < 9)            // 如果还没有接收完应答位
                begin
                    scl <= 1;        // 时钟线拉高,锁存数据
                    delay <= T_WAIT; // 等待一定周期
                    step <= 0;       // 转到下一个子状态
                end 
                else begin
                    scl <= 1;        // 时钟线拉高
                    delay <= T_WAIT; // 等待一定周期
                    step <= 3;       // 转到下一个子状态
                end
            end
            3:begin
                scl <= 0;            // 时钟线拉低
                sda <= 0;            // 数据线拉低
                delay <= T_WAIT;     // 等待一定周期
                step <= 4;           // 转到下一个子状态
            end
            4:begin
                step <= 0;           // 子状态设置为0
                i <= 0;              // 计数器清零
                state <= STOP;       // 转移到停止状态
            end
            endcase
        end
        
        STOP:begin                  // 停止状态
            case(step)
            0:begin
                scl <= 1;            // 时钟线拉高
                sda <= 0;            // 数据线拉低
                delay <= T_WAIT;     // 等待一定周期
                step <= step + 1;    // 转到下一个子状态
            end
            1:begin
                state <= IDEL;       // 转移到空闲状态
                busy <= 0;           // 清除忙碌标志
                step <= 0;           // 子状态设置为0
            end
            endcase
        end
        endcase
    end
end
endmodule

 

最新回复

虽然有点晕,还是要感谢楼主分享,慢慢学习吧。笨鸟先飞了。  详情 回复 发表于 2024-4-24 21:49
点赞 关注
 
 

回复
举报

419

帖子

0

TA的资源

纯净的硅(中级)

沙发
 
虽然有点晕,还是要感谢楼主分享,慢慢学习吧。笨鸟先飞了。
 
 
 

回复
您需要登录后才可以回帖 登录 | 注册

随便看看
查找数据手册?

EEWorld Datasheet 技术支持

相关文章 更多>>
关闭
站长推荐上一条 1/6 下一条

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

About Us 关于我们 客户服务 联系方式 器件索引 网站地图 最新更新 手机版

站点相关: 国产芯 安防电子 汽车电子 手机便携 工业控制 家用电子 医疗电子 测试测量 网络通信 物联网

北京市海淀区中关村大街18号B座15层1530室 电话:(010)82350740 邮编:100190

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2025 EEWORLD.com.cn, Inc. All rights reserved
快速回复 返回顶部 返回列表