lynxeyedの電音鍵盤

MBDとFPGAと車載で使うデバイスの備忘録

自作RISC-Vでスイッチサイエンスの測距ToFセンサー VL53L1X を動かす

2000円もせずに400cm測れる超優秀なセンサーモジュールがあります。

www.switch-science.com

4mまで測れるので単眼カメラの停止時の車間詰め補助に使おうと思っています。
ADAS搭載車両で、前方車両が発進したことを知らせる機能がある車両がありますが、大抵4m以上車両間隔が開くと通知されます。


チッ どいつもこいつもI2C使いやがって


というわけで自作RISC-VにI2Cモジュールを接続します。
といっても、私がI2Cモジュールを一生懸命1から作成するわけではなく、すでにintel Quartus Prime/Platform Designerに用意されているので、これを接続するだけです。
Avalon-MM Masterに対応するとこのようにモジュールの低レイヤを意識せず接続できるのでとても便利です。
CPU側から見ると、スレーブモジュールがSDRAMだろうとSPIメモリだろうと特定のアドレスに向けてLoad/Store命令を実行してデータのRWをしているだけです。

I2C HALの整備

NiosII用のHALを使用することはライセンス違反となる可能性があります。なので、取り急ぎHALを作りました。
Embedded Peripherals IP User Guideのp.170から、Figure 47~51,54を参考にすればさほど難しいモジュールではありません。

全体は以下を参照ください。
KyogenRV/qsys_i2c.c at master · panda5mt/KyogenRV · GitHub

VL53L1X API

STMicroからC API、PololuからArduino用のC++ APIが提供されています。Pololu提供のAPIが使いやすく思えましたので、C用に書き直しました。
全体は以下を参照ください。ライセンスはPololuをベースにしているため、3条項修正BSDライセンスに準拠しています。
KyogenRV/VL53L1X.c at master · panda5mt/KyogenRV · GitHub

動かしてみた

以下にmain.cを示します。

#include <stdio.h>
#include <stdint.h>
#include "krv_utils.h"
#include "xprintf.h"

#define USE_VL53L1X (1)
#define USE_SDRAM   (1)

#ifdef USE_VL53L1X
    #include "qsys_i2c.h"
    #include "VL53L1X.h"
#endif //USE_VL53L1X

#ifdef USE_SDRAM
int32_t sdram_test(void) {
    uint32_t
    data,length;

    length = SDRAM_0_END - SDRAM_0_BASE;

    xprintf("SDRAM write start\r\n");
    for (int k = 0 ; k < length ; k = k + 4) {
        put32(SDRAM_0_BASE + k, k);
    }

    xprintf("SDRAM read start\r\n");
    for (int k = 0 ; k < length ; k = k + 4) {
        data = get32(SDRAM_0_BASE + k);
        if(data != k) {
            xprintf("error fount at 0x%x: expecting %d but got %d\r\n",k,k,data);
            return -1;
        }
    }
    return 0;
}
#endif // USE_SDRAM

// main function
int32_t main(int argc, char *argv[]) {
    uint64_t i;
    xdev_out(&uart_putc);       // override xprintf

#ifdef USE_VL53L1X
    xprintf("I2C init\r\n");
    i2c_init(I2C_0_BASE);
    i2c_disable_isr(I2C_0_BASE);

    if(true == VL53L1X_init()) {
        xprintf("VL5351X init OK.\r\n");
        VL53L1X_setDistanceMode(VL53L1X_Long);
        VL53L1X_setMeasurementTimingBudget(50000);

        VL53L1X_startContinuous(50);
    } else {
        xprintf("VL5351X init failed.\r\n");
    }
#endif //USE_VL53L1X

uint32_t data = 0;

#ifdef USE_SDRAM
    if(0 == sdram_test()) {
        xprintf("SDRAM r/w test OK!\r\n");
    } else {
        xprintf("SDRAM r/w test fail......\r\n");
    }
#endif //USE_SDRAM

    xprintf("KyogenRV (RV32I) Start...\r\n");
    while(1){
        wait_ms(500);
        put32(PIO_0_BASE, 0x55);
        wait_ms(500);
        put32(PIO_0_BASE, 0xAA);

#ifdef USE_VL53L1X
        data = VL53L1X_read(true);
        xprintf("value = %d\r\n",data);
#endif //USE_VL53L1X

        i = get_time_ms() / 1000;
        xprintf("machine time = %llu second\r\n",i);
    }
    return 0;
}

define文の

#define USE_VL53L1X (1)
#define USE_SDRAM   (1)

はそれぞれ、VL53L1Xを使う場合、及びSDR SDRAMを接続する場合の設定です。
使わない場合はそれぞれ0にしてください。

動作風景

youtu.be