历史上的今天
返回首页

历史上的今天

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

正在发生

2018年05月20日 | STM32F1使用I/0模拟I2C接口

2018-05-20 来源:eefocus

使用模拟时序的方法,对比于硬件I2C接口来说,在实时性和传输速度上会带来一些无法避免的下降,但是I2C总线本身也不是一种速度很快的总线(据相关资料可查,最高的速度为400KHZ),同时也不需要具备很高的实时性能。
所以,模拟I2C时序完全能满足绝大部分的场合要求,并且移植性得到了很大的提高。
闲话不多说,贴上代码,大家一起分享下。

首先贴出 i2c_soft.h实现:

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

 * 文件名  :i2c_soft.h 

 * 描述    :使用I/0模拟I2C接口          

 * 实验平台:神舟III号 

 * 库版本  :ST3.0.0 


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

#ifndef __I2C_SOFT_H  

#define __I2C_SOFT_H  

      

#include "stm32f10x.h"  

#define SCL_PIN        GPIO_Pin_6  

#define SDA_PIN        GPIO_Pin_7  

#define SCL_PORT       GPIOB  

#define SDA_PORT       GPIOB  

#define SCL_RCC_CLOCK  RCC_APB2Periph_GPIOB  

#define SDA_RCC_CLOCK  RCC_APB2Periph_GPIOB  

      

#define SCL_H         GPIOB->BSRR = GPIO_Pin_6  

#define SCL_L         GPIOB->BRR  = GPIO_Pin_6   

         

#define SDA_H         GPIOB->BSRR = GPIO_Pin_7  

#define SDA_L         GPIOB->BRR  = GPIO_Pin_7  

      

#define SCL_read      GPIOB->IDR  & GPIO_Pin_6  

#define SDA_read      GPIOB->IDR  & GPIO_Pin_7  

      

#define I2C_PageSize  8  //24C02每页8字节  

      

void I2C_GPIO_Config(void);  

bool I2C_WriteByte(u8 SendByte, u16 WriteAddress, u8 DeviceAddress);  

bool I2C_BufferWrite(u8* pBuffer, u8 length, u16 WriteAddress, u8 DeviceAddress);  

void I2C_PageWrite(u8* pBuffer, u8 length, u16 WriteAddress, u8 DeviceAddress);  

bool I2C_ReadByte(u8* pBuffer, u8 length, u16 ReadAddress, u8 DeviceAddress);  

void I2C_Test(void);  

      

#endif  


然后贴出 i2c_soft.c实现:


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

 * 文件名  :i2c_soft.c 

 * 描述    :使用I/0模拟I2C接口          

 * 实验平台:神舟III号 

 * 库版本  :ST3.0.0 

 * 

 * 作者    :xiayufeng  xiayufeng90520@163.com  

 * 博客    :http://hi.baidu.com/xiayufeng520 

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

#include "i2c_soft.h"  

#include "usart1.h"  

       

extern void Delay_ms(__IO u32 nTime);  

       

/*  

 * 函数名:void I2C_GPIO_Config(void) 

 * 描述  : I2C管脚初始化 

 * 输入  :无 

 * 输出  : 无 

 */  

void I2C_GPIO_Config(void)  

{  

    GPIO_InitTypeDef  GPIO_InitStructure;   

           

    RCC_APB2PeriphClockCmd(SCL_RCC_CLOCK | SDA_RCC_CLOCK ,ENABLE);  

           

    //初始化SCL管脚  

    GPIO_InitStructure.GPIO_Pin =  SCL_PIN;  

    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;  

    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;    

    GPIO_Init(SCL_PORT, &GPIO_InitStructure);  

           

    //初始化SDA管脚  

    GPIO_InitStructure.GPIO_Pin =  SDA_PIN;  

    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;  

    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;  

    GPIO_Init(SDA_PORT, &GPIO_InitStructure);  

}  

       

/*  

 * 函数名: void I2C_delay(void) 

 * 描述  : 短暂延时 

 * 输入  : 无 

 * 输出  : 无 

 * 说明  : 内部定义的i可以优化速度,经测试最低到5还能写入 

 */  

static void I2C_delay(void)  

{     

    u8 i=100;   

    while(i)   

    {   

        i--;   

    }   

}  

       

/*  

 * 函数名: bool I2C_Start(void) 

 * 描述  : 起始信号 

 * 输入  : 无 

 * 输出  : TRUE : 成功 

                     FALSE : 失败 

 * 说明  :  

 */  

