4

SPIDEV ドライバーを使用して SPI センサーにアクセスしようとしていますが、コードが IOCTL で停止します。

SAM9X5EK (AT91SAM9G25 をマウント) で組み込み Linux を実行しています。デバイスは SPI0 に接続されています。menuconfig で CONFIG_SPI_SPIDEV と CONFIG_SPI_ATMEL を有効にし、適切なコードを BSP ファイルに追加しました。

static struct spi_board_info spidev_board_info[] {
    {
        .modalias = "spidev",
        .max_speed_hz = 1000000,
        .bus_num = 0,
        .chips_select = 0,
        .mode = SPI_MODE_3,
    },
    ...
};
spi_register_board_info(spidev_board_info, ARRAY_SIZE(spidev_board_info));

センサーが受け入れる最大値は 1MHz です。500kHz を試しましたが、Linux の起動中にエラーが発生しました (明らかに遅すぎます)。.bus_num と .chips_select が修正されるはずです (他のすべての組み合わせも試しました)。SPI_MODE_3 データシートを確認しました。

起動中にエラーは発生せず、デバイスは /dev/spidevX.X として正しく表示されます。私はなんとかファイルを開き、有効なファイル記述子を取得しました。現在、次のコードを使用してデバイスにアクセスしようとしています (オンラインで見つけた例に触発されました)。

#define MY_SPIDEV_DELAY_USECS 100
// #define MY_SPIDEV_SPEED_HZ 1000000
#define MY_SPIDEV_BITS_PER_WORD 8
int spidevReadRegister(int fd,
                       unsigned int num_out_bytes,
                       unsigned char *out_buffer,
                       unsigned int num_in_bytes,
                       unsigned char *in_buffer)
{
    struct spi_ioc_transfer mesg[2] = { {0}, };
    uint8_t num_tr = 0;
    int ret;

    // Write data
    mesg[0].tx_buf = (unsigned long)out_buffer;
    mesg[0].rx_buf = (unsigned long)NULL;
    mesg[0].len = num_out_bytes;
    // mesg[0].delay_usecs = MY_SPIDEV_DELAY_USECS,
    // mesg[0].speed_hz = MY_SPIDEV_SPEED_HZ;
    mesg[0].bits_per_word = MY_SPIDEV_BITS_PER_WORD;
    mesg[0].cs_change = 0;
    num_tr++;

    // Read data
    mesg[1].tx_buf = (unsigned long)NULL;
    mesg[1].rx_buf = (unsigned long)in_buffer;
    mesg[1].len = num_in_bytes;
    // mesg[1].delay_usecs = MY_SPIDEV_DELAY_USECS,
    // mesg[1].speed_hz = MY_SPIDEV_SPEED_HZ;
    mesg[1].bits_per_word = MY_SPIDEV_BITS_PER_WORD;
    mesg[1].cs_change = 1;
    num_tr++;

    // Do the actual transmission
    if(num_tr > 0)
    {
        ret = ioctl(fd, SPI_IOC_MESSAGE(num_tr), mesg);
        if(ret == -1)
        {
            printf("Error: %d\n", errno);
            return -1;
        }
    }

    return 0;
}

次に、この関数を使用しています:

#define OPTICAL_SENSOR_ADDR "/dev/spidev0.0"

...

int fd;
fd = open(OPTICAL_SENSOR_ADDR, O_RDWR);
if (fd<=0) {
   printf("Device not found\n");
   exit(1);
}

uint8_t buffer1[1] = {0x3a};
uint8_t buffer2[1] = {0};
spidevReadRegister(fd, 1, buffer1, 1, buffer2);

実行すると、コードが IOCTL でスタックします。

センサーのレジスタを読み取るには、そのアドレスを含むバイトを送信し、CS を変更せずに応答を返す必要があるため、この方法を使用しました (ただし、write() と read() を使用しようとしたとき)。関数、学習中に同じ結果が得られ、それらに固執しました)。.speed_hz を指定すると Atmel で ENOPROTOOPT エラーが発生することを認識しているので (spidev.c を確認しました)、その部分をコメントしました。

なぜ行き詰まるのですか?デバイスが作成されたときに発生する可能性がありますが、実際にはハードウェアを「感じ」ません。ハードウェア SPI0 が bus_num 0 または 1 に対応するかどうかわからなかったので、両方を試しましたが、まだ成功しませんでした (ところで、どちらですか?)。

更新: SPI を動作させることができました! その半分.. MOSI は正しいデータを送信していますが、CLK は開始しません... 何か考えはありますか?

4

2 に答える 2

3

SPI を使用しているときは、常にオシロスコープを使用して io の出力を確認します。4 チャネル スコープを使用している場合、ypu で問題を簡単にデバッグでき、適切な IO を使用しているか、適切な速度を使用しているかなどを確認できます。通常、取得した信号をデータシートの図と比較します。

于 2012-05-31T06:33:27.983 に答える
2

ここにはいくつかの問題があると思います。まず、SPI は双方向です。したがって、バスを介して何かを送信したい場合は、何かを取得することもできます。したがって、常に rx_buf と tx_buf に有効なバッファを提供する必要があります。

次に、構造体 spi_ioc_transfer のすべてのメンバーを有効な値で初期化する必要があります。そうしないと、それらはメモリアドレスを指しているだけで、基になるプロセスが任意のデータにアクセスしているため、未知の動作が発生します。

第三に、なぜ ioctl で for ループを使用するのですか? 既に、spi_ioc_transfer 構造体の配列があることを ioctl に伝えています。したがって、定義されたすべてのトランザクションは、1 回の ioctl 呼び出しで実行されます。

4 番目の ioctlには、構造体配列へのポインターが必要です。したがって、ioctl は次のようになります。

 ret = ioctl(fd, SPI_IOC_MESSAGE(num_tr), &mesg);

コードに改善の余地があることがわかります。

これは、ラズベリー pi の C++ ライブラリで行う方法です。ライブラリ全体がまもなく github に公開されます。完了したら、回答を更新します。

void SPIBus::spiReadWrite(std::vector<std::vector<uint8_t> > &data, uint32_t speed,
                          uint16_t delay, uint8_t bitsPerWord, uint8_t cs_change)
{
    struct spi_ioc_transfer transfer[data.size()];
    int i = 0;
    for (std::vector<uint8_t> &d : data)
    {
        //see <linux/spi/spidev.h> for details!
        transfer[i].tx_buf = reinterpret_cast<__u64>(d.data());
        transfer[i].rx_buf = reinterpret_cast<__u64>(d.data());
        transfer[i].len = d.size(); //number of bytes in vector
        transfer[i].speed_hz = speed;
        transfer[i].delay_usecs = delay;
        transfer[i].bits_per_word = bitsPerWord;
        transfer[i].cs_change = cs_change;
        i++
    }
    int status = ioctl(this->fileDescriptor, SPI_IOC_MESSAGE(data.size()), &transfer);
    if (status < 0)
    {
        std::string errMessage(strerror(errno));
        throw std::runtime_error("Failed to do full duplex read/write operation "
                                 "on SPI Bus " + this->deviceNode + ". Error message: " +
                                 errMessage);
    }
}
于 2014-02-01T10:20:45.407 に答える