简介
• STM32 内部集成了硬件 I2C 收发电路,可以由硬件自动执行时钟生成、起始终止条件生成、应答位收发、数据收发等功能,减轻 CPU 的负担• 支持多主机模型• 支持 7 位 /10 位地址模式• 支持不同的通讯速度,标准速度 ( 高达 100 kHz) ,快速 ( 高达 400 kHz)• 支持 DMA• 兼容 SMBus 协议•• STM32F103C8T6 硬件 I2C 资源: I2C1 、 I2C2
硬件电路
• 所有 I2C 设备的 SCL 连在一起, SDA 连在一起• 设备的 SCL 和 SDA 均要配置成开漏输出模式• SCL 和 SDA 各添加一个上拉电阻,阻值一般为 4.7KΩ 左右
SDA是半双工,主机和从机之间会切换输入和输出。
SCL主机设置开漏输出(强下拉/浮空),从机设置浮空输入。
I2C时序基本单元
• 起始条件: SCL 高电平期间, SDA 从高电平切换到低电平• 终止条件: SCL 高电平期间, SDA 从低电平切换到高电平
发送一个字节: SCL 低电平期间,主机将数据位依次放到 SDA 线上(高位先行),然后释放 SCL ,从机将在 SCL 高电平期间读取数据位,所以 SCL 高电平期间 SDA 不允许有数据变化,依次循环上述过程 8 次,即可发送一个字节。
接收一个字节:SCL低电平期间,从机将数据位依次放到SDA线上(高位先行),然后释放SCL,主机将在SCL高电平期间读取数据位,所以SCL高电平期间SDA不允许有数据变化,依次循环上述过程8次,即可接收一个字节(主机在接收之前,需要释放SDA)
• 发送应答:主机在接收完一个字节之后,在下一个时钟发送一位数据,数据 0 表示应答,数据 1 表示非应答• 接收应答:主机在发送完一个字节之后,在下一个时钟接收一位数据,判断从机是否应答,数据 0 表示应答,数据 1 表示非应答(主机在接收之前,需要释放 SDA )
I2C时序
• 指定地址写(7位地址,)• 对于指定设备( Slave Address ),在指定地址( Reg Address )下,写入指定数据( Data )
代码部分
#include "stm32f10x.h" // 导入STM32F10x设备头文件
#include "Delay.h" // 导入延迟函数头文件
void MyI2C_W_SCL(uint8_t BitValue)
{
GPIO_WriteBit(GPIOB, GPIO_Pin_10, (BitAction)BitValue); // 写入SCL引脚的值
Delay_us(10); // 延迟10微秒
}
void MyI2C_W_SDA(uint8_t BitValue)
{
GPIO_WriteBit(GPIOB, GPIO_Pin_11, (BitAction)BitValue); // 写入SDA引脚的值
Delay_us(10); // 延迟10微秒
}
uint8_t MyI2C_R_SDA(void)
{
uint8_t BitValue;
BitValue = GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11); // 读取SDA引脚的值
Delay_us(10); // 延迟10微秒
return BitValue; // 返回读取到的值
}
void MyI2C_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); // 使能GPIOB时钟
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD; // 将GPIO模式设置为开漏输出
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10 | GPIO_Pin_11; // 设置SCL和SDA引脚
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; // 设置GPIO速度为50MHz
GPIO_Init(GPIOB, &GPIO_InitStructure); // 初始化GPIOB
GPIO_SetBits(GPIOB, GPIO_Pin_10 | GPIO_Pin_11); // 设置SCL和SDA引脚的初始电平为高电平
}
void MyI2C_Start(void)
{
MyI2C_W_SDA(1); // 发送起始信号时,先将SDA引脚设置为高电平
MyI2C_W_SCL(1); // 然后将SCL引脚设置为高电平
MyI2C_W_SDA(0); // 接着将SDA引脚设置为低电平
MyI2C_W_SCL(0); // 最后将SCL引脚设置为低电平
}
void MyI2C_Stop(void)
{
MyI2C_W_SDA(0); // 发送停止信号时,先将SDA引脚设置为低电平
MyI2C_W_SCL(1); // 然后将SCL引脚设置为高电平
MyI2C_W_SDA(1); // 最后将SDA引脚设置为高电平
}
void MyI2C_SendByte(uint8_t Byte)
{
uint8_t i;
for (i = 0; i < 8; i ++)
{
MyI2C_W_SDA(Byte & (0x80 >> i)); // 逐位发送字节数据,从高位到低位
MyI2C_W_SCL(1); // 每发送一位数据,将SCL引脚设置为高电平
MyI2C_W_SCL(0); // 然后将SCL引脚设置为低电平
}
}
uint8_t MyI2C_ReceiveByte(void)
{
uint8_t i, Byte = 0x00;
MyI2C_W_SDA(1); // 读取字节数据时,先将SDA引脚设置为高电平
for (i = 0; i < 8; i ++)
{
MyI2C_W_SCL(1); // 然后将SCL引脚设置为高电平
if (MyI2C_R_SDA() == 1) { Byte |= (0x80 >> i); } // 逐位读取字节数据,从高位到低位
MyI2C_W_SCL(0); // 最后将SCL引脚设置为低电平
}
return Byte; // 返回读取到的字节数据
}
void MyI2C_SendAck(uint8_t AckBit)
{
MyI2C_W_SDA(AckBit); // 发送应答位,根据AckBit的值设置SDA引脚的电平
MyI2C_W_SCL(1); // 将SCL引脚设置为高电平
MyI2C_W_SCL(0); // 然后将SCL引脚设置为低电平
}
uint8_t MyI2C_ReceiveAck(void)
{
uint8_t AckBit;
MyI2C_W_SDA(1); // 读取应答位时,先将SDA引脚设置为高电平
MyI2C_W_SCL(1); // 然后将SCL引脚设置为高电平
AckBit = MyI2C_R_SDA(); // 读取应答位的值
MyI2C_W_SCL(0); // 最后将SCL引脚设置为低电平
return AckBit; // 返回读取到的应答位的值
}