【问题标题】:Problem utilizing spi module of beaglebone black使用 beaglebone black 的 spi 模块的问题
【发布时间】: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_bufxfer[1].tx_buf设置为(uintptr_t)NULL以进行半双工操作。它们可能已经设置好了,但是xfer 是(不明智的)全局的,所以这可能并不总是正确的——你不希望这些成员中的一些早期操作的残余。您确定您在逻辑分析仪上观察到的“回读”数据不仅仅是 SPI 在读取期间从先前的写入操作中输出数据吗?也就是说是在MOSI还是MISO上?

标签: c linux-kernel embedded beagleboneblack spi


【解决方案1】:

我发现我的设置有什么问题。但即使现在整件事情都有效,但我应用的解决方案提出的问题多于答案。

所以我在 beaglebone wiki 上的一篇文章 (https://elinux.org/BeagleBone_Black_Enable_SPIDEV) 中找到了这个解决方案。

我注意到,在设备树覆盖中,他们将 CLK 设置为输入。阅读整篇文章并没有发现为什么 BBB 端的 CLOCK 必须是输入。虽然是主控……文章只说明了如何构建和安装新的DTBO以激活SPI1模块。

所以,即使它对我来说没有任何意义,但我尝试将 DTBO 文件表单输出中的 CLK 行更改为输入以查看会发生什么并没有什么坏处……而且它奏效了! :O

现在,为什么设置这样工作如此奇怪。 BBB 应该是 SPI 主机,所以它的时钟线应该是一个输出,以便驱动这种同步通信。这意味着 FM25CL64B 芯片必须充当 SPI 从机。我刚刚检查了数据表,是的,芯片侧的 CLK 是一个输入。所以 BBB 上的 CLK 必须是输出。这就是我在 BBB 上配置 CLK 引脚的方式。作为输出。它没有用。

这就是我如此困惑的原因。所以即使 CLK 线的两端都是输入,它也能工作?!查看逻辑分析仪的输出,我可以清楚地看到 CLK 线被正确驱动。但是,如果主机和从机在那条线上都有输入,就不应该有任何东西产生这些脉冲?!没有其他东西连接到那条线......

【讨论】:

  • 赞成问题和答案。但您应该发布一个 问题(也指这个问题),而不是从答案中提出新问题。
  • 好的,我将改写我的答案并发布一个新问题。谢谢:)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-02-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多