SPI linux 驱动程序

SPI linux driver

我正在尝试学习如何编写基本的 SPI 驱动程序,下面是我编写的探测函数。

我在这里要做的是为 fram(datasheet) and use the spi_sync_transfer()api description 设置 spi 设备以从芯片获取制造商 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_element 未在此示例中声明。您应该展示它以及数组的所有元素是如何填充的。但是仅从那里的代码我就看到了一些错误。

您需要设置spi_transferlen参数。您已将 TX 或 RX 缓冲区分配给 ch16rx16,但在这两种情况下均未设置缓冲区的长度。

您应该将 spi_transfer 中未使用的所有字段归零。

如果将长度设置为四,则不会根据数据表发送正确的命令。 RDID 需要一个 one 字节命令,之后将跟随四个字节的输出数据。您在第一次传输中写了一个 four 字节命令,然后读取了四个字节的数据。第一次传输的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));

既然你有示波器,一定要检查这个操作是在芯片select脉冲下发生的。我发现不止一个 Linux SPI 驱动程序有一个错误,该错误会在不应该时向芯片 select 发送脉冲。在某些情况下,从 TX 切换到 RX(如上所示)将触发 CS 脉冲。在其他情况下,为数据的每个字(此处为 8 位)生成一个 CS 脉冲。

您应该更改的另一件事是使用 dev_info(&spi->dev, "device version %d", id)'dev_err() 来打印消息。这将以标准方式插入设备名称,而不是您的 hard-coded non-standard 和不一致的“[FRAME DRIVER]::”文本。并适当设置消息的级别。

另外,请考虑在您的驱动程序中支持设备树以读取设备属性。然后你可以做一些事情,比如改变这个设备的 SPI 总线频率而无需重建内核驱动程序。