SPI(Serial Peripheral Interface)串行外设接口,是一种高速、全双工的通信总线,只占用芯片的四个引脚,分别为数据输入(SDI)、数据输出(SDO)、时钟信号(SCLK)、片选信号(CS),目前越来越多的芯片集成了这种方式。通常情况下,一个SPI主控器能外接多个从设备,然后通过CS片选信号选择从设备,通过SDI、SDO进行数据的传输。
1、硬件连接
SPI主控制器与从设备的连接示意图如下:
SPI主控器与从设备连接示意图
2、SPI驱动架构分析
SPI驱动框架可以分为SPI核心层、SPI控制器驱动层、SPI设备驱动层。
2.1、SPI核心层
SPI核心层主要负责注册SPI总线和提供通用API接口,与平台无关。核心层代码在源码目录的<drivers/spi/spi.c>,主要定义了总线类型和主控制器设备类,总线类型的代码如下:
上述程序定义了SPI总线类型,通过bus_register()函数,将SPI总线注册进总线,成功注册后在sys/bus下即可找到spi节点。
控制器设备类代码如下:
上述程序主要定义了SPI总线主控制器的设备类,通过调用class_register()函数注册设备类,成功注册后,在/sys/class目录下即可找到spi_master文件节点。
2.2、SPI控制器驱动层
spi_master结构体对应着主控制器驱动,在<include/linux/spi/spi.h>文件中定义,如下:
注:bus_num通常从0开始计数,比如控制器有3路SPI,则bus_num设置为2
在<drivers/spi/spi.c>中定义了三个函数用于分配、注册和注销spi_master
为spi_master分配空间
注册spi_master
注销spi_master
3、SPI设备驱动层
在<include/linux/spi/spi.h>中定义了描述spi设备信息的结构体spi_device,如下:
向总线注册设备时还需要spi_board_info结构体,它的字段定义时大部分与spi_device相同,定义在<include/linux/spi/spi.h>中,如下:
定义好spi_board_info后,就可以向SPI总线注册设备了。注册设备的API在<drivers/spi/spi.c>中被定义,如果SPI控制器已经被加载,调用:
如果在板子的初始化代码中,调用以下API注册函数:
spi_driver结构体在<include/linux/spi/spi.h>中定义,代码如下:
定义好spi_driver后,在<drivers/spi/spi.c>通过spi_register_driver()可以完成驱动注册,spi_register_driver如下:
178~184行是给spi_driver结构体赋值,185行注册驱动,187行将spi_register_driver导入内核符号表,以供其他模块使用。
SPI数据传输最核心的结构体是spi_transfer,在<include/linux/spi/spi.h>中定义,如下:
SPI传输数据的另一个核心数据结构是spi_message,代码定义在<include/linux/spi/spi.h>,每次SPI传输数据中,都可能包含几个数据包,这时就应该通过spi_message结构体拼接起来。如下:
在<include/linux/spi/spi.h>中定义了一些spi相关接口函数,对于SPI设备驱动编程而言,通常只关心spi_write()和spi_read()两个函数即可。
spi_write()完成同步数据发送
spi_read()完成同步数据接收