这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » 嵌入式开发 » 软件与操作系统 » rtthread软件I2C框架解析

共7条 1/1 1 跳转至

rtthread软件I2C框架解析

助工
2024-09-26 22:37:52     打赏

背景

   前面分析硬件I2C框架时,提到了个人不认可在方案不支持硬件I2C时,通过在驱动层用硬件I2C的接口实现软件I2C的逻辑。刚好看RTT框架层中,有软件I2C的对应实现,因此开始分析此接口,以期在后续使用中直接使用该接口。

功能实现分析

软件I2C注册

   在查看I2C设备注册入口时,偶然发现组件层也有调用I2C设备注册入口,很好奇为何会有此入口,便顺势查看了这个入口的调用关系:

rt_err_t rt_i2c_bus_device_register(struct rt_i2c_bus_device *bus,
                                    const char               *bus_name)
{
    rt_err_t res = RT_EOK;

    rt_mutex_init(&bus->lock, "i2c_bus_lock", RT_IPC_FLAG_PRIO);

    if (bus->timeout == 0) bus->timeout = RT_TICK_PER_SECOND;

    res = rt_i2c_bus_device_device_init(bus, bus_name);

    LOG_I("I2C bus [%s] registered", bus_name);

#ifdef RT_USING_DM
    if (!res)
    {
        i2c_bus_scan_clients(bus);
    }
#endif

    return res;
}

static const struct rt_i2c_bus_device_ops i2c_bit_bus_ops =
{
    i2c_bit_xfer,
    RT_NULL,
    RT_NULL
};

rt_err_t rt_i2c_bit_add_bus(struct rt_i2c_bus_device *bus,
                            const char               *bus_name)
{
    bus->ops = &i2c_bit_bus_ops;

    return rt_i2c_bus_device_register(bus, bus_name);
}

/* I2C initialization function */
int rt_soft_i2c_init(void)
{
    int err = RT_EOK;
    struct rt_soft_i2c *obj;
    int i;

    for(i = 0; i < sizeof(i2c_bus_obj) / sizeof(i2c_bus_obj[0]); i++)
    {
        struct soft_i2c_config *cfg = &i2c_cfg[i];

        pin_init(cfg);

        obj = &i2c_bus_obj[i];
        obj->ops = soft_i2c_ops;
        obj->ops.data = cfg;
        obj->i2c_bus.priv = &obj->ops;
        obj->ops.delay_us = cfg->timing_delay;
        obj->ops.timeout = cfg->timing_timeout;
        if(rt_i2c_bit_add_bus(&obj->i2c_bus, cfg->bus_name) == RT_EOK)
        {
            i2c_bus_unlock(cfg);
            LOG_D("Software simulation %s init done"
                  ", SCL pin: 0x%02X, SDA pin: 0x%02X"
                  , cfg->bus_name
                  , cfg->scl_pin
                  , cfg->sda_pin
                 );
        }
        else
        {
            err++;
            LOG_E("Software simulation %s init fail"
                  ", SCL pin: 0x%02X, SDA pin: 0x%02X"
                  , cfg->bus_name
                  , cfg->scl_pin
                  , cfg->sda_pin
                 );
        }
    }

    return err;
}
INIT_PREV_EXPORT(rt_soft_i2c_init);

       当反查到函数INIT_PREV_EXPORT(rt_soft_i2c_init);时,已经可以通过函数名确认了,这个实现是软件I2C的实现,再进一步的查,可以发现以下信息:

if GetDepend('RT_USING_SOFT_I2C'):
    src = src + ['soft_i2c.c']

       也就是说,只要在.config中使能宏RT_USING_SOFT_I2C便可启用该功能,而此功能的选现在组件的Kconfig中有维护。

