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

共3条 1/1 1 跳转至

瑞萨已适配的硬件I2C驱动解析

助工
2024-09-28 22:11:55     打赏

背景

   在rtthread环境中使用过瑞萨的RA系列芯片的人都会发现,瑞萨其实已经标准化了大部分驱动层的接口,也就是说,适配一块新的板卡,其实并不需要再去像其他家那样,每块板卡都去单独编写驱动文件。

       虽然不用重新写对应模块的驱动文件,但是我们可以通过分析驱动文件代码,获取出一个通用的驱动适配模板。

分析思路

驱动注册入口

     通过前面分析硬件I2C框架中,其实我们就已经知道了硬件I2C的注册入口,即:

rt_err_t rt_i2c_bus_device_register(struct rt_i2c_bus_device *bus,
                                    const char               *bus_name);

       而在瑞萨适配该驱动的位置,可以发现存在这么一段代码:

static const struct rt_i2c_bus_device_ops ra_i2c_ops =
{
    .master_xfer        = ra_i2c_mst_xfer,
    .slave_xfer         = RT_NULL,
    .i2c_bus_control    = RT_NULL
};

int ra_hw_i2c_init(void)
{
    fsp_err_t err     = FSP_SUCCESS;
    for (rt_uint32_t i = 0; i < sizeof(ra_i2cs) / sizeof(ra_i2cs[0]); i++)
    {
        ra_i2cs[i].bus.ops = &ra_i2c_ops;
        ra_i2cs[i].bus.priv = 0;

        if (RT_EOK != rt_event_init(&ra_i2cs[i].event, ra_i2cs[i].bus_name, RT_IPC_FLAG_FIFO))
        {
            LOG_E("Init event failed");
            continue;
        }
        /* opening IIC master module */
        err = R_IIC_MASTER_Open(ra_i2cs[i].i2c_ctrl, ra_i2cs[i].i2c_cfg);
        if (FSP_SUCCESS != err)
        {
            LOG_E("R_I2C_MASTER_Open API failed,%d", err);
            continue;
        }
        err = R_IIC_MASTER_CallbackSet(ra_i2cs[i].i2c_ctrl, i2c_master_callback, &ra_i2cs[i], RT_NULL);
        /* handle error */
        if (FSP_SUCCESS != err)
        {
            LOG_E("R_I2C_CallbackSet API failed,%d", err);
            continue;
        }
        rt_i2c_bus_device_register(&ra_i2cs[i].bus, ra_i2cs[i].bus_name);
    }

    return 0;
}
INIT_DEVICE_EXPORT(ra_hw_i2c_init);

       从这里,我们可以看出,除去作用不明的rt_event_init,以及瑞萨独有的硬件配置接口,其实在注册时,驱动所需要干的事情是构造一个结构体struct rt_i2c_bus_device_ops,其中需要实现master_xfer这个入口,至于另外两个入口为何不实现,个人理解为其实很少有人真的用I2C做从模式,因此也就不去适配I2C从模式了,另外i2c_bus_control这个入口,在驱动没有专有接口的情况下,其实也不需要去实现

master_xfer

    要知道rt_event_init的作用,以及I2C master模式下的读写入口,就不得不分析结构体中的master_xfer实现了。而在驱动代码中,我们能看到的实现如下:

#define RT_I2C_WR                0x0000
#define RT_I2C_RD               (1u << 0)
#define RT_I2C_ADDR_10BIT       (1u << 2)  /* this is a ten bit chip address */
#define RT_I2C_NO_START         (1u << 4)
#define RT_I2C_IGNORE_NACK      (1u << 5)
#define RT_I2C_NO_READ_ACK      (1u << 6)  /* when I2C reading, we do not ACK */
#define RT_I2C_NO_STOP          (1u << 7)

struct rt_i2c_msg
{
    rt_uint16_t addr;
    rt_uint16_t flags;
    rt_uint16_t len;
    rt_uint8_t  *buf;
};

static rt_ssize_t ra_i2c_mst_xfer(struct rt_i2c_bus_device *bus,
                                  struct rt_i2c_msg msgs[],
                                  rt_uint32_t num)
{
    rt_size_t i;
    struct rt_i2c_msg *msg = msgs;
    RT_ASSERT(bus != RT_NULL);
    fsp_err_t err = FSP_SUCCESS;
    bool restart = false;

    struct ra_i2c_handle *ra_i2c = rt_container_of(bus, struct ra_i2c_handle, bus);
    i2c_master_ctrl_t *master_ctrl = ra_i2c->i2c_ctrl;

    for (i = 0; i < num; i++)
    {
        if (msg[i].flags & RT_I2C_NO_START)
        {
            restart = true;
        }
        if (msg[i].flags & RT_I2C_ADDR_10BIT)
        {
            R_IIC_MASTER_SlaveAddressSet(master_ctrl, msg[i].addr, I2C_MASTER_ADDR_MODE_10BIT);
        }
        else
        {
            R_IIC_MASTER_SlaveAddressSet(master_ctrl, msg[i].addr, I2C_MASTER_ADDR_MODE_7BIT);
        }

        if (msg[i].flags & RT_I2C_RD)
        {
            err = R_IIC_MASTER_Read(master_ctrl, msg[i].buf, msg[i].len, restart);
            if (FSP_SUCCESS == err)
            {
                if (RT_EOK != validate_i2c_event(ra_i2c))
                {
                    LOG_E("POWER_CTL reg I2C read failed");
                    break;
                }
            }
            /* handle error */
            else
            {
                /* Write API returns itself is not successful */
                LOG_E("R_I2C_MASTER_Write API failed");
                break;
            }
        }
        else
        {
            err = R_IIC_MASTER_Write(master_ctrl, msg[i].buf, msg[i].len, restart);
            if (FSP_SUCCESS == err)
            {
                if (RT_EOK != validate_i2c_event(ra_i2c))
                {
                    LOG_E("POWER_CTL reg I2C write failed");
                    break;
                }
            }
            /* handle error */
            else
            {
                /* Write API returns itself is not successful */
                LOG_E("R_I2C_MASTER_Write API failed");
                break;
            }
        }
    }
    return (rt_ssize_t)i;
}

       从这里面,我们可以发现,实际上master_xfer函数的实现是基于struct rt_i2c_msg中的变量flags来维护的,这个标记决定了当前的msg是读还是写,地址位长度是十位还是八位(这里的长度设置,个人觉得可能是个bug,因为没必要在此设置,硬件I2C框架中的control接口,就有设置总线地址长度的入口,没必要在每次写数据时再去重新指定一遍地址位长度)。

       另外,前面存在疑惑的点,rt_event_init貌似在这里面没体现,但是进一步观察代码validate_i2c_event,就会发现其中的蹊跷。

