I2C 是很常见的一种总线协议,I2C 是飞利浦公司在80年代开发的两线式串行总线,用于连接微控制器及其外围设备。IIC属于半双工同步通信方式。I2C 使用两条线在主控制器和从机之间进行数据通信。
I2C 总线在物理连接上非常简单,分别由SDA(双向串行数据线)和SCL(串行时钟线)及上拉电阻组成。通信原理是通过对SCL和SDA线高低电平时序的控制,来产生I2C总线协议所需要的信号进行数据的传递。I2C通信方式为半双工,只有一根SDA线,同一时间只可以单向通信。每个连接到总线的设备都有一个独立的地址,主机可以利用这个地址进行不同设备直接的访问。下图是I2C总线系统结构具体电路原理图。
硬件IIC:对应芯片上的IIC外设,有相对应的IIC驱动电路,其所使用的IIC管教也是专用的;
软件IIC:一般是用GPIO管教,用软件控制管脚状态以及模拟IIC通信波形;
区别:
1. 硬件IIC的效率要远高于软件的,而软件IIC不受引脚限制,接口比较灵活。
2. 软件IIC是通过GPIO,软件模拟寄存器的工作方式,而硬件IIC是直接调用内部寄存器进行配置。
如何区分?
1.硬件IIC用法复杂,模拟IIC流程更加清楚
2.硬件IIC速度比模拟快,并且可以用DMA
3.模拟IIC可以在任何管脚上,硬件IIC在固定管脚上
#define SCLK_Set() GPIO_SetBits(GPIOB, GPIO_Pin_0) //PB0(SCL)输出高
#define SCLK_CLr() GPIO_ResetBits(GPIOB, GPIO_Pin_0) //PB0(SCL)输出低
#define SDIN_Set() GPIO_SetBits(GPIOB, GPIO_Pin_1) //PB1(SDA)输出高
#define SDIN_CLr() GPIO_ResetBits(GPIOB, GPIO_Pin_1) //PB1(SDA)输出低
#define READ_SDIN() GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) //读取PB1(SDA)电平
#define IIC_ACK 0 //应答
#define IIC_NO_ACK 1 //不应答
//对应模拟I2C引脚IO口初始化
static void OLED_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure; //定义一个GPIO_InitTypeDef类型的结构体
RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB, ENABLE); //打开GPIOC的外设时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1; //选择控制的引脚
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD; //设置为通用开漏输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //设置输出速率为50MHz
GPIO_Init(GPIOB,&GPIO_InitStructure); //调用库函数初始化GPIOA
SCLK_Set(); //设PC2(SCL)为高电平
SDIN_Set(); //设PC3(SDA)为高电平
}
static void IIC_Start(void)
{
SCLK_Set(); //时钟线置高
SDIN_Set(); //数据线置高
delay_us(1);
SDIN_CLr(); //数据线置低,数据发送从高到低跳变
delay_us(1);
SCLK_CLr(); //时钟线置低
delay_us(1);
}
static void OLED_IIC_Stop(void)
{
//时钟线高时,数据从低到高跳变
SDIN_CLr(); //数据线置低
delay_us(1);
SCLK_Set(); //时钟线置高
delay_us(1);
SDIN_Set(); //数据线置高
delay_us(1);
}
//模拟I2C读取从机应答信号
static unsigned char IIC_Wait_Ack(void)
{
unsigned char ack;
SCLK_CLr(); //时钟线置低
delay_us(1);
SDIN_Set(); //信号线置高
delay_us(1);
SCLK_Set(); //时钟线置高
delay_us(1);
if(READ_SDIN()) //读取SDA的电平
ack = IIC_NO_ACK; //如果为1,则从机没有应答
else
ack = IIC_ACK; //如果为0,则从机应答
SCLK_CLr();//时钟线置低
delay_us(1);
return ack; //返回读取到的应答信息
}
//I2C读取一个字节Byte
static void Write_IIC_Byte(unsigned char IIC_Byte)
{
unsigned char i; //定义变量
for(i=0;i<8;i++) //for循环8次
{
SCLK_CLr(); //时钟线置低,为传输数据做准备
delay_us(1);
if(IIC_Byte & 0x80) //读取最高位
SDIN_Set(); //最高位为1
else
SDIN_CLr(); //最高位为0
IIC_Byte <<= 1; //数据左移1位
delay_us(1);
SCLK_Set(); //时钟线置高,产生上升沿,发送数据
delay_us(1);
}
SCLK_CLr(); //时钟线置低
delay_us(1);
while(IIC_Wait_Ack());//从机应答
}
//I2C写命令
static void Write_IIC_Command(unsigned char IIC_Command)
{
OLED_IIC_Start();
Write_IIC_Byte(0x78); //写入从机地址,SD0 = 0
Write_IIC_Byte(0x00); //写入命令
Write_IIC_Byte(IIC_Command);//数据
OLED_IIC_Stop(); //发送停止信号
}
static void Write_IIC_Data(unsigned char IIC_Data)
{
OLED_IIC_Start();
Write_IIC_Byte(0x78); //写入从机地址,SD0 = 0
Write_IIC_Byte(0x40); //写入数据
Write_IIC_Byte(IIC_Data);//数据
OLED_IIC_Stop(); //发送停止信号
}
STM32 I2C的结构体
typedef struct
{
uint32_t I2C_ClockSpeed; //设置SCL时钟频率,不得高于4000000
uint16_t I2C_Mode; //指定I2C工作模式,可选I2C模式及SMBUS模式
uint16_t I2C_DutyCycle; //时钟占空比,可选low/high = 2:0 或 16:9
uint16_t I2C_OwnAddress1; //自身的I2C设备地址
uint16_t I2C_Ack; //使能或关闭相应,一般是使能
uint16_t I2C_AcknowledgedAddress; //指定地址长度,可为7或10
}I2C_InitTypeDef;
STM32 I2C常用库函数
//配置自身设备地址2
void I2C_OwnAddress2Config(I2C_TypeDef* I2Cx, uint8_t Address);
//发送设备地址
void I2C_Send7bitAddress(I2C_TypeDef* I2Cx, uint8_t Address, uint8_t I2C_Direction);
//接收数据
uint8_t I2C_ReceiveData(I2C_TypeDef* I2Cx);
//停止接收
void I2C_AcknowledgeConfig(I2C_TypeDef* I2Cx, FunctionalState NewState);
//外设开始正常工作
void I2C_Cmd(I2C_TypeDef* I2Cx, FunctionalState NewState);
I2C1 使能配置
void I2C_Configuration(void)
{
I2C_InitTypeDef I2C_InitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB , ENABLE); //使能GPIOB时钟
RCC_APB1PeriphClockCmd( RCC_APB1Periph_I2C1, ENABLE ); //使能I2C1时钟
//PB6 --SCL ;PB7 --SDA
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD; //复用开漏
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7; //I2C1 SCL:PB6 SDA: PB7
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure); //初始化GPIOB结构体
I2C_DeInit(I2C1); //初始化I2C1
I2C_InitStructure.I2C_Ack = I2C_Ack_Enable; //使能应答
I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit; //7位地址
I2C_InitStructure.I2C_ClockSpeed = 400000 ; //时钟速度 400K
I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2 ; //2分频 16:9
I2C_InitStructure.I2C_Mode = I2C_Mode_I2C; //I2C模式
I2C_InitStructure.I2C_OwnAddress1 = 0X30 ; //主机地址 随便写 不影响
I2C_Init(I2C1,&I2C_InitStructure ); //初始化结构体
I2C_Cmd(I2C1,ENABLE); //使能I2C1
}
I2C写一个字节
//I2C写一个字节
void I2C_WriteByte(uint8_t addr,uint8_t data)
{
while (I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY)); //检查I2C总线是否繁忙
I2C_GenerateSTART(I2C1, ENABLE); //开启I2C1
while( !I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT)); //EV5,主模式
I2C_Send7bitAddress(I2C1,OLED_ADDRESS, I2C_Direction_Transmitter); //发送器件地址
while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));
I2C_SendData(I2C1, addr); //寄存器地址
while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTING));
I2C_SendData(I2C1, data); //发送数据
while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTING));
I2C_GenerateSTOP( I2C1, ENABLE); //关闭I2C总线
}
I2C写命令
//写命令
void WriteCmd(unsigned char I2C_Command)
{
I2C_WriteByte(0X00,I2C_Command);
}
I2C写数据
//写数据
void WriteData(unsigned char I2C_Data)
{
I2C_WriteByte(0x40,I2C_Data);
}