if RT_USING_I2C
    config RT_I2C_DEBUG
        bool "Use I2C debug message"
        default n

    config RT_USING_I2C_BITOPS
        bool "Use GPIO to simulate I2C"
        default y

    if RT_USING_I2C_BITOPS
        config RT_I2C_BITOPS_DEBUG
            bool "Use simulate I2C debug message"
            default n
    endif

    config RT_USING_SOFT_I2C
        bool "Use GPIO to soft simulate I2C"
        default n
        select RT_USING_PIN
        select RT_USING_I2C_BITOPS
    if RT_USING_SOFT_I2C
        config RT_USING_SOFT_I2C1
            bool "Enable I2C1 Bus (software simulation)"
            default y
            if RT_USING_SOFT_I2C1
                config RT_SOFT_I2C1_SCL_PIN
                    int "SCL pin number"
                    range 0 32767
                    default 1
                config RT_SOFT_I2C1_SDA_PIN
                    int "SDA pin number"
                    range 0 32767
                    default 2
                config RT_SOFT_I2C1_BUS_NAME
                    string "Bus name"
                    default "i2c1"
                config RT_SOFT_I2C1_TIMING_DELAY
                    int "Timing delay (us)"
                    range 0 32767
                    default 10
                config RT_SOFT_I2C1_TIMING_TIMEOUT
                    int "Timing timeout (tick)"
                    range 0 32767
                    default 10
            endif
        config RT_USING_SOFT_I2C2
            bool "Enable I2C2 Bus (software simulation)"
            default n
            if RT_USING_SOFT_I2C2
                config RT_SOFT_I2C2_SCL_PIN
                    int "SCL pin number"
                    range 0 32767
                    default 3
                config RT_SOFT_I2C2_SDA_PIN
                    int "SDA pin number"
                    range 0 32767
                    default 4
                config RT_SOFT_I2C2_BUS_NAME
                    string "Bus name"
                    default "i2c2"
                config RT_SOFT_I2C2_TIMING_DELAY
                    int "Timing delay (us)"
                    range 0 32767
                    default 10
                config RT_SOFT_I2C2_TIMING_TIMEOUT
                    int "Timing timeout (tick)"
                    range 0 32767
                    default 10
            endif
        config RT_USING_SOFT_I2C3
            bool "Enable I2C3 Bus (software simulation)"
            default n
            if RT_USING_SOFT_I2C3
                config RT_SOFT_I2C3_SCL_PIN
                    int "SCL pin number"
                    range 0 32767
                    default 5
                config RT_SOFT_I2C3_SDA_PIN
                    int "SDA pin number"
                    range 0 32767
                    default 6
                config RT_SOFT_I2C3_BUS_NAME
                    string "Bus name"
                    default "i2c3"
                config RT_SOFT_I2C3_TIMING_DELAY
                    int "Timing delay (us)"
                    range 0 32767
                    default 10
                config RT_SOFT_I2C3_TIMING_TIMEOUT
                    int "Timing timeout (tick)"
                    range 0 32767
                    default 10
            endif
        config RT_USING_SOFT_I2C4
            bool "Enable I2C4 Bus (software simulation)"
            default n
            if RT_USING_SOFT_I2C4
                config RT_SOFT_I2C4_SCL_PIN
                    int "SCL pin number"
                    range 0 32767
                    default 7
                config RT_SOFT_I2C4_SDA_PIN
                    int "SDA pin number"
                    range 0 32767
                    default 8
                config RT_SOFT_I2C4_BUS_NAME
                    string "Bus name"
                    default "i2c4"
                config RT_SOFT_I2C4_TIMING_DELAY
                    int "Timing delay (us)"
                    range 0 32767
                    default 10
                config RT_SOFT_I2C4_TIMING_TIMEOUT
                    int "Timing timeout (tick)"
                    range 0 32767
                    default 10
            endif
        config RT_USING_SOFT_I2C5
            bool "Enable I2C5 Bus (software simulation)"
            default n
            if RT_USING_SOFT_I2C5
                config RT_SOFT_I2C5_SCL_PIN
                    int "SCL pin number"
                    range 0 32767
                    default 9
                config RT_SOFT_I2C5_SDA_PIN
                    int "SDA pin number"
                    range 0 32767
                    default 10
                config RT_SOFT_I2C5_BUS_NAME
                    string "Bus name"
                    default "i2c5"
                config RT_SOFT_I2C5_TIMING_DELAY
                    int "Timing delay (us)"
                    range 0 32767
                    default 10
                config RT_SOFT_I2C5_TIMING_TIMEOUT
                    int "Timing timeout (tick)"
                    range 0 32767
                    default 10
            endif
        config RT_USING_SOFT_I2C6
            bool "Enable I2C6 Bus (software simulation)"
            default n
            if RT_USING_SOFT_I2C6
                config RT_SOFT_I2C6_SCL_PIN
                    int "SCL pin number"
                    range 0 32767
                    default 11
                config RT_SOFT_I2C6_SDA_PIN
                    int "SDA pin number"
                    range 0 32767
                    default 12
                config RT_SOFT_I2C6_BUS_NAME
                    string "Bus name"
                    default "i2c6"
                config RT_SOFT_I2C6_TIMING_DELAY
                    int "Timing delay (us)"
                    range 0 32767
                    default 10
                config RT_SOFT_I2C6_TIMING_TIMEOUT
                    int "Timing timeout (tick)"
                    range 0 32767
                    default 10
            endif
        config RT_USING_SOFT_I2C7
            bool "Enable I2C7 Bus (software simulation)"
            default n
            if RT_USING_SOFT_I2C7
                config RT_SOFT_I2C7_SCL_PIN
                    int "SCL pin number"
                    range 0 32767
                    default 13
                config RT_SOFT_I2C7_SDA_PIN
                    int "SDA pin number"
                    range 0 32767
                    default 14
                config RT_SOFT_I2C7_BUS_NAME
                    string "Bus name"
                    default "i2c7"
                config RT_SOFT_I2C7_TIMING_DELAY
                    int "Timing delay (us)"
                    range 0 32767
                    default 10
                config RT_SOFT_I2C7_TIMING_TIMEOUT
                    int "Timing timeout (tick)"
                    range 0 32767
                    default 10
            endif
        config RT_USING_SOFT_I2C8
            bool "Enable I2C8 Bus (software simulation)"
            default n
            if RT_USING_SOFT_I2C8
                config RT_SOFT_I2C8_SCL_PIN
                    int "SCL pin number"
                    range 0 32767
                    default 15
                config RT_SOFT_I2C8_SDA_PIN
                    int "SDA pin number"
                    range 0 32767
                    default 16
                config RT_SOFT_I2C8_BUS_NAME
                    string "Bus name"
                    default "i2c8"
                config RT_SOFT_I2C8_TIMING_DELAY
                    int "Timing delay (us)"
                    range 0 32767
                    default 10
                config RT_SOFT_I2C8_TIMING_TIMEOUT
                    int "Timing timeout (tick)"
                    range 0 32767
                    default 10
            endif
    endif
