SPI全双工通信解读和调试问题分析汇总

写在前言:

  致敬对这篇文档感兴趣的所有读者,关于对spi通信的解读,目前花费大概几个星期进行相关验证和调试,在这个过程中发生了很多奇怪的现象,一般来说spi控制形式多种多样,对于spi的全双工、半双工通信形式也会有所不同,这篇文档主要是帮助大家解读spi不同机制通信和相关问题分析,以供后期方便类似问题及时参考。本文档重点解读spi下相关控制机制,其中包括spi的控制器形式和spi的gpio通信形式多方位解读。

  • 解读spi.c文件

想必大家对spi基本原理有所了解,如还有不怎么了解的可参考我发布的写文章-CSDN创作中心这篇文档,可以帮助大家熟悉spi是什么,做什么,怎么做的!

接下来就是浓墨重彩部分啦,在这个文件中主要看spi_sync、spi_async两个函数逻辑,首先解读一下spi_sync函数:

1.1 spi_sync函数解读

从上图中分析,单一指令进入就上锁(这里会产生不可避免的阻塞),此时其他指令无法进入,必须当前指令释放控制权方可操作,重点看看_spi_sync函数,也是在这个函数里面是最容易有问题的部分:

1.2 _spi_sync函数解读

对于这个全双工函数首先看spi_validate函数,这个函数是对status状态进行合法性校验,同时这个状态也会传给上层的iotcl逻辑中(return status),再看trace_spi_submit函数,这个函数进行spi消息的提交,上报给应用层控制,而这个消息是通过_spi_queued_transfer消息队列函数进行存储的,每次存储就返回这个状态,通过返回的这个状态来进行_spi_pump_messages函数进行push out。通过wait_for_completion函数进行等待全双工通信字节传输完成,这里的一个坑就是阻塞问题,没有解决会一直通过这个状态等待,这个状态我的想法是在wait函数中进行改变,阻塞等待造成cpu其他事件无法继续处理,所以在这里我添加了status=message->status = 0进行置位,并通过message->complete函数进行消息内容写入完成信息的状态告知,这样cpu才会继续处理其他指令事件。

 1.3 spi_async函数解读:

对异步通信形式这里我不做太多说明,其和同步通信有点类似。重点需要关注_spi_async这个函数逻辑是如何执行的。

1.4 _spi_async函数解读:

对于异步通信这里不像同步通信那么复杂,通过spi->controller控制器进行数据传输并通过submit函数进行消息提交,直接返回提交状态即可。

  • 解读spidev.c文件:

2.1 file_operations驱动内核与应用空间交互结构体:

通过这个结构体知晓,重点关注spidev_write、spidev_read、spidev_ioctl、spidev_open这几个函数逻辑,用户空间也是通过结构体中这几个函数进行spi的相关操控。(对这个结构体不熟悉可参考我的这篇文档:展锐平台led灯调试总结 - ODMSWA-BSP - Confluence (ikotek.com)四、解析led-class文件中)。

2.2 spidev_open函数解读:

对spi的open函数也是通过status的状态进行判别的,所以就其根源,当前状态没有及时改变就会造成阻塞,阻塞导致延时的一个原因。

2.3 spi_ioctl函数解读:

应用层便是通过相应的cmd到达驱动相关判断逻辑进行spi的读写、模式、频率等进行控制,这里不太细讲,重点关注在spidev_get_ioc_message函数和spidev_message函数。

2.4 spidev_get_ioc_message函数解读:

这个函数才是咱们全双工通信最关键的地方,应用层通过spi_ioc_transfer结构体内context的赋值会通过这个函数进入驱动逻辑,并且这个结构体内的delay也是导致驱动通信延时的一个存在。重点看一下_IOC_NR(SPI_IOC_MESSAGE(n)),这个逻辑其实跟应用层设置相关,具体如下:

这个n代表的就是spi每次发送过去的时钟和数据的包数量。其具体实际通过这个函数里面的命令进行判断控制。在这里多说一句,就是在这个spi_ioc_transfer结构体中的延时delay,

其实际是通过这个delay微秒级别延时去进行每条指令从传输到完成之间的延时,这个延时会影响实际通信的速率,同时消耗cpu资源等待,肯定有人会问,这是为何,还记得上面说的wait等待函数吧,这里的延时会造成wait一直处于等待接收状态直到这个动作执行完成才会释放cpu资源。再看下一句,memdup_user函数,其和set_user、get_user、copy_to_user函数作用相似,可节省时钟周期。

2.5 spidev_message函数解读:

对于spidev_message函数,主要的工作是负责通信传输的工作,来实际看一下驱动逻辑吧。

看这里,首先明白一个点就是消息,在开始时候就是消息队列的初始化工作,并申请相应的内存空间,咱们关注的是copy_from_user函数和copy_to_user函数,这两个函数是关键,copy_from_user函数,实际从应用层空间赋值数据到位移寄存器然后传输到从机,copy_to_user函数实际从位移寄存器获取数据通过驱动到应用层操作。细看copy_from_user函数之后还有一个动作就是spi_message_add_tail函数,这个是在队列添加通信消息,我是这么理解的,就比如我通信多少次这个message就会有多少个,而每个message都会有一个status状态,通过状态进行入队和出队操作,向下面的spidev_sync全双工通信处理。

到这里spi控制器部分的讲解过程大致就明白了,对于spi实际大家只要把控住主线分析起来就比较可以找到问题原因(通信过程中产生阻塞导致延时的现象)所在。

  • 解读spi-gpio.c文件

思考一下,如果没有spi控制器操作的话,那么怎么进行spi通信呢?,那么接下来就讲到重点啦,没有spi控制器,还可以用gpio火速救援。接下来就解读一下spi-gpio文件都干了什么吧。

3.1 spi_gpio_probe函数解读:

对于probe函数一般都是申请一些操作的实际初始化函数,在这个函数中重点关注spi_gpio_probe_dt、spi_gpio_request、spi_bitbang_setup_transfer函数的操作逻辑。因为这三个函数是spi控制的关键。

3.2 spi_gpio_probe_dt函数解读:

这个动作并不陌生,就是对设备树中spi的相关gpio的匹配控制,这里可以多说一点就是设备树怎么配置,看下图:

如果我想在这个基础上继续增加其他gpio口进行封装也是可以的,那么对应需要在这个函数中增加相应gpio匹配逻辑判断。或者说,我不需要sck、miso、mosi、cs这些gpio口那么这些口就注释掉,增加自己需要的口进行匹配。

3.2 spi_gpio_request函数解读:

这个函数主要是申请相应gpio端口方向和电平状态值相关信息,端口方向是通过spi_gpio_alloc函数调用gpio_direction_input函数进行判断方向和电平值。因此咱们通过gpio的spi形式可以增加其他gpio口进行一起通过相应节点控制,当然也可以在cat >/sys/class/kernel/debug/gpio查看所有gpio口并进行相应操作。

3.3 spi_bitbang_setup_transfer函数解读:

这里就是transfer进行通信的入口函数逻辑,有点类似于spidev_iotcl函数逻辑,这里清楚spi_bitbang_start函数就是类似于spi_sync的函数。有这个概念,那么就清楚gpio通信的大概啦。

这里多说一点,对于spi的gpio形式通信,在编译宏中需要注意一点:

CONFIG_SPI_BITBANG=y

CONFIG_SPI_GPIO=y

同时编译,这是因为spi-gpio文件在gpio_transfer时需要使用bitbang文件。

这废话不用多少,只要有解读价值,帮助到大家就是值得的,大家有什么其他问题也可以在评论区留言,我会及时回复相应问题,编写不易,感谢大家一键三连关注、点赞支持走一波,大家的关注是小易持续投笔下去的动力~

  • 4
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

唯我aier

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值