static bool I2C_Start(void)  

{  

    SDA_H;  

    SCL_H;  

    I2C_delay();  

    if(!SDA_read)  

        return FALSE;   //SDA线为低电平则总线忙,退出  

    SDA_L;  

    I2C_delay();  

    if(SDA_read)   

        return FALSE;   //SDA线为高电平则总线出错,退出  

    SDA_L;  

    I2C_delay();  

    return TRUE;  

}  

/*  

 * 函数名: static void I2C_Stop(void) 

 * 描述  : 终止信号 

 * 输入  : 无 

 * 输出  : 无 

 * 说明  :  

 */  

static void I2C_Stop(void)  

{  

    SCL_L;  

    I2C_delay();  

    SDA_L;  

    I2C_delay();  

    SCL_H;  

    I2C_delay();  

    SDA_H;  

    I2C_delay();  

}  

/*  

 * 函数名: static void I2C_Ack(void) 

 * 描述  : 应答信号 

 * 输入  : 无 

 * 输出  : 无 

 * 说明  :  

 */  

static void I2C_Ack(void)  

{     

    SCL_L;  

    I2C_delay();  

    SDA_L;  

    I2C_delay();  

    SCL_H;  

    I2C_delay();  

    SCL_L;  

    I2C_delay();  

}  

/*  

 * 函数名: void I2C_NoAck(void) 

 * 描述  : 无应答信号 

 * 输入  : 无 

 * 输出  : 无 

 * 说明  :  

 */  

static void I2C_NoAck(void)  

{     

    SCL_L;  

    I2C_delay();  

    SDA_H;  

    I2C_delay();  

    SCL_H;  

    I2C_delay();  

    SCL_L;  

    I2C_delay();  

}  

/*  

 * 函数名: bool I2C_Start(void) 

 * 描述  : 等待应答信号 

 * 输入  : 无 

 * 输出  : TRUE : 有应答 

                     FALSE : 无应答 

 * 说明  :  

 */  

static bool I2C_WaitAck(void)     

{  

    SCL_L;  

    I2C_delay();  

    SDA_H;            

    I2C_delay();  

    SCL_H;  

    I2C_delay();  

    if(SDA_read)  

    {  

        SCL_L;  

        return FALSE;  

    }  

    SCL_L;  

    return TRUE;  

}  

/*  

 * 函数名: static void I2C_SendByte(u8 SendByte)  

 * 描述  : 发送一个字节 

 * 输入  : SendByte : 字节数据 

 * 输出  : 无 

 * 说明  : 数据从高位到低位 

 */  

static void I2C_SendByte(u8 SendByte)   

{  

    u8 i=8;  

    while(i--)  

    {  

        SCL_L;  

        I2C_delay();  

        if(SendByte&0x80)  

            SDA_H;    

        else  

            SDA_L;     

        SendByte<<=1;  

        I2C_delay();  

        SCL_H;  

        I2C_delay();  

    }  

    SCL_L;  

}  

/*  

 * 函数名: static u8 I2C_ReceiveByte(void)  

 * 描述  : 读取一个字节 

 * 输入  : 无  

 * 输出  : 字节数据 

 * 说明  : ReceiveByte : 数据从高位到低位 

 */  

static u8 I2C_ReceiveByte(void)    

{   

    u8 i=8;  

    u8 ReceiveByte=0;  

           

    SDA_H;                

    while(i--)  

    {  

        ReceiveByte<<=1;        

        SCL_L;  

        I2C_delay();  

        SCL_H;  

        I2C_delay();      

        if(SDA_read)  

        {  

            ReceiveByte|=0x01;  

        }  

    }  

    SCL_L;  

    return ReceiveByte;  

}  

       

/*  

 * 函数名: bool I2C_WriteByte(u8 SendByte, u16 WriteAddress, u8 DeviceAddress) 

 * 描述  : 写入1字节数据   

 * 输入  : SendByte : 要写入数据 

                     WriteAddress : 写入地址 

                     DeviceAddress : 器件地址 

 * 输出  : TRUE : 成功 

                     FALSE : 失败 

 * 说明  :  

 */  

bool I2C_WriteByte(u8 SendByte, u16 WriteAddress, u8 DeviceAddress)  

