历史上的今天
返回首页

历史上的今天

今天是:2025年05月20日(星期二)

正在发生

2018年05月20日 | GPIO口模拟I2C

2018-05-20 来源:eefocus

根据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采用的延时就是一个空等待,对于整个操作系统来说,这是一个资源浪费,同时还有可能会影响到其他任务的运行。


推荐阅读

史海拾趣

Herley New York公司的发展小趣事

Hengstler GmbH在电子行业中的五个发展故事

故事一:从钟表工坊到工业计数器的先驱

Hengstler GmbH的故事始于1846年,当时创始人Johannes Hengstler在德国西南部的奥尔丁根镇开设了一家钟表工坊,专注于生产钟表弹簧。随着时间的推移,Hengstler逐渐扩展其业务领域。1926年,公司收购了Efunda公司的计数装置业务,这标志着Hengstler正式进入工业计数和控制元件领域。此后,Hengstler不断推出创新产品,如20世纪50年代发布的革命性“计数系统400”,这是一种带有插件系统的塑料计数系统,不仅功能强大,而且价格实惠,极大地推动了公司在市场上的地位。

故事二:技术革新与全球化布局

进入20世纪后半叶,Hengstler继续加大技术研发投入,并在全球范围内扩展其业务。1962年,公司在英国成立了第一家海外子公司,随后几年内,在法国、比利时、加拿大、意大利和日本等地也相继设立了分支机构。这些举措不仅增强了Hengstler的国际影响力,还使其能够更好地服务全球客户。同时,公司还引入了先进的生产设备和技术,如塑料注塑成型机,实现了塑料部件的大批量生产,进一步提升了产品质量和生产效率。

故事三:编码器技术的突破

在编码器技术方面,Hengstler也取得了显著成就。公司不断推出新型编码器产品,如光电增量型编码器RI58-O/RI58-T,其分辨率高达10,000脉冲和40,000步,防护等级达到IP67,广泛应用于各种工业场合。此外,公司还开发了重载磁性绝对值型编码器AR62/63,该编码器具有极高的抗冲击和抗振动能力,适用于恶劣环境下的使用。这些产品的推出不仅丰富了Hengstler的产品线,还进一步巩固了其在编码器领域的领先地位。

故事四:被丹纳赫收购后的新发展

1995年,Hengstler被美国丹纳赫公司收购,成为其旗下的一员。这一收购为Hengstler带来了更多的资源和支持,推动了公司的进一步发展。在丹纳赫的支持下,Hengstler加大了对新技术和新产品的研发力度,不断推出具有创新性和竞争力的产品。同时,公司还加强了与国际市场的联系和合作,进一步提升了其国际竞争力。

故事五:持续创新与客户服务

多年来,Hengstler始终坚持创新和服务至上的理念。公司不断投入研发资金,引进先进技术和设备,提升产品质量和生产效率。同时,公司还注重客户服务,建立了完善的售后服务体系,为客户提供及时、专业的技术支持和解决方案。这些努力不仅赢得了客户的信任和好评,还使Hengstler在电子行业中保持了良好的声誉和地位。如今,Hengstler已成为全球领先的工业计数和控制元件制造商之一,其产品广泛应用于自动化技术、风力发电机、商用车辆、售票机和医疗等领域。

Holy Stone公司的发展小趣事

1999年,Holy Stone在台湾桃园龙潭设立了生产基地,开始自主生产积层陶瓷电容(MLCC)。这一举措标志着公司从单纯的代理商向集研发、生产、销售于一体的多元化企业转型。同年,公司创立了自有品牌“HEC”,并以该品牌推出了一系列多元客制化产品,行销全球。这些产品凭借其高品质和满足客户全方位需求的特点,赢得了客户的广泛信赖,进一步巩固了公司在电子元件市场的地位。

Dialog公司的发展小趣事

到了1981年,Dialog公司从洛克希德导弹航空公司中独立出来,成为了一个子公司,并开始独立经营。在独立发展的初期,Dialog公司继续专注于通信技术的研发,并逐渐将业务范围拓展到了个人便携式应用、低功耗短程无线应用以及LED固态照明、显示和汽车应用等领域。

超霸(GP)公司的发展小趣事

