spi设备创建
在tiny6410开发板上外接了spi1的引脚SPICLK、MOSI、SPICS、MISO,所以以spi1为例创建spi设备。
首先介绍2个相关到的结构
1.用于片选的信息
struct s3c64xx_spi_csinfo {
u8fb_delay; //从机指定的返回延时
unsignedline; //指定的CS线对应的管脚
void(*set_level)(unsigned line_id, int lvl); //片选线控制函数
};
2.用来设置与芯片相关的具体spi参数
struct spi_board_info {
char modalias[SPI_NAME_SIZE]; //驱动的id
constvoid *platform_data;
void *controller_data; //一些控制器所需的关于硬件设置的索引
int irq; //中断号
u32 max_speed_hz; //通讯速率
u16 bus_num; //总线号
u16 chip_select; //片选
u8 mode; 工作模式
};
注:在6410手册中对四种工作方式的概述。
2个相关的函数
设置SPI相关资源的函数:
void __init s3c64xx_spi_set_info(intcntrlr, int src_clk_nr, int num_cs)
在开发办上注册SPI设备的函数:
int __init spi_register_board_info(structspi_board_info const *info, unsigned n)
之后就要开工了,注册一个spi设备的具体做法:
打开/arch/arm/mach-s3c64xx/mach-mini64xx.c
1. 初始化相关的结构信息
/*创建一个用于片选IO管理的函数,函数的具体结构在s3c64xx_spi_csinfo. set_level上有明确的定义*/
staticvoid cs_set (unsigned line_id, int lvl){
gpio_direction_output(line_id, lvl);
};
staticstruct s3c64xx_spi_csinfo s3c64xx_spi1_csinfo = {
.fb_delay = 100,
.line = S3C64XX_GPC(7),
.set_level = cs_set,
};
staticstruct spi_board_info s3c6410_spi1_board[] = {
[0] = {
.modalias = "spi",
.bus_num = 1,
.chip_select = 0,
.irq = IRQ_SPI1, // S3C64XX_IRQ_VIC1(16)
.max_speed_hz = 500*1000,
.mode = SPI_MODE_0,
/*
* #define SPI_MODE_0 (0|0)
* #define SPI_MODE_1 (0|SPI_CPHA)
* #define SPI_MODE_2 (SPI_CPOL|0)
* #define SPI_MODE_3 (SPI_CPOL|SPI_CPHA)
*/
.controller_data =&s3c64xx_spi1_csinfo,
},
};
设备信息初始化完成后,就可以添加具体的注册操作了,基本步骤是和I2C相似的。
首先是在static struct platform_device *mini6410_devices[]上添加spi的platform_device结构,如下:
staticstruct platform_device *mini6410_devices[] __initdata = {
//#ifdefCONFIG_S3C64XX_DEV_SPI
&s3c64xx_device_spi1,
//#endif
//#ifdefCONFIG_KEYBOARD_GPIO
&mini6410_device_button,
//#endif
。。。
}
在machine_init函数中注册设备
staticvoid __init mini6410_machine_init(void)
{
u32 cs1;
s3c64xx_spi_set_info(0,0,1);
。。。
if(ARRAY_SIZE(s3c6410_spi0_board)) {
spi_register_board_info(s3c6410_spi0_board,ARRAY_SIZE(s3c6410_spi0_board));
}
。。。
}
到此在设备的基本注册功能就完成了,下面配置以下menuconfig,打开spi device。
此时编译会提示s3c64xx_spi_set_info未定义,这是因为在arch\arm\mach-s3c64xx的Makefile中编译dev-spi.c的前提是要定义CONFIG_S3C64XX_DEV_SPI,我自己找了一下午都没找到CONFIG_S3C64XX_DEV_SPI这个选项在那里,索性自己在kconfig中添加一个好了。
上述工作作完之后重起开发板在/dev下面会看到一个spi的设备,目前spidev支持最多32个设备。设备的名字是spidevX.D,其中X是总线编号,D是设备的片选号。允许多个用户同时打开设备节点,spidev使用mutext进行互斥,多个用户同时读写时只有一个活动的用户,其他用户睡眠。
下面简单介绍一下用户空间操作spi的方法:
对于/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,使用的方法如下:
ioctl(fd,SPI_IOC_RD_MODE, &mode);
其中第三个参数是一个uint8_t类型的变量。
- SPI_IOC_WR_MODE
设置SPI设备对应的spi_device.mode。使用的方式如下:
ioctl(fd,SPI_IOC_WR_MODE, &mode);
- SPI_IOC_RD_LSB_FIRST
查看设备传输的时候是否先传输低比特位。如果是的话,返回1。使用的方式如下:
ioctl(fd,SPI_IOC_RD_LSB_FIRST, &lsb);
其中lsb是一个uint8_t类型的变量。返回的结果存在lsb中。
- SPI_IOC_WR_LSB_FIRST
设置设备传输的时候是否先传输低比特位。当传入非零的时候,低比特在前,当传入0的时候高比特在前(默认)。使用的方式如下:
ioctl(fd,SPI_IOC_WR_LSB_FIRST, &lsb);
- SPI_IOC_RD_BITS_PER_WORD
读取SPI设备的字长。使用的方式如下:
ioctl(fd,SPI_IOC_RD_BITS_PER_WORD, &bits);
其中bits是一个uibt8_t类型的变量。返回的结果保存在bits中。
- SPI_IOC_WR_BITS_PER_WORD
设置SPI通信的字长。使用的方式如下:
ioctl(fd,SPI_IOC_WR_BITS_PER_WORD, &bits);
- SPI_IOC_RD_MAX_SPEED_HZ
读取SPI设备的通信的最大时钟频率。使用的方式如下:
ioctl(fd,SPI_IOC_RD_MAX_SPEED_HZ, &speed);
其中speed是一个uint32_t类型的变量。返回的结果保存在speed中。
- SPI_IOC_WR_MAX_SPEED_HZ
设置SPI设备的通信的最大时钟频率。使用的方式如下:
ioctl(fd,SPI_IOC_WR_MAX_SPEED_HZ, &speed);
- SPI_IOC_MESSAGE(N)
一次进行双向/多次读写操作。使用的方式如下:
structspi_ioc_transfer xfer[2];
......
status= ioctl(fd,SPI_IOC_MESSAGE(2), xfer);
其中N是本次通信中xfer的数组长度。