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