{         

    if(!I2C_Start())  

            return FALSE;  

    I2C_SendByte(((WriteAddress & 0x0700) >>7) | DeviceAddress & 0xFFFE);//设置高起始地址+器件地址   

    if(!I2C_WaitAck())  

        {  

            I2C_Stop();   

            return FALSE;  

        }  

    I2C_SendByte((u8)(WriteAddress & 0x00FF));   //设置低起始地址        

    I2C_WaitAck();    

    I2C_SendByte(SendByte);  

    I2C_WaitAck();     

    I2C_Stop();   

           

    Delay_ms(10);//注意:因为这里要等待EEPROM写完,可以采用查询或延时方式(10ms)  

           

        return TRUE;  

}  

/*  

 * 函数名: bool I2C_WriteByte(u8 SendByte, u16 WriteAddress, u8 DeviceAddress) 

 * 描述  : 写入1串数据  

 * 输入  : pBuffer : 要写入数据缓冲区指针 

           length : 待写入长度   

                     WriteAddress : 写入地址 

                     DeviceAddress : 器件地址 

 * 输出  : TRUE : 成功 

                     FALSE : 失败 

 * 说明  : 注意不能跨页写 

 */  

bool I2C_BufferWrite(u8* pBuffer, u8 length, u16 WriteAddress, u8 DeviceAddress)  

{  

    if(!I2C_Start())  

            return FALSE;  

    I2C_SendByte(((WriteAddress & 0x0700) >>7) | DeviceAddress & 0xFFFE);//设置高起始地址+器件地址   

    if(!I2C_WaitAck())  

        {  

            I2C_Stop();   

            return FALSE;  

        }  

    I2C_SendByte((u8)(WriteAddress & 0x00FF));   //设置低起始地址        

        I2C_WaitAck();    

        while(length--)  

        {  

            I2C_SendByte(* pBuffer);  

            I2C_WaitAck();  

            pBuffer++;  

        }  

        I2C_Stop();  

               

        Delay_ms(10);//注意:因为这里要等待EEPROM写完,可以采用查询或延时方式(10ms)  

               

        return TRUE;  

}  

       

/*  

 * 函数名: bool I2C_WriteByte(u8 SendByte, u16 WriteAddress, u8 DeviceAddress) 

 * 描述  : 写入1串数据  

 * 输入  : pBuffer : 要写入数据缓冲区指针 

           length : 待写入长度   

                     WriteAddress : 写入地址 

                     DeviceAddress : 器件地址 

 * 输出  : TRUE : 成功 

                     FALSE : 失败 

 * 说明  : 跨页写入1串数据 

 */  

void I2C_PageWrite(  u8* pBuffer, u8 length, u16 WriteAddress, u8 DeviceAddress)  

{  

    u8 NumOfPage = 0, NumOfSingle = 0, Addr = 0, count = 0;  

    Addr  = WriteAddress % I2C_PageSize;      //写入地址是开始页的第几位  

    count = I2C_PageSize - Addr;                        //在开始页要写入的个数  

    NumOfPage   =  length / I2C_PageSize;     //要写入的页数  

    NumOfSingle =  length % I2C_PageSize;     //不足一页的个数  

           

    if(Addr == 0)         //写入地址是页的开始   

    {  

        if(NumOfPage == 0)  //数据小于一页   

        {  

            I2C_BufferWrite(pBuffer,NumOfSingle,WriteAddress,DeviceAddress);   //写少于一页的数据  

        }  

        else                    //数据大于等于一页    

        {  

            while(NumOfPage)//要写入的页数  

            {  

                I2C_BufferWrite(pBuffer,I2C_PageSize,WriteAddress,DeviceAddress);//写一页的数据  

                WriteAddress +=  I2C_PageSize;  

                pBuffer      +=  I2C_PageSize;  

                NumOfPage--;  

                Delay_ms(10);  

            }  

            if(NumOfSingle!=0)//剩余数据小于一页  

            {  

                I2C_BufferWrite(pBuffer,NumOfSingle,WriteAddress,DeviceAddress); //写少于一页的数据  

                Delay_ms(10);  

            }  

        }  

    }  

           

    else                  //写入地址不是页的开始   

    {  

        if(NumOfPage== 0)   //数据小于一页   

        {  

            I2C_BufferWrite(pBuffer,NumOfSingle,WriteAddress,DeviceAddress);   //写少于一页的数据  

        }  

        else                //数据大于等于一页  

        {  

            length       -= count;  

            NumOfPage     = length / I2C_PageSize;  //重新计算要写入的页数  

            NumOfSingle   = length % I2C_PageSize;  //重新计算不足一页的个数     

                   

            if(count != 0)  

            {    

                I2C_BufferWrite(pBuffer,count,WriteAddress,DeviceAddress);      //将开始的空间写满一页  

                WriteAddress += count;  

                pBuffer      += count;  

            }   

                   

            while(NumOfPage--)  //要写入的页数  

            {  

                I2C_BufferWrite(pBuffer,I2C_PageSize,WriteAddress,DeviceAddress);//写一页的数据  

                WriteAddress +=  I2C_PageSize;  

                pBuffer      +=  I2C_PageSize;   

            }  

            if(NumOfSingle != 0)//剩余数据小于一页  

            {  

                I2C_BufferWrite(pBuffer,NumOfSingle,WriteAddress,DeviceAddress); //写少于一页的数据   

            }  

        }  

    }   

}  

       

