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