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接口,有些有没有绑定,导致上层使用上会有一些细微的差异。

 
					
				
 
			
			
			
						
			 
					
				 
					
				 
					
				 
					
				 我要赚赏金
 我要赚赏金 STM32
STM32 MCU
MCU 通讯及无线技术
通讯及无线技术 物联网技术
物联网技术 电子DIY
电子DIY 板卡试用
板卡试用 基础知识
基础知识 软件与操作系统
软件与操作系统 我爱生活
我爱生活 小e食堂
小e食堂

