i目录第1章用户空间操作SPI设备..................................................................................11.1概述...........................................................................................................................11.2重要的数据结构.......................................................................................................11.3获得同SPI设备通信的设备节点...........................................................................31.4用户空间同设备节点的接口...................................................................................41.5用户空间的测试例程...............................................................................................5广州周立功单片机发展有限公司Tel:(020)3873097638730977Fax:38730925设备通信有两种方式。第一种就是为SPI设备编写专门的内核空间驱动,由驱动负责处理所有的SPI协议,使用这种方式用户空间不需要关心具体的硬件知识,大大降低了用户空间程序的复杂度,但是用户空间程序的功能受驱动的限制,不够灵活;还有一种方式就是使用户空间的进程直接访问SPI设备,使得内核的编码变得简单,驱动的功能转移到了用户空间,用户空间的程序可以比较灵活的实现各种功能,但要求用户空间的开发者对芯片的工作方式有全面的了解。本章不介绍如何编写SPI内核驱动,仅仅介绍第二种方法,也就是直接访问SPI设备来通信。这种方法访问SPI设备的流程如图1.1所示。1.2重要的数据结构1.spi_device虽然用户空间不需要直接用到spi_device结构体,但是这个结构体和用户空间的程序有密切的关系,理解它的成员有助于理解SPI设备节点的IOCTL命令,所以首先来介绍它。在内核中,每个spi_device代表一个物理的SPI设备。它的成员如程序清单1.1所示。spi_masterspi_device用户进程spi控制器spi设备2spi设备1内核空间用户空间片内硬件片外硬件spi总线设备节点图1.1用户空间直接访问SPI设备广州周立功单片机发展有限公司Tel:(020)3873097638730977Fax:38730925{structdevicedev;structspi_master*master;u32max_speed_hz;/*通信时钟最大频率*/u8chip_select;/*片选号*/u8mode;/*SPI设备的模式,下面的宏是它各bit的含义*/#defineSPI_CPHA0x01/*采样的时钟相位*/#defineSPI_CPOL0x02/*时钟信号起始相位:高或者是低电平*/#defineSPI_MODE_0(0|0)#defineSPI_MODE_1(0|SPI_CPHA)#defineSPI_MODE_2(SPI_CPOL|0)#defineSPI_MODE_3(SPI_CPOL|SPI_CPHA)#defineSPI_CS_HIGH0x04/*为1时片选的有效信号是高电平*/#defineSPI_LSB_FIRST0x08/*发送时低比特在前*/#defineSPI_3WIRE0x10/*输入输出信号使用同一根信号线*/#defineSPI_LOOP0x20/*回环模式*/u8bits_per_word;/*每个通信字的字长(比特数)*/intirq;/*使用到的中断*/void*controller_state;void*controller_data;charmodalias[32];/*设备驱动的名字*/};由于一个SPI总线上可以有多个SPI设备,因此需要片选号来区分它们,SPI控制器根据片选号来选择不同的片选线,从而实现每次只同一个设备通信。spi_device的mode成员有两个比特位含义很重要。SPI_CPHA选择对数据线采样的时机,0选择每个时钟周期的第一个沿跳变时采样数据,1选择第二个时钟沿采样数据;SPI_CPOL选择每个时钟周期开始的极性,0表示时钟以低电平开始,1选择高电平开始。这两个比特有四种组合,对应SPI_MODE_0~SPI_MODE_3。另一个比较重要的成员是bits_per_word。这个成员指定每次读写的字长,单位是比特。虽然大部分SPI接口的字长是8或者16,仍然会有一些特殊的例子。需要说明的是,如果这个成员为零的话,默认使用8作为字长。最后一个成员并不是设备的名字,而是需要绑定的驱动的名字。2.spi_ioc_transfer在用户使用设备节点的IOCTL命令传输数据的时候,需要用到spi_ioc_transfer结构体,它的成员如程序清单1.2所示。程序清单1.2spi_ioc_transferstructspi_ioc_transfer{__u64tx_buf;/*写数据缓冲*/__u64rx_buf;/*读数据缓冲*/__u32len;/*缓冲的长度*/广州周立功单片机发展有限公司Tel:(020)3873097638730977Fax:38730925*通信的时钟频率*/__u16delay_usecs;/*两个spi_ioc_transfer之间的延时*/__u8bits_per_word;/*字长(比特数)*/__u8cs_change;/*是否改变片选*/__u32pad;};每个spi_ioc_transfer都可以包含读和写的请求,其中读和写的长度必须相等。所以成员len不是tx_buf和rx_buf缓冲的长度之和,而是它们各自的长度。SPI控制器驱动会先将tx_buf写到SPI总线上,然后再读取len长度的内容到rx_buf。如果只想进行一个方向的传输,把另一个方向的缓冲置为0就可以了。speed_hz和bits_per_word这两个成员可以为每次通信配置不同的通信速率(必须小于spi_device的max_speed_hz)和字长,如果它们为0的话就会使用spi_device中的配置。delay_usecs可以指定两个spi_ioc_transfer之间的延时,单位是微妙。一般不用定义。cs_change指定这个cs_change结束之后是否需要改变片选线。一般针对同一设备的连续的几个spi_ioc_transfer,只有最后一个需要将这个成员置位。这样省去了来回改变片选线的时间,有助于提高通信速率。1.3获得同SPI设备通信的设备节点为了在用户空间获得和SPI设备直接通信的设备节点,必须有两个条件要满足:首先要有SPI控制器驱动,其次是要在内核初始化的时候注册一个spi_board_info,它的modalias成员必须为“spidev”。有了这两个条件,就可以和SPI设备进行通信了。控制器的驱动一般由芯片厂家提供,开发者只需提供第二个条件。spi_board_info的定义如程序清单1.3所示。程序清单1.3structspi_board_infostructspi_board_info{charmodalias[32];/*要绑定的驱动的名字*/constvoid*platform_data;void*controller_data;intirq;u32max_speed_hz;/*通信时钟最大速率*/u16bus_num;/*总线编号*/u16chip_select;/*片选号*/u8mode;/*和spi_device中的mode成员类似*/};要了解这个结构体各个成员的意义请参考程序清单1.1。定义并注册structspi_board_info的位置一般是内核的arch/xxx/mach-xxxx/board-xxxx.c,比如3250的内核,这个文件是arch/arm/mach-lpc32xx/board-smartarm3250.c。定义并注册structspi_board_info的代码如程序清单1.4所示。广州周立功单片机发展有限公司Tel:(020)3873097638730977Fax:38730925(void){structspi_board_infoinfo={.modalias=spidev,.max_speed_hz=5000000,.bus_num=0,.chip_select=0,};returnspi_register_board_info(&info,1);}arch_initcall(smartarm3250_spi_usp_register);由于3250内核代码在arch/arm/mach-lpc32xx/board-smartarm3250.c已经定义了一个smartarm3250_spi_eeprom_register函数,因此在增加程序清单1.4代码前先将这个函数注释掉。程序清单1.4注册了一个挂在0号SPI总线上的设备信息,它的片选号为0。增加完这段代码后将内核重新编译。在内核启动的时候,会为这个设备建立一个spi_device并和0号SPI总线的驱动进行绑定。同时内核会为这个设备申请一个主设备号为153的的设备号,次设备号和注册的顺序有关,最多支持32个同类设备。内核重新编译并重启之后,如果系统中运行了udev,/dev下就会生成一个spidevX.D设备节点,其中X是总线编号,D是片选号。对于程序清单1.4的代码应该自动生成的设备节点是spidev0.0。一般SPI控制器驱动由芯片厂商提供,开发者所要在内核做的工作就是添加类似程序清单1.4的内容。这样内核空间的工作减少了,用户空间的工作量加大了,因为用户空间的开发者需要全面了解SPI设备的工作方式和接口协议。1.4用户空间同设备节点的接口对于/dev/spidevX.D设备节点,可以进行各种操作,这一小节介绍它支持的函数接口。1.open/close打开和关闭设备节点没有特别之处,直接使用open/write就可以了。2.read/write读写SPI设备可以直接使用read/write函数,但是每次读或者写的大小不能大于4096Byte。3.IOCTL命令用户空间对spidev设备节点使用IOCTL命令失败会返回-1。SPI_IOC_RD_MODE读取SPI设备对应的spi_device.mode,mode的含义请参考程序清单1.1。使用的方法如下:ioctl(fd,SPI_IOC_RD_MODE,&mode);其中第三个参数是一个uint8_t类型的变量。广州周立功单片机发展有限公司Tel:(020)3873097638730977Fax:38730925SPI_IOC_WR_MODE设置SPI设备对应的spi_device.mode。使