1.SPI简介
SPI 通常指串行外设接口(Serial Peripheral Interface),它是一种高速、全双工、同步的通信总线。
SPI 总线在芯片的管脚上只占用四根线,节约了芯片的管脚,也为 PCB 的布局节省了空间。这四根线分别是:
- SCK(Serial Clock):串行时钟线,由主设备产生,用于同步数据传输。
- MOSI(Master Output/Slave Input):主设备输出/从设备输入数据线。
- MISO(Master Input/Slave Output):主设备输入/从设备输出数据线。
- CS(Chip Select):从设备使能信号线,也称为片选线,由主设备控制,用于选择与之通信的从设备。
SPI分成了四种模式,见下表, 主机与从机需要工作在相同的模式下才可以正常通讯,实际中采用较多的是“模式0”与“模式3”。
* CPOL :
极性为0,初始时钟为低电平;
极性为1,初始时钟为高电平;
* CPHA :
相位为0,在时钟的第一个边缘传输数据;
相位为1,在时钟的第二个边缘传输数据;
2.主要函数
编写应用程序需要使用到spi_ioc_transfer结构体,包含在spidev.h里
struct spi_ioc_transfer {
__u64 tx_buf; //发送数据缓存
__u64 rx_buf; //接收数据缓存
__u32 len; //数据长度
__u32 speed_hz; //通讯速率
__u16 delay_usecs; //两个spi_ioc_transfer之间的延时,微秒
__u8 bits_per_word; //数据长度
__u8 cs_change; //取消选中片选
__u8 tx_nbits; //单次数据宽度(多数据线模式)
__u8 rx_nbits; //单次数据宽度(多数据线模式)
__u16 pad;
};
还需要设备控制接口函数,使用ioctl函数设置spi相关配置
#include <sys/ioctl.h>
int ioctl(int fd, unsigned long request, ...);
*fd为设备文件句柄
*request的常用值有:
- 初始化
void spi_init(void)
{
int ret = 0;
//打开 SPI 设备
fd = open(SPI_DEV_PATH, O_RDWR);
if (fd < 0)
printf("can't open %s\n",SPI_DEV_PATH);
//spi mode 设置SPI 工作模式
ret = ioctl(fd, SPI_IOC_WR_MODE32, &mode);
if (ret == -1)
printf("can't set spi mode\n");
//bits per word 设置一个字节的位数
ret = ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, &bits);
if (ret == -1)
printf("can't set bits per word\n");
//max speed hz 设置SPI 最高工作频率
ret = ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed);
if (ret == -1)
printf("can't set max speed hz\n");
//打印
printf("spi mode: 0x%x\n", mode);
printf("bits per word: %d\n", bits);
printf("max speed: %d Hz (%d KHz)\n", speed, speed / 1000);
}
- 数据传输
void transfer(int fd, uint8_t const *tx, uint8_t const *rx, size_t len)
{
int ret;
struct spi_ioc_transfer tr = {
.tx_buf = (unsigned long)tx, //发送缓冲区地址
.rx_buf = (unsigned long)rx, //接收缓冲区地址
.len = len, //一次传输的数据长度
.delay_usecs = delay, //如果不为零则用于设置两次传输之间的时间延迟
.speed_hz = speed, //speed_hz,指定SPI通信的比特率
.bits_per_word = bits, //指定字节长度,既一个字节占用多少比特
.tx_nbits = 1,
.rx_nbits = 1
};
ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr);
if (ret < 1)
3.实现
参考:"\Linux-4.9.88\tools\spi\spidev_fdx.c"
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <linux/types.h>
#include <linux/spi/spidev.h>
// 定义 SPI 设备文件路径
#define SPI_DEVICE "/dev/spidevx"
int main(int argc, char **argv) {
int fd;
unsigned int val;
struct spi_ioc_transfer xfer[1];
int status;
unsigned char tx_buf[2];
unsigned char rx_buf[2];
if(argc != 2)
{
printf("Usage: %s <val>\n",argv[1]);
return -1;
}
// 打开 SPI 设备
fd = open(SPI_DEVICE, O_RDWR);
if (fd < 0) {
perror("Failed to open SPI device");
return -1;
}
val = strtoul(argv[1],NULL,0);
val <<= 2;
val &= 0xffc;
tx_buf[1] = val & 0xff;
tx_buf[0] = (val>>8) & 0xff;
memset(xfer, 0, sizeof xfer);
xfer[0].tx_buf = tx_buf;
xfer[0].rx_buf = rx_buf;
xfer[0].len = 2;
status = ioctl(fd, SPI_IOC_MESSAGE(1), xfer);
if (status < 0) {
printf("SPI_IOC_MESSAGE");
return -1;
}
val = (rx_buf[0] << 8) | (rx_buf[1]);
val >>= 1;
printf("Pre val = %d\n",val);
return 0;
}