一、spi协议介绍
1、硬件连接
DO(MOSI):Master Output ,Slave Input;SPI主控用来发出数据,SPI从设备用来接受数据
DI(MISO):Master Input,Slave Output;SPI主控用来接受数据,SPI从设备用来发送数据
SCK:时钟
CS:芯片选择引脚
2、传输实例
假设现在主控芯片要传输一个0x56数据给SPI Flash,时序如下:
首先CS0先拉低选中SPI Flash,0x56的二进制就是0b0101 0110,因此在每个SCK时钟周期,DO输出对应的电平。SPI Flash会在每个时钟周期的上升沿读取DO上的电平。
3、SPI模式
在spi协议中,有两个值来确定SPI的模式。
CPOL:表示SPICLK的初始电平,0为低电平,1为高电平
CPHA:表示相位,即第一个还是第二个时钟沿采样数据,0为第一个时钟沿,1为第二个时钟沿
CPOL | CPHA | 模式 | 含义 |
0 | 0 | 0 | SPICLK初始电平为低电平, 在第一个时钟沿采样数据 |
0 | 1 | 1 | SPICLK初始电平为低电平, 在第二个时钟沿采样数据 |
1 | 0 | 2 | SPICLK初始电平为高电平, 在第一个时钟沿采样数据 |
1 | 1 | 3 | SPICLK初始电平为高电平, 在第二个时钟沿采样数据 |
我们常见的是模式0和,因为他们都是在上升沿采集数据,不用去在乎初始电平是什么,只要在上升沿采集数据就行 |
二、SPI总线设备
平台总线设备驱动模型我们前面已经有讲解:
左边注册一个platform_driver结构体,里面代码比较固定,通用;
右边注册一个platform_device结构体,里面书硬件资源;
可以在C文件中注册platform_device;
也可在设备树中创建一个节点,内核解析设备树时注册platform_device
有前面的硬件连接图我们可知,SPI控制器有驱动程序,提供SPI的传输能力。
SPI设备也有自己的驱动程序,提供SPI设备的访问能力:
他知道怎么访问这个设备,这个设备的数据含义是什么
他会调用SPI控制器的函数来收发数据。
1、spi控制器数据结构
Linux中使用spi_master结构体描述SPI控制器,里面最重要的成员就是transfer函数指针:
2、spi设备数据结构
Linux中使用spi_device结构体描述SPI设备,里面记录有设备的片选引脚、频率、挂载在那个SPI控制器下面:
3、SPI设备驱动
Linux中使用spi_driver结构体描述SPI设备驱动:
三、SPI驱动框架
1、spi控制器驱动程序
SPI控制器的驱动程序是基于平台总线设备驱动模型来实现:
在设备树里描述SPI控制器的硬件信息,在设备树子节点里描述挂在下面的SPI设备信息
在platform_driver中提供一个probe函数
他会注册一个spi_master
还会解析设备树子节点,创建spi_device结构体
2、SPI设备驱动程序
跟平台总线设备模型类似,Linux中也有一个SPI总线设备驱动模型:
左边是一个spi_driver,使用C文件实现,里面有id_table表示能支持哪些SPI设备,有probe函数
右边是spi_device,用来描述SPI设备,比如他的片选引脚、频率
可以来自设备树:比如由SPI控制器驱动程序解析设备树后创建、注册spi_devcie
可以来自C文件:比如使用spi_register_board_info创建、注册spi_device
四、SPI设备树处理过程
1、spi_device结构体
struct spi_device {
struct device dev;
struct spi_master *master;
u32 max_speed_hz;
u8 chip_select;
u8 bits_per_word;
u16 mode;
#define SPI_CPHA 0x01 /* clock phase */
#define SPI_CPOL 0x02 /* clock polarity */
#define SPI_MODE_0 (0|0) /* (original MicroWire) */
#define SPI_MODE_1 (0|SPI_CPHA)
#define SPI_MODE_2 (SPI_CPOL|0)
#define SPI_MODE_3 (SPI_CPOL|SPI_CPHA)
#define SPI_CS_HIGH 0x04 /* chipselect active high? */
#define SPI_LSB_FIRST 0x08 /* per-word bits-on-wire */
#define SPI_3WIRE 0x10 /* SI/SO signals shared */
#define SPI_LOOP 0x20 /* loopback mode */
#define SPI_NO_CS 0x40 /* 1 dev/bus, no chipselect */
#define SPI_READY 0x80 /* slave pulls low to pause */
#define SPI_TX_DUAL 0x100 /* transmit with 2 wires */
#define SPI_TX_QUAD 0x200 /* transmit with 4 wires */
#define SPI_RX_DUAL 0x400 /* receive with 2 wires */
#define SPI_RX_QUAD 0x800 /* receive with 4 wires */
int irq;
void *controller_state;
void *controller_data;
char modalias[SPI_NAME_SIZE];
int cs_gpio; /* chip select gpio */
/* the statistics */
struct spi_statistics statistics;
/*
* likely need more hooks for more protocol options affecting how
* the controller talks to each chip, like:
* - memory packing (12 bit samples into low bits, others zeroed)
* - priority
* - drop chipselect after each word
* - chipselect delays
* - ...
*/
};
各个成员含义如下:
- max_speed_hz:该设备能支持的SPI时钟最大值
- chip_select:是这个spi_master下的第几个设备
- 在spi_master中有一个cs_gpio数组,里面存放有下面各个spi设备的片选引脚
- spi_device的片选引脚就是:cs_gpio[spi_device.chip_select]
- cs_gpio:这是可选项,也可以把spi_device的片选引脚记录在这里
- bits_per_word:每个基本的SPI传输涉及多少位
- word:我们使用SPI控制器时,一般是往某个寄存器里写入数据,SPI控制器就会把这些数据一位一位地发送出去
- 一个寄存器是32位的,被称为一个word(有时候也称为double word)
- 这个寄存器里多少位会被发送出去?使用bits_per_word来表示
- 扩展:bits_per_word是可以大于32的,也就是每次SPI传输可能会发送多于32位的数据,这适用于DMA突发传输
- mode:含义广泛
- SPI_CPHA:在第一个周期采样,还是在第2个周期采样?
- SPI_CPOL:平时时钟极性
- SPI_CPHA和SPI_CPOL组合起来就可以得到4种模式
- SPI_MODE_0:平时SCK为低(SPI_CPOL为0),在第1个周期采样(SPI_CPHA为0)
- SPI_MODE_1:平时SCK为低(SPI_CPOL为0),在第2个周期采样(SPI_CPHA为1)
- SPI_MODE_2:平时SCK为高(SPI_CPOL为1),在第1个周期采样(SPI_CPHA为0)
- SPI_MODE_3:平时SCK为高(SPI_CPOL为1),在第2个周期采样(SPI_CPHA为1)
- SPI_CS_HIGH:一般来说片选引脚时低电平有效,SPI_CS_HIGH表示高电平有效
- SPI_LSB_FIRST:
- 一般来说先传输MSB(最高位),SPI_LSB_FIRST表示先传LSB(最低位);
- 很多SPI控制器并不支持SPI_LSB_FIRST
- SPI_3WIRE:SO、SI共用一条线
- SPI_LOOP:回环模式,就是SO、SI连在一起
- SPI_NO_CS:只有一个SPI设备,没有片选信号,也不需要片选信号
- SPI_READY:SPI从设备可以拉低信号,表示暂停、表示未就绪
- SPI_TX_DUAL:发送数据时有2条信号线
- SPI_TX_QUAD:发送数据时有4条信号线
- SPI_RX_DUAL:接收数据时有2条信号线
- SPI_RX_QUAD:接收数据时有4条信号线
2、SPI Master设备树
在设备树中,对于SPI Master,必须的属性如下:
- #address-cells:这个SPI Master下的SPI设备,需要多少个cell来表示它的片选引脚
- #size-cell:必须设置为0
- compatible:根据他找到SPI Master驱动
可选属性如下:
- cs-gpio:SPI Master可以使用多个GPIO当做片选,可以在这个属性列出那些GPIO
- num-cs:片选引脚总数
其他属性都是驱动程序相关的,不同的SPI Master驱动程序要求的属性可能不一样。
3、SPI Device设备树
在SPI Master对应的设备树节点下,每一个子节点都对应一个SPI设备,这个SPI设备连接在该SPI Master下面。
- compatible:根据他找到SPI Device驱动
- reg:用来表示它使用哪个片选引脚
- spi-max-frequency:必选,该SPI设备支持的最大SPI时钟
可选属性:
- spi-cpol:这是一个空属性,表示CPOL为1,即平时SPI时钟为低电平
- spi-cpha:这是一个空属性,表示CPHA为1,即在时钟的第二个边沿采样数据
- spi-cs-high:这是一个空属性,表示片选引脚高电平有效
- spi-3wire:这是一个空属性,表示使用SPI三线模式
- spi-lsb-first:这是一个空属性,表示使用SPI传输数据时先传输最低位(LSB)
- spi-tx-bus-width:表示有几条MOSI引脚;没有这个属性时默认只有1条MOSI引脚
- spi-rx-bus-width:表示有几条MISO引脚;没有这个属性时默认只有1条MISO引脚
- spi-rx-delay-us:单位是毫秒,表示每次读传输后要延时多久
- spi-tx-delay-us:单位是毫秒,表示每次写传输后要延时多久
4、设备树实例
使用GPIO模拟的SPI控制器
IMX6ULL SPI控制器
以上就是SPI的协议和驱动框架,以及驱动的设备在设备树中的实现