转自:https://blog.csdn.net/DroidPhone/article/details/23367051
SPI是“Serial Peripheral Interface”的缩写,是一种四线制的同步串行通信接口,用来连接微控制器、传感器、存储设备、SPI设备分为主设备和从设备两种,用于通信和控制的 四根线分别是:
CS :片选信号
SCK:时钟信号
MISO:主设备的数据输入、从设备的数据输出脚
MOSI:主设备的数据输出、从设备的数据输入脚
大多情况下,CPU或SOC一侧通常都是工作在主设备模式,所以目前的linux内核版本中,只实现了主模式的驱动框架。
通常,一个SOC中可能存在多个SPI控制器,每个控制器下可以连接多个SPI设备,每个设备有各自独立的CS引脚,每个从设备共享另外3个引脚
工作时序
按照时钟信号和数据信号之间的相位关系,SPI有4种工作时序模式:
用CPOL表示时钟信号的空闲电平状态,,CPOL为0表示时钟信号空闲状态为低电平,1表示时钟信号空闲电平是高电平。另外,我们用CPHA来表示在哪个时钟沿采样数据,CPHA为0表示在首个时钟变化沿采样数据,而CPHA为1则 表示要在第二个时钟变化沿采样数据。内核用CPOL和CPHA的组合来表示当前SPI的工作模式:
CPOL = 0, CPHA = 0 模式0
CPOL = 0, CPHA = 1 模式1
CPOL = 1, CPHA = 0 模式2
CPOL = 1, CPHA = 1 模式3
软件架构
在内核的SPI驱动的软件架构中,进行了合理的分成抽象。
SPI控制器驱动程序:
SPI控制器不用关心设备具体功能,只负责把上层协议驱动准备好的数据按SPI总线的时序要求发送给SPI设备,同时吧从设备收到的数据返回给上层协议驱动,因此,内核把SPI控制器的驱动程序独立出来,SPI控制器驱动负责控制具体的硬件,诸如DMA和中断操作等等,因为多个上层的协议驱动可能会通过控制器请求数据传输操作,所以SPI控制器驱动同时也要负责对这些请求进行队列管理,保证先进先出的原则。
SPI通用接口封装层
为了简化SPI驱动程序的编程工作,同时也降低协议驱动程序和控制驱动程序的藕合程度,内核把控制器驱动和协议驱动的一些通用操作封装成标准的接口,加上一些通用的逻辑操作,组成了SPI通用接口封装层,这样的好处是,对于控制器驱动程序,只要实现标准的接口和回调API,并把它注册到通用接口层即可,无需直接和协议层驱动程序进行交互,而对于协议层驱动程序来说,只需要通过接口层提供的API即可完成设备注册和驱动注册,并通过通用接口层的API完成数据传输,无需关注SPI控制器驱动的实现细节。
SPI协议驱动程序
上面我们提到,控制器驱动程序并不清楚和关注设备的具体功能,SPI设备的具体功能是由SPI协议驱动程序完成的,SPI协议驱动程序了解设备的功能和通信数据的协议格式。向下,协议驱动程序通过通用接口层和控制器交换数据,向上,协议驱动通常会根据设备的具体功能和内核的其他子系统进行交互,例如,和MTD层交互以便把SPI接口的存储设备实现为某个文件系统,和TTY子系统交互把SPI设备实现为一个TTY设备,和网络子系统交互以便把一个SPI设备实现为一个网络设备,等等。当然,如果是一个转有的SPI设备,我们也可以按设备的协议要求,实现自己的专有协议驱动。
SPI通用设备驱动程序
有时候,考虑到连接在SPI控制器上的设备的可能性,在内核没有配备相应的协议驱动程序,对于这种情况,内核为我们准备了通用的SPI设备驱动程序,该通用设备驱动程序向用户空间提供了控制SPI的控制接口,具体的协议控制和数据传输工作,交由用户空间根据具体的设备来完成,在这种方式中,真能采用同步的方式和SPI设备进行通信,所以通常用于一些数据量较少的简单SPI设备。