spi_device

虽然用户空间不需要直接用到spi_device结构体,但是这个结构体和用户空间的程序有密切的关系,理解它的成员有助于理解SPI设备节点的IOCTL命令,所以首先来介绍它。在内核中,每个spi_device代表一个物理的SPI设备:

struct spi_device {
structdevice dev;
structspi_master *master;
u32 max_speed_hz; /* 通信时钟最大频率 */
u8 chip_select; /* 片选号 */
u8 mode; /*SPI设备的模式,下面的宏是它各bit的含义 */
#define SPI_CPHA 0x01 /* 采样的时钟相位 */
#define SPI_CPOL 0x02 /* 时钟信号起始相位:高或者是低电平 */
#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)
#define SPI_CS_HIGH 0x04 /* 为1时片选的有效信号是高电平 */
#define SPI_LSB_FIRST 0x08 /* 发送时低比特在前 */
#define SPI_3WIRE 0x10 /* 输入输出信号使用同一根信号线 */
#define SPI_LOOP 0x20 /* 回环模式 */
u8 bits_per_word; /* 每个通信字的字长(比特数) */
int irq; /*使用到的中断 */
void *controller_state;
void *controller_data;
char modalias[32]; /* 设备驱动的名字 */
};

spi_device的mode成员有两个比特位含义很重要:

SPI_CPHA选择对数据线采样的时机,0选择每个时钟周期的第一个沿跳变时采样数据,1选择第二个时钟沿采样数据;

SPI_CPOL选择每个时钟周期开始的极性,0表示时钟以低电平开始,1选择高电平开始。

这两个比特有四种组合,对应SPI_MODE_0~SPI_MODE_3。

另一个比较重要的成员是bits_per_word。这个成员指定每次读写的字长,单位是比特。虽然大部分SPI接口的字长是8或者16,仍然会有一些特殊的例子。需要说明的是,如果这个成员为零的话,默认使用8作为字长。

最后一个成员并不是设备的名字,而是需要绑定的驱动的名字。

spi_ioc_transfer

linux中,应用开发常用的结构体主要是struct spi_ioc_transfer:

struct spi_ioc_transfer {
__u64 tx_buf;
__u64 rx_buf; __u32 len;
__u32 speed_hz; __u16 delay_usecs;
__u8 bits_per_word;
__u8 cs_change;
__u32 pad;
};

每个 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,只有最后一个需要将这个成员置位。这样省去了来回改变片选线的时间,有助于提高通信速率。

SPI设备的初始化

void spi_Init()
{
int ret = 0; spifd = open(device, O_RDWR);
if (spifd < 0)
pabort("can't open device"); /*
* spi mode
*/
ret = ioctl(spifd, SPI_IOC_WR_MODE, &mode);
if (ret == -1)
pabort("can't set spi mode"); ret = ioctl(spifd, SPI_IOC_RD_MODE, &mode);
if (ret == -1)
pabort("can't get spi mode"); /*
* bits per word
*/
ret = ioctl(spifd, SPI_IOC_WR_BITS_PER_WORD, &bits);
if (ret == -1)
pabort("can't set bits per word"); ret = ioctl(spifd, SPI_IOC_RD_BITS_PER_WORD, &bits);
if (ret == -1)
pabort("can't get bits per word"); /*
* max speed hz
*/
ret = ioctl(spifd, SPI_IOC_WR_MAX_SPEED_HZ, &speed);
if (ret == -1)
pabort("can't set max speed hz"); ret = ioctl(spifd, SPI_IOC_RD_MAX_SPEED_HZ, &speed);
if (ret == -1)
pabort("can't get max speed hz");
}

首先open打开SPI的设备,然后通过ioctl函数进行数据位、速率、模式进行配置。

IOCTL命令

用户空间对spidev设备节点使用IOCTL命令失败会返回-1。

SPI_IOC_RD_MODE

读取SPI设备对应的spi_device.mode,使用的方法如下:

ioctl(fd,SPI_IOC_RD_MODE, &mode);

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);

SPI_IOC_MESSAGE(N)

一次进行双向/多次读写操作。使用的方式如下:

structspi_ioc_transfer xfer[2];

......

status= ioctl(fd, SPI_IOC_MESSAGE(2), xfer);

其中N是本次通信中xfer的数组长度。

SPI的读写

int spi_read()
{
bt_devide_msg msg;
unsigned char ucRegVal;
int ret,i;
unsigned char tx[20];
for(i = 0;i<20;i++)
{
tx[i] = 0xda;
}
unsigned char rx[ARRAY_SIZE(tx)] = {0, };
struct spi_ioc_transfer tr = {
.tx_buf = (unsigned long)tx,
.rx_buf = (unsigned long)rx,
.len = ARRAY_SIZE(tx),
.delay_usecs = udelay,
.speed_hz = speed,
.bits_per_word = bits,
}; ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr);
if (ret < 1)
{
printf("can't read spi message\n");
return -1;
} if(rx[0] !=0xAA)
{
printf("read spi data: ");
for (ret = 0; ret < ARRAY_SIZE(tx); ret++)
{
printf("%02X ", rx[ret]);
}
printf("\n");
} ucRegVal = rx[ARRAY_SIZE(tx)-1];
get_data_process(rx); return 1;

