STM32中I2C协议时序和使用

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

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

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

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

STM32中I2C协议时序和使用_第1张图片

c. 数据的通讯方式

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

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

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

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

       4. I2C字节的传送与应答

每一位字节必须保持8位长度。数据传送时,先传送最高位MSB, 每一个被传送的字节最后必须跟随一个应答位(及一帧9位,应答位要用于说明一个字节已经传完,I2C总线重新置空闲态STM32中I2C协议时序和使用_第2张图片

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

STM32中I2C协议时序和使用_第3张图片

 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写数据,读数据驱动

STM32中I2C协议时序和使用_第4张图片

由图可知,发送的数据(地址和有效数据)不是只使用高低电平就可以的,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_I2CI2C_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);
}

你可能感兴趣的:(STM32)