免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 2735 | 回复: 3
打印 上一主题 下一主题

[操作系统] 模拟I2C通信 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2016-03-30 17:58 |只看该作者 |倒序浏览
本帖最后由 duyonghong 于 2016-03-30 17:56 编辑

    近来做一个小项目,需要用到4个I2C接口,无奈MCU(增强版8051内核)接口不够,只能使用I/O口模拟I2C通信,做下来也算是温故知新,总想着写点东西出来,一来算是小小的个人体会,二来希望有所帮助。当然,有任何疏漏、不足之处,希望一起交流学习。
一、        概述
    I2C是由Philips公司开发的串行总线,主要用于MCU与外部设备通信,简单有效,且支持多主控仲裁,使用范围极其广泛。

二、        硬件构成
    I2C总线是由SDA数据线和SCL时钟线构成,实现数据双向传递,时钟频率可达400KHz。其中,SDA和SCL引脚需要接上拉电阻(内部上拉亦可),调试时用示波器观察调试I2C波形。


三、        时序图
    1.        通信过程
    master(主机)向I2C总线上发送一个8位slave地址,总线上I2C为该地址的slave(从机)收到数据后发送一个应答信号,此时双方建立联系,从而依据I2C总线时序图可进行数据通信。其中,salve地址由7bit从机地址+R/W构成,简要时序图如下:


    2.时序图分析
    由I2C时序图可知,SDA引脚信号(0或1)在SCL为高电平时被采样,SDA引脚信号在SCL为低电平时可发生改变。其中通信过程中主要包括三种信号:起始信号、终止信号和应答信号。
    起始信号:SCL保持高电平,SDA由高电平变为低电平,表示数据传输开始;
    终止信号:SCL保持高电平,SDA由低电平变为高电平,表示数据传输结束;
    应答信号:slave收到8bit数据后,获得SDA控制权,下一个采样周期将SDA拉低


    (说明:信号跳变和时钟采样具体时序要求需要查询I2C通信协议)
四、        模拟通信代码(具体代码、注释和文件介绍)
    熟悉I2C通信协议和时序图之后,接下来便是具体的编程操作,具体问题具体分析,如下所示。
    写时序:
    START->从设备地址->ACK->从设备寄存器地址->ACK->写入的数据->ACK->STOP
    读时序:
    START->从设备地址->ACK->从设备寄存器地址->ACK->接收的数据->ACK->STOP

