微波EDA网,见证研发工程师的成长!
首页 > 研发问答 > 硬件电路设计 > 电子工程师杂谈 > 利用GPIO口模拟I2C总线的时序以及代码实现。可以直接移植!

利用GPIO口模拟I2C总线的时序以及代码实现。可以直接移植!

时间:10-02 整理:3721RD 点击:

/*
        作者:天空
        日期:2014.5.12
        功能:利用GPIO口模拟I2C总线,对传感器寄存器读取数据
        注意:如果需要移植些文件到其他设备上,需要更改的函数地方
        1、利用GPIO口模拟的数据线SDA和时钟线SCL,注意需要更改GPIO口
        2、从设备地址。不同的从设备,地址不一样,注意修改。(在I2C写寄存器和读寄存器的函数中,如果是写,在读的地址上加1.)
        经验:利用GPIO口模拟I2C总线时,需要注意的是,在应答信号和读数据的时候,一定要将SDA数据线配置为输入。
*/
#define SCL P0_5//定义P0.5口为时钟线
#define SDA P0_4//定义P0.4口为数据线
typedef unsigned int uint;
typedef unsigned char uchar;
typedef unsigned short ushort;
/*延时函数*/
void Delay(void)
{
}
/*I2C总线实始化,将P0.4和P0.5配置为输出口,并且拉高*/
/*
        I2C总线的开始时序:
        1、将数据线SDA和时钟线SCL全部拉高
*/
void I2C_Init(void)
{
    P0SEL &= ~0x30;//将P0.4和P0.5配置为GPIO口
    P0DIR |= 0x30;//将P0.4和P0.5配置为输出
    P0 |= 0x30;//将P0.4和P0.5设置为高电平
}
/*I2C总线的开始信号*/
/*
        I2C总线的开始时序:
        1、将SDA数据线拉高
        2、将SCL时钟线拉高,延时一段时间
        3、在SCL为高电平时,拉低SDA,延时一段时间
        总结:就是在SCL为高电平时,在SDA上给一个下降沿,表示开始信号。
*/
void I2C_Start(void)
{
    SDA = 1;
    SCL = 1;
    Delay();
    SDA = 0;
    Delay();
}
/*I2C总线的停止信号*/
/*
        I2C总线的停止时序:
        1、将SDA数据线拉低
        2、将SCL数据线拉高,延时一段时间
        3、在SCL为高电平时,将SDA数据线拉高,延时一段时间
        总结:在SCL为高电平时,在SDA上给一下上升沿,表示停止信号。
*/
void I2C_Stop(void)
{
    SDA = 0;
    SCL = 1;
    Delay();
    SDA = 1;
    Delay();
}