endif

       此Kconfig路径为: ./components/drivers/Kconfig

       而从注册函数的实现上看,我们可以看出,本质上RTT所实现的软件I2C和在驱动层套用硬件I2C框架实现软件I2C的思路是一致的,唯一的区别是,把这套实现挪至组件层,那所有不支持硬件I2C的器件都能直接实现软件I2C,而不需要再去单独写一套软件I2C的实现。而既然弄清楚了软件I2C的注册过程,其实软件I2C的使用方法也很清晰了,各层的调用逻辑和硬件I2C逻辑一致,唯一的区别可能就是软件I2C的设备名与硬件I2C的设备名一致。

软件I2C实现逻辑

   软件I2C的实现,无非就是实现以下几个接口:

   SetSCL

   SetSDA

   GetSDA

   Delay

在这个接口的基础上,封装以下几个接口:

   Start

   Stop

   ReadAck

   SendBit

需要注意的是:ReadAck需要一个超时机制,以防止从机无响应而死等。

最后基于这些接口上,实现以下接口:

   Read

   Write

而交给应用层的接口,其实仅仅是把Read和Write接口以某种形式暴露给上层使用。

我们可以按照这么个思路去套用,看RTT的软件I2C框架是否与该思路一致:

最底层接口

// 最底层的IO操作定义
// 至于ops对应的接口定义,在 rt_soft_i2c_init有定义,具体可见文件soft_i2c.c
#define SET_SDA(ops, val)   ops->set_sda(ops->data, val)
#define SET_SCL(ops, val)   ops->set_scl(ops->data, val)
#define GET_SDA(ops)        ops->get_sda(ops->data)
#define GET_SCL(ops)        ops->get_scl(ops->data)

