历史上的今天
返回首页

历史上的今天

今天是:2025年08月09日(星期六)

2019年08月09日 | STM32中I2C协议时序和使用

2019-08-09 来源:eefocus

作为使用目前嵌入式设备使用最多的协议之一,I2C和SPI都是要研究透的。在我使用的开发板上,SPI协议集成了,只需要使用3个GPIO引脚即可。但I2C协议没有集成,还得自己写I2C的时序驱动,I2C的时序图大家可以百度到,专门的协议文档也有。关于时序图我会贴到博客里。


1. I2C串行总线的组成与工作原理


a. 组成:2根双向信息线,一根数据线SDA,一根时钟线SCL


b. I2C总线上多个器件的挂接(注意,每个器件都有唯一的地址)

c. 数据的通讯方式


主从方式。主机负责主动联系从机,从机被动回应主机(上图里I2C设备都是从机)


2. 工作方式:I2C总线通过上拉电阻接正电源,当总线空闲时,2根线均为高电平,各期件的SDA及SCL都是“线与”关系[单一器件输出低电平,整条总线的信号都拉低,I2C总线上的器件都可以读取到,主从机通讯就是这种机制]


       3. 数据位的有效性规定(规定就是没有理由)


SCL处于高电平期间,这时对数据进行操作,数据线上的数据必须保持稳定。只有SCL处于低电平期间,SDA状态才能变化


       4. I2C字节的传送与应答


每一位字节必须保持8位长度。数据传送时,先传送最高位MSB, 每一个被传送的字节最后必须跟随一个应答位(及一帧9位,应答位要用于说明一个字节已经传完,I2C总线重新置空闲态)

5. 时序协议的驱动的理解要逻辑性良好(这点是对各个应答信号时序的理解与编程)


 4个基本时序信号:起始信号, 终止信号, 应答“0”,  应答“1”。(注意,一定要严格按照时序图来编写程序,延时也有严格的要求)


void I2C_Start()     /*起始信号*/

{

I2C_SDA_OUT();   /*先SDA输出模式,主机MCU负责主动联系从机,所以MCU是输出*/


I2C_SDA_H;

I2C_SCL_H;

delay_us(5);

I2C_SDA_L;

delay_us(6);

I2C_SCL_L;

}




void I2C_Stop()    /*终止信号*/

{

I2C_SDA_OUT();   /*先SDA输出模式,同理主机也负责切断与从机的通讯*/


I2C_SCL_L;

I2C_SDA_L;

I2C_SCL_H;

delay_us(6);

I2C_SDA_H;

delay_us(6);

}  




void I2C_Ack()   /*主机应答*/

{

I2C_SCL_L;

I2C_SDA_OUT();   /*SDA输出,主机应答函数,从机器件自然内置相应的应答函数*/

I2C_SDA_L;

delay_us(2);

I2C_SCL_H;

delay_us(5);

I2C_SCL_L;          /*SDA在“0”时,SCL保持高电平则作为读状态*/

}





void I2C_NAck()   /*主机非应答*/

{

I2C_SCL_L;

 I2C_SDA_OUT();  /*SDA输出*/

I2C_SDA_H;

delay_us(2);

I2C_SCL_H;

delay_us(5);

I2C_SCL_L;          /*同理*/

}


这是这上面的几个基础时序程序。要使用I2C协议收,发数据还要研究


6. I2C写数据,读数据驱动


由图可知,发送的数据(地址和有效数据)不是只使用高低电平就可以的,2个字节的间隔区分,主从机是否接受到,数据的正确与否。。。都要求要有起始信号,终止信号,应答,还有检测应答函数。 所以主机也要写检测应答函数(从机集成了不用关心,即使你想了解也和主机的检测应答函数一样,只是它转成从机了)


u8 I2C_Wait_ACK()       /*主机应答函数,检测从机应答的*/

