63 linux内核的SPI设备驱动模型及应用程序调用SPI控制器的方法

SPI设备驱动模型与I2C设备驱动模型基本一样.

SPI的控制器驱动由平台设备与平台驱动来实现. 驱动后用spi_master对象来描述.在设备驱动中就可以通过函数spi_write, spi_read, spi_w8r16, spi_w8r8, spi_write_then_read来调用控制器.

注意:上面的函数不可以同时收发数据。如需要同时收发可用下面方法调用控制器:

    struct spi_message msg; //spi控制器的传输操作是以消息来提交. 一条消息里可有多个传输操作
    struct spi_transfer x = {  //每个spi_transfer对象来描述收、发、收发操作 
        .tx_buf = txbuf,  //指定发出数据的缓冲区地址
        .rx_buf = rxbuf,  //指定接收数据的缓冲区地址
        .len = strlen(txbuf),  //收发数据的长度
    };  

    spi_message_init(&msg);  //初始化消息对象
    spi_message_add_tail(&x, &msg); //把传输对象增加到消息对象里
	spi_sync(spi, &msg); //提交消息对象到控制器

如需要先读然后再写, 可以用一个消息对象,两个传输对象(一写一读)先后加入消息对象提交即可.
///
spi的设备也是先用spi_board_info来描述,在spi_master对象注册时再生成相应的spi_device对象. spi设备驱动由spi_driver对象描述. spi_device对象与spi_driver都是挂载到spi总线上的.

描述spi设备的结构体:

struct spi_device {
        struct device           dev; //基于device成员扩展, 在/sys/bus/spi/devie目录有相应的子目录(名为spi%d.%d)
        struct spi_master       *master; //spi控制器对象的地址
        u32                     max_speed_hz; //设备工作时钟最大多少HZ
        u8                      chip_select; 
        u8                      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 */
        u8                      bits_per_word; //传输的数据以多少位为单位
        int                     irq;    //中断号
        void                    *controller_state;
        void                    *controller_data; //给控制器的驱动使用 
	char                    modalias[SPI_NAME_SIZE]; //spi设备的名字
};
// spi设备驱动类型
struct spi_driver {
    const struct spi_device_id *id_table;
    int         (*probe)(struct spi_device *spi);
    int         (*remove)(struct spi_device *spi);
    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; //基于device_driver扩展, 驱动模型
};


extern int spi_register_driver(struct spi_driver *sdrv);
static inline void spi_unregister_driver(struct spi_driver *sdrv);

/

spi总线:
struct bus_type spi_bus_type = {
    .name       = "spi",
    .dev_attrs  = spi_dev_attrs,
    .match      = spi_match_device,
    .uevent     = spi_uevent,
    .pm     = &spi_pm,
};
EXPORT_SYMBOL_GPL(spi_bus_type);

static int spi_match_device(struct device *dev, struct device_driver *drv)
{
    const struct spi_device *spi = to_spi_device(dev);
    const struct spi_driver *sdrv = to_spi_driver(drv);
	...
    if (sdrv->id_table)
        return !!spi_match_id(sdrv->id_table, spi); //如设备驱动有id_table则按id_table里指定的设备名来匹配 

    return strcmp(spi->modalias, drv->name) == 0; //如设备驱动没有id_table则按名字来匹配 
}


声明spi设备有两种方法:

  1. 在script.bin里声明spi设备. 这种方法只适用于SOC里spi控制器, 不适用基于GPIO口的控制器.
[spi_devices]
spi_dev_num = 1   //表示spi设备的个数
  
[spi_board0]  //第0个spi设备
modalias = "spidev"  //设备名
max_speed_hz = 33000000 //设备最高的工作时钟
bus_num = 0            //此设备是连接在编号为0的控制器上的
chip_select = 0        //使用控制器的第0个片选 
mode = 0               //工作时序的方式
full_duplex = 1        //全双工, 表示MISO,MOSI都用上
manual_cs = 0          //表示片选线是由控制器自动控制的

  1. 在内核源码声明spi_board_info对象, 再内核初始化函数里spi_register_board_info注册设备信息.
struct spi_board_info myspi_info = {
        .modalias = "spidev",
        .controller_data = GPIOA(20), //注意这里是指定设备的片选脚, 需查看控制器驱动代码里对controller_data成员的使用。
        .max_speed_hz = 100000, //100Khz
        .bus_num = 0,           //控制器的编号
        .chip_select = 0,      //第0个片选
        .mode = SPI_MODE_2,
};

