目录
前言
上一篇介绍了Linux内核层spi的读写操作,本文主要介绍Linux应用层spi的读写操作。
设备树
&spi0 {
status = "okay";
#address-cells=<1>;
#size-cells=<0>;
pinctrl-names = "default";
pinctrl-0 = <&spi0_pins_0>;
spidev0:spi0@0 {
compatible = "spi-mt65xx-dev";//与驱动文件spidev.c中的compatible匹配
reg = <0>;
spi-max-frequency = <10000000>;
};
smi230:spi0@1 {
compatible = "SMI230GYRO";
reg = <1>; //每个子节点的reg值不能一样
spi-max-frequency = <10000000>;
};
};
&spi1 {
status = "okay";
#address-cells=<1>;
#size-cells=<0>;
pinctrl-names = "default";
pinctrl-0 = <&spi1_pins_1>;
spidev1:spi1@0 {
compatible = "spi-mt65xx-dev";//与驱动文件spidev.c中的compatible匹配
reg = <0>;
spi-max-frequency = <10000000>;
};
};
&spi2 {
status = "okay";
#address-cells=<1>;
#size-cells=<0>;
pinctrl-names = "default";
pinctrl-0 = <&spi2_pins_2>;
spidev2:spi2@0 {
compatible = "spi-mt65xx-dev";//与驱动文件spidev.c中的compatible匹配
reg = <0>;
spi-max-frequency = <10000000>;
};
};
spi0增加了子节点spidev0,spi1增加了子节点spidev1,spi2增加了子节点spidev2。设备树中为什么是compatible = "spi-mt65xx-dev"呢,因为在文件drivers/spi/spidev.c中,也添加了scompatible = "spi-mt65xx-dev"(如下代码段),所以设备树和spidev.c中的compatible的值匹配,然后进入probe函数,最终在应用层生成设备文件(/dev/spidev0.0、/dev/spidev1.0、/dev/spidev2.0)。
static const struct of_device_id spidev_dt_ids[] = {
{ .compatible = "rohm,dh2228fv" },
{ .compatible = "lineartechnology,ltc2488" },
{ .compatible = "ge,achc" },
{ .compatible = "semtech,sx1301" },
{ .compatible = "lwn,bk4" },
{ .compatible = "dh,dhcom-board" },
{ .compatible = "menlo,m53cpld" },
{ .compatible = "spi-mt65xx-dev" },//与设备树中的compatible匹配
{},
};
SPI 应用层读写操作
spi0、spi1、spi2在应用层对应的设备文件分别为/dev/spidev0.0、/dev/spidev1.0、/dev/spidev2.0,然后我们就可以通过open、write、read、ioctl、close操作这些设备文件,从而实现spi的读写操作。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <unistd.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <linux/spi/spidev.h>
typedef enum
{
SPIMODE0 = SPI_MODE_0,
SPIMODE1 = SPI_MODE_1,
SPIMODE2 = SPI_MODE_2,
SPIMODE3 = SPI_MODE_3,
}SPI_MODE;
typedef enum
{
S_6_75M = 6750000,
S_13_5M = 13500000,
S_27M = 27000000,
}SPI_SPEED;
int fd_spi = -1;
static int spi_init(void)
{
SPI_MODE mode;
char spi_bits;
SPI_SPEED spi_speed;
int ret;
if(fd_spi > 0)
{
printf("spi is already init");
return -1;
}
fd_spi = open("/dev/spidev0.0", O_RDWR); //open spi设备/dev/spidev0.0
if (fd_spi < 0)
{
printf("can't open spi dev %s, fd_spi = %d\n", dev_name, fd_spi);
return -1;
}
/*
* spi mode
*/
mode = SPIMODE3;
ret = ioctl(fd_spi, SPI_IOC_WR_MODE32, &mode);//设置spi的模式
if (ret == -1)
{
perror("can't set spi mode");
close(fd_spi);
return ret;
}
/*
* bits per word
*/
spi_bits = 8;
ret = ioctl(fd_spi, SPI_IOC_WR_BITS_PER_WORD, &spi_bits);//设置spi的字长
if (ret == -1)
{
perror("can't set bits per word");
close(fd_spi);
return ret;
}
/*
* speed
*/
spi_speed = (uint32_t)S_13_5M;
ret = ioctl(fd_spi, SPI_IOC_WR_MAX_SPEED_HZ, &spi_speed);//设置spi的最大传输速率
if (ret == -1)
{
perror("can't set max speed hz");
close(fd_spi);
return ret;
}
printf("spi mode:0x%x\n", mode);
printf("bits per word: %d\n", spi_bits);
printf("max speed : %d Hz (%d KHz)\n", spi_speed, spi_speed/1000);
printf("Successfully to init i2c %s, fd_i2c = %d\n", dev_name, fd_spi);
return fd_spi;
}
static int spi_uninit(void)
{
int ret;
ret = close(fd_spi);//关闭spi设备
if (0 != ret)
{
printf("Failed to uninit i2c, ret = %d\n", ret);
}
else
{
fd_spi = -1;
printf("Successfully to uninit i2c, ret = %d\n", ret);
}
return 0;
}
static void spi_read(void)
{
int ret;
int i;
int len = 1024;
char readbuf[1024] = {0};
ret = read(fd_spi, readbuf, len);//spi读操作
if (ret < 0)
{
perror("can't read spi message");
return ret;
}
for (i = 0; i<1024; i++) {
if (!(i % 32))
puts("");
printf("%.2X ", readbuf[i]);
}
puts("");
return 0;
}
static void spi_write(void)
{
int ret;
int i;
int len = 1024;
char writebuf[1024];
for(i = 0 ;i < 1024;i++)
{
writebuf[i] = i%256;
}
ret = write(fd_spi, writebuf, len);//spi写操作
if (ret < 0)
{
perror("can't write spi message");
return ret;
}
return 0;
}
static void spi_write_read(void)//spi边写边读,全双工通信
{
int ret;
int i;
int len = 1024;
char writebuf[1024];
char readbuf[1024] = {0};
for(i = 0 ;i < 1024;i++)
{
writebuf[i] = i%256;
}
struct spi_ioc_transfer tr = { //构建结构体变量
.tx_buf = (unsigned long)writebuf, //发送缓存
.rx_buf = (unsigned long)readbuf, //接收缓存
.len = len, //读写长度
.delay_usecs = 0,
.speed_hz = 10000000, //spi最大传输速率
.bits_per_word = 8, //spi字长
};
ret = ioctl(fd_spi, SPI_IOC_MESSAGE(1), &tr);//结构体变量传到内核,实现spi的边读边写
if (ret < 0)
{
perror("can't send spi message");
return ret;
}
for (i = 0; i<1024; i++) {
if (!(i % 32))
puts("");
printf("%.2X ", readbuf[i]); //打印读到的spi数据
}
puts("");
return 0;
}
应用层操作SPI是不是很简单,通过调用open、write、read、ioctl、close就实现了。我们继续深入到内核,看是怎么实现的。
内核中如何实现
这里只简单分析一下函数调用关系,后续专门介绍drivers/spi/spidev.c
spi_write() //应用层
-->write() //应用层
-->spidev_write() //内核
-->spidev_sync_write() //内核
spidev_sync_write(struct spidev_data *spidev, size_t len)
{
struct spi_transfer t = {
.tx_buf = spidev->tx_buffer,
.len = len,
.speed_hz = spidev->speed_hz,
};
struct spi_message m;
spi_message_init(&m);
spi_message_add_tail(&t, &m);
return spidev_sync(spidev, &m);
}
spi_read() //应用层
-->read() //应用层
-->spidev_read() //内核
-->spidev_sync_read() //内核
spidev_sync_read(struct spidev_data *spidev, size_t len)
{
struct spi_transfer t = {
.rx_buf = spidev->rx_buffer,
.len = len,
.speed_hz = spidev->speed_hz,
};
struct spi_message m;
spi_message_init(&m);
spi_message_add_tail(&t, &m);
return spidev_sync(spidev, &m);
}
spi_write_read() //应用层
-->iioctl(fd_spi, SPI_IOC_MESSAGE(1), &tr) //应用层
-->spidev_ioctl() //内核
-->spidev_message() //内核
static int spidev_message(struct spidev_data *spidev, struct spi_ioc_transfer *u_xfers, unsigned n_xfers)
{
......
spi_message_init(&msg);
......
spi_message_add_tail(k_tmp, &msg);
......
status = spidev_sync(spidev, &msg);
}
可以看出,应用层的read、write、ioctl实现的spi读写操作,最终在内核中都是通过调用spidev_sync实现的,而spidev_sync又是通过调用spi_sync实现的。分析到这里,是不是和上一篇spi设备驱动介绍的一样了呢。
总结
应用层操作spi的读写比较简单,具体怎么实现spi的读写操作,还得看深入内核研究,做到知其然,并知其所以然,否则遇到问题的时候不知如何定位问题。