linux下SPI驱动

来源:互联网 发布:西南交大网络教育网站 编辑:程序博客网 时间:2024/05/22 01:53
框架:
1. LINUX SPI 核心层
       核心层代码负责这个框架中通用的部分,满足分层的思想,位于drivers/spi/spi.c。主要承担的工作包括:注册 spi总线,提供基本 SPI 总线操作 API:
int spi_register_driver(struct spi_driver *sdrv);
struct spi_master *spi_alloc_master(struct device *dev, unsigned size);
int spi_register_master(struct spi_master *master);
void spi_unregister_master(struct spi_master *master);
int spi_sync(struct spi_device *spi, struct spi_message *message);
int spi_write_then_read(struct spi_device *spi, const u8 *txbuf, unsigned n_tx, u8 *rxbuf, unsigned n_rx);
spi_register_board_info(struct spi_board_info const *info, unsigned n);
等......(具体查看/driver/spi/spi.c)
2. spi主机master(对应于i2c的i2c_adpter)
在 Linux 中,每一个类型的驱动都会有一个相应的结构体来描述,spi_master 就是用来描述 SPI 主机控制器驱动的,其主要成员是bus_num,cs,spi 模式和时钟设置用到的函数,数据传输用到的函数等。一个 master 对应一个 spi 总线,或者说是一个 spi 接口:
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_FIRST0x08//低位数据先传输
#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

原创粉丝点击