我国知名白癜风专家 https://m.39.net/disease/a_5464419.html第六十二章LinuxSPI驱动实验上一章我们讲解了如何编写Linux下的I2C设备驱动,SPI也是很常用的一个串行通信协议,本章我们就来学习一下如何在Linux下编写SPI设备驱动。本章实验的最终目的就是驱动I.MX6U-ALPHA开发板上的ICM-这个SPI接口的六轴传感器,可以在应用程序中读取ICM-的原始传感器数据。62.1Linux下SPI驱动框架简介SPI驱动框架和I2C很类似,都分为主机控制器驱动和设备驱动,主机控制器也就是发SOC的SPI控制器接口。比如在裸机篇中的《第二十七章SPI实验》,我们编写了bsp_spi.c和bsp_spi.h这两个文件,这两个文件是I.MX6U的SPI控制器驱动,我们编写好SPI控制器驱动以后就可以直接使用了,不管是什么SPI设备,SPI控制器部分的驱动都是一样,我们的重点就落在了种类繁多的SPI设备驱动。62.1.1SPI主机驱动SPI主机驱动就是SOC的SPI控制器驱动,类似I2C驱动里面的适配器驱动。Linux内核使用spi_master表示SPI主机驱动,spi_master是个结构体,定义在include/linux/spi/spi.h文件中,内容如下(有缩减):示例代码62.1.1.1spi_master结构体structspi_master{structdevicedev;structlist_headlist;......s16bus_num;/*chipselectswillbeintegraltomanycontrollers;someothers*mightuseboard-specificGPIOs.*/u16num_chipselect;/*someSPIcontrollersposealignmentrequirementsonDMAable*buffers;letprotocoldriversknowabouttheserequirements.*/u16dma_alignment;/*spi_device.modeflagsunderstoodbythiscontrollerdriver*/u16mode_bits;/*bitmaskofsupportedbits_per_wordfortransfers*/u32bits_per_word_mask;....../*limitsontransferspeed*/u32min_speed_hz;u32max_speed_hz;/*otherconstraintsrelevanttothisdriver*/u16flags;....../*lockandmutexforSPIbuslocking*/spinlock_tbus_lock_spinlock;structmutexbus_lock_mutex;/*flagindicatingthattheSPIbusislockedforexclusiveuse*/boolbus_lock_flag;......int(*setup)(structspi_device*spi);......int(*transfer)(structspi_device*spi,structspi_message*mesg);......int(*transfer_one_message)(structspi_master*master,structspi_message*mesg);......};第行,transfer函数,和i2c_algorithm中的master_xfer函数一样,控制器数据传输函数。第行,transfer_one_message函数,也用于SPI数据发送,用于发送一个spi_message,SPI的数据会打包成spi_message,然后以队列方式发送出去。也就是SPI主机端最终会通过transfer函数与SPI设备进行通信,因此对于SPI主机控制器的驱动编写者而言transfer函数是需要实现的,因为不同的SOC其SPI控制器不同,寄存器都不一样。和I2C适配器驱动一样,SPI主机驱动一般都是SOC厂商去编写的,所以我们作为SOC的使用者,这一部分的驱动就不用操心了,除非你是在SOC原厂工作,内容就是写SPI主机驱动。SPI主机驱动的核心就是申请spi_master,然后初始化spi_master,最后向Linux内核注册spi_master。1、spi_master申请与释放spi_alloc_master函数用于申请spi_master,函数原型如下:structspi_master*spi_alloc_master(structdevice*dev,unsignedsize)函数参数和返回值含义如下:dev:设备,一般是platform_device中的dev成员变量。size:私有数据大小,可以通过spi_master_get_devdata函数获取到这些私有数据。返回值:申请到的spi_master。spi_master的释放通过spi_master_put函数来完成,当我们删除一个SPI主机驱动的时候就需要释放掉前面申请的spi_master,spi_master_put函数原型如下:voidspi_master_put(structspi_master*master)函数参数和返回值含义如下:master:要释放的spi_master。返回值:无。2、spi_master的注册与注销当spi_master初始化完成以后就需要将其注册到Linux内核,spi_master注册函数为spi_register_master,函数原型如下:intspi_register_master(structspi_master*master)函数参数和返回值含义如下:master:要注册的spi_master。返回值:0,成功;负值,失败。I.MX6U的SPI主机驱动会采用spi_bitbang_start这个API函数来完成spi_master的注册,spi_bitbang_start函数内部其实也是通过调用spi_register_master函数来完成spi_master的注册。如果要注销spi_master的话可以使用spi_unregister_master函数,此函数原型为:voidspi_unregister_master(structspi_master*master)函数参数和返回值含义如下:master:要注销的spi_master。返回值:无。如果使用spi_bitbang_start注册spi_master的话就要使用spi_bitbang_stop来注销掉spi_master。62.1.2SPI设备驱动spi设备驱动也和i2c设备驱动也很类似,Linux内核使用spi_driver结构体来表示spi设备驱动,我们在编写SPI设备驱动的时候需要实现spi_driver。spi_driver结构体定义在include/linux/spi/spi.h文件中,结构体内容如下:示例代码62.1.1.2spi_driver结构体structspi_driver{conststructspi_device_id*id_table;int(*probe)(structspi_device*spi);int(*remove)(structspi_device*spi);void(*shutdown)(structspi_device*spi);structdevice_driverdriver;};可以看出,spi_driver和i2c_driver、platform_driver基本一样,当SPI设备和驱动匹配成功以后probe函数就会执行。同样的,spi_driver初始化完成以后需要向Linux内核注册,spi_driver注册函数为spi_register_driver,函数原型如下:intspi_register_driver(structspi_driver*sdrv)函数参数和返回值含义如下:sdrv:要注册的spi_driver。返回值:0,注册成功;赋值,注册失败。注销SPI设备驱动以后也需要注销掉前面注册的spi_driver,使用spi_unregister_driver函数完成spi_driver的注销,函数原型如下:voidspi_unregister_driver(structspi_driver*sdrv)函数参数和返回值含义如下:sdrv:要注销的spi_driver。返回值:无。spi_driver注册示例程序如下:示例代码62.1.1.3spi_driver注册示例程序1/*probe函数*/2staticintxxx_probe(structspi_device*spi)3{4/*具体函数内容*/5return0;6}78/*remove函数*/9staticintxxx_remove(structspi_device*spi)10{11/*具体函数内容*/12return0;13}14/*传统匹配方式ID列表*/15staticconststructspi_device_idxxx_id[]={16{xxx,0},17{}18};/*设备树匹配列表*/21staticconststructof_device_idxxx_of_match[]={22{.
转载请注明:http://www.aierlanlan.com/cyrz/8204.html