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