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

共5条 1/1 1 跳转至

瑞萨已适配的SPI驱动分析

助工
2024-10-13 19:48:45     打赏

SPI驱动通用实现模板

   通过前面的分析,我们可以获得以下硬件spi适配模板。

static const struct rt_spi_ops spi_ops =
{
    .configure = hw_spi_configure, // SPI配置函数
    .xfer = spixfer,    // SPI传输函数
};

int hw_spi_init(void)
{
    struct rt_spi_bus *bus = (struct rt_spi_bus *)rt_malloc(sizeof(struct rt_spi_bus));
    const char bus_name[RT_NAME_MAX] = "xxxx";

    // 驱动内部资源初始化
    ...

    // 注册spi总线设备
    rt_err_t err = rt_spi_bus_register(bus, bus_name, &spi_ops);
    if (RT_EOK != err)
    {
        LOG_E("%s bus register failed.", bus_name);
        return -RT_ERROR;
    }

    return RT_EOK;
}
INIT_BOARD_EXPORT(hw_spi_init);

瑞萨对模板的适配

   相对应的,我们可以套着这个模板去看瑞萨适配的驱动部分。

SPI注册接口

static struct ra_spi_handle spi_handle[] =
{
#ifdef BSP_USING_SPI0
    {.bus_name = "spi0", .spi_ctrl_t = &g_spi0_ctrl, .spi_cfg_t = &g_spi0_cfg,},
#endif

#ifdef BSP_USING_SPI1
    {.bus_name = "spi1", .spi_ctrl_t = &g_spi1_ctrl, .spi_cfg_t = &g_spi1_cfg,},
#endif
};

static struct ra_spi spi_config[sizeof(spi_handle) / sizeof(spi_handle[0])] = {0};

static const struct rt_spi_ops ra_spi_ops =
{
    .configure = ra_hw_spi_configure,
    .xfer = ra_spixfer,
};

int ra_hw_spi_init(void)
{
    for (rt_uint8_t spi_index = 0; spi_index < sizeof(spi_handle) / sizeof(spi_handle[0]); spi_index++)
    {
        spi_config[spi_index].ra_spi_handle_t = &spi_handle[spi_index];

        /**< register spi bus */
        rt_err_t err = rt_spi_bus_register(&spi_config[spi_index].bus, spi_handle[spi_index].bus_name, &ra_spi_ops);
        if (RT_EOK != err)
        {
            LOG_E("%s bus register failed.", spi_config[spi_index].ra_spi_handle_t->bus_name);
            return -RT_ERROR;
        }
    }

    if (RT_EOK != rt_event_init(&complete_event, "ra_spi", RT_IPC_FLAG_PRIO))
    {
        LOG_E("SPI transfer event init fail!");
        return -RT_ERROR;
    }
    return RT_EOK;
}
INIT_BOARD_EXPORT(ra_hw_spi_init);

   从设备注册适配上来看,瑞萨已经把spi驱动标准化成对接fsp的结构体struct ra_spi_handle,也就是说,适配fsp适配spi驱动,只需要按照结构体struct ra_spi_handle的要求设置fsp配置即可。另外,驱动中用到了事件集,至于怎么使用的,需要在后面的分析中去了解。

spi配置接口

static rt_err_t ra_hw_spi_configure(struct rt_spi_device *device,
                                    struct rt_spi_configuration *configuration)
{
    RT_ASSERT(device != NULL);
    RT_ASSERT(configuration != NULL);
    rt_err_t err = RT_EOK;

    struct ra_spi *spi_dev =  rt_container_of(device->bus, struct ra_spi, bus);

    /**< data_width : 1 -> 8 bits , 2 -> 16 bits, 4 -> 32 bits, default 32 bits*/
    rt_uint8_t data_width = configuration->data_width / 8;
    RT_ASSERT(data_width == 1 || data_width == 2 || data_width == 4);
    configuration->data_width = configuration->data_width / 8;
    spi_dev->rt_spi_cfg_t = configuration;

    spi_extended_cfg_t spi_cfg = *(spi_extended_cfg_t *)spi_dev->ra_spi_handle_t->spi_cfg_t->p_extend;

    /**< Configure Select Line */
    rt_pin_write(device->cs_pin, PIN_HIGH);

    /**< config bitrate */
#ifdef SOC_SERIES_R7FA8M85
    R_SPI_B_CalculateBitrate(spi_dev->rt_spi_cfg_t->max_hz, SPI_B_CLOCK_SOURCE_PCLK, &spi_cfg.spck_div);
#else
    R_SPI_CalculateBitrate(spi_dev->rt_spi_cfg_t->max_hz, &spi_cfg.spck_div);
#endif