{

u8 tempTime=0;

I2C_SDA_IN();

I2C_SDA_H;

delay_us(1);

I2C_SCL_H;

delay_us(1);


while(GPIO_ReadInputDataBit(GPIO_I2C,I2C_SDA))    /*GPIO_ReadInputBit读取GPIO_I2C:I2C_SDA的状态*/

{

tempTime++;

if(tempTime>250)

{

I2C_Stop();

return 1;

}

I2C_SCL_L;

return 0;

}                                                                       //前期主机释放SDA, SCL总线




void I2C_Send_Byte(u8 txd)     /*发送一个字节的数据: txd*/·

{

u8 i=0;

I2C_SDA_OUT();     //SDA

I2C_SCL_L;


for(i=0;i<8;i++)

{

if((txd&0x80)>0)    /*这里的发送与0,1电平的发送没什么区别。SCL高电平保持一段时间,SDA则传到从机哪里了*/

{

I2C_SDA_H;

}

else

{

I2C_SDA_L;

}

txd<<=1;          

I2C_SCL_H;

delay_us(2);

I2C_SCL_L;

delay_us(2);

}

}



u8  I2C_Read_Byte(u8 ack)    /*读取一个字节的数据*/

{

u8 i=0,receive=0;

I2C_SDA_IN();       //SDA


for(i=0;i<8;i++)

{

I2C_SCL_L;

delay_us(2);

I2C_SCL_H;

receive<<=1;   

if(GPIO_ReadInputDataBit(GPIO_I2C,I2C_SDA))   //¶ÁÈ¡I/O״̬µÄº¯Êý

{

receive++;   

}

delay_us(1);

}

if(ack==0)    //ackΪ0´ú±í·ÇÓ¦´ð

{

I2C_NAck();

}

else

{

I2C_Ack();

}

return receive;


这里使用并没有用到主机向从机读写数据的具体用法。下面贴代码AT24C02的使用,这个就是典型的I2C芯片


I2C.c文件


#include "I2C.h" 


void I2C_init()     

{

GPIO_InitTypeDef GPIO_InitStructure;        


RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);


/* GPIOµÄÅäÖà */

GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;    

GPIO_InitStructure.GPIO_Pin = I2C_SCL|I2C_SDA;                

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;        

GPIO_Init(GPIOB,&GPIO_InitStructure);      



I2C_SCL_H;       

I2C_SDA_H;

}



void I2C_SDA_OUT()   

{

GPIO_InitTypeDef GPIO_InitStructure;      

 /* GPIOµÄÅäÖà */

GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;      

GPIO_InitStructure.GPIO_Pin = I2C_SDA;              

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;      

GPIO_Init(GPIOB,&GPIO_InitStructure);   

}



void I2C_SDA_IN()    

{

GPIO_InitTypeDef GPIO_InitStructure;        


 //GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;  

GPIO_InitStructure.GPIO_Pin = I2C_SDA;                 //Ö»ÅäÖÃSDA¹Ü½Å£¬SCLÊÇͬ²½Ïß

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;       //ÅäÖÃģʽ(ÊäÈëÉÏÀ­)

GPIO_Init(GPIOB,&GPIO_InitStructure);      //µ÷ÓÃGPIO³õʼ»¯ÅäÖú¯Êý

}

/** GPIOÓëI/O²»Í¬¾ÍÊÇÒªÅäÖÃģʽ£¬ËùÒÔI/O²»ÊÇËæ±ãÓà **/





/********************    ¸÷ÖÖÓ¦´ðº¯Êý    **********************************/

void I2C_Start()     

{

I2C_SDA_OUT();   


I2C_SDA_H;

I2C_SCL_H;

delay_us(5);

I2C_SDA_L;

 delay_us(6);

I2C_SCL_L;

}



void I2C_Stop()    

{

I2C_SDA_OUT();  


I2C_SCL_L;

I2C_SDA_L;

I2C_SCL_H;

delay_us(6);

I2C_SDA_H;

 delay_us(6);

}



void I2C_Ack()  

{

I2C_SCL_L;

 I2C_SDA_OUT();  

I2C_SDA_L;

delay_us(2);

I2C_SCL_H;

delay_us(5);

I2C_SCL_L;          

}



void I2C_NAck()  

{

I2C_SCL_L;

 I2C_SDA_OUT(); 

I2C_SDA_H;

delay_us(2);

I2C_SCL_H;

delay_us(5);

I2C_SCL_L;         

}



u8 I2C_Wait_ACK()       

{

u8 tempTime=0;

I2C_SDA_IN();

I2C_SDA_H;

delay_us(1);

I2C_SCL_H;

delay_us(1);     


while(GPIO_ReadInputDataBit(GPIO_I2C,I2C_SDA))  

{

tempTime++;

if(tempTime>250)          

{

I2C_Stop();

return 1;

}

I2C_SCL_L;

return 0;

}



void I2C_Send_Byte(u8 txd)     

{

u8 i=0;

I2C_SDA_OUT();  

I2C_SCL_L;


for(i=0;i<8;i++)

{

if((txd&0x80)>0)  

{

I2C_SDA_H;

}

else

{

I2C_SDA_L;

}

txd<<=1;         

I2C_SCL_H;

delay_us(2);

I2C_SCL_L;

delay_us(2);

}

}



u8 I2C_Read_Byte(u8 ack)    {

u8 i=0,receive=0;

I2C_SDA_IN();     


for(i=0;i<8;i++)

{

I2C_SCL_L;

delay_us(2);

I2C_SCL_H;      

receive<<=1;   //7´Î

if(GPIO_ReadInputDataBit(GPIO_I2C,I2C_SDA))  

{

receive++;   

}

delay_us(1);

}

if(ack==0)    //ackΪ0´ú±í·ÇÓ¦´ð

{

I2C_NAck();

}

else

{

I2C_Ack();

}

return receive;

}




AT24Cxx.c文件


#include  "AT24Cxx.h"

//¼Çס£¬AT24CxxҪʹÓõ½I2C¡£ËùÒÔÒªÔÚÆäAT24Cxx.hÖÐÒýÈëI2C.h



u8 AT24Cxx_ReadOneByte(u16 addr)       //¶ÁÒ»¸ö×Ö½Ú¡£ ´ÓÄĸöµØÖ·¿ªÊ¼¶ÁÊý¾Ý

{

u8 temp;

I2C_Start(); 

if(EE_TYPE>AT24C16)

{

I2C_Send_Byte(0xa0);

I2C_Wait_ACK();

I2C_Send_Byte(addr>>8);       //·¢ËÍÊý¾ÝµØÖ·¸ßλ

}

else

{

I2C_Send_Byte(0xa0+((addr/256)<<1));    //Æ÷¼þµØÖ·+Êý¾ÝµØÖ·

}

I2C_Wait_ACK();

I2C_Send_Byte(addr%256);   //Ë«×Ö½ÚÊǶÁÈ¡µØÖ·µÍλ

I2C_Wait_ACK();


I2C_Start();    //ÆðʼÐźÅ

I2C_Send_Byte(0xa1);     //´Ó»úµØÖ·+1

I2C_Wait_ACK();


temp=I2C_Read_Byte(0);    //0´ú±í·ÇÓ¦´ð£¬1´ú±íÓ¦´ð

I2C_NAck();

I2C_Stop();

return temp;

}



void AT24Cxx_WriteOneByte(u16 addr,u8 dt)      //дһ¸ö×Ö½Ú

{

 I2C_Start(); 

if(EE_TYPE>AT24C16)

{

I2C_Send_Byte(0xa0);

I2C_Wait_ACK();

I2C_Send_Byte(addr>>8);       //Æ÷¼þµØÖ·£¬AT24C16ÒÔÉÏǰ8λ»áÌîÂú

}

else

{

I2C_Send_Byte(0xa0+((addr/256)<<1));    //Æ÷¼þµØÖ·+Êý¾ÝµØÖ·

}

I2C_Wait_ACK();

I2C_Send_Byte(addr%256);   //¶ÁÈ¡µØÖ·

I2C_Wait_ACK();


I2C_Send_Byte(dt);

I2C_Wait_ACK();

I2C_Stop();

delay_ms(10); 

}



u16 AT24Cxx_ReadTwoByte(u16 addr)       //¶Á2¸ö×Ö½Ú¡£ ´ÓÄĸöµØÖ·¿ªÊ¼¶ÁÊý¾Ý

{

u16 temp=0;

I2C_Start(); 

if(EE_TYPE>AT24C16)

{

I2C_Send_Byte(0xa0);

I2C_Wait_ACK();

I2C_Send_Byte(addr>>8);       //Æ÷¼þµØÖ·£¬AT24C16ÒÔÉÏǰ8λ»áÌîÂú

}

else

{

I2C_Send_Byte(0xa0+((addr/256)<<1));    //Æ÷¼þµØÖ·+Êý¾ÝµØÖ·

}

I2C_Wait_ACK();

I2C_Send_Byte(addr%256);   //¶ÁÈ¡µØÖ·

I2C_Wait_ACK();


I2C_Start();    //ÆðʼÐźÅ

I2C_Send_Byte(0xa1);     //´Ó»úµØÖ·+1

I2C_Wait_ACK();


temp=I2C_Read_Byte(1);    //0´ú±í·ÇÓ¦´ð£¬1´ú±íÓ¦´ð

temp<<=8;

temp|=I2C_Read_Byte(0);    //0´ú±í·ÇÓ¦´ð£¬1´ú±íÓ¦´ð.¶ÁÁË2¸ö×Ö½Ú£¬²»ÓÃÓ¦´ðÁË

I2C_Stop();

return temp;

}



void AT24Cxx_WriteTwoByte(u16 addr,u16 dt)      //д2¸ö×Ö½Ú

{

 I2C_Start(); 

if(EE_TYPE>AT24C16)

{

I2C_Send_Byte(0xa0);

I2C_Wait_ACK();

I2C_Send_Byte(addr>>8);       //Æ÷¼þµØÖ·£¬AT24C16ÒÔÉÏǰ8λ»áÌîÂú

}

else

{

I2C_Send_Byte(0xa0+((addr/256)<<1));    //Æ÷¼þµØÖ·+Êý¾ÝµØÖ·

}

I2C_Wait_ACK();

I2C_Send_Byte(addr%256);   //¶ÁÈ¡µØÖ·

I2C_Wait_ACK();


I2C_Send_Byte(dt>>8);    //ÏÈд¸ß×Ö½Ú

I2C_Wait_ACK();

I2C_Send_Byte(dt&0xff);    //ÏÈдµÍ×Ö½Ú

I2C_Wait_ACK();

I2C_Stop();

delay_ms(10); 

}


main.c文件


#include "public.h"

#include "Systick.h"

#include "AT24Cxx.h"

#include "printf.h" 



int main()

{

u16 wdata=16;

u16 value=0;

printf_init();    //³õʼ»¯

 I2C_init();

 delay_ms(1);      //I2CÒª½øÐÐÉϵ磬עÒâ¿´ÊֲᣬÓÐÎÊÌâÒ²¿ÉÒÔ¿´¿´


AT24Cxx_WriteOneByte(0,wdata);

printf("%d ",wdata);


value=(AT24Cxx_ReadOneByte(0)+1);       //µ«Êµ¼Ê³öÏÖµÄÎÊÌâÊÇ255+1Õâ¸öÖµ¡£½©Ó²

    printf("%d ",value); 

 while(1);

}


推荐阅读

史海拾趣

Advance Tapes公司的发展小趣事

为了进一步扩大市场份额,Advance Tapes公司开始积极参与国际展会和交流活动,向全球客户展示其优质的产品和技术实力。同时,公司还加大了对品牌建设的投入,通过广告宣传、赞助活动等方式提升品牌知名度和美誉度。这些努力使得Advance Tapes的胶带产品逐渐在国际市场上占据了一席之地。

AD Semiconductor公司的发展小趣事

面对日益复杂的市场环境和客户需求,Advance Tapes公司积极寻求与其他企业的合作机会。通过与上游原材料供应商建立长期稳定的合作关系,确保了原材料的稳定供应和质量保障;通过与下游电子制造企业的深度合作,共同开发定制化胶带产品,满足了客户的特殊需求。这些合作不仅提升了Advance Tapes的市场竞争力,也促进了整个电子产业链的健康发展。

CSB公司的发展小趣事

在竞争激烈的电子行业中,成本控制是企业生存和发展的关键。CSB公司深知这一点,因此在供应链管理和成本控制方面下足了功夫。公司通过与供应商建立长期合作关系、优化采购流程、提高生产效率等方式,有效降低了生产成本。同时,公司还注重库存管理和物流配送的优化,确保产品能够及时、准确地送达客户手中。

AXSEM公司的发展小趣事

CSB公司深知人才是企业发展的核心竞争力。因此,公司一直注重人才培养和团队建设。通过建立完善的培训体系、提供丰富的职业发展机会、营造积极向上的企业文化等方式,CSB公司吸引了一批批优秀的人才加入。这些人才为公司的发展注入了新的活力,推动了公司在技术、市场、管理等方面的不断进步。

B+B SmartWorx公司的发展小趣事

B+B SmartWorx的前身是B&B Electronics,一个在1981年成立的公司,起初主要为无线和有线网络提供设备连接解决方案。随着技术的快速发展,物联网和M2M连接的需求日益增长,B&B Electronics意识到必须进行创新以应对市场的变化。于是,公司开始致力于开发“边缘智能”技术,使网络连接设备更加智能、自主和响应迅速。这一创新转型使得公司在物联网行业中崭露头角,并在2015年决定将公司名称更改为B+B SmartWorx,以更好地反映其业务重心和技术方向。

AMOTECH(阿莫泰克)公司的发展小趣事

进入21世纪,AMOTECH继续深耕电子领域,特别是在压敏电阻技术上取得了重大突破。2003年,公司的压敏电阻产品被产业资源部评为世界一流产品,这一成就不仅提升了AMOTECH在全球电子行业中的地位,也为其后续发展奠定了坚实基础。同年,公司还成功在韩国安全商协会自动报价首次公开募股,为公司的进一步扩张提供了资金支持。

问答坊 | AI 解惑

楼宇对讲系统

楼宇对讲系统是一种用于高层住宅、公寓大厦内外,户间信息传递,防盗门控制和在紧急情况下住户向楼宇值班室报警的设备。它以功能齐全、性能可靠、其容量大、造型美观、安装使用方便而深受广大用户欢迎,并且也在安全生活小区中得到了广泛的应用。 ...…

查看全部问答>

$(_COMMONOAKROOT)\lib\$(_CPUINDPATH)\LayoutManager.lib \这个是什么意思 ?

$(_COMMONOAKROOT)\\lib\\$(_CPUINDPATH)\\LayoutManager.lib \\这个路径在sources文件中用到了 对应的实际路径是C:\\WINCE600\\PUBLIC\\COMMON\\OAK\\LIB\\ARMV4I\\RETAIL。也就是说 $(_COMMONOAKROOT)对应C:\\WINCE600\\PUBLIC\\COMMON\\OAK\\ ...…

查看全部问答>

如何在控制面板加上一个背光控制的高级选项?

ADVBACKLIGHT,这个文件源代码是用在控制面板里的,请问怎么样可以使控制面板的背光设置里有一个高级选项?因为默认是没有的!…

查看全部问答>

简单winCE例程开发,有偿。有愿意做的朋友进

本人现需几个winCE的示例程序,要求EVC4.0开发,内容涉及绘图、文字显示、串口通讯以及打印几个方面,运行环境WinCE,CPU ARM4/ARM41。 若有掌握相关技术的朋友感兴趣,请加QQ:575336,工作日白天9:00-17:00在线。 开发周期及费用详谈。…

查看全部问答>

哪位大虾有比较好的KEIL或C51的视频教程呀?

哪位大虾有比较好的KEIL,C51视频教程呀,请把网址告诉小弟。万分感激。。。。 帮小弟顶下也有分噢!!…

查看全部问答>

我是该版块的新人,有几个基础的问题想请各位前辈指点指点,顶者有分

我的基本情况 1> 对 Visual studio 2003/2005 中vb.net c#.net 相对熟悉    (因为开发过.Net 的项目所以数据也还算熟悉) 2> 熟悉 JSDK2.0 DOS下的java 开发, 也用JBuilder 8.0以下版本开发一些东西,因此对JAVA 算是有一定的基础 ...…

查看全部问答>

Xilinx公司的XC9500XL有什么好的教程书籍可以推荐

产品需要应用Xilinx公司的XC9500XL系列产品, 请问各位大虾有什么好的教程书籍可以推荐?要精通级的书…

查看全部问答>

【转】超级STM32粉丝的DIY大作(附图)

相关链接:http://blog.ednchina.com/atom6037/187072/message.aspx…

查看全部问答>

我终于回来了,不容易啊

本帖最后由 paulhyde 于 2014-9-15 09:27 编辑 前段时间骨折了,做了个手术,现在还在恢复,不方便上网,一直没来坛子,电子大赛也放下了,考研也落下了,辛昕的活动也没参加完,不好意思啊,现在终于可以稍微活动了,唉,还是正常人好,以后要好 ...…

查看全部问答>

LM3S811使用手记1——GPIO

话说拿到板子有那么一段日子了。一直以来由于这样那样的原因,冷落了这LM3S811评估板呢。今天终于都把LM3S811翻了出来。感觉跟之前用过的STM32类似。都是在已有库的基础上调用函数就可以了。不过,一开始接触LM3S811,还得认真研究下库函数的结构, ...…

查看全部问答>