八、软件I2C
1.简介
I2C总线是由Philips公司开发的一种简单、双向二线制同步串行总线。它只需要两根线即可在连接于总线上的器件之间传送信息。
两根通信线:SCL(Serial Clock)、SDA(Serial Data) 同步,半双工
设备的SCL和SDA均要配置成开漏输出模式
SCL和SDA各添加一个上拉电阻,阻值一般为4.7KΩ左右
2.时序基本单元
起始条件:SCL高电平期间,SDA从高电平切换到低电平
终止条件:SCL高电平期间,SDA从低电平切换到高电平
发送一个字节:SCL低电平期间,主机将数据位依次放到SDA线上(高位先行),然后释放SCL,从机将在SCL高电平期间读取数据位,所以SCL高电平期间SDA不允许有数据变化,依次循环上述过程8次,即可发送一个字节
接收一个字节:SCL低电平期间,从机将数据位依次放到SDA线上(高位先行),然后释放SCL,主机将在SCL高电平期间读取数据位,所以SCL高电平期间SDA不允许有数据变化,依次循环上述过程8次,即可接收一个字节(主机在接收之前,需要释放SDA)
发送应答:主机在接收完一个字节之后,在下一个时钟发送一位数据,数据0表示应答,数据1表示非应答
接收应答:主机在发送完一个字节之后,在下一个时钟接收一位数据,判断从机是否应答,数据0表示应答,数据1表示非应答(主机在接收之前,需要释放SDA)
3.通信时序
对于指定设备(Slave Address),在指定地址(Reg Address)下,写入指定数据(Data)
对于指定设备(Slave Address),在当前地址指针指示的地址下,读取从机数据(Data)
对于指定设备(Slave Address),在指定地址(Reg Address)下,读取从机数据(Data)
4.软件I2C读取EEPROM代码实现
使用两个IO口按照I2C协议输出的信号来作为SCL和SDA通信线,具体代码如下:
#define EEPROM_I2C_SCL_PIN GPIO_Pin_2 /* 连接到SCL时钟线的GPIO */
#define EEPROM_I2C_SDA_PIN GPIO_Pin_3 /* 连接到SDA数据线的GPIO */
//控制SCL输出高低电平
void MyI2C_W_SCL(uint8_t BitValue)
{
GPIO_WriteBit(GPIOA, EEPROM_I2C_SCL_PIN, (BitAction)BitValue);
Delay_us(10);
}
//控制SDA输出高低电平
void MyI2C_W_SDA(uint8_t BitValue)
{
GPIO_WriteBit(GPIOA, EEPROM_I2C_SDA_PIN, (BitAction)BitValue);
Delay_us(10);
}
//读取SDA此时电平
uint8_t MyI2C_R_SDA(void)
{
uint8_t BitValue;
BitValue = GPIO_ReadInputDataBit(GPIOA, EEPROM_I2C_SDA_PIN);
Delay_us(10);
return BitValue;
}
//初始化GPIO
void MyI2C_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD; //开漏输出
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2 | GPIO_Pin_3;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_SetBits(GPIOA, GPIO_Pin_2 | GPIO_Pin_3);
}
//I2C总线启动信号
void MyI2C_Start(void)
{
MyI2C_W_SDA(1);
MyI2C_W_SCL(1);
MyI2C_W_SDA(0);
MyI2C_W_SCL(0);
}
//I2C总线停止信号
void MyI2C_Stop(void)
{
MyI2C_W_SDA(0);
MyI2C_W_SCL(1);
MyI2C_W_SDA(1);
}
//发送应答或非应答
void MyI2C_SendAck(uint8_t AckBit)
{
MyI2C_W_SDA(AckBit);
MyI2C_W_SCL(1);
MyI2C_W_SCL(0);
}
//接收应答
uint8_t MyI2C_ReceiveAck(void)
{
uint8_t AckBit;
MyI2C_W_SDA(1);
MyI2C_W_SCL(1);
AckBit = MyI2C_R_SDA();
MyI2C_W_SCL(0);
return AckBit;
}
//发送一个字节
void MyI2C_SendByte(uint8_t Byte)
{
uint8_t i;
for (i = 0; i < 8; i ++)
{
//Byte & (1000 0000)、Byte & (0100 0000)…… 高位先行
MyI2C_W_SDA(Byte & (0x80 >> i));
MyI2C_W_SCL(1);
MyI2C_W_SCL(0);
}
}
//读取一个字节 返回接收到的Byte
uint8_t MyI2C_ReceiveByte(void)
{
uint8_t i, Byte = 0x00;
MyI2C_W_SDA(1);
for (i = 0; i < 8; i ++)
{
MyI2C_W_SCL(1);
if (MyI2C_R_SDA() == 1){Byte |= (0x80 >> i);}
MyI2C_W_SCL(0);
}
return Byte;
}
//发送应答或非应答
void MyI2C_SendAck(uint8_t AckBit)
{
MyI2C_W_SDA(AckBit);
MyI2C_W_SCL(1);
MyI2C_W_SCL(0);
}
//接收应答
uint8_t MyI2C_ReceiveAck(void)
{
uint8_t AckBit;
MyI2C_W_SDA(1);
MyI2C_W_SCL(1);
AckBit = MyI2C_R_SDA();
MyI2C_W_SCL(0);
return AckBit;
}
#include "stm32f10x.h" // Device header
#include "MyI2C.h"
#define EEPROM_Address 0xA0
void EEPROM_WriteReg(uint8_t RegAddress, uint8_t Data)
{
MyI2C_Start();
MyI2C_SendByte(EEPROM_Address);
MyI2C_ReceiveAck();
MyI2C_SendByte(RegAddress);
MyI2C_ReceiveAck();
MyI2C_SendByte(Data);
MyI2C_ReceiveAck();
MyI2C_Stop();
}
uint8_t EEPROM_ReadReg(uint8_t *_pReadBuf, uint8_t RegAddress, uint8_t Len)
{
uint8_t i;
MyI2C_Start();
MyI2C_SendByte(EEPROM_Address);
MyI2C_ReceiveAck();
MyI2C_SendByte(RegAddress);
MyI2C_ReceiveAck();
MyI2C_Start();
MyI2C_SendByte(EEPROM_Address | 0x01);
for(i = 0;i < Len; i++)
{
_pReadBuf[i] = MyI2C_ReceiveByte();
}
MyI2C_SendAck(1);
MyI2C_Stop();
return 1;
}
void EEPROM_Init(void)
{
MyI2C_Init();
}
九、硬件I2C
1.I2C外设简介
STM32内部集成了硬件I2C收发电路,可以由硬件自动执行时钟生成、起始终止条件生成、应答位收发、数据收发等功能,减轻CPU的负担
支持多主机模型 支持7位/10位地址模式
支持不同的通讯速度,标准速度(高达100 kHz),快速(高达400 kHz)
支持DMA 兼容SMBus协议
2.I2C框图
3.传送序列图
3.1 主机发送
3.2 主机接收
4.硬件I2C代码实现
读写流程按照传送序列图。
#define EEPROM_Address 0xA0
//I2C_CheckEvent函数检测标志位,事件未完成进入此函数。
//时间完成或超时(防止进入死循环卡死)退出
void EEPROM_WaitEvent(I2C_TypeDef* I2Cx, uint32_t I2C_EVENT)
{
uint16_t Timeout = 10000;
while(I2C_CheckEvent(I2Cx, I2C_EVENT) != SUCCESS)
{
Timeout--;
if(Timeout == 0)
{
break;
}
}
}
void EEPROM_Init(void)
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C2, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD; //复用开漏输出
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10 | GPIO_Pin_11;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
I2C_InitTypeDef I2C_InitStructure;
I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;
I2C_InitStructure.I2C_ClockSpeed = 50000; //波特率50k
I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2; //占空比2:1
I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;
I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
I2C_InitStructure.I2C_OwnAddress1 = 0x00; //STM32的I2C地址
I2C_Init(I2C2, &I2C_InitStructure);
I2C_Cmd(I2C2, ENABLE);
}
//写数据
void EEPROM_WriteReg(uint8_t RegAddress, uint8_t Data)
{
I2C_GenerateSTART(I2C2, ENABLE);
EEPROM_WaitEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT);
I2C_Send7bitAddress(I2C2, EEPROM_Address, I2C_Direction_Transmitter);
EEPROM_WaitEvent(I2C2, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED) ;
I2C_SendData(I2C2, RegAddress);
EEPROM_WaitEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTING) ;
I2C_SendData(I2C2, Data);
EEPROM_WaitEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED);
I2C_GenerateSTOP(I2C2, ENABLE);
}
//读数据
uint8_t EEPROM_ReadReg( uint8_t RegAddress)
{
uint8_t Data;
I2C_GenerateSTART(I2C2, ENABLE);
EEPROM_WaitEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT) ;
I2C_Send7bitAddress(I2C2, EEPROM_Address, I2C_Direction_Transmitter);
EEPROM_WaitEvent(I2C2, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED);
I2C_SendData(I2C2, RegAddress);
EEPROM_WaitEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED);
I2C_GenerateSTART(I2C2, ENABLE);
EEPROM_WaitEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT);
I2C_Send7bitAddress(I2C2, EEPROM_Address, I2C_Direction_Receiver);
EEPROM_WaitEvent(I2C2, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED);
I2C_AcknowledgeConfig(I2C2, DISABLE);
I2C_GenerateSTOP(I2C2, ENABLE);
EEPROM_WaitEvent(I2C2, I2C_EVENT_MASTER_BYTE_RECEIVED); //等待EV7事件
Data = I2C_ReceiveData(I2C2);
I2C_AcknowledgeConfig(I2C2, ENABLE);
return Data;
}