SPI Linux驱动程序

3
我正在尝试学习如何编写基本的SPI驱动程序,下面是我编写的探测函数。
我在这里尝试设置spi设备以用于fram(datasheet),并使用spi_sync_transfer()api description从芯片中获取制造商ID。
当我执行此代码时,我可以使用逻辑分析仪看到SPI总线上的数据,但无法使用rx缓冲区读取它。我错过了什么吗?请有人帮帮我吗?
static int fram_probe(struct spi_device *spi)
{
    int err;
    unsigned char ch16[] = {0x9F,0x00,0x00,0x00};// 0x9F => 10011111
    unsigned char rx16[] = {0x00,0x00,0x00,0x00};

    printk("[FRAM DRIVER] fram_probe called \n");

    spi->max_speed_hz = 1000000;
    spi->bits_per_word = 8;
    spi->mode = (3);

    err = spi_setup(spi);
        if (err < 0) {
            printk("[FRAM DRIVER::fram_probe spi_setup failed!\n");
            return err;
        }
    printk("[FRAM DRIVER] spi_setup ok, cs: %d\n", spi->chip_select);
    spi_element[0].tx_buf = ch16;
    spi_element[1].rx_buf = rx16;

    err = spi_sync_transfer(spi, spi_element, ARRAY_SIZE(spi_element)/2);
    printk("rx16=%x %x %x %x\n",rx16[0],rx16[1],rx16[2],rx16[3]);

    if (err < 0) {
        printk("[FRAM DRIVER]::fram_probe spi_sync_transfer failed!\n");
        return err;
    }

    return 0;
}

这段代码存在很多问题。首先,它不是一个SPI驱动程序。其次,你必须了解如何在内核和用户空间中与SPI通信。第三,你必须学习硬件层面上SPI总线的工作原理。它有哪些信号以及它们如何随时间变化。 - 0andriy
内核有两种可以称为SPI驱动程序的驱动程序。这似乎是一个spi_driver的探测函数,通过使用module_spi_driver()注册一个struct spi_driver来创建。所以我认为将其称为SPI驱动程序是完全合理的。另一种类型是主驱动程序,由一个struct spi_master定义,并使用spi_register_master()进行注册。 - TrentP
@TrentP,这不是SPI主控驱动程序,我改正了。 - 0andriy
1个回答

1

spi_element在这个例子中没有被声明。你需要展示它并且展示数组的所有元素是如何填充的。但是从现有的代码中,我看到了一些错误。

你需要设置spi_transferlen参数。你已经将TX或RX缓冲区分配给了ch16rx16,但在任一情况下都没有设置缓冲区的长度。

你应该将spi_transfer中未使用的所有字段清零。

如果你将长度设置为四,那么根据数据表,你将不能发送正确的命令。RDID在一个字节的命令之后期望四个字节的输出数据。你正在写入一个四字节的命令,并读取四个字节的数据。第一个传输中的tx_buf应该只有一个字节。

最后一个参数传递给 spi_sync_transfer() 的转移数量是不正确的。在这种情况下应该是2,因为您定义了两个 spi_element[0]spi_element[1]。如果 spi_element 是为了此消息而声明的,并且您想将数组中的所有转移发送,则可以使用 ARRAY_SIZE()
考虑这种方法来更好地填充 spi_transfers。它将处理未使用的字段,以易于查看的方式定义转移,并且更改缓冲区大小或转移数量会自动在剩余代码中进行调整。
const char ch16[] = { 0x8f };
char rx16[4];
struct spi_transfer rdid[] = {
    { .tx_buf = ch16, .len = sizeof(ch16) },
    { .rx_buf = rx16, .len = sizeof(rx16) },
};
spi_transfer(spi, rdid, ARRAY_SIZE(rdid));

由于您有一个作用域,请确保此操作在单个芯片选择脉冲下进行。我发现不止一个Linux SPI驱动程序存在错误,会在不应该的情况下脉冲芯片选择。在某些情况下,从TX切换到RX(如上所述)将触发CS脉冲。在其他情况下,每个数据字(这里为8位)都会生成CS脉冲。
另一件您应该更改的事情是使用dev_info(&spi->dev, "device version %d", id)',并且还要使用dev_err()来打印消息。这以标准方式插入设备名称,而不是硬编码的非标准和不一致的“[FRAME DRIVER] ::”文本,并设置适当的消息级别。
此外,请考虑在驱动程序中支持设备树以读取设备属性。然后,您可以执行诸如更改此设备的SPI总线频率而无需重新构建内核驱动程序之类的操作。

非常感谢您的解释和帮助。我按照您的建议修改了函数,现在看起来它给了我期望的结果。非常感谢! - Furhad Jidda

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接