【发布时间】:2021-09-06 11:58:48
【问题描述】:
我已经为嵌入式板构建了一个设备驱动程序,它使用spi_write_then_read() 函数通过 SPI 总线读取和写入外部设备。执行写入按预期工作。 SPI接口为4总线(SCLK、CS、MOSI、MISO)。
下图显示了一个事务,其中(SCK = SCLK,SDI = MOSI 和 MUXOUT = MISO)
这是读取例程的内核 sn-p,
static int lmx_read(struct lmx2xxx_driver *lmx2xxx, u16 reg, u16 * rbuf)
{
u8 buf[3];
u8 rbbuf[2];
int ret;
struct spi_device *spi = lmx2xxx->spi;
if (reg > lmx2xxx->nregs) {
dev_err(&spi->dev, "Read Error, reg 0x%x out of range", reg);
return -1;
}
/* send lower 7 bits, highest bit = 1 when reading */
buf[0] = (uint8_t)((reg & 0x7f) | 0x80);
/* pad with dummy bytes for shifting in reading */
buf[1] = 0x0;
buf[2] = 0x0;
ret = spi_write_then_read(spi, &buf[0], 1, &rbbuf[0], 2);
if (ret < 0) {
dev_err(&spi->dev, "Read Error %d", ret);
return ret;
}
*rbuf = (uint16_t)((rbbuf[0]<<8) | (rbbuf[1]));
return 0;
}
查看示波器上的信号,我发现 8 位写入后有一段时间的延迟(远大于时钟周期),然后 SCLK 在延迟后返回 16 位阅读部分。但是,当时钟回来时,我看不到 MISO 数据,因为我认为我没有遵循他们的数据表中描述的协议会引入错误(即写入和读取之间的大量延迟)。
如果我将写入缓冲区设置为 3 个字节 (ret = spi_write_then_read(spi, &buf[0], 3, &rbbuf[0], 2);),我可以看到数据在第一个字节之后进入 MISO 行,但我正在使用的函数没有捕获它,而是执行另一个延迟,然后将 SCLK 断言为 2 字节长度,当然没有什么可读取的。
是否有另一个我可以使用的函数可以在不停止 SCLK 的情况下执行背靠背写入然后读取?我尝试过spi_w8r16(),但它在功能上与spi_write_then_read(spi, &buf[0], 1, &rbbuf[0], 2); 方法相同。
在范围内捕获顶部 MISO 底部的 SCLK。
使用时的输出,spi_write_then_read(spi, &buf[0], 1, &rbbuf[0], 2);.
使用时的完整输出,spi_write_then_read(spi, &buf[0], 3, &rbbuf[0], 2); 显示延迟后标记的额外 2 个读取周期
使用时可以看到设备的正确所需输出,spi_write_then_read(spi, &buf[0], 3, &rbbuf[0], 2); 在正确的位置显示数据以供捕获,但当然此数据不会保存到仅查看捕获的数据的 rbbuf在大量延迟后拖尾 2 个字节。
【问题讨论】:
-
我不认为延迟是一个真正的问题。 (读取字节之间的延迟可能是某些硬件上的问题,但这里不是这种情况)您的示波器图片缺少的是 CS#,以确保它不会在两者之间切换。另外,有硬件数据表的链接吗?
-
CS# 在写入和读取周期之间保持有效。 ti.com/product/LMX2595
-
spi_write_then_read()使用两个struct spi_transfers 的列表设置struct spi_message,一个用于tx-only,一个用于rx-only。也许您可以改为设置一个struct spi_message,其中包含一个为双向传输设置的struct spi_transfer列表,其中包含长度为 3 的 tx 和 rx 缓冲区。(请注意,缓冲区需要是 DMA 安全的,例如由kmalloc, not 堆栈内存。)然后使用spi_sync()执行传输。结果应该在 rx 缓冲区的字节 [1] 和 [2] 中。
标签: linux-kernel linux-device-driver spi