/*  

 * 函数名: bool I2C_ReadByte(u8* pBuffer,   u8 length,     u16 ReadAddress,  u8 DeviceAddress) 

 * 描述  : 读出1串数据 

 * 输入  : pBuffer : 要读取数据缓冲区指针 

           length : 待读取长度   

                     WriteAddress : 读取地址 

                     DeviceAddress : 器件地址 

 * 输出  : TRUE : 成功 

                     FALSE : 失败 

 * 说明  : 跨页写入1串数据 

 */  

bool I2C_ReadByte(u8* pBuffer,   u8 length,     u16 ReadAddress,  u8 DeviceAddress)  

{         

    if(!I2C_Start())return FALSE;  

    I2C_SendByte(((ReadAddress & 0x0700) >>7) | DeviceAddress & 0xFFFE);//设置高起始地址+器件地址   

    if(!I2C_WaitAck()){I2C_Stop(); return FALSE;}  

    I2C_SendByte((u8)(ReadAddress & 0x00FF));   //设置低起始地址        

    I2C_WaitAck();  

    I2C_Start();  

    I2C_SendByte(((ReadAddress & 0x0700) >>7) | DeviceAddress | 0x0001);  

    I2C_WaitAck();  

    while(length)  

    {  

        *pBuffer = I2C_ReceiveByte();  

        if(length == 1)I2C_NoAck();  

        else I2C_Ack();   

        pBuffer++;  

        length--;  

    }  

    I2C_Stop();  

    return TRUE;  

}  

       

/*  

 * 函数名: void I2C_Test(void) 

 * 描述  : 测试函数 

 * 输入  : 无  

 * 输出  : 无 

 * 说明  : 无 

 */  

void I2C_Test(void)  