// 实现了两个版本的延时,为何这么操作,估计得看后面的具体操作
rt_inline void i2c_delay(struct rt_i2c_bit_ops *ops)
{
    ops->udelay((ops->delay_us + 1) >> 1);
}

rt_inline void i2c_delay2(struct rt_i2c_bit_ops *ops)
{
    ops->udelay(ops->delay_us);
}

// 设置输出高低电平
#define SDA_L(ops)          SET_SDA(ops, 0)
#define SDA_H(ops)          SET_SDA(ops, 1)
#define SCL_L(ops)          SET_SCL(ops, 0)

static rt_err_t SCL_H(struct rt_i2c_bit_ops *ops)
{
    rt_tick_t start;

    SET_SCL(ops, 1);

    if (!ops->get_scl)
        goto done;

    start = rt_tick_get();
    while (!GET_SCL(ops))  // 由于在框架里,IO口被初始化成开漏输出,因此置高需要等待外部拉高,因此需要等待外部实际变高
    {
        if ((rt_tick_get() - start) > ops->timeout)
            return -RT_ETIMEOUT;
        i2c_delay(ops);
    }
#ifdef RT_I2C_BITOPS_DEBUG
    if (rt_tick_get() != start)
    {
        LOG_D("wait %ld tick for SCL line to go high",
              rt_tick_get() - start);
    }
#endif

done:
    i2c_delay(ops);

    return RT_EOK;
}

中间层接口

static void i2c_start(struct rt_i2c_bit_ops *ops)
{    // 起始信号,SDA低电平后SCL低电平
#ifdef RT_I2C_BITOPS_DEBUG
    if (ops->get_scl && !GET_SCL(ops))
    {
        LOG_E("I2C bus error, SCL line low");
    }
    if (ops->get_sda && !GET_SDA(ops))
    {
        LOG_E("I2C bus error, SDA line low");
    }
#endif
    SDA_L(ops);
    i2c_delay(ops);
    SCL_L(ops);
}

static void i2c_restart(struct rt_i2c_bit_ops *ops)
{    // I2C复位操作, SDA和SCL全高后再发出起始波形,其实起始波形可以复用i2c_start
    SDA_H(ops);
    SCL_H(ops);
    i2c_delay(ops);
    SDA_L(ops);
    i2c_delay(ops);
    SCL_L(ops);
}

static void i2c_stop(struct rt_i2c_bit_ops *ops)
{    //I2C停止波形,由于I2C通信的最后一个波形是等待从机拉低,因此若未等到从机拉低时,需主动拉低保证波形符合标准,因此先拉低SDA后,再拉高SCL,最后拉高SDA,保证波形为停止波形。
    SDA_L(ops);
    i2c_delay(ops);
    SCL_H(ops);
    i2c_delay(ops);
    SDA_H(ops);
    i2c_delay2(ops);
}

rt_inline rt_bool_t i2c_waitack(struct rt_i2c_bit_ops *ops)
{    //等待ACK信号,在8bit数据发送完毕后,从机端若收到信息,则需要在第九个时钟位置拉低SDA
     // 主机端读到高电平判定为NACK,低电平判定位ACK
     // 这个函数并没有SDA输入输出态转换的操作,原因是在soft_i2c.c的pin_init中,框架直接将IO口设置为开漏输出,
     // 也就是说,这个管脚硬件需接上拉,而管脚的输出低电平,本质上是将接口引到地上,输出高电平,本质上是变成输入高阻态
     // 另外,为何在拉高SCL的时候去读SDA,这操作没看明白,个人理解实际波形为低电平时去读取
    rt_bool_t ack;

    SDA_H(ops);
    i2c_delay(ops);

    if (SCL_H(ops) < 0)
    {
        LOG_W("wait ack timeout");

        return -RT_ETIMEOUT;
    }

    ack = !GET_SDA(ops);    /* ACK : SDA pin is pulled low */
    LOG_D("%s", ack ? "ACK" : "NACK");

    SCL_L(ops);

    return ack;
}

