Armadilloフォーラム

Armadillo-420 SPI通信の転送バイト数について

k-oka

2015年1月27日 15時28分

お世話になります。岡と申します。

Armadillo-420でSPI通信の転送バイト数についての質問です。

writeシステムコールを使用して、データを転送しているのですが、データサイズを8以上に指定すると
SPI writeに失敗してしまいます。

転送を行うデータサイズが10MB以上になるため、一度に多くのデータを転送を行いたいと考えております。

(ロジアナでみると、8Byte転送ごとに75uのインターバルが空いてしまいます。そのため、全ての転送に
時間がかかってしまいます)

転送サイズの拡張について何か方法がございましたら、
ご教示いただけませんでしょうか。

以上、よろしくお願いいたします。

コメント

中村です。

> Armadillo-420でSPI通信の転送バイト数についての質問です。
>
> writeシステムコールを使用して、データを転送しているのですが、データサイズを8以上に指定すると
> SPI writeに失敗してしまいます。

2014/06/27の
Armadill-460 SPIの16ビット転送での転送落ち
https://armadillo.atmark-techno.com/forum/armadillo/793

にSPIバグ情報(パッチ)があります。
これが関係しているということはないでしょうか?

この記事はv2.6.26-at19ですが、現時点での最新版の
v2.6.26-at21でも、この部分は修正されていないようです。(不確か)

--
なかむら

岡 様、 中村 様の投稿にあるパッチで回避できるかと思いますので、
取り急ぎパッチでの対応をお願いします。

このパッチでサポートするのは write ではなく
ioctl(fd, SPI_IOC_MESSAGE(1), &buffer)になります。
このioctlの使い方については、Armadillo 実践開発ガイド 第3部に
サンプルコードを掲載しております。

中村 様
> v2.6.26-at21でも、この部分は修正されていないようです。(不確か)
その通りです。
来月に v2.6.26 のアップデートを予定しているので、そこで過去にフォーラム
やMLに出ていたパッチを入れられるよう動いています。

中村です。

えーと、これ(以下のこと)は確か、自分ではMLにもフォーラムにも
書いてなかったと思うのですが・・・

> このパッチでサポートするのは write ではなく
> ioctl(fd, SPI_IOC_MESSAGE(1), &buffer)になります。
> このioctlの使い方については、Armadillo 実践開発ガイド 第3部に
> サンプルコードを掲載しております。

このサンプルコードは、8バイト以下のことしか考えてなく、
また、SPI_IOC_MESSAGE(1)の引数"1"の説明が何もありません。
それから、SPI_IOC_MESSAGE()の戻り値の扱いに不備があると思います。

開発ガイドの
2.3.4. spidevドライバー
の最後のあたりに
コールのSPI_IOC_MESSAGE(N)メッセージを使用します。
SPI_IOC_MESSAGE(N)の使用方法は、サンプルプログラムで解説します。
と書いてありますが、具体的な説明は何もありません。

それから、この後のところに
spidevドライバーに関する詳しい情報は、
linux-2.6.26-at19/Documentation/spi/spidev を参照してください。
と書いてありますが、ここにあるサンプルコード
linux-2.6.26-atXX/Documentation/spi/spidev_test.c
にはあきらかなバグがありますので、注意が必要です。
(800シリーズの3.4に入っているspidev_test.cでは
このバグは修正されてます)

ついでなので書いておくと、8バイトを超えるときには、
次のようなコードになります。(24バイトの例です)

    unsigned char tx_buf[24] = { 0 };
    unsigned char rx_buf[24] = { 0 };
    struct spi_ioc_transfer tr[3];
 
    // ここでtx_bufにデータ格納ですが、省略
 
    for (int i = 0; i < 3; i++) {
        tr[i].tx_buf = (unsigned long)tx_buf + i * 8;
        tr[i].rx_buf = (unsigned long)rx_buf + i * 8;
        tr[i].len = 8;
        tr[i].delay_usecs = 0;
        tr[i].speed_hz = spi_speed;
        tr[i].bits_per_word = spi_bits;
    }
    int ret = ioctl(fd_spi, SPI_IOC_MESSAGE(3), tr);
    // このioctlの戻り値は送受信したバイト数。エラー時は負。
    if (ret < 0) {
        pabort("spi_transfer");
    }
    // ここでrx_bufから受信データ回収ですが、省略

--
なかむら

早速の回答ありがとうございます。

事前にパッチの存在は見ておりましたが、質問内容が異なっていたためスルーしておりました。
今回の症状もパッチで解決できそうということですので、パッチを当てたイメージを作成してみます。

> なかむら様
サンプルコードありがとうございます。
SPI_IOC_MESSAGE()の使用方法について注意ありがとうございます。
サンプルコードをいただかなければ、構造体のlenにそのまま転送バッファサイズを指定してしまっていたと思います。

ほかのarm組み込み機器で、4096までのバッファサイズで転送できた経緯がありましたので、armadilloでもこれを目指して試してみます。
(ドライバのソースの一部を見ると、バッファサイズに4096を確保しているようでしたので、4096までは対応できるのかなと淡い期待をしております)

中村です。

3年以上前の投稿
https://armadillo.atmark-techno.com/forum/armadillo/1178#comment-1417
ですが、400シリーズ以外の話題でもこの記事を
参照されることもあるみたいなので、
補足しておきます。

> ついでなので書いておくと、8バイトを超えるときには、
> 次のようなコードになります。(24バイトの例です)
で記載した24バイトの転送で、
> struct spi_ioc_transfer tr[3];
のように3つのspi_ioc_transfer構造体を用意して
8バイトず3に分けて
> int ret = ioctl(fd_spi, SPI_IOC_MESSAGE(3), tr);
とした例を書きましたが、たしかこれは、
Armadillo-400シリーズのspiの下位ドライバが
1度に8バイトまでしか転送できない
という制限への対策だっと思います。

そういう制限がなければ、8バイトを超えても
1つのspi_ioc_transfer構造体でOKみたいです。

カーネルソースのドキュメントについているサンプル
linux-2.6.26-atXX/Documentation/spi/spidev_test.c
に次のようなコードがあります。

static void transfer(int fd)
{
        int ret;
        uint8_t tx[] = {
                0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
                0x40, 0x00, 0x00, 0x00, 0x00, 0x95,
                0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
                0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
                0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
                0xDE, 0xAD, 0xBE, 0xEF, 0xBA, 0xAD,
                0xF0, 0x0D,
        };
        uint8_t rx[ARRAY_SIZE(tx)] = {0, };
        struct spi_ioc_transfer tr = {
                .tx_buf = (unsigned long)tx,
                .rx_buf = (unsigned long)rx,
                .len = ARRAY_SIZE(tx),
                .delay_usecs = delay,
                .speed_hz = speed,
                .bits_per_word = bits,
        };
 
        ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr);
        if (ret == 1)
                pabort("can't send spi message");
 
        for (ret = 0; ret < ARRAY_SIZE(tx); ret++) {
                if (!(ret % 6))
                        puts("");
                printf("%.2X ", rx[ret]);
        }
        puts("");
}

3年前の投稿でも書きましたが、このioctlの戻り値チェックは
間違いだと思います。
>

>     int ret = ioctl(fd_spi, SPI_IOC_MESSAGE(3), tr);
>     // このioctlの戻り値は送受信したバイト数。エラー時は負。
>     if (ret < 0) {
>         pabort("spi_transfer");
>     }
> 

--
なかむら