×
单片机 > 单片机程序设计 > 详情

GPIO口模拟I2C

发布时间:2020-08-26 发布时间:
|

根据I2C通信规范(具体可以参考“浅谈I2C总线”),通过普通IO端口模拟可以实现单片机(主设备)与从设备的I2C通信,其中SCL通过IO口延时高低电平变化实现,SDA根据SCL状态变化产生开始信号,结束信号,以及实现发送接收数据等,以下是相关代码


/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *  

* @Purpose:  I2C Communication driver(By IO)  

* @Author:  Purple  

* @Version:  1.0  

* @Date:  Create By Purple 2014.08.09  

*   

*   

* Copyright (C) BlueWhale Tech.     

* All rights reserved.    

* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */  

  

  

#ifndef IICDRV_H  

#define IICDRV_H  

  

  

/* Include Files */  

  

  

/* Macros */  

#define I2C_SDA         PTT_PTT0   //举例以Freescale PT0端口为SDA线,PT1端口为SCL线  

#define I2C_SDA_IO      DDRT_DDRT0  

#define I2C_SCL         PTT_PTT1  

#define I2C_SCL_IO      DDRT_DDRT1  

  

#define IO_OUT_MODE     1   //Freescale:1为输出模式,0为输入模式;NEC:0为输出模式,1为输入模式  

#define IO_IN_MODE      0  

  

  

/* Function Prototypes */  

void I2CStart(void);  

void I2CStop(void);  

void I2CFree(void);  

void I2CSendACK(void);  

void I2CSendNoACK(void);  

bool I2CCheckACK(void);  

void I2CNoAck(void);  

void I2CSendByte(unsigned char sendData);  

unsigned char I2CReceiveByte(void);  

  

  

#endif  

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *  

* @Purpose:  I2C Communication driver(By IO)  

* @Author:  Purple  

* @Version:  1.0  

* @Date:  Create By Purple 2014.08.09  

*   

*   

* Copyright (C) BlueWhale Tech.     

* All rights reserved.    

* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */  

  

  

/* Include Files */  

#include "IICDrv.h"  

  

  

/* Function Prototypes */  

static void I2CDelay(void);  

  

  

/* Function Definitions */  

/*  

 * FunctionName: I2CDelay  

 * Purpose: I2C时序模拟SCL时间间隔(周期),需要根据Slave性能及单片机工作频率调整  

 * Parameters: 无  

*/  

static void I2CDelay(void)  

{  

    _asm("nop");  

    _asm("nop");  

    _asm("nop");  

    _asm("nop");  

    _asm("nop");  

}  

  

  

/*  

 * FunctionName: I2CStart  

 * Purpose: 模拟I2C开始信号  

 * Parameters: 无  

*/  

void I2CStart(void)  

{  

    I2C_SDA_IO=IO_OUT_MODE;     //设置SDA端口为输出端口  

    I2C_SCL_IO=IO_OUT_MODE;     //设置SCL端口为输出端口  

      

    I2C_SCL=1;  

    I2CDelay();  

    I2C_SDA=1;  

    I2CDelay();  

    I2C_SDA=0;  

    I2CDelay();  

    I2C_SCL=0;  

    I2CDelay();  

}  

  

/*  

 * FunctionName: I2CStop  

 * Purpose: 模拟I2C结束信号  

 * Parameters: 无  

*/  

void I2CStop(void)  

{  

    I2C_SDA_IO=IO_OUT_MODE;     //设置SDA端口为输出端口  

    I2C_SCL_IO=IO_OUT_MODE;     //设置SCL端口为输出端口  

      

    I2C_SCL = 0;  

    I2CDelay();  

    I2C_SDA = 0;  

    I2CDelay();  

    I2C_SDA = 1;  

    I2CDelay();  

    I2C_SCL = 1;  

    I2CDelay();  

}  

  

/*  

 * FunctionName: I2CFree  

 * Purpose: 模拟I2C空闲状态信号  

 * Parameters: 无  

*/  

void I2CFree(void)  

{  

    I2C_SDA_IO=IO_OUT_MODE;     //设置SDA端口为输出端口  

    I2C_SCL_IO=IO_OUT_MODE;     //设置SCL端口为输出端口  

      

    I2C_SDA = 1;  

    I2CDelay();  

    I2C_SCL = 1;  

    I2CDelay();  

}  

  

