【发布时间】:2020-01-24 11:50:17
【问题描述】:
我有以下奇怪的问题。 我已经设置了 BBB 来激活 spi1 模块。该模块连接到 F-RAM 芯片 (FM25CL64B)。我已经完成了所有必要的配置。 /dev/spidev1.0 存在,我编写了一个小程序来写入和读取芯片,方法是打开 /dev/spidev1.0 并使用 ioctl 和 SPI_IOC_MESSAGE 宏命令。
使用该程序,我成功地将 32 字节的文本写入 F-RAM 芯片。阅读似乎也成功了……我怎么知道他们都成功了?我使用了一个激活了 SPI 解码器的逻辑分析仪来实际查看所有 4 条 SPI 线路的情况。监控所有 SPI 线路,我可以看到写入和读取操作以正确的时序生成正确的信号,并且所有信号都是同步的。 CS 在事务期间启用芯片,CLK 以 8 位字(如配置)为每个字节提供时钟,数据线显示正确的值,这要归功于 SPI 解码器,它显示每个 8- 正上方的字节值MOSI和MISO线的位信号序列。
问题在于,即使我可以看到在读取操作期间通过 MISO 线路发送了正确的信息,但我提供给 ioctl(iSPIR, SPI_IOC_MESSAGE(2), xfer) 的缓冲区被零填充。
我故意用其他值初始化该缓冲区,因此我可以查看 ioctl 是否甚至写入其中。它确实如此。零。
现在,我可以看到在读取操作期间通过 MISO 线路发送的所有字节,这证明写入操作不仅在分析器中看起来正确,而且实际上在之前的写入操作期间写入了预期的数据。
我在 dts 文件中检查了几次 MISO 行是否配置正确(我可以按需重建和重新安装)。我检查了它是否是正确的引脚,以及它是否配置为输入。一切似乎都已正确配置。
我以 root 身份运行程序以防出现权限问题 - 没有区别。
我还在 GPIO 模式下实现了 spi 通信。 IE。 spi 模块被禁用,所有线路配置为 GPIO。 CE、CLK、MOSI 配置为输出,MISO 配置为输入。这样我就可以在软件中实现整个通信,这样我就可以完全控制线路。这样做,这一次,我能够成功地用来自 F-RAM 芯片的正确数据填充缓冲区。 IE。顺序读取操作从 F-RAM 芯片一直到我的用户空间缓冲区都很好。我能够将数据打印到控制台中。然而,这工作太慢了。此外,当有模块可供使用时,我发现使用 SPI com 的纯软件实现效率低下。
为了编写我的示例程序,我使用了在线提供的 spi_test.c 开源示例。 我还自己构建并运行了 spi_test.c,没有进行任何修改,结果相同。
这是我的程序列表(相关的 sn-ps):
// SPI config ...
int InitSPIReadMode(const char* pstrDeviceF)
{
int file;
__u8 wr_mode = SPI_MODE_0, rd_mode = SPI_MODE_0, lsb = 0, bits = 8;
__u32 speed = CLOCK_FREQ_HZ; // 500kHz
if((file = open(pstrDeviceF, O_RDWR)) < 0)
{
printf("Failed to open the bus.");
/* ERROR HANDLING; you can check errno to see what went wrong */
exit(1);
}
if(ioctl(file, SPI_IOC_RD_MODE, &rd_mode) < 0)
{
printf("SPI rd_mode\n");
return -1;
}
if(ioctl(file, SPI_IOC_RD_LSB_FIRST, &lsb) < 0)
{
printf("SPI rd_lsb_fist\n");
return -1;
}
if(ioctl(file, SPI_IOC_RD_BITS_PER_WORD, &bits) < 0)
{
printf("SPI bits_per_word\n");
return -1;
}
if(ioctl(file, SPI_IOC_RD_MAX_SPEED_HZ, &speed) < 0)
{
printf("SPI max_speed_hz\n");
return -1;
}
printf("%s: spi wr-mode=%d, spi rd-mode=%d, %d bits per word, %s, %d Hz max\n", pstrDeviceF, wr_mode, rd_mode, bits, lsb ? "(lsb first) " : "(msb first)", speed);
xfer[0].cs_change = 0; /* Keep CS activated */
xfer[0].delay_usecs = 0; //delay in us
xfer[0].speed_hz = CLOCK_FREQ_HZ; //speed
xfer[0].bits_per_word = 8; // bites per word 8
xfer[1].cs_change = 0; /* Keep CS activated */
xfer[1].delay_usecs = 0;
xfer[1].speed_hz = CLOCK_FREQ_HZ;
xfer[1].bits_per_word = 8;
return file;
}
在主函数中:(如逻辑分析仪显示此代码正确发送命令、地址并为 32 字节数据提供时钟)
int iSPIR = InitSPIReadMode("/dev/spidev1.0"); //open("/dev/spidev1.0", O_RDWR | O_SYNC);
char arrInstruct[3] = { OPCO_READ, 0x00, 0x00 };
char arrFRamData[512];
for(int pos = 0; pos < 512; pos++) arrFRamData[pos] = pos;
xfer[0].tx_buf = (unsigned long)arrInstruct;
xfer[0].len = 3;
xfer[1].rx_buf = (unsigned long)arrFRamData;
xfer[1].len = 32;
if(ioctl(iSPIR, SPI_IOC_MESSAGE(2), xfer) < 0) printf("ioctl write error %s.\n", strerror(errno));
// hex dumping of the arrFRamData buffer.
xfer 是一个全局变量,定义为:
struct spi_ioc_transfer xfer[2];
提前非常感谢! :)
【问题讨论】:
-
"CLK 为 8 位字中的每个字节提供时钟(根据配置)" 并非如此简单。除了波特率,SPI 还有另外两种时钟设置:时钟极性(通常称为 CPOL)和时钟相位(通常称为 CPHA)。这些必须符合从机的要求,否则您将得到“时钟偏差”,时钟运行半点关闭,这反过来意味着 SPI 大部分时间都可以工作,但偶尔会出现奇怪的间歇性错误。这是一个非常常见的问题,在逻辑分析仪上不容易发现。
-
抱歉没有提及。 CPHA 和 CPOL 也已正确配置。正如您在 InitSPIReadMode 中看到的代码,我设置了 rd_mode = SPI_MODE_0。这需要 CPHA 和 CPOL。芯片本身可以自动工作在模式0和模式3,无需配置。毕竟,如果不是这样,我就无法将数据写入芯片然后再读回来。
-
建议:
xfer[0].tx_buf = (uintptr_t)arrInstruct;和rx_buf类似 - 但同样,这不是问题的原因 - 只是最佳实践。 -
您有评论
// hex dumping of the arrFRamData buffer.,如果这是您观察到数据全为零的唯一方式,那么您要求我们相信您的调试代码是正确的。很可能是这样,但如果问题是有缺陷的检查而不是真正的错误,您应该将其包括在内。或者更好的是,为什么不简单地检查调试器中的缓冲区,然后复制并粘贴调试器内存转储? -
你应该明确地将
xfer[0].rx_buf和xfer[1].tx_buf设置为(uintptr_t)NULL以进行半双工操作。它们可能已经设置好了,但是xfer是(不明智的)全局的,所以这可能并不总是正确的——你不希望这些成员中的一些早期操作的残余。您确定您在逻辑分析仪上观察到的“回读”数据不仅仅是 SPI 在读取期间从先前的写入操作中输出数据吗?也就是说是在MOSI还是MISO上?
标签: c linux-kernel embedded beagleboneblack spi