/
应用程序调用spi控制器的方法.

在内核里有实现好一个spi的设备驱动,用于供应用程序调用.

make menuconfig ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf-
Device Drivers  --->
	[*] SPI support  --->
		<*>   User mode SPI device driver support //spi设备驱动,供应用程序调用控制器用

源码在"drivers/spi/spidev.c"

static int __init spidev_init(void)
{
    int status;

    status = register_chrdev(SPIDEV_MAJOR, "spi", &spidev_fops); // cdev字符设备初始化
 	...

    spidev_class = class_create(THIS_MODULE, "spidev");
 	...

    status = spi_register_driver(&spidev_spi_driver); //注册spi设备驱动
 	...
}
module_init(spidev_init);

//

static struct spi_driver spidev_spi_driver = {
    .driver = {
        .name =     "spidev", //只可与名为"spidev"的spi设备匹配
        .owner =    THIS_MODULE,
    },
    .probe =    spidev_probe,
	...
};

static int __devinit spidev_probe(struct spi_device *spi)
{
    struct spidev_data  *spidev;
    int         status;
    unsigned long       minor;

    
    spidev = kzalloc(sizeof(*spidev), GFP_KERNEL);
	...    

    spidev->spi = spi;
    	...

    minor = find_first_zero_bit(minors, N_SPI_MINORS);
    if (minor < N_SPI_MINORS) {
        struct device *dev;
        spidev->devt = MKDEV(SPIDEV_MAJOR, minor); //准备设备号
        dev = device_create(spidev_class, &spi->dev, spidev->devt,
                    spidev, "spidev%d.%d", //创建出设备文件, 第1个%d指定控制器的编号,第2指定第几个片选 
                    spi->master->bus_num, spi->chip_select);
        status = IS_ERR(dev) ? PTR_ERR(dev) : 0;
   	...
    }
	...
}

///
全志h3的script.bin里默认增加了一个名为"spidev"的spi设备, 只要内核编译项选上"spidev"设备驱动, 在系统的/dev目录下应出现设备文件"spidev0.0".

可使用内核源码目录下的"Documentation/spi/spidev_test.c"测试spi控制器程序.
只要把spi控制器的MISO与MOSI短接起来,执行spidev_test.c应会接到收到程序发出的数据.

  • 0
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
SPI(Serial Peripheral Interface)是一种串行通信协议,常用于片上系统和外围设备之间的通信。在Linux内核SPI驱动程序由两部分组成:SPI核心框架和SPI设备驱动程序。下面是一个简单的SPI设备驱动程序的例子代码: ```c #include <linux/init.h> #include <linux/module.h> #include <linux/spi/spi.h> static struct spi_device *spi_dev; static int spi_driver_probe(struct spi_device *spi) { // 保存SPI设备的指针 spi_dev = spi; // 设置SPI设备参数 // ... return 0; } static int spi_driver_remove(struct spi_device *spi) { // 释放SPI设备资源 // ... return 0; } static struct spi_driver spi_driver = { .probe = spi_driver_probe, .remove = spi_driver_remove, .driver = { .name = "spi_driver", .owner = THIS_MODULE, }, }; static int __init spi_driver_init(void) { // 注册SPI设备驱动程序 return spi_register_driver(&spi_driver); } static void __exit spi_driver_exit(void) { // 取消注册SPI设备驱动程序 spi_unregister_driver(&spi_driver); } module_init(spi_driver_init); module_exit(spi_driver_exit); MODULE_LICENSE("GPL"); ``` 在上面的代码,`spi_driver_probe()` 函数SPI设备驱动程序的入口函数,当系统检测到一个新的SPI设备时,内核就会调用函数来初始化该设备。`spi_driver_remove()` 函数则是当一个SPI设备被卸载时调用的,用于释放该设备所占用的资源。 在 `spi_driver_probe()` 函数,我们可以通过 `spi` 指针来获取SPI设备的相关信息,例如设备的名称、设备的总线、设备的CS引脚等等。然后我们可以根据需要对这些参数进行设置。最后,我们需要返回0表示初始化成功。 在 `spi_driver_remove()` 函数,我们需要释放与SPI设备相关的资源,例如GPIO引脚、硬件资源等等。最后同样返回0表示卸载成功。 需要注意的是,上面的代码只是一个简单的例子,实际的SPI驱动程序可能需要更多的功能和处理逻辑。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值