绿索超容深知品质是企业生存的根本。因此,公司从原材料采购到生产制造,再到成品检验,都实施了严格的质量控制措施。此外,公司还通过了ISO9001体系认证,进一步提升了其产品质量管理水平。这些努力不仅保证了绿索超容产品的优良品质,也为其赢得了客户的信任和好评。

Electronicon Kondensatoren GmbH公司的发展小趣事

ELECTRONICON Kondensatoren GmbH,其前身为RFT Kondensatorenwerk Gera,于20世纪30年代末与德国图林根东部的Gera的电容器制造商建立了紧密的合作。自此,ELECTRONICON开始崭露头角,专注于电容器技术的研发与生产。公司始终将产品的质量和可靠性作为核心理念,为未来的发展奠定了坚实的基础。

Elytone Electronics Co Ltd公司的发展小趣事

随着公司规模的扩大,Elytone公司意识到品质管理对于企业发展至关重要。他们引入了先进的质量管理体系,加强了对原材料采购、生产过程和售后服务的监控。通过持续改进和优化,公司的产品质量得到了显著提升,赢得了客户的广泛认可。

问答坊 | AI 解惑

高速数据传输问题

请问一下数字电路高手,PCBlay out 高速信号应注意那些问题? 2.我有一块硬盘的板,从USB的输入端D+到芯片的输入端为0.3R正常吗?…

查看全部问答>

【红色飓风推荐】FPGA学术论文下载

随着性能不断提高,成本持续降低,FPGA在各个领域都得到了广泛应用。 红色飓风载选了一些国内核心期刊上发表的FPGA设计方面的论文,供大家参考! 希望对正在从事FPGA设计的工程技术人员或者高校里面做毕业设计的学生有一点点帮助。 ====== ...…

查看全部问答>

电子设计比赛要点

本帖最后由 paulhyde 于 2014-9-15 09:08 编辑 电子设计比赛要点1、不要追求高精尖,要通。电赛时间有限,与其花时间去弄自己没接触过的东西,不如把自己已经学到的东西弄透。对大部分人来讲,如果之前没有接触DSP、FPGA、ARM之类高端的控制器,可 ...…

查看全部问答>

LTIB介绍和安装(三)

3.运行ltibcd到ltib安装到的目录,这个是在上面install过程中让你选择的。然后:./ltib即可运行,第一次运行,实际上就是启动build image的工作,最后会调用编译器等工具连构建出最终内核image和文件系统image。这个过程可能出现错误(但我没碰到) ...…

查看全部问答>

alsa驱动不能初始化硬件

在用alsa的应用程序测试开发板上的音频放音的时候,下面的函数出错了:         /* Write the parameters to the driver */         rc= snd_pcm_hw_params(handle,params);       &nbs ...…

查看全部问答>

高分求教NDIS问题

最多只能给100,先这样吧,如果问题解决了,小可另外送分哈! 最近在研究NDIS中间层,有些问题不明白,请教: ProtocolReceivePacket可以处理批量数据包,是怎么处理的?参数只是一个包啊,难道这个批量只是相对于ProcotolReceive偶尔一个包需要 ...…

查看全部问答>

关于输入法的两个问题

1,我的开发环境为evc4+ppc2003+中文镜像 下载了个freepyCE,编译通过,看freepyCE自带的安装说明,替换了注册表中的键值: HKEY_LOCAL_MACHINE\\System\\CurrentControlSet\\Control\\Layouts\\e0010804修改,把Ime File键值从msimepy.dll改为fr ...…

查看全部问答>

最牛的无线数传资料

无线数传模组: a.通用数传模组    WAP200B、EM200D、GW200B,最大100mW,64个频道,传输距离800米左右,内置前向纠错,最高速度19200(接口可达115200); b.高灵敏度数传模组   WAP200C、GW200KB,最大20mW,64个频道,传 ...…

查看全部问答>

请问2530+2591能做到多少米啊?

本信息来自合作QQ群:电子工程师技术交流(12425841) 群主在坛子ID:Kata   请问2530+2591能做到多少米啊 …

查看全部问答>

sht20 温湿度传感器交流区

论坛各位大佬,谁用过sht20这块温湿度传感器,有调通的程序分享下吧,51单片机的代码最好,或者FPGA的。 这里可以变为一个关于sht1X、sht2X和sht7X  瑞士Sensirion的温湿度传感器的交流区 我之前也发过一个帖子,是关于sht10的调试通 ...…

查看全部问答>