一、SPI简介
1.SPI,全称SerialPerripheral Interface,也就是串行外围设备接口,是一种高速全双工穿的同步通信总线,SPI时钟频率相比I2C要高得多,最高可以达到上百MHz,SPI以主从方式工作,通常是一个主设备和一个或多个从设备,一般SPI需要4根线,也可以使用3根线(单向传输),下面介绍标准的4线通信:
- CS/SS,Slave Select/Chip Select,这个是片选信号线,用于选择需要进行通信的从设备。
I2C 主机是通过发送从机设备地址来选择需要进行通信的从机设备的,SPI 主机不需要发送从机
设备,直接将相应的从机设备片选信号拉低即可。
- SCK,串行时钟(Serial Clock),和I2C的SCL一样,为SPI通信提供时钟。
- MOSI/SDO,主出从入信号线(Master Out Slave In/Serial Data Output),此数据线智能用于主机向从机发送数据,也就是主机输出,从机输入。
- MISO/SDI,主入从出信号线(Master In Slave Out/Serial Data Input),此数据线智能用户从机向主机发送数据,也就是主机输入,从机输出。
SPI通信都是由主机发起的,主机提供时钟信号,
SPI四种工作模式,串行时钟极性(CPOL)和相位(CPHA)的搭配来得到四种工作模式,如图:
- CPOL(极性)=0,串行时钟空闲状态为低电平。
- CPOL(极性)=1,串行时钟空闲状态为高电平,此时可以通过配置时钟相位(CPHA)来选择具体的传输协议。
- CPHA(相位)=0,串行时钟的第一个跳变沿(上升沿或下降沿)采集数据。
- CPHA(相位)=1,串行时钟的第二个跳变沿(上升沿或下降沿)采集数据。
SPI全双工通信时序图,CPOL=0,CPHA=0。
片选(CS)信号拉低,即选中进行通信的从设备,SCLK时钟CPOL=0(极性为0是低电平),CPHA=0(相位为0,第一个跳变沿),然后MOSI发送数据0xD2(1101 0010)给从设备,同时从设备也通过MISO数据线给主设备返回0x66(110 0110)。
二、linux驱动中SPI读写代码实现
#include <linux/spi/spi.h>
struct spi_test_data{
struct spi_device *spi_dev;
char *tx_buf;
char *rx_buf;
//int tx_len;
//int rx_len;
};
static u32 bit_per_word = 8; //每个字长的比特数
int demo_spi_read(struct spi_test_data *st,size_t len)
{
struct spi_message msg;
struct spi_transfer xfer = {
.rx_buf = st->rx_buf,
.len = len,
};
spi_message_init(&msg);
//将spi_transfer添加到spi_message队列
spi_message_add_tail(&xfer,&msg);
//同步传输
return spi_sync(st->spi_dev, &m);
}
int demo_spi_write(struct spi_test_data *st,size_t len)
{
struct spi_message msg;
struct spi_transfer xfer = {
.tx_buf = st->tx_buf,
.len = len,
};
spi_message_init(&msg);
//将spi_transfer添加到spi_message队列
spi_message_add_tail(&xfer,&msg);
return spi_sync(st->spi_dev, &m);
}
int spi_write_then_read_demo(struct spi_test_data *st,unsigned tx_len,unsigned rx_len)
{
int ret = -1;
ret = spi_write_then_read(st->spi,st->tx_buf,tx_len,st->rx_buf,rx_len);
return ret;
}
int spi_write_and_read_demo(struct spi_test_data *st,size_t len)
{
struct spi_message msg;
struct spi_transfer xfer = {
.tx_buf = st->tx_buf,
.rx_buf = st->rx_buf,
.len = len,
};
spi_message_init(&msg);
//将spi_transfer添加到spi_message队列
spi_message_add_tail(&xfer,&msg);
return spi_sync(st->spi_dev, &m);
}
以上代码只提供思路验证,如有错误请多多批评指正