static rt_int32_t i2c_writeb(struct rt_i2c_bus_device *bus, rt_uint8_t data)
{    // 写一个字节,由低到高的顺序写完8bit后,拉低SCL再去读SDA
    rt_int32_t i;
    rt_uint8_t bit;

    struct rt_i2c_bit_ops *ops = (struct rt_i2c_bit_ops *)bus->priv;

    for (i = 7; i >= 0; i--)
    {
        SCL_L(ops);
        bit = (data >> i) & 1;
        SET_SDA(ops, bit);
        i2c_delay(ops);
        if (SCL_H(ops) < 0)
        {
            LOG_D("i2c_writeb: 0x%02x, "
                    "wait scl pin high timeout at bit %d",
                    data, i);

            return -RT_ETIMEOUT;
        }
    }
    SCL_L(ops);
    i2c_delay(ops);

    return i2c_waitack(ops);
}

static rt_int32_t i2c_readb(struct rt_i2c_bus_device *bus)
{//读取一个字节的数据,拉高SDA后,由低到高的顺序读取8bit
    rt_uint8_t i;
    rt_uint8_t data = 0;
    struct rt_i2c_bit_ops *ops = (struct rt_i2c_bit_ops *)bus->priv;

    SDA_H(ops);
    i2c_delay(ops);
    for (i = 0; i < 8; i++)
    {
        data <<= 1;

        if (SCL_H(ops) < 0)
        {
            LOG_D("i2c_readb: wait scl pin high "
                    "timeout at bit %d", 7 - i);

            return -RT_ETIMEOUT;
        }

        if (GET_SDA(ops))
            data |= 1;
        SCL_L(ops);
        i2c_delay2(ops);
    }

    return data;
}

Read Write接口

static rt_ssize_t i2c_send_bytes(struct rt_i2c_bus_device *bus,
                                struct rt_i2c_msg        *msg)
{ // 写msg->len个字节
    rt_int32_t ret;
    rt_size_t bytes = 0;
    const rt_uint8_t *ptr = msg->buf;
    rt_int32_t count = msg->len;
    rt_uint16_t ignore_nack = msg->flags & RT_I2C_IGNORE_NACK;

    while (count > 0)
    {
        ret = i2c_writeb(bus, *ptr);

        if ((ret > 0) || (ignore_nack && (ret == 0)))
        {
            count --;
            ptr ++;
            bytes ++;
        }
        else if (ret == 0)
        {
            LOG_D("send bytes: NACK.");

            return 0;
        }
        else
        {
            LOG_E("send bytes: error %d", ret);

            return ret;
        }
    }

    return bytes;
}

static rt_err_t i2c_send_ack_or_nack(struct rt_i2c_bus_device *bus, int ack)
{    // 连续读时,需要在读完一个字节时读取一次ack标记,若标记为ack,则继续读,若标记为nack,则停止读取,因此有这函数的实现
    struct rt_i2c_bit_ops *ops = (struct rt_i2c_bit_ops *)bus->priv;

    if (ack)
        SET_SDA(ops, 0);
    i2c_delay(ops);
    if (SCL_H(ops) < 0)
    {
        LOG_E("ACK or NACK timeout.");

        return -RT_ETIMEOUT;
    }
    SCL_L(ops);

    return RT_EOK;
}

static rt_ssize_t i2c_recv_bytes(struct rt_i2c_bus_device *bus,
                                struct rt_i2c_msg        *msg)
{    // 连续读入口,每读完一个字节便读取一下ack状态,并决定是否继续读写
    rt_int32_t val;
    rt_int32_t bytes = 0;   /* actual bytes */
    rt_uint8_t *ptr = msg->buf;
    rt_int32_t count = msg->len;
    const rt_uint32_t flags = msg->flags;

