STM32 - 使用 HAL 库实现软件模拟 I2C

本文介绍了一种基于单片机的I2C通信模拟实现方法,包括开漏输出模式的配置、延时函数、引脚初始化、启动及停止信号的生成等关键步骤,并提供了详细的代码示例。

I2C 的两个引脚(SCL 引脚和 SDA 引脚)需要既能输出又能输入,为了避免复杂的配置操作需要把该引脚配置为开漏输出模式,该模式的说明如下图所示:
在这里插入图片描述
当单片机的 SDA 引脚配置为低电平时,SDA 线被拉低;
当单片机的 SDA 引脚配置为高电平时,引脚端口为高阻态,SDA 线通过上拉电阻被 VCC 拉高。
因此一定要注意在进行 I2C 通讯时确保 SDA 线和 SCL 线外接上拉电阻。

my_i2c.h

#pragma once

#include "main.h"
#include "gpio.h"

#define I2C_SCL_PORT GPIOA      /* GPIO端口 */
#define I2C_SCL_PIN GPIO_PIN_9  /* 连接到SCL时钟线的GPIO */
#define I2C_SDA_PIN GPIO_PIN_10 /* 连接到SDA数据线的GPIO */

#define i2c_scl_high() I2C_SCL_PORT->BSRR = I2C_SCL_PIN /* SCL = 1 */
#define i2c_scl_low() I2C_SCL_PORT->BRR = I2C_SCL_PIN   /* SCL = 0 */

#define i2c_sda_high() I2C_SCL_PORT->BSRR = I2C_SDA_PIN /* SDA = 1 */
#define i2c_sda_low() I2C_SCL_PORT->BRR = I2C_SDA_PIN   /* SDA = 0 */

#define i2c_read_sda() ((I2C_SCL_PORT->IDR & I2C_SDA_PIN) != 0) /* 读SDA口线状态 */

void i2c_gpio_Init(void);
void i2c_Start(void);
void i2c_Stop(void);
uint8_t i2c_WaitAck(void);
void i2c_Ack(void);
void i2c_NAck(void);
void i2c_SendByte(uint8_t data);
uint8_t i2c_ReadByte(void);

my_i2c.c

#include "my_i2c.h"

/**
 * @brief 模拟I2C延时
 *
 */
static void i2c_Delay(void)
{
    volatile uint8_t i;

    for (i = 0; i < 5; i++)
        ;
}

/**
 * @brief 软件模拟I2C初始化
 *
 */
void i2c_gpio_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStruct = {0};

    /*Configure GPIO pins : SCL_Pin SDA_Pin */
    GPIO_InitStruct.Pin = GPIO_PIN_9 | GPIO_PIN_10;
    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
}

/**
 * @brief CPU发起I2C总线启动信号
 *     _____
 * SDA      \_____________
 *     __________
 * SCL           \________
 */
void i2c_Start(void)
{
    i2c_sda_high();
    i2c_scl_high();
    i2c_Delay();
    i2c_sda_low();
    i2c_Delay();
    i2c_scl_low();
    i2c_Delay();
}

/**
 * @brief CPU发起I2C总线停止信号
 *                _______
 * SDA __________/
 *           ____________
 * SCL _____/
 */
void i2c_Stop(void)
{
    i2c_sda_low();
    i2c_scl_high();
    i2c_Delay();
    i2c_sda_high();
}

/**
 * @brief I2C 等待响应(CPU产生一个时钟,并读取器件的ACK应答信号)
 *
 * @return uint8_t
 */
uint8_t i2c_WaitAck(void)
{
    uint8_t re;
    i2c_sda_high();
    i2c_Delay();
    i2c_scl_high();
    i2c_Delay();
    if (i2c_read_sda())
    {
        re = 1;
    }
    else
    {
        re = 0;
    }
    i2c_scl_low();
    i2c_Delay();
    return re;
}

/**
 * @brief I2C 响应
 * @retval none
 * @author Mr.W
 * @date 2020-10-12
 */

/**
 * @brief CPU产生一个ACK信号
 *            ____
 * SCL ______/    \______
 *     ____         _____
 * SDA     \_______/
 */
void i2c_Ack(void)
{
    i2c_sda_low();
    i2c_Delay();
    i2c_scl_high();
    i2c_Delay();
    i2c_scl_low();
    i2c_Delay();
    i2c_sda_high();
}

/**
 * @brief I2C 不响应(CPU产生1个NACK信号)
 *            ____
 * SCL ______/    \______
 *     __________________
 * SDA
 */
void i2c_NAck(void)
{
    i2c_sda_high();
    i2c_Delay();
    i2c_scl_high();
    i2c_Delay();
    i2c_scl_low();
    i2c_Delay();
}

/**
 * @brief CPU向I2C总线设备发送8bit数据
 *
 * @param data
 */
void i2c_SendByte(uint8_t data)
{
    uint8_t i;

    for (i = 0; i < 8; i++)
    {
        if (data & 0x80)
        {
            i2c_sda_high();
        }
        else
        {
            i2c_sda_low();
        }

        i2c_Delay();
        i2c_scl_high();
        i2c_Delay();
        i2c_scl_low();
        if (i == 7)
        {
            i2c_sda_high();
        }
        data <<= 1;
        i2c_Delay();
    }
}

/**
 * @brief CPU从I2C总线设备读取8bit数据
 *
 * @return uint8_t
 */
uint8_t i2c_ReadByte(void)
{
    uint8_t i, data = 0;
    for (i = 0; i < 8; i++)
    {
        data <<= 1;
        i2c_scl_high();
        i2c_Delay();
        if (i2c_read_sda())
        {
            data++;
        }
        i2c_scl_low();
        i2c_Delay();
    }

    return data;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值