I2C总线

I2C只有两个双向信号线:SDA和SCL。总线采用漏极开路工艺,总线上一定要通过上拉电阻接正电源,当总线空闲时,总线均为高电平。例如下面的图片是STC单片机驱动I2C总线的CLED屏原理图,可以看到总线都被上拉电阻上拉(当然如果STC的IO做标准IO没有上拉电阻也可以,因为STC的标准IO本身有上拉电阻,如果是开漏模式则需要了)
STC单片机驱动I2C总线的CLED屏原理图
每个连到总线上的器件都有唯一的地址。总线必须由主器件控制,主器件产生SCL控制总线的传输方向,并产生起始和停止条件

  • 数据位的传输协议

I2C总线在进行数据传送时,SDA上的数据必须在SCL为高电平期间保持稳定,SCL为低电平的期间,SDA上的电平状态才允许变化

  • 字节传输的起始和停止的条件

起始条件,SCL为高电平,SDA从高电平变为低电平
停止条件,SCL为高电平,SDA从低电平变为高电平
接收器收到一个完整的字节数据后,可能需要完成一些其他工作无法立即接收下一个字节,这时接收器可以将SCL拉低,从而使主机处于等待状态,直到接收器准备接收好下一个字节时,再释放SCL为高电平

  • 数据传输格式

每个字节后跟1位响应位(即1帧共有9位)。
由于某种原因从机不对主机的寻址信号应答时(如从机正在进行实时性的处理工作),它必须将数据线置于高电平,而主机产生一个停止条件以结束总线的数据传送。
如果从机对主机进行了应答,但在数据传送一段时间后无法继续接收更多的数据是,从机可以通过对无法接收的第一个数据字节进行非应答来通知主机,主机则发出停止条件以结束数据传送。
当主机接收数据时,它收到最后一个数据字节后必须向从机发出一个结束传送的信号。这个信号是通过对从机的“非应答”来实现的。然后从机释放SDA线,以允许主机产生终止条件。

  • 数据帧格式

在起始信号后传送一个从机地址(7位),第8位是数据传送的方向位(R/T,0表示主机发送,1表示主机接收)。每次数据传送总是由主机产生的停止条件结束的。但是,如果主机希望继续占用总线进行新的数据传送,则可以不产生停止条件,再次发出起始条件对另一从机进行寻址

STM32 I2C源码

头文件 i2c.h

#ifndef I2C_H
#define I2C_H

void I2C_delay(void);
void delay5ms(void);

unsigned char I2C_Start(void);
void I2C_Stop(void);

void I2C_NoAck(void);
void I2C_Ack(void);
unsigned char I2C_WaitAck(void);

void I2C_SendByte(unsigned char data);
unsigned char I2C_ReadByte(void);


unsigned char I2CSendSingleData(unsigned char slaveAddress, unsigned char subAddress, unsigned char sendData);
unsigned char I2CSendDatas(unsigned char slaveAddress, unsigned char subAddress, const unsigned char* data_P,int dataCount);
unsigned char I2CReadSingleData(unsigned char slaveAddress, unsigned char subAddress);

#endif

源程序 i2c.c

/***
I2C通信协议,SCL为GPIO-B6,SDA为GPIO-B7
***/
#include "i2c.h"
#include "stm32f10x.h"

//I2C总线
#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  


/*******************************************************************************
* Function Name  : void I2C_delay(void) 
* Description    : 总线高低电平转换延时
* Input          : None
* Output         : None
* Return         : None
*******************************************************************************/
void I2C_delay(void){
    unsigned char i=5; //这里可以优化速度   ,经测试最低到5还能写入
    while(i) 
     i--;  
};

/*******************************************************************************
* Function Name  : void delay5ms(void); 
* Description    : 一次数据通信完成后的延时
* Input          : None
* Output         : None
* Return         : None
*******************************************************************************/
void delay5ms(void){
    int i=5000;  
    while(i) 
        i--; 
};

/*******************************************************************************
* Function Name  : unsigned char I2C_Start(void); 
* Description    : 主机发起开始条件
* Input          : None
* Output         : None
* Return         : 总线忙或出错返回0,否则返回1
*******************************************************************************/
unsigned char I2C_Start(void){
    SDA_H;
    SCL_H;
    I2C_delay();
    if(!SDA_read){
        return 0;   //SDA线为低电平则总线忙,退出
    };
    SDA_L;
    I2C_delay();
    if(SDA_read){
        return 0;   //SDA线为高电平则总线出错,退出 
    };
    SDA_L;
    I2C_delay();
    return 1;
};

/*******************************************************************************
* Function Name  : void I2C_Stop(void); 
* Description    : 主机发起结束
* Input          : None
* Output         : None
* Return         : None
*******************************************************************************/
void I2C_Stop(void){
    SCL_L;
    I2C_delay();
    SDA_L;
    I2C_delay();
    SCL_H;
    I2C_delay();
    SDA_H;
    I2C_delay();
};

/*******************************************************************************
* Function Name  : void I2C_NoAck(void); 
* Description    : 主机不应答
* Input          : None
* Output         : None
* Return         : None
*******************************************************************************/
void I2C_NoAck(void){
    SCL_L;
    I2C_delay();
    SDA_H;
    I2C_delay();
    SCL_H;
    I2C_delay();
    SCL_L;
    I2C_delay();
};

/*******************************************************************************
* Function Name  : void I2C_Ack(void); 
* Description    : 主机应答
* Input          : None
* Output         : None
* Return         : None
*******************************************************************************/
void I2C_Ack(void){
    SCL_L;
    I2C_delay();
    SDA_L;
    I2C_delay();
    SCL_H;
    I2C_delay();
    SCL_L;
    I2C_delay();
};