    while (count > 0)
    {
        val = i2c_readb(bus);
        if (val >= 0)
        {
            *ptr = val;
            bytes ++;
        }
        else
        {
            break;
        }

        ptr ++;
        count --;

        LOG_D("recieve bytes: 0x%02x, %s",
                val, (flags & RT_I2C_NO_READ_ACK) ?
                "(No ACK/NACK)" : (count ? "ACK" : "NACK"));

        if (!(flags & RT_I2C_NO_READ_ACK))
        {
            val = i2c_send_ack_or_nack(bus, count);
            if (val < 0)
                return val;
        }
    }

    return bytes;
}

static rt_int32_t i2c_send_address(struct rt_i2c_bus_device *bus,
                                   rt_uint8_t                addr,
                                   rt_int32_t                retries)
{ // 正常I2C读取的时候,地址发送失败后不会再去尝试
  // 但rtt的框架中,增加了这个操作,发现读地址失败后,尝试多次读取,读取次数由retries决定
    struct rt_i2c_bit_ops *ops = (struct rt_i2c_bit_ops *)bus->priv;
    rt_int32_t i;
    rt_err_t ret = 0;

    for (i = 0; i <= retries; i++)
    {
        ret = i2c_writeb(bus, addr);
        if (ret == 1 || i == retries)
            break;
        LOG_D("send stop condition");
        i2c_stop(ops);
        i2c_delay2(ops);
        LOG_D("send start condition");
        i2c_start(ops);
    }

    return ret;
}

static rt_err_t i2c_bit_send_address(struct rt_i2c_bus_device *bus,
                                     struct rt_i2c_msg        *msg)
{
    rt_uint16_t flags = msg->flags;
    rt_uint16_t ignore_nack = msg->flags & RT_I2C_IGNORE_NACK;
    struct rt_i2c_bit_ops *ops = (struct rt_i2c_bit_ops *)bus->priv;

    rt_uint8_t addr1, addr2;
    rt_int32_t retries;
    rt_err_t ret;

    retries = ignore_nack ? 0 : bus->retries;

    if (flags & RT_I2C_ADDR_10BIT)
    {    // 发送10位地址的方式,本质上是发送两个字节的数据,没数据的位默认置1,10bit模式,如果是读,则后面补了个读的操作
        addr1 = 0xf0 | ((msg->addr >> 7) & 0x06);
        addr2 = msg->addr & 0xff;

        LOG_D("addr1: %d, addr2: %d", addr1, addr2);

        ret = i2c_send_address(bus, addr1, retries);
        if ((ret != 1) && !ignore_nack)
        {
            LOG_W("NACK: sending first addr");

            return -RT_EIO;
        }

        ret = i2c_writeb(bus, addr2);
        if ((ret != 1) && !ignore_nack)
        {
            LOG_W("NACK: sending second addr");

            return -RT_EIO;
        }
        if (flags & RT_I2C_RD)
        {
            LOG_D("send repeated start condition");
            i2c_restart(ops);
            addr1 |= 0x01;
            ret = i2c_send_address(bus, addr1, retries);
            if ((ret != 1) && !ignore_nack)
            {
                LOG_E("NACK: sending repeated addr");

                return -RT_EIO;
            }
        }
    }
    else
    { //7bit操作,如果是读,则直接把读写位标记了,然后发出地址
        /* 7-bit addr */
        addr1 = msg->addr << 1;
        if (flags & RT_I2C_RD)
            addr1 |= 1;
        ret = i2c_send_address(bus, addr1, retries);
        if ((ret != 1) && !ignore_nack)
            return -RT_EIO;
    }

    return RT_EOK;
}