测试:

void main()
{
spi_init();
spi_read();
}

SPI 用户空间的读写操作的更多相关文章

  1. SPI编程1:用户空间的读写操作

    spi_device 虽然用户空间不需要直接用到spi_device结构体,但是这个结构体和用户空间的程序有密切的关系,理解它的成员有助于理解SPI设备节点的IOCTL命令,所以首先来介绍它.在内核中 ...

  2. linux下在用户空间访问I/O端口的ioperm和iopl函数

    1.ioperm函数      功能描述:为调用进程设置I/O端口访问权能.ioperm的使用需要具有超级用户的权限,只有低端的[0-0x3ff] I/O端口可被设置,要想指定更多端口的权能,可使用i ...

  3. linux驱动开发:用户空间操作LCD显示简单的图片【转】

    转自:http://blog.csdn.net/changliang7731/article/details/53074616 上一章我们简单介绍了LCD的一些基本原理.当然更深奥的还有,比如gamm ...

  4. 在linux的用户空间操作gpio

    1. 使能linux内核选项CONFIG_GPIO_SYSFS CONFIG_GPIO_SYSFS=y 2. 测试方法 2.1 关注/sys/class/gpio下的文件 --export/unexp ...

  5. CK:User mode Bus Error(用户空间操作内核地址导致的异常)

    关键词:VEC_ACCESS.coredump.LR.PC等. CK中存在一种VEC_ACCESS异常,可能原因是用户空间访问了内核空间,还有一种是内核访问不存在的总线地址. 下面简单构造VEC_AC ...

  6. 嵌入式Linux设备驱动程序:用户空间中的设备驱动程序

    嵌入式Linux设备驱动程序:用户空间中的设备驱动程序 Embedded Linux device drivers: Device drivers in user space Interfacing ...

  7. c语言文件读写操作总结

    C语言文件读写操作总结 C语言文件操作 一.标准文件的读写 1.文件的打开 fopen() 文件的打开操作表示将给用户指定的文件在内存分配一个FILE结构区,并将该结构的指针返回给用户程序,以后用户程 ...

  8. 【转】用户空间使用i2c_dev--不错

    原文网址:http://blog.csdn.net/yuanlulu/article/details/6161706 ========================================= ...

  9. Linux usb子系统(三):通过usbfs操作设备的用户空间驱动

    内核中提供了USB设备文件系统(usbdevfs,Linux 2.6改为usbfs,即USB文件系统),它和/proc类似,都是动态产生的.通过在/etc/fstab文件中添加如下一行:none /p ...

随机推荐

  1. Python爬虫进阶五之多线程的用法

    前言 我们之前写的爬虫都是单个线程的?这怎么够?一旦一个地方卡到不动了,那不就永远等待下去了?为此我们可以使用多线程或者多进程来处理. 首先声明一点! 多线程和多进程是不一样的!一个是 thread ...

  2. error:Your local changes to the follwing files would be overwritten by merge

  3. Java 设计模式系列(十八)备忘录模式(Memento)

    Java 设计模式系列(十八)备忘录模式(Memento) 备忘录模式又叫做快照模式(Snapshot Pattern)或Token模式,是对象的行为模式.备忘录对象是一个用来存储另外一个对象内部状态 ...

  4. Android 上传文件到XP

    Android部分: AsyncHttpClient client = new AsyncHttpClient(); RequestParams requestParams = new Request ...

  5. word 写博客,直接上传

    目前大部分的博客作者在用Word写博客这件事情上都会遇到以下3个痛点: 1.所有博客平台关闭了文档发布接口,用户无法使用Word,Windows Live Writer等工具来发布博客.使用Word写 ...

  6. 书籍索引 #C++

    卷 计算机 的文件夹 PATH 列表卷序列号为 00000200 0001:8890F:.│ 21天学通C++.pdf│ C++ Primer Plus 第6版 中文版.pdf│ C++ Templa ...

  7. 设置UITextField键盘上return key不可点击

    今天在做搜索栏时候,发现系统软键盘有下角的“搜索”按钮在输入框无论有没有文字的情况下都是可以点击的状态,记得其他软件在无文字的状态下是不可点击的状态,起初还以为要对textfield的内容做一个判断, ...

  8. [转]分布式中使用Redis实现Session共享(二)

    本文转自:http://www.cnblogs.com/yanweidie/p/4678095.html 上一篇介绍了一些redis的安装及使用步骤,本篇开始将介绍redis的实际应用场景,先从最常见 ...

  9. Android-解析JSON数据(JSON对象/JSON数组)

    在上一篇博客中,Android-封装JSON数据(JSON对象/JSON数组),讲解到Android真实开发中更多的是去解析JSON数据(JSON对象/JSON数组) 封装JSON的数据是在服务器端进 ...

  10. DateUtils常用方法

    一.DateUtils常用方法   1.1.常用的日期判断 isSameDay(final Date date1, final Date date2):判断两个时间是否是同一天: isSameInst ...