    /**< init */
    err = R_SPI_Open((spi_ctrl_t *)spi_dev->ra_spi_handle_t->spi_ctrl_t, (spi_cfg_t const * const)spi_dev->ra_spi_handle_t->spi_cfg_t);
    /* handle error */
    if (RT_EOK != err)
    {
        LOG_E("%s init failed.", spi_dev->ra_spi_handle_t->bus_name);
        return -RT_ERROR;
    }
    return RT_EOK;
}

   从实现上看,瑞萨的spi并不带cs脚,因此个人觉得瑞萨的spi驱动其实还可以再通过rt_spi_bus_attach_device_cspin封装一层。而从瑞萨的实现上看,瑞萨并未这么操作,而是再定义了一个接口rt_hw_spi_device_attach,其实现如下:

rt_err_t rt_hw_spi_device_attach(const char *bus_name, const char *device_name, rt_base_t cs_pin)
{
    RT_ASSERT(bus_name != RT_NULL);
    RT_ASSERT(device_name != RT_NULL);

    rt_err_t result;
    struct rt_spi_device *spi_device;

    /* attach the device to spi bus*/
    spi_device = (struct rt_spi_device *)rt_malloc(sizeof(struct rt_spi_device));
    RT_ASSERT(spi_device != RT_NULL);

    result = rt_spi_bus_attach_device_cspin(spi_device, device_name, bus_name, cs_pin, RT_NULL);
    if (result != RT_EOK)
    {
        LOG_E("%s attach to %s faild, %d\n", device_name, bus_name, result);
    }

    RT_ASSERT(result == RT_EOK);

    LOG_D("%s attach to %s done", device_name, bus_name);

    return result;
}

虽然 rt_hw_spi_device_attach接口在rtt源码的文档中有推荐,但个人认为这并非最优的实现,而是早期的实现。因为这个接口需要直接将驱动的实现暴露给到应用,而不是经过框架的封装调用。且接口的参数,并不一定是通用的。

spi传输接口

// 很奇怪,两个硬件中断回调处理函数,居然没有被宏包裹
void spi0_callback(spi_callback_args_t *p_args)
{
    rt_interrupt_enter();
    if (SPI_EVENT_TRANSFER_COMPLETE == p_args->event)
    {//发送spi0完成事件
        rt_event_send(&complete_event, RA_SPI0_EVENT);
    }
    rt_interrupt_leave();
}

void spi1_callback(spi_callback_args_t *p_args)
{
    rt_interrupt_enter();
    if (SPI_EVENT_TRANSFER_COMPLETE == p_args->event)
    {//发送spi1完成事件
        rt_event_send(&complete_event, RA_SPI1_EVENT);
    }
    rt_interrupt_leave();
}

static rt_err_t ra_wait_complete(rt_event_t event, const char bus_name[RT_NAME_MAX])
{ // 等待对应spi的事件
    rt_uint32_t recved = 0x00;

    if (bus_name[3] == '0')
    {
        return rt_event_recv(event,
                             RA_SPI0_EVENT,
                             RT_EVENT_FLAG_OR | RT_EVENT_FLAG_CLEAR,
                             RT_WAITING_FOREVER,
                             &recved);
    }
    else if (bus_name[3] == '1')
    {
        return rt_event_recv(event,
                             RA_SPI1_EVENT,
                             RT_EVENT_FLAG_OR | RT_EVENT_FLAG_CLEAR,
                             RT_WAITING_FOREVER,
                             &recved);
    }
    return -RT_EINVAL;
}

static spi_bit_width_t ra_width_shift(rt_uint8_t data_width)
{ // 从数据字节书换算成bit数,若是不支持的字节数,则换算成8bit模式
    spi_bit_width_t bit_width = SPI_BIT_WIDTH_8_BITS;
    if (data_width == 1)
        bit_width = SPI_BIT_WIDTH_8_BITS;
    else if (data_width == 2)
        bit_width = SPI_BIT_WIDTH_16_BITS;
    else if (data_width == 4)
        bit_width = SPI_BIT_WIDTH_32_BITS;

    return bit_width;
}

// 收,发,收发函数的实现其实是一样的逻辑,区别仅仅是fsp层的接口差异(具体功能与函数名一致)
static rt_err_t ra_write_message(struct rt_spi_device *device, const void *send_buf, const rt_size_t len)
{
    RT_ASSERT(device != NULL);
    RT_ASSERT(send_buf != NULL);
    RT_ASSERT(len > 0);
    rt_err_t err = RT_EOK;
    struct ra_spi *spi_dev =  rt_container_of(device->bus, struct ra_spi, bus);

    spi_bit_width_t bit_width = ra_width_shift(spi_dev->rt_spi_cfg_t->data_width);
    /**< send msessage */
    err = R_SPI_Write((spi_ctrl_t *)spi_dev->ra_spi_handle_t->spi_ctrl_t, send_buf, len, bit_width);
    if (RT_EOK != err)
    {
        LOG_E("%s write failed.", spi_dev->ra_spi_handle_t->bus_name);
        return -RT_ERROR;
    }
    /* Wait for SPI_EVENT_TRANSFER_COMPLETE callback event. */
    ra_wait_complete(&complete_event, spi_dev->ra_spi_handle_t->bus_name);
    return len;
}

