在上一篇文章中,我们讲了VL53L0传感器与MCU之间的硬件连接,以及如何用STM32F103对其寄存器进行读写操作,接下来我们一起学习在STM32F103中如何用它的GPIO口来模拟I2C通信。
对于STM32F103单片机来说,它是自带了硬件 IIC,但是出于使用习惯和程序可移植性考虑,我们一般选择用 IO 口去模拟 I2C 协议。
使用 IO 口模拟 IIC 的好处有三点:
1. 使用 IO 模拟 IIC 协议可以让大家把之前学过的 GPIO 知识再进行深度的理解和扩展;
2. 加深对 IIC 时序流程的认识;
3. 方便移植到 STM32 的任何一个引脚,如再做修改可以移植到其他 MCU 平台。
当然这种方法也存在一些缺点,比如考虑这样一种极端情况,在程序运行时会有执行时
间很长的中断服务函数打断 IIC 时序,造成 IIC 写失败或者读失败。如果存在这种情况,建
议大家在进行 IIC 操作之前关闭全局中断,使用后再打开。
首先,对模拟I2C的I/O管脚进行宏定义,包括SDA线、SCL线管脚的定义等,代码如下:
#define RCC_I2C_PORT RCC_APB2Periph_GPIOB
#define PORT_I2C_SCL GPIOB
#define PIN_I2C_SCL GPIO_Pin_6
#define PORT_I2C_SDA GPIOB
#define PIN_I2C_SDA GPIO_Pin_7
#define I2C_SCL_PIN GPIO_Pin_6
#define I2C_SDA_PIN GPIO_Pin_7
#define I2C_SCL_1() PORT_I2C_SCL->BSRR = I2C_SCL_PIN
#define I2C_SCL_0() PORT_I2C_SCL->BRR = I2C_SCL_PIN
#define I2C_SDA_1() PORT_I2C_SDA->BSRR = I2C_SDA_PIN
#define I2C_SDA_0() PORT_I2C_SDA->BRR = I2C_SDA_PIN
#define I2C_SDA_READ() ((PORT_I2C_SDA->IDR & I2C_SDA_PIN) != 0)
#define I2C_SCL_READ() ((PORT_I2C_SCL->IDR & I2C_SCL_PIN) != 0)
void SetSDA_IN(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_I2C_PORT, ENABLE);
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Pin = PIN_I2C_SDA;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_Init(PORT_I2C_SDA, &GPIO_InitStructure);
}
void SetSDA_OUT(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_I2C_PORT, ENABLE);
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Pin = PIN_I2C_SDA;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;
GPIO_Init(PORT_I2C_SDA, &GPIO_InitStructure);
}
接下来便是模拟I2C的初始化函数与时序操作的函数
初始化操作与GPIO初始化基本一致:
1.配置时钟;
2.配置管脚的输入输出模式,其中输出模式最好设为上拉输出;
3.设置管脚。
void bsp_InitI2C(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_I2C_PORT, ENABLE);
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;
GPIO_InitStructure.GPIO_Pin = PIN_I2C_SCL;
GPIO_Init(PORT_I2C_SCL, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = PIN_I2C_SDA;
GPIO_Init(PORT_I2C_SDA, &GPIO_InitStructure);
i2c_Stop();
}
static void i2c_Delay(void)
{
uint8_t i;
for (i = 0; i < 30; i++);
}
void i2c_Start(void)
{
SetSDA_OUT();
I2C_SDA_1();
I2C_SCL_1();
i2c_Delay();
I2C_SDA_0();
i2c_Delay();
I2C_SCL_0();
i2c_Delay();
}
void i2c_Stop(void)
{
SetSDA_OUT();
I2C_SDA_0();
I2C_SCL_1();
i2c_Delay();
I2C_SDA_1();
i2c_Delay();
}
void i2c_SendByte(uint8_t _ucByte)
{
uint8_t i;
SetSDA_OUT();
for (i = 0; i < 8; i++)
{
if (_ucByte & 0x80)
{
I2C_SDA_1();
}
else
{
I2C_SDA_0();
}
i2c_Delay();
I2C_SCL_1();
i2c_Delay();
I2C_SCL_0();
if (i == 7)
{
I2C_SDA_1();
}
_ucByte <<= 1;
i2c_Delay();
}
}
uint8_t i2c_ReadByte(void)
{
uint8_t i;
uint8_t value;
SetSDA_IN();
value = 0;
for (i = 0; i < 8; i++)
{
value <<= 1;
I2C_SCL_1();
i2c_Delay();
if (I2C_SDA_READ())
{
value++;
}
I2C_SCL_0();
i2c_Delay();
}
return value;
}
uint8_t i2c_WaitAck(void)
{
uint8_t re;
SetSDA_IN();
I2C_SDA_1();
i2c_Delay();
I2C_SCL_1();
i2c_Delay();
if (I2C_SDA_READ())
{
re = 1;
}
else
{
re = 0;
}
I2C_SCL_0();
i2c_Delay();
return re;
}
void i2c_Ack(void)
{
I2C_SDA_0();
i2c_Delay();
I2C_SCL_1();
i2c_Delay();
I2C_SCL_0();
i2c_Delay();
I2C_SDA_1();
}
void i2c_NAck(void)
{
I2C_SDA_1();
i2c_Delay();
I2C_SCL_1();
i2c_Delay();
I2C_SCL_0();
i2c_Delay();
}
uint8_t i2c_CheckDevice(uint8_t _Address)
{
uint8_t ucAck;
if (I2C_SDA_READ() && I2C_SCL_READ())
{
i2c_Start();
i2c_SendByte(_Address | I2C_WR);
ucAck = i2c_WaitAck();
i2c_Stop();
return ucAck;
}
return 1;
}
以上便是STM32F103模拟I2C的全部操作。