I2C 的两个引脚(SCL 引脚和 SDA 引脚)需要既能输出又能输入,为了避免复杂的配置操作需要把该引脚配置为开漏输出模式,该模式的说明如下图所示:
当单片机的 SDA 引脚配置为低电平时,SDA 线被拉低;当单片机的 SDA 引脚配置为高电平时,引脚端口为高阻态,SDA 线通过上拉电阻被 VCC 拉高。因此一定要注意在进行 I2C 通讯时确保 SDA 线和 SCL 线外接上拉电阻。
二、代码实现
(一)编写软件模拟 I2C 的一些函数
I2C 软件延时
/**
* @brief 模拟I2C延时
* @retval none
* @author Mr.W
* @date 2020-10-12
*/
static void analog_i2c_delay(void)
{
volatile uint8_t i;
for (i = 0; i < 10; i++);
}
I2C 引脚初始化
/**
* @brief 软件模拟I2C初始化
* @retval none
* @author Mr.W
* @date 2020-10-12
*/
void bsp_analog_i2c_init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
__HAL_RCC_GPIOB_CLK_ENABLE();
/*Configure GPIO pins : PB6 PB7 */
GPIO_InitStruct.Pin = GPIO_PIN_6|GPIO_PIN_7;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
bsp_analog_i2c_stop();
}
- I2C 开始
/**
* @brief I2C 开始,SCL为高电平的时候SDA产生一个下降沿信号
* @retval none
* @author Mr.W
* @date 2020-10-12
*/
void bsp_analog_i2c_start(void)
{
/* _____
*SDA \_____________
* __________
*SCL \________
*/
i2c_sda_high();
i2c_scl_high();
analog_i2c_delay();
i2c_sda_low();
analog_i2c_delay();
i2c_scl_low();
analog_i2c_delay();
}
- I2C 停止
/**
* @brief I2C 停止,SCL为高电平的时候SDA产生一个上升沿信号
* @retval none
* @author Mr.W
* @date 2020-10-12
*/
void bsp_analog_i2c_stop(void)
{
/* _______
*SDA __________/
* ____________
*SCL _____/
*/
i2c_sda_low();
i2c_scl_high();
analog_i2c_delay();
i2c_sda_high();
analog_i2c_delay();
}
- I2C 等待响应
/**
* @brief I2C 等待响应
* @retval none
* @author Mr.W
* @date 2020-10-12
*/
uint8_t bsp_analog_i2c_wait_ack(void)
{
uint32_t timeout = 0;
i2c_sda_high();
analog_i2c_delay();
i2c_scl_high();
analog_i2c_delay();
while(i2c_read_sda())
{
timeout++;
if(timeout > 2000)
{
return 0;
}
}
i2c_scl_low();
analog_i2c_delay();
return 1;
}
- I2C 响应
/**
* @brief I2C 响应
* @retval none
* @author Mr.W
* @date 2020-10-12
*/
void bsp_analog_i2c_ack(void)
{
/* ____
*SCL ______/ \______
* ____ _____
*SDA \_______/
*/
i2c_sda_low();
analog_i2c_delay();
i2c_scl_high();
analog_i2c_delay();
i2c_scl_low();
analog_i2c_delay();
i2c_sda_high();
}
- I2C 不响应
/**
* @brief I2C 不响应
* @retval none
* @author Mr.W
* @date 2020-10-12
*/
void bsp_analog_i2c_nack(void)
{
/* ____
*SCL ______/ \______
* __________________
*SDA
*/
i2c_sda_high();
analog_i2c_delay();
i2c_scl_high();
analog_i2c_delay();
i2c_scl_low();
analog_i2c_delay();
}
- I2C 发送一个字节数据
/**
* @brief I2C 发送一个字节数据
* @retval none
* @author Mr.W
* @date 2020-10-12
*/
void bsp_analog_i2c_send_byte(uint8_t data)
{
uint8_t i;
for(i = 0; i < 8; i++)
{
if(data & 0x80)
{
i2c_sda_high();
}
else
{
i2c_sda_low();
}
analog_i2c_delay();
i2c_scl_high();
analog_i2c_delay();
i2c_scl_low();
if(i == 7)
{
i2c_sda_high();
}
data <<= 1;
analog_i2c_delay();
}
}
- I2C 接收一个字节数据
/**
* @brief I2C 读一个字节数据
* @retval none
* @author Mr.W
* @date 2020-10-12
*/
uint8_t bsp_analog_i2c_read_byte(void)
{
uint8_t i, data = 0;
for(i = 0; i < 8; i++ )
{
data <<= 1;
i2c_scl_high();
analog_i2c_delay();
if(i2c_read_sda())
{
data++;
}
i2c_scl_low();
analog_i2c_delay();
}
return data;
}