// 这个函数在注册函数时是以回调的形式给到瑞萨的接口上的,个人理解为瑞萨的中断回调
void i2c_master_callback(i2c_master_callback_args_t *p_args)
{
    rt_interrupt_enter();
    if (NULL != p_args)
    {
        /* capture callback event for validating the i2c transfer event*/
        struct ra_i2c_handle *obj = (struct ra_i2c_handle *)p_args->p_context;
        uint32_t event = 0;
        RT_ASSERT(obj != RT_NULL);
        switch (p_args->event)
        {
        case I2C_MASTER_EVENT_ABORTED:
            event |= RA_SCI_EVENT_ABORTED;
            break;
        case I2C_MASTER_EVENT_RX_COMPLETE:
            event |= RA_SCI_EVENT_RX_COMPLETE;
            break;
        case I2C_MASTER_EVENT_TX_COMPLETE:
            event |= RA_SCI_EVENT_TX_COMPLETE;
            break;
        }
        rt_event_send(&obj->event, event);
    }
    rt_interrupt_leave();
}

static rt_err_t validate_i2c_event(struct ra_i2c_handle *handle)
{
    rt_uint32_t event = 0;
    if (RT_EOK != rt_event_recv(&handle->event, RA_SCI_EVENT_ALL, RT_EVENT_FLAG_OR | RT_EVENT_FLAG_CLEAR, (int32_t)rt_tick_from_millisecond(100), &event))
    {
        return -RT_ETIMEOUT;
    }
    if ((event & (RA_SCI_EVENT_ABORTED | RA_SCI_EVENT_ERROR)) == 0)
    {
        return RT_EOK;
    }

    return -RT_ERROR;
}

       从这里,基本上就可以看出问题了,实际上加这个逻辑,仅仅是为了实现等待硬件执行完成罢了。但疑问是,这个使用完成量会不会更好?

总结

   分析至此,其实我们已经可以获取到硬件I2C的注册模板了,其注册模板如下:

#include <rtdevice.h>
#include <rtthread.h>
#include "board.h"

#include <stdlib.h>

#ifdef BSP_USING_HW_I2C

#define DRV_DEBUG
#define LOG_TAG             "drv.hwi2c"
#include <drv_log.h>

// 芯片接口头文件
#include <xxxx.h>

// 资源结构体
struct xx_xx_i2c_handle
{
    struct rt_i2c_bus_device bus;
    char bus_name[RT_NAME_MAX];
    const i2c_master_cfg_t *i2c_cfg;
    void *i2c_ctrl;
};


static rt_ssize_t i2c_mst_xfer(struct rt_i2c_bus_device *bus,
                                  struct rt_i2c_msg msgs[],
                                  rt_uint32_t num)
{
    rt_size_t i;
    struct rt_i2c_msg *msg = msgs;
    RT_ASSERT(bus != RT_NULL);

    // 获取I2C设备
    struct ra_i2c_handle *ra_i2c = rt_container_of(bus, struct ra_i2c_handle, bus);

    for (i = 0; i < num; i++)
    {
        if (msg[i].flags & RT_I2C_RD)
        {
            // 硬件I2C主模式读实现
        }
        else
        {
            // 硬件I2C主模式写实现
        }
    }
    return (rt_ssize_t)i; // 返回实际读写数据量
}

static const struct rt_i2c_bus_device_ops i2c_ops =
{
    .master_xfer        = i2c_mst_xfer,
    .slave_xfer         = RT_NULL,
    .i2c_bus_control    = RT_NULL
};

int hw_i2c_init(void)
{
    struct ra_i2c_handle *ra_i2cs;

    ra_i2cs = (struct ra_i2c_handle *)rt_malloc(sizeof(struct ra_i2c_handle));
    ra_i2cs->bus.ops = &i2c_ops;
    ra_i2cs->bus.priv = 0;
    rt_memcpy(ra_i2cs->bus_name, "I2C0", sizeof("I2C0"));

    //硬件I2C资源初始化
    
    //注册硬件I2C
    rt_i2c_bus_device_register(&ra_i2cs->bus, ra_i2cs->bus_name);

    return 0;
}
INIT_DEVICE_EXPORT(hw_i2c_init);

#endif /* BSP_USING_I2C */

有了这套模板,其实后面做硬件I2C适配时,直接往里面填功能便可快速适配硬件I2C。







关键词: 瑞萨     适配     硬件     驱动     解析     I2C    

专家
2024-09-29 22:40:40     打赏
2楼

感谢楼主分享


专家
2024-09-29 22:41:32     打赏
3楼

感谢楼主分享


共3条 1/1 1 跳转至

回复

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