https://www.cnblogs.com/schips/p/protocol_spi_about.html (主要)
https://www.zhihu.com/question/308406342/answer/2901148391
https://www.bbsmax.com/A/lk5aa4Pm51/ (有基础测试代码)
https://baijiahao.baidu.com/s?id=1746087964061209214&wfr=spider&for=pc
https://blog.csdn.net/qq_44810226/article/details/127991578
其中上图的通信流程如下:
SPI主机首先先将SS或CS线拉低,以此来告知SPI从机通信开始。
2.主机通过发送SCLK时钟信号,来告知从机即将进行的读写操作。这里的SCLK时钟信号是由SPI的模式来决定是高电平还是低电平有效的,这点在稍后会进行介绍。
3.主机(Master)将要发送的数据写到发送数据缓存区(Memory),缓存区经过移位寄存器(0~7),串行移位寄存器通过MOSI信号线将字节一位一位地移出去传送给从机,同时MISO接口接收到的数据经过移位寄存器一位一位地移到接收缓存区。
4.从机(Slave)也将自己的串行移位寄存器(0~7)中的内容通过MISO信号线返回给主机。同时通过MOSI信号线接收主机发送的数据,这样,两个移位寄存器中的内容就被交换。
最初的疑问:
1、必须由master先发送才能读取slave端数据吗?
答:应该是,暂时理解的是由主机发送时钟信号来通知采样和发送
2、spi一侧的收发只有一个缓冲区吗?
答:理解的应该是,因为本质上是master的一个buffer与slave的buffer的一个数据交互
3、master时候发送,什么时候接收?
答:根据极性和相位决定,但是在同一个上升沿和下降沿,一定会一个负责读取(采集),一个负责发送
QNX的spi接口理解:
QNX/qnx_ap/AMSS/platform/qal/clients/spi_client/src/spi_client.c
可以发现,在封装spi的读、取、交换三个接口的时候,一定会有发送的一个buffer存在,所以总会是先发送
在exchange接口,发送参数有3个(包括设备、长度和buf),接收参数只有1个buf
在spi_write_ex接口,发送参数3个,接收参数为0,应该代表不在乎接收的数据
spi_read_ex接口,发送参数为2个(设备和buf),接收参数为1个(buf)
static int spi_xchange_ex(int fd, uint32_t device, void *wbuf, void *rbuf, int len)
{
int32_t retval=0;
iov_t wiov[4];
iov_t riov[1];
if(fd < 0) {
errno = EINVAL;
return -1;
}
SETIOV(wiov+0, &device, sizeof(device));
SETIOV(wiov+1, &len, sizeof(len));
SETIOV(wiov+2, wbuf, len);
SETIOV(riov+0, rbuf, len);
if (devctlv(fd, DCMD_SPI_XCHANGE, 3, 1, wiov, riov, &retval)) {
LOGE("devctlv failed");
errno = EINVAL;
goto err;
}
return retval;
err:
return -1;
}
static int spi_write_ex(int fd, uint32_t device, void *buf, int len)
{
int32_t retval=0;
iov_t wiov[4];
if(fd < 0) {
errno = EINVAL;
return -1;
}
SETIOV(wiov+0, &device, sizeof(device));
SETIOV(wiov+1, &len, sizeof(len));
SETIOV(wiov+2, buf, len);
if (devctlv(fd, DCMD_SPI_WRITE, 3, 0, wiov, NULL, &retval)) {
LOGE("devctlv failed");
errno = EINVAL;
goto err;
}
if (-1 == retval) {
LOGE("spi write failed, len:%d ret:0x%x", len, retval);
errno = EIO;
goto err;
} else if (-1 > retval) {
errno = -retval;
goto err;
}
return len;
err:
return -1;
}
int spi_read_ex(int fd, uint32_t device, void *buf, int len)
{
int32_t retval=0;
iov_t wiov[3];
iov_t riov[1];
if(fd < 0) {
errno = EINVAL;
return -1;
}
SETIOV(wiov+0, &device, sizeof(device));
SETIOV(wiov+1, &len, sizeof(len));
SETIOV(riov+0, buf, len);
if (devctlv(fd, DCMD_SPI_READ, 2, 1, wiov, riov, &retval)) {
LOGE("devctlv failed");
errno = EINVAL;
goto err;
}
if (-1 == retval) {
LOGE("spi read failed, len:%d ret:0x%x", len, retval);
errno = EIO;
goto err;
} else if (-1 > retval) {
errno = -retval;
goto err;
}
return retval;
err:
return -1;
}
spi更底层的接口封装:
QNX/qnx_ap/AMSS/platform/resources/spi_drv/spi_drv.c
static int io_devctl(resmgr_context_t *ctp, io_devctl_t *msg, IOFUNC_OCB_T *ocbp)
{
case DCMD_SPI_READ:
{
char *data = (char*)_DEVCTL_DATA(msg->i);
uint32_t device = *(uint32_t*)data;
data += sizeof(device);
int len = *(int*)data;
data += sizeof(len);
/* ensure that we don't transfer junk bytes for padded TX */
memset(ocb->wbuf.virtualAddr, 0, len);
memset(&msg->o, 0, sizeof(msg->o));
msg->o.ret_val = start_hwd_transfer(ocb, device, &ocb->wbuf, len, &ocb->rbuf, len);
}
case DCMD_SPI_WRITE:
{
char *data = (char*)_DEVCTL_DATA(msg->i);
uint32_t device = *(uint32_t*)data;
data += sizeof(device);
int len = *(int*)data;
data += sizeof(len);
void *buf = (void*)data;
msg->o.ret_val = start_hwd_transfer(ocb, device, &ocb->wbuf, len, &ocb->rbuf, len);
}
break;
case DCMD_SPI_XCHANGE:
{
char *data = (char*)_DEVCTL_DATA(msg->i);
uint32_t device = *(uint32_t*)data;
data += sizeof(device);
int len = *(int*)data;
data += sizeof(len);
void *wbuf = (void*)data;
memset(&msg->o, 0, sizeof(msg->o));
msg->o.ret_val = start_hwd_transfer(ocb, device, &ocb->wbuf, len, &ocb->rbuf, len);
}
break;
}