1. START信号
/*
*起始函数,发起I2C通讯
*/
static void Start_I2C(void)
{
        SIMU_SDA = 1;
        asm("nop";asm("nop";asm("nop";asm("nop";asm("nop";
       
        SIMU_SCL = 1;
        asm("nop";asm("nop";asm("nop";asm("nop";asm("nop";
       
        SIMU_SDA = 0;        //Falling Edge
        asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");
        asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");
        asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");
        asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");
        asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");
        asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");
        asm("nop");asm("nop");asm("nop");asm("nop");
        SIMU_SCL = 0;
        asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");
        asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");
        asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");
        asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");
        asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");
        asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");
        asm("nop");asm("nop");asm("nop");asm("nop");
       
}


2. STOP信号
/*
*停止函数,结束I2C通讯
*/
static void Stop_I2C(void)
{
        SIMU_SDA = 0;
        asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");
       
        SIMU_SCL = 1;
        asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");
        asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");
        asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");
        asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");
        asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");
        asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");
        asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");
        SIMU_SDA= 1;        //Rising Edge
        asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");
}

3. 应答信号
/*
*应答函数,I2C通讯标志位
*/
static void Ack_I2C(uint8 enable)
{
        if(enable)        //No Ack Signal
        {
                SIMU_SDA = 1;
        }
        else                //Ack Signal
        {
                SIMU_SDA = 0;
        }
        asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");
        asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");
        asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");
        asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");
        asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");
        asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");
        asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");

        SIMU_SCL = 1;
        asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");
        asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");
        asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");
        asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");
        asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");
        asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");
        asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");
       
        SIMU_SCL = 0;
        asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");
        asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");
        asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");
        asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");
        asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");
        asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");
        asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");
       
}

4. 发送单字节
/*
*发送1byte数据
*/
static void Send_Byte(uint8 data)
{
        uint8 bitcnt = 0;

        //for(bitcnt = 0; bitcnt < sizeof(uint; bitcnt ++)----Cautions!!! - the result of sizeof(uint is 1 byte not 8
        for(bitcnt = 0; bitcnt < 8; bitcnt ++)
        {
                if((data << bitcnt)&0x80)
                {
                        SIMU_SDA = 1;
                }
                else
                {
                        SIMU_SDA = 0;
                }
               
                asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");

                SIMU_SCL = 1;
                asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");
                asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");
                asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");
                asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");
                asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");
                asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");
                asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");

                SIMU_SCL = 0;
                asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");
                asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");
                asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");
                asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");
                asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");
                asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");
                asm("nop");asm("nop");asm("nop");asm("nop");
        }

        SIMU_SDA = 1;        //Release SDA Line
        Simu_SDA_DIR(SIMU_SDA_INPUT);        //Set SDA To Input
        asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");
        SIMU_SCL = 1;
        asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");
        asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");
        asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");
        asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");
        asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");
        asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");
        asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");
       
        if(SIMU_SDA)
        {
                ack = 1;
        }
        else
        {
                ack = 0;
        }
        SIMU_SCL = 0;
        asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");
        asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");
        asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");
        asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");
        asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");
        asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");
        asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");
        Simu_SDA_DIR(SIMU_SDA_OUTPUT);        //Set SDA To Output
       
}

5. 接收单字节
/*
*接收1byte数据
*/
static uint8 Rcv_Byte(void)
{
        uint8 data[8] = {0x0};
        uint8 data_tmp = 0;
        uint8 bitcnt = 0;
       
        Simu_SDA_DIR(SIMU_SDA_INPUT);        //Set SDA to Input

        for(bitcnt = 0; bitcnt < 8; bitcnt ++)
        {
                SIMU_SCL = 0;
                asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");
                asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");
                asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");
                asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");
                asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");
                asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");
                asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");
                SIMU_SCL = 1;
                asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");
                asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");
                asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");
                asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");
                asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");
                asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");
                asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");

                //data_tmp == (data_tmp << 1);
                data_tmp = (data_tmp << 1);
                if(SIMU_SDA)
                {
                        data_tmp ++;
                        data[bitcnt] = 1;
                }
        }

        SIMU_SCL = 0;
        asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");

        Simu_SDA_DIR(SIMU_SDA_OUTPUT);
        return(data_tmp);
}

6. 发送函数
/*
*发送函数
*/
uint8 simu_i2c_send_byte(uint8 slave_addr,uint8 reg_addr, uint8 data)        //Caution:slave_addr is a 7-bit I2C address
{
        slave_addr = ((slave_addr << 1) &(0xFE));        //Make sure the lowest bit is 0
       
        Start_I2C();
        Send_Byte(slave_addr);
        if(ack == ACK_FAILED)
        {
                return (SIMU_I2C_FAIL);
        }
       
        Send_Byte(reg_addr);
        if(ack == ACK_FAILED)
        {
                return (SIMU_I2C_FAIL);
        }

        Send_Byte(data);
        if(ack == ACK_FAILED)
        {
                return (SIMU_I2C_FAIL);
        }

        Stop_I2C();
       
        return(SIMU_I2C_SUCCESS);
}

7. 接收函数
/**/
uint8 simu_i2c_rcv_byte(uint8 slave_addr,uint8 reg_addr, uint8 *data)
{
        slave_addr = ((slave_addr << 1) &(0xFE));        //Make sure the lowest bit is 0

        Start_I2C();
        Send_Byte(slave_addr);
       
        if(ack == ACK_FAILED)
        {
                return (SIMU_I2C_FAIL);
        }

        Send_Byte(reg_addr);
        if(ack == ACK_FAILED)
        {
                dprintf("%s,%d---ACK failed",__FUNCTION__,__LINE__);
                return (SIMU_I2C_FAIL);
        }

        Start_I2C();
        Send_Byte(slave_addr + 1);
        if(ack == ACK_FAILED)
        {
                return (SIMU_I2C_FAIL);
        }
       
        (*data) = Rcv_Byte();
        Ack_I2C(ACK_SUCCESS);
        Stop_I2C();

        return(SIMU_I2C_SUCCESS) ;
}
五、        总结


    对于模拟I2C通讯,具体延迟需要查询I2C标准时序。当然,具体延时函数跟板子时钟有关,需要特别注意。



论坛徽章:
0
2 [报告]
发表于 2016-04-22 16:26 |只看该作者
nop整成函数呗

论坛徽章:
0
3 [报告]
发表于 2016-04-22 16:28 |只看该作者
觉得iic主要就是根据手册来决定delay的时间,模拟比硬件好控制

论坛徽章:
0
4 [报告]
发表于 2016-05-23 22:43 |只看该作者
代码用<>框起来阿

楼主很有心,多谢分享了
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP