一、SPI协议
SPI是英语Serial Peripheral interface的缩写,顾名思义就是串行外围设备接口。是Motorola首先在其MC68HCXX系列处理器上定义的一种高速的,全双工,同步的通信总线,并且在芯片的管脚上只占用四根线,节约了芯片的管脚,提供方便,简单易用。
二、SPI硬件接口设计
咱们学习SPI首先得SPI本身是什么开始看起,单纯看代码移植代码其实意义不大,咱们还是摆脱工具人的想法,从硬件接口出发探索整个SPI的设计实现的推导逻辑。
1) 硬件接口
SPI接口共有4根信号线,分别是:片选线、时钟线、串行输出数据线、串行输入数据线,SPI总线模型是一个环形总线结构,由ss(cs)、sck、sdi、sdo构成,其时序其主要是在sck的控制下,两个双向移位寄存器进行数据交换。
- MOSI(masterout slave input)简称MO:主器件数据输出,从器件数据输入
- MISO(masterinput slave output)简称MI:主器件数据输入,从器件数据输出
- SCLK简称CLK:CLK时钟信号,由主器件产生
- SS:从器件使能片选信号,由主器件控制
2)SPI CLK
由于信号有高低之分和相位之分,所以SPI有四种工作模式
CPOL:SPI空闲时的时钟信号电平(1:高电平, 0:低电平)
CPHA:SPI在时钟第几个边沿采样(1:第二个边沿开始, 0:第一个边沿开始)
MODE0和MODE3最常用。
3) MOSI
如字面意思,master output slave input,就是主机向从机发送指令的过程,通常来说,这个发送的信号由sensor的主控发送来获取sensor中的数据。
4) MISO
这里就是主控数据采集的过程了。从机向主机发送获取到的数据,由主机去处理数据的过程。
5) SS也称CS
Cs如字面意思,片选信号,通常来说我们一款平台不止支持一路的spi,OEM芯片设计方可以设计多路SPI接口来供vendor厂商选择,芯片要去知道哪款sensor该工作,就取决于CS这个片选信号。
6) RST,AVDD,INT
这里补上三个非标信号,通常来说,SPI只是一个通信接口,但是真正要组装成一款sensor,其中,AVDD,RST这两个输出以及INT反馈出入这三个根线必不可少。他们一个是提供sensor方的电源输入,一个提供sensor方的复位引脚处理常见的异常提供保护,一个提供sensor方反向通信master信号的功能,这三者提供了作为器件的基本功能的保障。
所以一个完整的SPI的工作流程应该如下:
三、SPI时序图
在对SPI有了一个基础的硬件接口认知之后,接下来,我们看下SPI的时序图,学习了SPI的时序图有助于我们对这几个信号之间的协同工作更进一步的了解。
这是一幅比较干净便于理解的SPI的时序通信图,首先还是解释下一些关键性的位置。
1) 时序图采取的是SPI模式0的方式,即CPOL空闲电平为低电平也即高电平有效,CPHA第一个边沿信号进行数据采集。
2) SS 片选信号,拉低有效。
3) 数据采集。上升沿发送、下降沿接收、高位先发送。上升沿到来的时候,sdo上的电平将被发送到从设备的寄存器中。下降沿到来的时候,sdi上的电平将被接收到主设备的寄存器中。
4) MSB数据高位先发送。
接下来我们着重分析下,SPI双工通信如何进行的。
如图:
1.首先CS拉低
2.CLK开始工作。
3.第一个高电平的上升沿数据开始进行采集。
4.MO/MI的数据发出。
5.MI/MO采集数据。
6.重复3~5
由此完成了一个周期内的数据交换,这里的难点在哪里呢,这里核心的难点在于,一定要关注到,MO和MI的同时工作,这里并非是一个周期内只有一条线进行数据采集,而是两根线同时都在进行半周期数据的发送和半周期的数据采集的动作。MO采集的是MI发的sensor的数据,MI接收的是来自MO发送的数据控制指令,通常来说是这种工作机制。
四、指纹SPI设备驱动框架
在对硬件有了较高的基础理解后,其实代码主要就是开始对SPI的接口做的软件实现了。由于平台厂商通常给我们做好了spicontroller以及spicore部分,接下来的总结主要是针对SPI设备驱动部分的。代码部分均来自于linuxkernel开源代码https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/tree/?h=v4.14.258
1)一条总线
Spi总线,spi总线注册、注销
2)三个数据结构
a、Spi_driver
b、spi_transfer
c、spi_message
3)三个使用步骤
a、注册SPI设备调用probe函数及创建class,设置spi通信速率
这段代码很好理解,spi设备驱动注册,匹配的table表是如下spidev_dt_ids,这里需要备注下,dts由于不是本章主讲,dts涉及的内容默认大家已有所了解。
b、配置与SPI设备相关参数
如图所示三个比较关键点,第一个是spi通信的数据结构体数据保存,第二个是设备节点的注册,第三个是设置spi速率。
c、数据发送与接收,以spi write为例
这里直接将kernel源码粘贴下来,给大家展示下这个数据的封装过程,由于具有借鉴的意义,这里的代码一行也没有进行删除。
首先我们看下这里的spidev_write,这个接口承担了和userspace的系统调用接口,由字符设备驱动 fops注册
spidev_sync_write有个核心的数据结构用来支撑双工通信。
tx_buf数据发出,rx_buf,数据接收,len发送长度。
数据经过spi_transfer封装后,由structspi_message组装,最终由spi这个核心数据结构通过spi_sync发出。
数据的读取和这个流程几乎一模一样,由于是双工通信,spi_transfer同时可以以tx输入,rx读出。
截至到目前为止,spi的设备驱动函数基本的基本流程就在此处了,一些基本的spi的ioctl config的设置可参考源码spidev.c即可。
五、总结
个人理解现在这个科技发展的风口下,手机、IOT、车机等终端设备齐放异彩的大背景下,新终端生态的搭建、基础智能设备的整合、大健康的趋势下,Sensor的发展是一个非常关键的驱动力,基础驱动软件能力的掌握也是重中之重,linux给了我们一个很好的平台让我们能在前辈的肩上进行各种高质量的代码学习,我们也需抓住这个机会,在做好本质工作的基础上静心努力钻研,不断前行,祝愿各位也祝愿我自己在技术的道路上越走越远。