{  

       

    u8 I2cVal_Write = 0xFE;  

    u8 I2cVal_Read = 0x00;  

           

    printf("Start IIC test\r\n");  

    printf("The Simulation_IIC has sended data:%d \r\n", I2cVal_Write);  

    I2C_WriteByte(I2cVal_Write, 0X01, 0xa0);  

           

       

    I2C_ReadByte(&I2cVal_Read, 1, 0x01 ,0xa0);  

    printf("The Simulation_IIC has Received data:%d \r\n", I2cVal_Read);  

           

    if(I2cVal_Read == I2cVal_Write)  

    {  

        printf("The Simulation IIC is successful!\r\n");  

    }  

    else  

    {  

        printf("The Simulation IIC is failed!\r\n");  

    }  

       


推荐阅读

史海拾趣

eLED.com Corp公司的发展小趣事

作为一家有影响力的企业,eLED.com Corp始终关注社会责任的履行。公司积极参与各类公益活动,关注教育、环保和扶贫等领域。通过捐款捐物、志愿服务等方式,eLED.com Corp为社会做出了积极的贡献。此外,公司还注重员工培训和福利待遇的提升,努力为员工创造一个良好的工作环境和发展空间。通过这些举措的实施,eLED.com Corp不仅赢得了社会的广泛赞誉和支持,还为企业的发展注入了新的动力。

DURATOOL公司的发展小趣事

DURATOOL公司自成立以来,一直致力于电子工具的技术创新。在20世纪90年代,公司研发出了一款具有革命性的数字万用表,该产品在精确度、稳定性和操作便捷性方面均大幅超越当时的市场主流产品。这一创新不仅为DURATOOL公司赢得了大量忠实用户,也为其在电子测量工具市场树立了技术领先的形象。随后,DURATOOL公司不断加大研发投入,推出了一系列具有竞争力的新产品,逐步巩固了其在电子工具行业的领先地位。

GarrettCom公司的发展小趣事

研发:GarrettCom(现为Belden的一部分)始终将技术创新作为企业发展的核心驱动力。公司不断投入研发资源,致力于开发更加先进、可靠的工业网络通信产品。通过持续的技术创新,GarrettCom不仅推动了工业通信技术的进步,还为客户提供了更加高效、便捷的通信解决方案。

应用:这些创新产品被广泛应用于工业自动化、交通监控、能源管理等多个领域,为客户创造了巨大的价值。同时,GarrettCom还积极参与国际标准的制定和推广工作,为行业的标准化发展做出了积极贡献。

安美通(APPCON)公司的发展小趣事

安美通非常重视企业文化建设和团队建设。公司倡导创新、务实、合作的企业文化,为员工提供了一个宽松、自由、充满激情的工作环境。同时,公司还积极开展各类培训活动,提升员工的专业素养和综合能力。这些措施使得安美通的团队更加团结、高效,为公司的持续发展提供了有力的保障。

这五个故事虽然只是安美通发展过程中的一部分,但它们充分展示了安美通在电子行业中不断努力、创新、拓展的精神和成果。未来,安美通将继续秉承这种精神,不断推动公司的发展和进步。

Azoteq公司的发展小趣事

最近,Azoteq宣布推出了一款电容、电感和霍尔效应组合传感器——IQS620。这款传感器是传感领域的重大突破,它将电容式触摸和接近传感、电感传感以及霍尔效应传感集成在一个低姿态的封装中。这种高度集成化的设计使得IQS620在可穿戴设备、移动设备和安全等多个领域都有广泛的应用前景。IQS620的推出,不仅展示了Azoteq在传感技术方面的深厚实力,也为其在未来的市场竞争中注入了新的活力。

这些故事展示了Azoteq公司在电子行业中的发展历程和取得的成就。从创立初期的LightSense™系列产品,到后续的电容式触摸和接近感应市场拓展,再到PowerSense™和ProxFusion®产品系列的推出,以及最新的IQS620传感器的发布,Azoteq始终紧跟市场趋势,不断创新,为客户提供高质量的半导体解决方案。

Elpac公司的发展小趣事

在电子行业的激烈竞争中,Elpac公司凭借其在电源管理领域的技术革新,逐渐崭露头角。公司创始人李明在创办之初就坚信,只有不断创新才能在市场中立足。他带领团队深入研究市场需求,不断推出具有创新性的电源管理产品。其中,一款高效节能的电源转换器在市场上引起了广泛关注,其出色的性能和稳定性赢得了客户的青睐。随着产品的热销,Elpac公司逐渐在电子行业中建立了自己的品牌地位。

问答坊 | AI 解惑

4位温度计,不能正确显示!

(有程序和Proteus仿真图)…

查看全部问答>

AVR(MEGA16)入门教程,详细的图解教程

AVR(MEGA16)入门教程,详细的图解教程…

查看全部问答>

如何用红外对射开关控制继电器

我想用一个红外对射开关控制欧姆龙MY2NJ继电器工作,对射开关的型号见图片 我用黄颜色的线接到继电器,继电器不能工作,继电器都是好的,我用12V电压直接控制继电器,是可以工作的,但用感应的不知道怎么接法。 红外对射开关工作电压是5V,继电器 ...…

查看全部问答>

在动车组两个车厢中的无线传输用什么样的方式更好

想在动车组两个车厢中实现数据的无线传输。 也考虑到一些实现方法,但是由于动车组有屏蔽大,速度快特点,造成无线数据包传输难度很大。 请问各位高手,用什么方式能达到这个目的呢?前提是不对车厢作出改造,比如车体外安装天线等 …

查看全部问答>

JAVA

用JAVA做嵌入式开发怎么样,效率高吗,前景如何?????…

查看全部问答>

软键盘,模拟发送小数点的奇怪问题

现在软键盘基本上可以用了,但还有一个字符:小数点\'.\'不能用,发送字符小数点消息,可在我的应用程序里收到的消息却是:     第一个消息是:pMsg->hwnd为编辑框的句柄,但pMsg->message为15,也就是WM_PAINT,wParam和lParam为0, & ...…

查看全部问答>

又是一个石沉大海的问题!!??

如何在驱动代码中,获得U盘的插入?并获得其盘符(注:此U盘无文件系统) 谢谢,有钱的捧个钱场,无钱的捧个情场…

查看全部问答>

请高手详细介绍一下键盘IO口扩展IC:74164

我是个菜鸟,在学校学习的单片机都是很多过时的芯片,所以有很多不懂。请大家指教一下,这个芯片我还没有接触过。…

查看全部问答>