平台 | 内核版本 | 安卓版本 |
---|---|---|
RK3399 | Linux4.4 | Android7.1 |
硬件框图
spi 重要的数据结构
spi_board_info
该结构主要用来匹配 spi master
struct spi_board_info {和初始化 spi_device
char modalias[SPI_NAME_SIZE];
const void *platform_data;
void *controller_data;
int irq;
u32 max_speed_hz;
u16 bus_num;
u16 chip_select;
u8 mode;
};
其中重要的是mode:
包括属于哪种传输模式,是否是三线模式,片选信号激活时候电平高低等
SPI 模块为了和外设进行数据交换,根据外设工作要求,其输出串行同步时钟极性和相位可以进行配置
spi_board_info{}.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 * /
如果 CPOL=0,串行同步时钟的空闲状态为低电平;
如果 CPOL=1,串行同步时钟的空闲状态为高电平。
时钟相位(CPHA)能够配置用于选择两种不同的传
输协议之一进行数据传输。
如果 CPHA=0,在串行同步时钟的第一个跳变沿(上
升或下降)数据被采样;
如果 CPHA=1,在串行同步时钟的第二个跳变沿(上升
或下降)数据被采样。
SPI 主模块和与之通信的外设备时钟相位和极性应该一致。SPI 主模块和与之通信的外设备时钟相位和极性应该一致。
其一,主设备SPI 时钟和极性的配置应该由外设来决定;
其二,二者的配置应该保持一致,即主
设备的 SDO 同从设备的 SDO 配置一致,主设备的 SDI 同从设备的 SDI 配置一致。因为主从设备是在 SCLK 的控制下,同时发送和接收数据,并通过 2 个双向移位寄存器来交换数据。
boardinfo
struct boardinfo {
struct list_head list;
unsigned n_board_info;
struct spi_board_info board_info[0];
};
spi_board_info
与 与 boardinfo
的关系:
pi_board_info
:每个 spi_board_info
代表一个设备,spi_board_info
以数组的形式组织
boardinfo
:boardinfo
会将 spi_board_info[]
的内容拷贝过来填充其board_info[]
结构,相关函数为
spi_register_board_ino(struct spi_board_info const *info, unsigned n)
,其中 n 代表有多少个 spi_board_info
这
样的数组元素。系统里可能有多个board_info
,所有的 boardinfo
都会连接到一个全局 SPI
设备链表。
spi_device
spi_device{}
用于spi slave
和 和cpu
之间数据传输 ,其大部分成员由spi_board_info{}
结构来初始化
struct spi_device {
struct device dev;
struct spi_master *master;
u32 max_speed_hz;
u8 chip_select;
u8 mode;
u8 bits_per_word;
int irq;
void *controller_state;
void *controller_data;
char modalias[SPI_NAME_SIZE];//SPI_NAME_SIZE=32
};
成员变量 max_speed_hz
;chip_select
;mode
;controller_data
;modalias
都是来自spi_board_info{}
master
指向所属的 master
控制器
bits_per_word
对于 SPI
总线协议来说,传输单位可以是 4-32 之间的任意bits
,但对于SPI
控制器来说,
bits_per_word
只能是 8/16/32,即需要取整,待收发的数据在内存里都是以主机字节序右对齐存储,SPI 控制
器会自动根据传输单位及大小端进行转换。
spi_master
struct spi_master {
struct device dev;
s16 bus_num;
u16 num_chipselect;
u16 dma_alignment;
u16 mode_bits;
u16 flags;
int (*setup)(struct spi_device *spi);
int (*transfer)(struct spi_device *spi, struct spi_message *mesg);
void (*cleanup)(struct spi_device *spi);
};
transfer
添加一个msg
到spi
控制器的队列.在进行 spi 通信的时候,会先将要传输的 msg
先添加到一个
主控制器的一个队列,稍后再处理这些 msg
。稍后的意思一般是指将这 msg
交给一个工作队列处理。
spi_transfer
struct spi_transfer {
const void *tx_buf;
void *rx_buf;
unsigned len;
dma_addr_t tx_dma;
dma_addr_t rx_dma;
unsigned cs_change:1;
u8 bits_per_word;
u16 delay_usecs;
u32 speed_hz;
struct list_head transfer_list;
};
单个spi_transfer
可表示一次读,一次写或者是一次读写。在SPI controlle
r 驱动下,所有操作常是全双
工的。控制器驱动会先写入tx
的数据,然后读取同样长度的数据。长度指示是len
。如果tx_buff
是空指针,
填充 rx_buff
的时候会输出 0
(为了产生接收的时钟),如果rx_buff
是 NULL
,接收到的数据将被丢弃。
spi_message
struct spi_message {
struct list_head transfers;
struct spi_device *spi;
unsigned is_dma_mapped:1;
void (*complete)(void *context);
void *context;
unsigned actual_length;
int status;
struct list_head queue;
void *state;
};
message 里面有两个链表,其中第一个 transfer 表示将所有的 spi transfer 的链表头,而 queue 一
的 般是在使用具体的 spi master 的时候,可能同时会有多个 msg 要处理,但是同一时段不能处理这么多,
为了不使信息丢失,于是就将所有待处理的 msg