/*  

 * FunctionName: I2CSendACK  

 * Purpose: 模拟I2C发送ACK响应  

 * Parameters: 无  

*/  

void I2CSendACK(void)  

{  

    I2C_SDA_IO=IO_OUT_MODE;     //设置SDA端口为输出端口  

    I2C_SCL_IO=IO_OUT_MODE;     //设置SCL端口为输出端口  

      

    I2C_SCL=0;  

    I2CDelay();  

    I2C_SDA=0;  

    I2CDelay();  

    I2C_SCL=1;  

    I2CDelay();  

    I2C_SCL=0;  

    I2CDelay();  

}  

  

/*  

 * FunctionName: I2CSendNoACK  

 * Purpose: 模拟I2C无ACK响应  

 * Parameters: 无  

*/  

void I2CSendNoACK(void)  

{  

    I2C_SDA_IO=IO_OUT_MODE;   //设置SDA端口为输出端口  

    I2C_SCL_IO=IO_OUT_MODE;   //设置SCL端口为输出端口  

  

    I2C_SCL=0;  

    I2CDelay();  

    I2C_SDA=1;  

    I2CDelay();  

    I2C_SCL=1;  

    I2CDelay();  

    I2C_SCL=0;  

    I2CDelay();  

}  

  

/*  

 * FunctionName: I2CCheckACK  

 * Purpose: 检查I2C是否有ACK响应  

 * Parameters: 无  

*/  

bool I2CCheckACK(void)  

{  

    bool tempACK;  

      

    I2C_SDA_IO=IO_OUT_MODE;   //设置SDA端口为输出端口  

    I2C_SCL_IO=IO_OUT_MODE;   //设置SCL端口为输出端口  

  

    I2C_SDA=1;  

    I2CDelay();  

    I2C_SCL = 1;  

    I2C_SDA_IO=IO_IN_MODE;    //设置SDA端口为输入端口,检查Slave是否有响应  

    I2CDelay();  

    if(I2C_SDA)  

        tempACK=FALSE;  

    else  

        tempACK=TRUE;  

  

    I2C_SCL=0;  

    I2CDelay();  

  

    return tempACK ;  

}  

  

/*  

 * FunctionName: I2CSendByte  

 * Purpose: 模拟I2C发送一个字节数据  

 * Parameters: sendData-发送的一个字节数据  

*/  

void I2CSendByte(unsigned char sendData)  

{  

    unsigned char serialNum = 0;  

  

    I2C_SDA_IO=IO_OUT_MODE;   //设置SDA端口为输出端口  

    I2C_SCL_IO=IO_OUT_MODE;   //设置SCL端口为输出端口  

  

    for(serialNum=8;serialNum>=1;serialNum--)   //以MSB方式按位发送一个字节数据  

    {  

        I2C_SDA = (sendData>>(serialNum-1))&0x01;  

        I2CDelay();  

        I2C_SCL = 1;  

        I2CDelay();  

        I2C_SCL = 0;  

        I2CDelay();  

    }  

}  

  

/*  

 * FunctionName: I2CReceiveByte  

 * Purpose: 模拟I2C接收一个字节数据  

 * Parameters: 无  

*/  

unsigned char I2CReceiveByte(void)  

{     

    unsigned char serialNum = 0;  

    unsigned char dataValue=0;  

  

    I2C_SDA_IO=IO_IN_MODE;    //设置SDA端口为输入端口  

    I2C_SCL_IO=IO_OUT_MODE;   //设置SCL端口为输出端口  

  

    for(serialNum=0;serialNum<=7;serialNum++)//以MSB方式按位接收一个字节数据  

    {  

        I2C_SCL=1;  

        I2CDelay();  

        if(I2C_SDA) dataValue|=(0b10000000>>serialNum);  

        I2C_SCL=0;  

        I2CDelay();  

    }  

  

    return dataValue;  

}  

需要注意模拟SCL采用的延时需要根据从设备的特性来调整,延时时间不能小于从设备的最小SCL间隔时间