static rt_err_t ra_read_message(struct rt_spi_device *device, void *recv_buf, const rt_size_t len)
{
    RT_ASSERT(device != NULL);
    RT_ASSERT(recv_buf != NULL);
    RT_ASSERT(len > 0);
    rt_err_t err = RT_EOK;
    struct ra_spi *spi_dev =  rt_container_of(device->bus, struct ra_spi, bus);

    spi_bit_width_t bit_width = ra_width_shift(spi_dev->rt_spi_cfg_t->data_width);
    /**< receive message */
    err = R_SPI_Read((spi_ctrl_t *)spi_dev->ra_spi_handle_t->spi_ctrl_t, recv_buf, len, bit_width);
    if (RT_EOK != err)
    {
        LOG_E("\n%s write failed.\n", spi_dev->ra_spi_handle_t->bus_name);
        return -RT_ERROR;
    }
    /* Wait for SPI_EVENT_TRANSFER_COMPLETE callback event. */
    ra_wait_complete(&complete_event, spi_dev->ra_spi_handle_t->bus_name);
    return len;
}

static rt_err_t ra_write_read_message(struct rt_spi_device *device, struct rt_spi_message *message)
{
    RT_ASSERT(device != NULL);
    RT_ASSERT(message != NULL);
    RT_ASSERT(message->length > 0);
    rt_err_t err = RT_EOK;
    struct ra_spi *spi_dev =  rt_container_of(device->bus, struct ra_spi, bus);

    spi_bit_width_t bit_width = ra_width_shift(spi_dev->rt_spi_cfg_t->data_width);
    /**< write and receive message */
    err = R_SPI_WriteRead((spi_ctrl_t *)spi_dev->ra_spi_handle_t->spi_ctrl_t, message->send_buf, message->recv_buf, message->length, bit_width);
    if (RT_EOK != err)
    {
        LOG_E("%s write and read failed.", spi_dev->ra_spi_handle_t->bus_name);
        return -RT_ERROR;
    }
    /* Wait for SPI_EVENT_TRANSFER_COMPLETE callback event. */
    ra_wait_complete(&complete_event, spi_dev->ra_spi_handle_t->bus_name);
    return message->length;
}

static rt_ssize_t ra_spixfer(struct rt_spi_device *device, struct rt_spi_message *message)
{
    RT_ASSERT(device != RT_NULL);
    RT_ASSERT(device->bus != RT_NULL);
    RT_ASSERT(message != RT_NULL);

    rt_err_t err = RT_EOK;

    // 如果配置为带cs,则将cs置为使能状态
    if (message->cs_take && !(device->config.mode & RT_SPI_NO_CS) && (device->cs_pin != PIN_NONE))
    {
        if (device->config.mode & RT_SPI_CS_HIGH)
            rt_pin_write(device->cs_pin, PIN_HIGH);
        else
            rt_pin_write(device->cs_pin, PIN_LOW);
    }

    // 传输消息
    if (message->length > 0)
    {
        if (message->send_buf == RT_NULL && message->recv_buf != RT_NULL)
        {
            /**< receive message */
            err = ra_read_message(device, (void *)message->recv_buf, (const rt_size_t)message->length);
        }
        else if (message->send_buf != RT_NULL && message->recv_buf == RT_NULL)
        {
            /**< send message */
            err = ra_write_message(device, (const void *)message->send_buf, (const rt_size_t)message->length);
        }
        else if (message->send_buf != RT_NULL && message->recv_buf != RT_NULL)
        {
            /**< send and receive message */
            err =  ra_write_read_message(device, message);
        }
    }

    // 若带cs,则将cs置为禁用状态
    if (message->cs_release && !(device->config.mode & RT_SPI_NO_CS) && (device->cs_pin != PIN_NONE))
    {
        if (device->config.mode & RT_SPI_CS_HIGH)
            rt_pin_write(device->cs_pin, PIN_LOW);
        else
            rt_pin_write(device->cs_pin, PIN_HIGH);
    }
    return err;
}

在这里,我们可以看到,前面不明白如何使用的事件集,被用作了不同SPI收发完成事件的消息同步了。

总结

   从实现上来说,个人认为RTT并没有严格要求SPI驱动需要做到什么程度(是否支持spi),因此在各家的实现上,spi的驱动有些细微的差异,有些驱动强制绑定了cs接口,有些有没有绑定,导致上层使用上会有一些细微的差异。





关键词: 瑞萨     适配     spi          驱动     实现     分析    

专家
2024-10-13 19:57:50     打赏
2楼

谢谢分享


专家
2024-10-14 08:02:19     打赏
3楼

感谢分享


专家
2024-10-14 19:45:19     打赏
4楼

感谢分享


专家
2024-10-16 13:50:21     打赏
5楼

谢谢楼主分享!瑞萨的开发,一言难尽。折腾过一次,就打了退堂鼓。


共5条 1/1 1 跳转至

回复

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