static rt_ssize_t i2c_bit_xfer(struct rt_i2c_bus_device *bus,
                              struct rt_i2c_msg         msgs[],
                              rt_uint32_t               num)
{
    struct rt_i2c_msg *msg;
    struct rt_i2c_bit_ops *ops = (struct rt_i2c_bit_ops *)bus->priv;
    rt_int32_t ret;
    rt_uint32_t i;
    rt_uint16_t ignore_nack;

    if((ops->i2c_pin_init_flag == RT_FALSE) && (ops->pin_init != RT_NULL))
    {   // 如果模拟I2C从未被使用过,则使用前初始化io口并置已初始化标记,前提是接口被注册
        // 不过很可惜,这个接口并未在soft_i2c.c中定义,而是在rt_soft_i2c_init调用时直接初始化了
        ops->pin_init();
        ops->i2c_pin_init_flag = RT_TRUE;
    }

    if (num == 0) return 0;

    for (i = 0; i < num; i++)
    {
        msg = &msgs[i];
        ignore_nack = msg->flags & RT_I2C_IGNORE_NACK;
        if (!(msg->flags & RT_I2C_NO_START))    // 如果有地址,则写地址
        {
            if (i)
            {
                i2c_restart(ops);
            }
            else
            {
                LOG_D("send start condition");
                i2c_start(ops);
            }
            ret = i2c_bit_send_address(bus, msg);
            if ((ret != RT_EOK) && !ignore_nack)
            {
                LOG_D("receive NACK from device addr 0x%02x msg %d",
                        msgs[i].addr, i);
                goto out;
            }
        }
        if (msg->flags & RT_I2C_RD)    //如果是读,则调用读接口,否则调用写接口
        {
            ret = i2c_recv_bytes(bus, msg);
            if (ret >= 1)
            {
                LOG_D("read %d byte%s", ret, ret == 1 ? "" : "s");
            }
            if (ret < msg->len)
            {
                if (ret >= 0)
                    ret = -RT_EIO;
                goto out;
            }
        }
        else
        {
            ret = i2c_send_bytes(bus, msg);
            if (ret >= 1)
            {
                LOG_D("write %d byte%s", ret, ret == 1 ? "" : "s");
            }
            if (ret < msg->len)
            {
                if (ret >= 0)
                    ret = -RT_ERROR;
                goto out;
            }
        }
    }
    ret = i;

out:
    if (!(msg->flags & RT_I2C_NO_STOP))    //如果操作完毕后存在停止命令,则发送停止波形
    {
        LOG_D("send stop condition");
        i2c_stop(ops);
    }

    return ret;
}

   从实现思路上看,RTT的软件I2C实现思路与通用做法基本一致,其中 i2c-bit-ops.c封装了通用思路中的顶层和中间层,而 soft_i2c.c则实现了最基础的底层接口。

总结

   分析了那么多,其实还漏了一个最关键的点,如何使用软件I2C,这里简要说一下步骤:

         a.命令行执行menuconfig后,去component目录下找到driver层,在此层找到软件I2C开关并使能

        b.使能软件I2C时,需配置对应的SDA和SCL对应的gpio管脚编号,根据以往的经验,各家对GPIO编号的规则都有差异,但最终都是转换成十六进制数以符合RTT的gpio框架,因此此处仅需将对应管脚的编号换算成对应的十进制数写进去即可

        c.设置好延时时间,这个延时时间其实就是对应的软件I2C的总线速度

        d.scons --target=mdk5     mdk5也可以是其他平台,这里只是举个例子

        e.打开对应工程,编写I2C使用代

       另外,RTT的软件I2C框架默认芯片的GPIO支持开漏模式,因此使用RTT的软件I2C,需要gpio驱动实现开漏模式。实际上,不支持开漏模式的GPIO也可以用作模拟I2C,但操作上会稍微复杂一些,且需要修改RTT的模拟I2C框架。此外若实现了不支持开漏模式的模拟I2C,外部I2C主设备便不能并入监控设备状态了。







关键词: rtthread     软件     I2C          框架     解析    

专家
2024-09-27 00:50:52     打赏
2楼

感谢楼主分享


专家
2024-09-27 08:24:49     打赏
3楼

这种是以软件模拟方式实现的?


高工
2024-09-27 16:06:56     打赏
4楼

软件模拟的方式靠谱吗?


专家
2024-09-27 19:45:02     打赏
5楼

感谢楼主分享


专家
2024-09-27 19:46:03     打赏
6楼

感谢楼主分享


院士
2024-09-28 10:26:43     打赏
7楼

I2C通讯还能 这么玩啊!

这兼容性可以有效保障吗?


共7条 1/1 1 跳转至

回复

匿名不能发帖!请先 [ 登陆 注册 ]