/*I2C总线的应答信号*/
/*
        I2C总线应答时序:
        1、首先是需要将模拟数据线的SDA口配置为输入口。
        2、将时钟线SCL拉高,延时一段时间
        3、等待从设备应答,从设备应答时,SDA上会产生一个高电平,主设备的GPIO口检测到高电平,则表示应答成功。
        4、如果检测到应答信号,则拉SCL时钟线拉低。延时一段时间。
        5、将模拟SDA口的GPIO口配置为输出。
*/
void I2C_Ack(void)
{
    P0DIR &= ~0x10;//注意这里,数据线应该设置为输入
    uint i = 0;
    SCL = 1;
    Delay();
    //等待应答
    while((SDA == 1)&&(i<255))
      i++;
    SCL = 0;
    Delay();
    P0DIR |= 0x10;//将数据线SDA设置为输出
}
/*I2C总线写数据函数,将要写的数据传递给形参I2CBuf*/
/*
        I2C写数据时序:
        注意:由于I2C属于串行总线,所以数据都是一位一位的传输
        1、将SCL时钟线拉高,准备传输数据,延时一段时间
        2、将I2CBuf的最高位传输到SDA数据线上
        3、拉低SCL时钟线,延时一段时间
        小结:当SCL有一个上升沿时,传输数据。
        以上三步表示传输完一位数据。然后将I2CBuf左移一位,再重复以上三步。
        最后,将SCL位低,延时一段时间,再将SDA位高,延时一段时间。此操作是为了释放时钟线SCL和数据线SDA.
*/
void I2C_Write(uint I2CBuf)
{
    uint iwi;
    for(iwi = 0; iwi < 8; iwi++)
    {
        SCL = 0;
        Delay();
        if(I2CBuf & 0x80)
          SDA = 1;
        else
          SDA = 0;
        SCL = 1;
        Delay();
        I2CBuf = I2CBuf << 1;
    }
    SCL = 0;
    Delay();
    SDA = 1;
    Delay();

}
/*I2C总线读数据函数,该函数具有返回值I2CBuf*/
/*
        I2C读数据函数的时序:
        注意:首先需要将模拟SDA数据线的GPIO口配置为输入
        1、将时钟线SCL拉高,延时一段时间
        2、读取数据线SDA上的电平,然后写入I2CBuf的最低位。
        3、拉低SCL时钟线,延时一段时间
        小结:当SCL有一个下降沿时,读取数据。
        以上三步表示读取一位数据,读一个字节,需要重复以上操作步骤8次。
*/
uint I2C_Read(void)
{
    uint iri;
    uint I2CBuf = 0;
    P0DIR &= ~0x10;//注意这里,需要将SDA配置为输入。
    Delay();
    for(iri = 0; iri < 8; iri++)
    {
        I2CBuf = I2CBuf << 1;//此步不可少
        SCL = 1;
        Delay();
        if(SDA == 1)
          I2CBuf |= 0x01;
        else
          I2CBuf &= 0xfe;
        SCL = 0;
        Delay();
    }
    P0DIR |= 0X10;//将SDA配置为输出
    return I2CBuf;//将读到的数据存入I2CBuf中,然后返回给主调函数
}
/*
I2C总线写寄存器函数,将寄存器地址传给形参RegAdd,将要写入的数据传递给RegValue。
注意:地址和数据最好都采用十六进制
*/
/*
        I2C写寄存器函数的时序:
        1、主设备先发出一个开始信号
        2、然后写入从设备地址。每一个从设备的地址是唯一的。
        3、主设备接收应答信号
        4、主设备向从设备写入寄存器的地址
        5、主设备接收应答信号
        6、主设备向从设备写入数据
        7、主设备接收应答信号
        8、主设备发出停止信号
*/
void I2C_WriteReg(uint RegAdd, uint RegValue)
{
    I2C_Start();
    I2C_Write(0xd0);//0xd0是从设置的地址,不同的设备,地址不同。所以移植些函数时,需要更改此处。
    I2C_Ack();
    I2C_Write(RegAdd);//先写地址,此地址为需要写入数据的寄存器地址
    I2C_Ack();
    I2C_Write(RegValue);//写入数据
    I2C_Ack();
    I2C_Stop();
}
/*I2C总线读寄存器函数,将需要读取数据的寄存器的地址传递给形参RegAdd,此函数有返回值*/
/*
        I2C读寄存器函数的时序:
        1、主设备发出开始信号
        2、写入从设备地址
        3、主设备接收从设备发来的应答信号
        4、写入寄存器地址
        5、主设备接收应答信号
        6、主设备再次发出开始信号
        7、主设备向从设备写入(从设备的地址+1),此时加1表示要从寄存器中读数据
        8、从设备给出应答信号
        9、从从设备的寄存器中读出数据传给变量
        10、主设备发出停止信号
        11、返回读出的数据
*/
uint I2C_ReadReg(uint RegAdd)
{
    uchar date;
    I2C_Start();
    I2C_Write(0xd0);//先写入从设备的地址。表示要向从设备中写地址和数据
    I2C_Ack();
    I2C_Write(RegAdd);//写入寄存器的地址,数据将从些地址中读出
    I2C_Ack();
    I2C_Start();
    I2C_Write(0xd1);//从设备的地址加1,表示要读从设备中寄存器的数据
    I2C_Ack();
    date = I2C_Read();//从地址RegAdd中读出数据
    I2C_Stop();
    return date;//将读出的数据存入date中,然后返回给主调函数。
}

感谢分享,回去试一试

好东西啊啊啊啊啊啊啊啊啊啊啊啊啊

国外伺服技术方案转让
编码器采用研发17-bit(160000ppr)高分辨率增量编码器;
转子磁铁采用稀土永磁材料;高速冲压模日本进口矽钢片铁芯
内置位置、速度和扭矩控制模式,支持外部模拟量控制;匹配公司自主研发17-bit高分辨率增量编码器;支持RS232、RS485通讯规格,标准Modbus通讯协议;精密控制、性能稳定、性价比高。
采用抗干扰和防震动设计,适用范围广,性能稳定;类型包含17-bit、20-bit增量式和绝对值编码器。
产品广泛应用于制造业(攻丝机、绕线机、雕铣机、数控车床、数控加工中心、线切割机、铣床、磨床、注塑机、绣花机、纺织机、包装机、印刷机、激光雕刻机、木工机械等),工业自动化(机器人、机械手臂、生产线自动化飞镖机械等),航空业、汽车工业、新能源(风能、电动汽车等)以及其他机械自动化工控领域。
扣扣:二五一二二六二四七一

不好意思,请问您能跟我具体说一下是怎么操作吗?因为我现在有一个传感器,然后需要用到GPIO口模拟I2C,开发板是TQ2440的,在其上移植了Linux系统。所以我的传感器驱动是按照接I2C的样子写还是写寄存器那样的驱动?

这里想请教一下,我看到其他的教程上说从设备ACK应答时,SDA是被拉低的,为什么您的应答时序是SDA是拉高的

Copyright © 2017-2020 微波EDA网 版权所有

网站地图

Top