/*******************************************************************************
* Function Name  : unsigned char I2C_WaitAck(void); 
* Description    : 主机等待应答
* Input          : None
* Output         : None
* Return         : 接收到从机应答返回1,否则返回0
*******************************************************************************/
unsigned char I2C_WaitAck(void){
    SCL_L;
    I2C_delay();
    SDA_H;          
    I2C_delay();
    SCL_H;
    I2C_delay();
    if(SDA_read)
    {
      SCL_L;
      I2C_delay(); 
      return 0;
    }
    SCL_L;
    I2C_delay();
    return 1;
};

/*******************************************************************************
* Function Name  : void I2C_SendByte(unsigned char sendData); 
* Description    : 在总线上发送一个字节
* Input          : sendData为发送的字节
* Output         : None
* Return         : None
*******************************************************************************/
void I2C_SendByte(unsigned char sendData){
    unsigned char i=8;
    while(i--){
        SCL_L;
        I2C_delay();
        if(sendData&0x80)
            SDA_H;  
        else 
            SDA_L;   
        sendData<<=1;
        I2C_delay();
        SCL_H;
        I2C_delay();
    }
    SCL_L;
};

/*******************************************************************************
* Function Name  : unsigned char I2C_ReadByte(void); 
* Description    : 在总线上读取一个字节
* Input          : None
* Output         : None
* Return         : 返回读取到的字节
*******************************************************************************/
unsigned char I2C_ReadByte(void){
    unsigned char i=8;
    unsigned char 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;
};

/*******************************************************************************
* Function Name  : unsigned char I2CSendSingleData(unsigned char slaveAddress, unsigned char subAddress, unsigned char sendData); 
* Description    : 在总线上向从机发送一个字节
* Input          : slaveAddress为从机地址,subAddress为从机子地址,sendData为发送的字节
* Output         : None
* Return         : 发送成功返回1,否则返回0
*******************************************************************************/
unsigned char I2CSendSingleData(unsigned char slaveAddress, unsigned char subAddress, unsigned char sendData){
    if(!I2C_Start())
        return 0;
    I2C_SendByte(slaveAddress); //发送器件地址
    if(!I2C_WaitAck()){
        I2C_Stop();
        return 0;
    };
    I2C_SendByte(subAddress); //发送器件子地址
    if(!I2C_WaitAck()){
        I2C_Stop();
        return 0;
    };
    I2C_SendByte(sendData); //发送数据
    if(!I2C_WaitAck()){
        I2C_Stop();
        return 0;
    };
    I2C_Stop(); //结束总线
    delay5ms();
    return 1;
};

/*******************************************************************************
* Function Name  : unsigned char I2CSendDatas(unsigned char slaveAddress, unsigned char subAddress, const unsigned char* data_P,int dataCount); 
* Description    : 在总线上向从机发送一串字节
* Input          : slaveAddress为从机地址,subAddress为从机子地址,data_P为发送的数组的地址,dataCount为要发送的数据长度
* Output         : None
* Return         : 发送成功返回1,否则返回0
*******************************************************************************/
unsigned char I2CSendDatas(unsigned char slaveAddress, unsigned char subAddress, const unsigned char* data_P,int dataCount){
    int i;
    if(!I2C_Start())
        return 0;
    I2C_SendByte(slaveAddress); //发送器件地址
    if(!I2C_WaitAck()){
        I2C_Stop(); 
        return 0;
    }
    I2C_SendByte(subAddress); //发送器件子地址
    if(!I2C_WaitAck()){
        I2C_Stop();
        return 0;
    };
    for(i=0; i<dataCount; i++){
        I2C_SendByte(*data_P); //发送数据
        if(!I2C_WaitAck()){
            I2C_Stop();
            return 0;
        };
        data_P++;
    }
    I2C_Stop(); //结束总线
    delay5ms();
    return 1;
};

/*******************************************************************************
* Function Name  : unsigned char I2CReadSingleData(unsigned char slaveAddress, unsigned char subAddress, unsigned char* readData) 
* Description    : 在总线上向从机读取一个字节
* Input          : slaveAddress为从机地址,subAddress为从机子地址,readData为读出数据存储的地址
* Output         : None
* Return         : 发送成功返回1,否则返回0
*******************************************************************************/
unsigned char I2CReadSingleData(unsigned char slaveAddress, unsigned char subAddress, unsigned char* readData){     
    if(!I2C_Start())
        return 0;
    I2C_SendByte(slaveAddress); //I2C_SendByte(((REG_Address & 0x0700) >>7) | REG_Address & 0xFFFE);//设置高起始地址+器件地址 
    if(!I2C_WaitAck()){
        I2C_Stop(); 
        return 0;
    }
    I2C_SendByte((u8) subAddress);   //设置低起始地址      
    if(!I2C_WaitAck()){
        I2C_Stop();
        return 0;
    }
    I2C_Start();
    I2C_SendByte(slaveAddress+1);
    if(!I2C_WaitAck()){
        I2C_Stop();
        return 0;
    }
    *readData= I2C_ReadByte();
    I2C_NoAck();
    I2C_Stop();
    return 1;
};

当然还要对GPIO进行配置

void IO_Config(void){
    GPIO_InitTypeDef GPIO_InitStructure;
    //GPIOB时钟初始化
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB/* | RCC_APB2Periph_GPIOD*/ , ENABLE);   
    //SCL初始化
    GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_6;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;         
    GPIO_Init(GPIOB, &GPIO_InitStructure);
    //SDA初始化
    GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_7;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;        //开漏
    GPIO_Init(GPIOB, &GPIO_InitStructure);               
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值