核心层代码负责这个框架中通用的部分,满足分层的思想,位于drivers/spi/spi.c。主要承担的工作包括:注册 spi总线,提供基本 SPI 总线操作 API:
int spi_write_then_read(struct spi_device *spi, const u8 *txbuf, unsigned n_tx, u8 *rxbuf, unsigned n_rx);
struct spi_master {
struct device dev;
s16 bus_num; //表示是SPI主机控制器的编号。由平台代码决定
u16 num_chipselect; //控制器支持的片选数量,即能支持多少个spi设备
int (*setup)(struct spi_device *spi); //针对设备设置SPI的工作时钟及数据传输模式等。在spi_add_device函数中调用。
int (*transfer)(struct spi_device *spi, struct spi_message *mesg); //实现数据的双向传输,可能会睡眠
void (*cleanup)(struct spi_device *spi); //注销时调用
};
注意:1. 成员bus_num用于匹配spi总线外设(具体流程看spi_device生成过程)
2. transfer是主控器实现的数据通信函数(spi_sync()函数时实际调用transfer(),同 i2c的 master_xfer)
3. spi外设驱动
struct spi_driver {
int (*probe)(struct spi_device *spi); // 同i2c 在driver和device适配后调用
int (*remove)(struct spi_device *spi); // 同i2c 在取消driver时调用,用于回收资源
void (*shutdown)(struct spi_device *spi);
int (*suspend)(struct spi_device *spi, pm_message_t mesg);
int (*resume)(struct spi_device *spi);
struct device_driver driver;
struct spi_device_id *id_table;
};
注意: driver 和 device的适配到底是成员driver 或 id_table(像 i2c一样)???
和spi_driver相关的常用函数
spi_register_driver(struct spi_driver *p) 、spi_unregister_driver(struct spi_driver *p)
4.spi_device的生成过程
外设的信息被添加到BSP的文件中,行如下:
static structspi_board_info
s3c_spi_devs
[]__initdata = {
{
[0] = {
.modalias = "m25p10",// 实际用于driver和device的匹配
.mode =SPI_MODE_0, //CPOL=0, CPHA=0 此处选择具体数据传输模式
.max_speed_hz = 10000000, //最大的spi时钟频率
/* Connected to SPI-0 as 1st Slave */
.bus_num = 0, //设备连接在spi控制器0上
.chip_select = 0, //片选线号,在S5PC100的控制器驱动中没有使用它作为片选的依据,而是选择了下文controller_data里的方法。
.controller_data = &smdk_spi0_csi[0],
},
};
注册流程:(只需手动添加信息,匹配spi_master 和生成spi_device由系统启动过程自动完成,当然也可以在系统运行状态下手动添加、注册)
1.在SPI协议层中,spi_device是通过spi_register_board_info来注册的(注:上述做法需要在注册spi控制器驱动即spi_master之前)。首先创建一个spi_board_info结构描述spi设备板级信息,然后调spi_register_board_info()将其添加到board_list中。
2.然后才继续调用spi_register_master注册SPI控制器驱动spi_master,此时会调用scan_boardinfo扫描board_list(判断的依据是bus_num是否相等),根据 spi_board_info调用spi_new_device生成spi_device结构,并用spi_add_device添加设备。
结论: 所以spi_device中有一个非常关键的成员struct spi_master *master就是这么来的。想一想这个成员为什么关键?
5. 实际驱动编写过程中常用结构体和函数
结构体:struct spi_transfer 用于单个消息(内存buff)的封装;struct spi_message主要是个链表(队列FIFO的运作方式),将spi_transfer'挂载'到spi_message上。
函数: (实例)
struct spi_transfer t[2]; //定义了两个spi_transfer
struct spi_message m; //定义了两个spi_message
spi_message_init(&m); //初始化其transfers链表,并生成头节点
t[0].
tx_buf = flash->command; //tx 表示写(master -> slave)
t[0].len = CMD_SIZE + FAST_READ_DUMMY_BYTE; //定义第一个transfer的写指针和长度
spi_message_add_tail(&t[0], &m); //添加到spi_message
t[1].
rx_buf = buf; // rx 表示读(slave -> master)
t[1].len = len; //定义第二个transfer的读指针和长度
spi_message_add_tail(&t[1], &m); //添加到spi_message
spi_sync(flash->spi, &m); //追踪代码源码发现,实际传输过程如下:sync->async->transfer(主控制器提供的传输函数)
6.应用层实际使用spi的流程: (实例)
第一:open
第二:ioctl ,ioctl有九种cmd,分别对应不同的arg
a、设置或获取SPI工作模式
SPI_IOC_RD_MODE //获取spi工作模式
SPI_IOC_WR_MODE //设置spi工作 模式
spi_device.mode有以下几种类型(系统提供的宏属性)
#define SPI_MODE_0 (0|0)//SCLK空闲时为低电平,第一个时间沿采样
#define SPI_MODE_1 (0|SPI_CPHA)//SCLK空闲时为低电平,第二个时间沿采样
#define SPI_MODE_2 (SPI_CPOL|0)//SCLK空闲时为高电平,第一个时间沿采样
#define SPI_MODE_3 (SPI_CPOL|SPI_CPHA)//SCLK空闲时为高电平,第二个时间沿采样
#define SPI_CS_HIGH 0x04//片选为高
#define SPI_LSB_FIRST 0x08//低位数据先传输
#define SPI_3WIRE 0x10//三线式,输入输出数据线为一条线
#define SPI_LOOP 0x20//回环模式
#define SPI_NO_CS 0x40//没有片选信号
#define SPI_READY 0x80//
用法:
mode |= SPI_MODE_0 | SPI_CS_HIGH | SPI_LSB_FIRST | SPI_LOOP;
ioctl(fd, SPI_IOC_WR_MODE, &mode);
注意:前面四种是对SCK时钟信号空闲时的电平,和采样时刻的选择,四个只能选择其中一种,后面的五种可以用或的形式选择任意几个,使用方法如上
b、设置或获取SPI读写数据位数
SPI_IOC_RD_BITS_PER_WORD //获取 每字多少位
SPI_IOC_WR_BITS_PER_WORD //设置 每字多少位
以上两种cmd对用arg是spi_device.bits_per_word
用法:
bits = 8;
ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, &bits);
c、设置或获取SPI读写的最大频率
SPI_IOC_RD_MAX_SPEED_HZ //获取 最大速率
SPI_IOC_WR_MAX_SPEED_HZ //设置 最大速率
以上两种cmd对用arg是spi_device.max_speed_hz
用法:
speed = 50*1000;
ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed);
第三:read或write
用法:和大多数的设备read函数一样的用法,但是每次读或者写的大小不能大于4096Byte。
char* buf[n];
read(fd,buf,sizeof(buf));或者write(fd,buf,sizeof(buf));
第四:close