6. SPI操作指南¶
6.1. 操作准备¶
SPI的操作准备如下:
使用SDK发布的内核以及文件系统。文件系统可使用SDK所发布的squashFS或ext4。也可透过本地文件系统再透过网络挂载至NFS。
6.2. 操作过程¶
加载内核。把SPI相关模块全部编入内核,不需要再执行加载命令。
在控制台下运行SPI读写命令或者自行在内核态或者用户态编写SPI读写程序,就可以对挂载在SPI控制器上的外围设备进行读写操作。
6.3. 操作示例¶
6.3.1. 内核态SPI读写程序示例:¶
此操作示例说明在内核态下如何通过SPI读写程序实现对SPI外围设备的读写操作。
步骤 1. 调用SPI核心层函数 spi_busnum_to_master(),以获取一个描述SPI控制器结构体:
master = spi_busnum_to_master(bus_num);
//bus_num为要读写的SPI外围设备所在的控制器号
//master为描述SPI控制器的spi_master结构体类型指针
步骤 2. 通过spi外围设备在核心层的名称调用SPI核心层函数取得挂载在SPI控制器上描述SPI外围设备的结构体:
snprintf(str, sizeof(str), “%s.%u”, dev_name(&master->dev), cs);
dev = bus_find_device_by_name(&spi_bus_type, NULL, str);
spi = to_spi_device(dev);
//spi_buf_type为描述SPI总线的bus_type结构体类型变量
//spi为描述SPI外围设备spi_device结构体类型指针
步骤 3. 调用SPI核心层函数将spi_transfer添加到spi_message队列中。
spi_message_init(&m)
spi_message_add_tail(&t, &m)
//t为spi_transfer结构体类型变量
//m为spi_message结构体类型变量
步骤 4. 调用SPI核心层停工的读写函数对外围设备进行读写操作
status = spi_sync(spi, &m);
status = spi_async(spi, &m)
//spi为描述SPI外围设备的spi_device结构体类型指针
//spi_sync函数为进行spi同步读写操作
//spi_async函数为进行spi异步读写操作
代码示例如下:
此段代码示例仅供参考,而非实际应用功能。
//传入SPI控制器总线号和片选号
static unsigned int busnum;module_param(busnum, uint, 0);
MODULE_PARM_DESC(busnum, "SPI busnumber (default=0)");
static unsigned int cs;
module_param(cs, uint, 0);
MODULE_PARM_DESC(cs, "SPI processor select (default=0)");
extern struct bus_typespi_bus_type;
//宣告SPI控制器的结构体
static struct spi_master *master;
//宣告SPI外围设备的结构体
static struct spi_device *spi_device;
static int __init spidev_init(void) {
char *spi_name;
struct device *spi;
master =spi_busnum_to_master(busnum);
spi_name = kzalloc(strlen(dev_name(&master->dev)), GFP_KERNEL);
if (!spi_name)
return -ENOMEM;
snprintf(spi_name,sizeof(spi_name), "%s.%u", dev_name(&master->dev),cs);
spi = bus_find_device_by_name(&spi_bus_type, NULL, spi_name);
if (spi == NULL)
return -EPERM;
spi_device = to_spi_device(spi);
if (spi_device ==NULL)
return -EPERM;
put_device(spi);
kfree(spi_name);
return 0;
}
int spi_dev_write(, void \*buf,unsigned long len, int buswidth)
{
struct spi_device *spi = spi_device;
struct spi_transfer t = {
.speed_hz = 2000000,
.tx_buf = buf,
//buf里需依外围设备规范填入device addr, register addr, write data等资讯
.len = len,
};
struct spi_message m;
spi->mode = SPI_MODE_0;
if (buswidth == 16)
t.bits_per_word = 16;
else
t.bits_per_word = 8;
if (!spi) {
return -ENODEV;
}
spi_message_init(&m);
spi_message_add_tail(&t, &m);
return spi_sync(spi, &m);
}
int spi_dev_read(unsigned char devaddr, unsigned char reg_addr,void*buf, size_t len)
{
struct spi_device *spi = spi_device;
int ret;
u8 txbuf[4] = { 0, };
struct spi_transfer t = {
.speed_hz = 2000000,
.rx_buf =buf,
.len = len,
};
struct spi_message m;
spi->mode = SPI_MODE_0;
if (!spi) {
return -ENODEV;
}
txbuf[0] = devaddr;
txbuf[1] = 0;
txbuf[2] = reg_addr;//txbuf[1]&txbuf[2]需根据外围设备位宽来决定填写1 byte or 2 bytes,此范例为2 |bytes位宽
t.tx_buf =txbuf;
spi_message_init(&m);
spi_message_add_tail(&t, &m);
ret = spi_sync(spi, &m);
return ret;
}
6.3.2. 用户态SPI读写程序示例:¶
此操作示例在用户态下实现对挂载在SPI控制器0上的SPI外围设备的读写操作。(具体实现可参考 tools/spi/spidev_test.c)
步骤1: 打开SPI总线对应的设备文件,获取文件描述符。
tatic const char *device = "/dev/spidev32766.0";
…
fd = open(device, O_RDWR);
if (fd < 0)
pabort("can't open device");
备注: SPI控制器1上挂载的外围设备默认节点为 “dev/spidev32765.0”
SPI控制器2上挂载的外围设备默认节点为 “dev/spidev32764.0”
SPI控制器3上挂载的外围设备默认节点为 “dev/spidev32763.0”
仅需替换节点名称即可,其馀操作与SPI控制器0上挂载的外围设备相同。
步骤2: 通过ioctl设置SPI传输模式:
/*
*spi mode
*/
ret = ioctl(fd, SPI_IOC_WR_MODE32, &mode);
if (ret == -1)
pabort("can't set spi mode");
ret = ioctl(fd, SPI_IOC_RD_MODE32, &mode);
if(ret == -1)
pabort("can't get spi mode");
备注:mode 值配置请参考下图或是内核代码 include/linux/spi/spi.h,
Ex. mode = SPI_MODE_3 | SPI_LSB_FIRST;
#define SPI_CPHA 0x01 /* clock phase */
#define SPI_CPOL 0x02 /* clock polarity */
#define SPI_MODE_0 (0|0) /* (original MicroWire) */
#define SPI_MODE_1 (0|SPI_CPHA)
#define SPI_MODE_2 (SPI_CPOL|0)
#define SPI_MODE_3 (SPI_CPOL|SPI_CPHA)
步骤3: 通过ioctl设置SPI传输频宽:
/*
* bits per word
*/
ret = ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, &bits);
if (ret == -1)
pabort("can't set bits per word");
ret = ioctl(fd,SPI_IOC_RD_BITS_PER_WORD, &bits);
if (ret == -1)
pabort("can't get bits per word");
步骤4: 通过ioctl设置SPI传输速度 (一般建议speed = 25M):
/*
* max speed hz
*/
ret = ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed);
if (ret == -1)
pabort("can't set max speed hz");
ret = ioctl(fd,SPI_IOC_RD_MAX_SPEED_HZ, &speed);
if (ret == -1)
pabort("can't get max speed hz");
步骤5: 使用ioctl进行数据读写:
ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr);
if (ret < 1)
pabort("can't send spi message");
备注: tr传输一帧消息的spi_ioc结构体数组首地址。