2016年12月9日金曜日

ToF型測距センサ VL53L0Xをつかってみる。

ちょうど、仕事で STマイクロの ToF型測距センサ VL53L0Xをつかう機会が
あったので、その報告をば。

昨年ETのときかな?にSTマイクロのブースでみつけて
いいなー、と思っていたデバイスなんで、
ことしのET Westで、これのNUCLEO Packをもらってきたん。

たしかに、応答性がよくて、取り回しも便利。
自分で実装できるパッケージじゃないのと、内部用に2.8Vが必要という
めんどくささ、も我慢できるかなーー、というのはあったんだけど
問題は、I2Cデバイスであるにも関わらず、利用するための
内部のアドレス情報がまーーーったく開示されておらず
STM32のNUCLEO用のサンプルから自分用にポーティングしないとどうしようもない
というイケてないデバイスで、つかうのを躊躇してたんですよね。

今年のETで、STの担当者にこの件、きいてみたら
『これの前のデバイスで、アドレス情報など、サポートにじゃんじゃん質問がきたから
データシートへの記載、やめてるんですよねー』
とか。
それ、あかんやろ(>_<)



で、躊躇してたんだけど、Pololuで、このデバイスボードを発見!
https://www.pololu.com/product/2490
 

しかも、だいぶ簡素化された状態で、Arduino用のライブラリもgitに上がっているという
なんとまぁ、うれしい状態。

で、これをPSoCでつかいたいので、これをCの形式に変換しなおしてつかってみた。
PSoCもGCCをつかっているので、本来C++が使えるはずなんだけど
現状ではCオンリーなので、しかたない。

ソースはここ。
VL53L0X.C
VL53L0X.H


[<====ソース抜粋====>]
// ================ 関数名の振り替え
#define IIC_MasterSendStart( x, y ) EZI2C_1_I2CMasterSendStart( x, y )
#define IIC_MasterStop() EZI2C_1_I2CMasterSendStop()
#define IIC_MasterClearStatus() EZI2C_1_I2CMasterClearStatus()
#define IIC_MasterWriteByte(x) EZI2C_1_I2CMasterWriteByte( x )
#define IIC_MasterStatus() EZI2C_1_I2CMasterStatus()
#define IIC_MasterReadBuf(x, y, z, f) EZI2C_1_I2CMasterReadBuf(x, y, z, f)
#define IIC_MasterWriteBuf(x, y, z, f) EZI2C_1_I2CMasterWriteBuf(x, y, z, f)
#define IIC_MSTR_NO_ERROR EZI2C_1_I2C_MSTR_NO_ERROR
#define IIC_MODE_COMPLETE_XFER EZI2C_1_I2C_MODE_COMPLETE_XFER
#define IIC_MODE_REPEAT_START EZI2C_1_I2C_MODE_REPEAT_START
#define IIC_MSTAT_RD_CMPLT EZI2C_1_I2C_MSTAT_RD_CMPLT
#define IIC_MSTAT_XFER_INP EZI2C_1_I2C_MSTAT_XFER_INP
// =============== I2C のアクセス関数
extern uint32 EZIIC_MasterStop(void);
extern uint32 EZI2C_1_I2CMasterStatus(void);
extern uint32 EZI2C_1_I2CMasterClearStatus(void);
extern uint32 EZI2C_1_I2CMasterWriteBuf(uint32 slaveAddress, uint8 * wrData, uint32 cnt, uint32 mode);
extern uint32 EZI2C_1_I2CMasterReadBuf(uint32 slaveAddress, uint8 * rdData, uint32 cnt, uint32 mode);
extern uint32 EZI2C_1_I2CMasterGetReadBufSize(void);
extern uint32 EZI2C_1_I2CMasterGetWriteBufSize(void);
extern void EZI2C_1_I2CMasterClearReadBuf(void);
extern void EZI2C_1_I2CMasterClearWriteBuf(void);
extern uint32 EZI2C_1_I2CMasterSendStart(uint32 slaveAddress, uint32 bitRnW);
extern uint32 EZI2C_1_I2CMasterSendRestart(uint32 slaveAddress, uint32 bitRnW);
extern uint32 EZI2C_1_I2CMasterSendStop(void);
extern uint32 EZI2C_1_I2CMasterWriteByte(uint32 theByte);
extern uint32 EZI2C_1_I2CMasterReadByte(uint32 ackNack);
// 外部参照
uint32_t timeout_start_ms ; // 経過時間の観測変数
view raw VL53L0X.c hosted with ❤ by GitHub

[<====  ここまで  ====>]

VL53L0Xの先頭部でI2Cへのアクセス関数部を別名定義してる。
PSoCの場合 PSoC5やPSoC4で、I2Cへのアクセス関数名がそれなりに変化しちゃって
固定してると、結構めんどくさい感じになりやすいので
VL53L0XでつかうI2Cの関数を、指定できるようにしてみた。

ほんとはClassがつかえれば、いいだろうし、Cであっても
関数ポインタつかえばできるのも理解してるけど、
なんとなく可読性がおちちゃうのが気になって、こういうベタなやり方にしてみた。

あと、タイムアウトを判定するために外部で
uint32_t timeout_start_ms ;    // 経過時間の観測変数
を減算してやる必要があります。

main.cなどでタイマーを利用してるソースの先頭に

     extern uint32_t timeout_start_ms ;

を定義して、1msのタイマーの中で

     if ( timeout_start_ms ) timeout_start_ms--;

としてください。


[<====ソースサンプル====>]
extern uint32_t timeout_start_ms ; // 経過時間の観測変数
extern uint8_t VL53L0X_init(uint8_t io_2v8 ); //= TRUE);
extern void VL53L0X_startContinuous(uint32_t period_ms ); //= 0);
extern void VL53L0X_stopContinuous(void);
extern uint16_t VL53L0X_readRangeContinuousMillimeters(void);
extern uint16_t VL53L0X_readRangeSingleMillimeters(void);
extern uint8_t VL53L0X_setSignalRateLimit(float limit_Mcps);
extern uint8_t VL53L0X_setVcselPulsePeriod(enum vcselPeriodType type, uint8_t period_pclks);
extern uint8_t VL53L0X_setMeasurementTimingBudget(uint32_t budget_us);
extern uint8_t VL53L0X_timeoutOccurred(void);
// Timer1ms
CY_ISR( timer_isr )
{
static int32 led_c = 0;
static int led_w = 0;
Timer_1_STATUS; // TCビットをクリアするためにSTATUSを空読みしておく
if ( timeout_start_ms) timeout_start_ms--;
}
void main(){
uint16 pdist = 0;
CyGlobalIntEnable; /* Uncomment this line to enable global interrupts. */
isr_Timer1_StartEx( timer_isr ); // タイマーの割り込みを関連づけ
Timer_1_Start(); // タイマースタート
EZI2C_1_Start(); //D6T用 I2C スタート
// イニシャライズ
VL53L0X_init( 1 ); //2.8Vモードで初期化
VL53L0X_startContinuous(0); // 連続動作
CyDelay(50); // 周辺デバイスの立ち上がり待ち
for(;;){
pdist = VL53L0X_readRangeContinuousMillimeters(); // 連続読み出し
CyDelay(50);
}
}
view raw main.c hosted with ❤ by GitHub

[<====  ここまで  ====>]

みたいなかんじかな。
標準状態だとサンプルが30ms間隔で行われるので、
それ以上は待ってやったほうが、いい、ってかんじですね。
観測早いし確実なのはいいなぁ。

超音波はつかいにくいし。