既然已经通过IO端口实现了I2C通信,那么,我们就可以用以上代码实现单片机与相应从设备I2C的通信了,以EEPROM 24C04为例,以下是读取和写入EEPROM数据相关函数的代码

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *  

* @Purpose:  Communication with EEPROM 24C04  

* @Author:  Purple  

* @Version:  1.0  

* @Date:  Create By Purple 2014.08.09  

*   

*   

* Copyright (C) BlueWhale Tech.     

* All rights reserved.    

* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */  

  

  

/* Include Files */  

#include "EEApp.h"  

  

  

  

/* Function Definitions */  

/*  

 * FunctionName: Slave24C04Write  

 * Purpose: 向EEPROM24C04中写入一个字节数据  

 * Parameters: tarAddress-写入数据的起始地址  

 *             wrNumber-待写入数据的长度(字节单位)  

 *             wrPointer-待写入数据的首字节地址  

*/  

void Slave24C04Write(unsigned char tarAddress,unsigned char wrNumber,unsigned char* wrPointer)  

{  

    bool rxdACK;  

  

    I2CStart();  

  

    I2CSendByte(SLAVE_ADDRESS);   //发送24C04的器件地址,地址LSB最后一位为0代表写入,1代表读取  

    rxdACK=I2CCheckACK();  

    I2CSendByte(tarAddress);   //发送写入数据的起始地址  

    rxdACK=I2CCheckACK();  

  

    for(;wrNumber!=0;wrNumber--,wrPointer++)  

    {  

        I2CSendByte(*wrPointer);   //按字节写入数据  

        rxdACK=I2CCheckACK();  

    }  

  

    I2CStop();  

}  

  

/*  

 * FunctionName: Slave24C04Read  

 * Purpose: 从EEPROM24C04中读取一个字节数据  

 * Parameters: tarAddress-读取数据的起始地址  

 *             wrNumber-读取数据的长度(字节单位)  

 *             wrPointer-读取数据的首字节存放地址  

*/  

void Slave24C04Read(unsigned char tarAddress,unsigned char rdNumber,unsigned char* rdPointer)  

{  

    bool rxdACK;  

      

    I2CStart();  

    I2CSendByte(SLAVE_ADDRESS);   //发送24C04的器件地址  

    rxdACK=I2CCheckACK();  

    I2CSendByte(tarAddress);   //发送读取数据的起始地址  

    rxdACK=I2CCheckACK();  

  

    I2CStart();  

    I2CSendByte(SLAVE_ADDRESS+1);  //发送24C04的器件地址,地址LSB最后一位为1代表读取  

    rxdACK=I2CCheckACK();  

  

    for(;rdNumber!=0;rdNumber--,rdPointer++)  

    {  

        *rdPointer=I2CReceiveByte();   //按字节读取数据  

  

        if(rdNumber!=1)  

            I2CSendACK();  

        else  

            I2CSendNoACK();  

    }  

  

    I2CStop();  

}  

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *  

* @Purpose:  Communication with EEPROM 24C04  

* @Author:  Purple  

* @Version:  1.0  

* @Date:  Create By Purple 2014.08.09  

*   

*   

* Copyright (C) BlueWhale Tech.     

* All rights reserved.    

* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */  

  

  

#ifndef EEAPP_H  

#define EEAPP_H  

  

  

/* Include Files */  

#include "IICDrv.h"  

  

  

/* Macros */  

#define SLAVE_ADDRESS   (0xA0)  

  

  

/* Function Prototypes */  

void Slave24C04Write(unsigned char tarAddress,unsigned char wrNumber,unsigned char* wrPointer);  

void Slave24C04Read(unsigned char tarAddress,unsigned char rdNumber,unsigned char* rdPointer);  

  

  

  

#endif  


需要注意不同的从设备要根据应用电路调整相应的从设备地址。

采用IO口来模拟I2C通信,一般仅用于单片机没有I2C功能的情况下,如果单片机本身具有I2C功能,还是应该通过配置单片机相应的寄存器,通过中断来实现I2C通信,因为模拟SCL采用的延时就是一个空等待,对于整个操作系统来说,这是一个资源浪费,同时还有可能会影响到其他任务的运行。



『本文转载自网络,版权归原作者所有,如有侵权请联系删除』

热门文章 更多
STM32中断向量表的位置.重定向