一、内部集成电路概述
基本概念
内部集成电路(Inter Integrated Circuit)的简称叫做IIC或者I2C,是一种简单的、半双工同步通信的串行通信接口,IIC总线是上世纪80年代(1982年)由飞利浦公司设计出来,当时的目的是为了给MCU和外围芯片提供更简单的交互方式。
引脚说明
IIC总线只需要两根引脚就可以实现通信,一根是数据线(SDA Serial Data),另一根是时钟线(SCL Serial Clock),所有通过IIC接口通信的外围器件都挂载在IIC总线上,通过这种机制就可以实现多机通信。
可以看到,外围器件的时钟线和数据线都是挂载在IIC总线(由主控芯片提供),并且在空闲状态下所有器件的时钟线(SCL)和数据线(SDA)都被总线的上拉电阻拉高,这样就可以把SDA引脚和SCL引脚设置为开漏模式即可,好处是防止短路。
思考:如果IIC总线上挂载了多个外围器件,如何与某一个器件进行单独通信? 器件地址
通信速率
可以看到IIC总线支持不同的通信速率,但是一般常用的标准速率100KHZ,但是有的外围器件可以支持高达400KHZ的通信速率,而由于IIC总线是半双工通信,所以同一时刻只能接收或者发送,也就是说IIC总线一般是为了控制,不适合作为大量数据传输的接口。
通信过程
(1)空闲状态
(2)开始信号
(3)数据传输
(4)应答信号
(5)停止信号
器件地址
程序设计
由于MCU内部只有3个IIC接口,所以想要使用IIC接口控制传感器,就必须使用IIC接口对应的引脚,把引脚复用为IIC功能,并且对硬件IIC进行配置和初始化,为了提高可移植性,用户也可以采用IO口模拟IIC时序的方案来控制传感器,该方案不受硬件的限制。
/************************************************************
利用IO口模拟IIC时序,需要使用2个IO口(SDA和SCL)
SCL时钟线只能由主器件进行控制,所以SCL引脚必须为输出模式
SDA数据线,在主器件发送数据时,SDA引脚为输出模式
SDA数据线,在主器件接收数据时,SDA引脚为输入模式
************************************************************/
#define SDA_SET(n) (n) ? GPIO_SetBits(GPIO?,GPIO_Pin_?) : GPIO_ResetBits(GPIO?,GPIO_Pin_?)
#define SCL_SET(n) (n) ? GPIO_SetBits(GPIO?,GPIO_Pin_?) : GPIO_ResetBits(GPIO?,GPIO_Pin_?)
#define SDA_READ GPIO_ReadInputDataBit(GPIO?,GPIO_Pin_?)
//SCL引脚设置
void IIC_SCLConfig(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
//打开GPIO端口的时钟
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIO?,ENABLE);
//SCL
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; //输出模式
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_?;
GPIO_Init(GPIO?, &GPIO_InitStructure);
}
//SDA引脚设置
void IIC_SDAConfig(GPIOMode_TypeDef GPIO_Mode)
{
GPIO_InitTypeDef GPIO_InitStructure;
//打开GPIO端口的时钟
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIO?,ENABLE);
//SCL
GPIO_InitStructure.GPIO_Mode = GPIO_Mode;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_?;
GPIO_Init(GPIO?, &GPIO_InitStructure);
}
//IIC的初始化
void IIC_Config(void)
{
//1.设置SDA和SCL为输出模式
IIC_SCLConfig();
IIC_SDAConfig(GPIO_Mode_OUT);
//2.确保SDA和SCL处于空闲状态
SDA_SET(1);
SCL_SET(1);
delay_us(5);
}
//IIC的开始信号
void IIC_Start(void)
{
//1.设置SDA引脚为输出模式
IIC_SDAConfig(GPIO_Mode_OUT);
//2.确保SDA和SCL处于空闲状态
SDA_SET(1);
SCL_SET(1);
delay_us(5);
//3.把SDA引脚电平拉低
SDA_SET(0);
delay_us(5);
//4.把SCL引脚电平拉低,此时准备数据
SCL_SET(0);
delay_us(5);
}
//IIC的发送字节
void IIC_SendByte(uint8_t Byte) //1011 0110
{
//1.设置SDA引脚为输出模式
IIC_SDAConfig(GPIO_Mode_OUT);
//2.把SCL引脚电平拉低,此时主机准备数据
SCL_SET(0);
delay_us(5);
//3.循环发送8bit,遵循MSB高位先出
for(i=0;i<8;i++)
{
//4.判断待发送的字节的最高位
if( Byte & 0x80 )
{
SDA_SET(1);
}
else
SDA_SET(0);
Byte <<= 1;
delay_us(5);
//5.把SCL电平拉高,此时从机读取bit
SCL_SET(1);
delay_us(5);
//6.把SCL引脚电平拉低,此时主机准备下一个bit
SCL_SET(0);
delay_us(5);
}
}
//判断从机是否应答
bool IIC_IsSlaveACK(void)
{
//1.设置SDA引脚为输入模式
IIC_SDAConfig(GPIO_Mode_IN);
//2.把SCL引脚电平拉低,此时为第9个脉冲的低电平,从机准备bit
SCL_SET(0);
delay_us(5);
//3.把SCL引脚电平拉高,此时为第9个脉冲的高电平,主机读取状态
SCL_SET(1);
delay_us(5);
//4.主机读取状态 1 表示未应答 0 表示已应答
if(SDA_READ)
return false;
else
return true;
}
//IIC读取字节
uint8_t IIC_ReadByte(void)
{
uint8_t i = 0,data = 0;
//1.设置SDA引脚为输入模式
IIC_SDAConfig(GPIO_Mode_IN);
//2.把SCL引脚电平拉低,此时从机准备数据
SCL_SET(0);
delay_us(5);
//3.循环读取8bit,遵循MSB高位先出
for (i = 0; i < 8; ++i)
{
//4.把SCL电平拉高,此时主机读取bit
SCL_SET(1);
delay_us(5);
//5.主机读取bit
data <<= 1;
data |= SDA_READ;
delay_us(5);
//6.把SCL引脚电平拉低,此时从机准备下一个bit数据
SCL_SET(0);
delay_us(5);
}
//7.返回结果
return data;
}
//ack=1 表示不应答 ack=0 表示要应答
void IIC_MasterACK(uint8_t ack)
{
//1.设置SDA引脚为输出模式
IIC_SDAConfig(GPIO_Mode_OUT);
//2.把SCL引脚电平拉低,此时主机准备
SCL_SET(0);
delay_us(5);
//3.判断ack的状态,从而对SDA操作
if(ack)
SDA_SET(1);
else
SDA_SET(0);
delay_us(5);
//4.把SCL电平拉高,此时从机读取bit
SCL_SET(1);
delay_us(5);
}
//IIC的停止信号
void IIC_Stop(void)
{
//1.设置SDA引脚为输出模式
IIC_SDAConfig(GPIO_Mode_OUT);
//2.设置SDA和SCL均为低电平
SDA_SET(0);
SCL_SET(0);
delay_us(5);
//3.把SCL电平拉高
SCL_SET(1);
delay_us(5);
//4.把SDA电平拉高
SDA_SET(1);
delay_us(5);
}
int main()
{
IIC_Config();
}
二、IC接口的0.96寸OLED屏
驱动芯片
器件地址