LPC810メモ:SCTの例その3(超音波距離センサ)

SCTの、event発生時点のタイマの値を記録するcapture機能を試してみたくて考えてみた例。

前提とする環境はこちら→LPC810メモ 共通の準備
SCT全般のメモはこちら→LPC810メモ:SCTimer/PWM (SCT)について

  • amazonで500円くらいで買ったこのHC-SR04というセンサを使う。
  • このセンサが5vなので、この昇圧コンバータも使う。
  • このセンサは、超音波を発して、音が反射して戻ってくるまでの経過時間を返してくれるので、音速で除算すれば距離がわかる、という仕組み。
    • trigger端子を10μsHIGHにしてやると超音波を発する。
      • → SCTの出力で、triggerを10μsだけHIGHにしてやればいい。
    • 経過時間は、「echo端子がHIGHになっている時間」という形で表現される。
      • → SCTの入力にechoをつなぎ、立ち上がり/下がりの時間をcaptureで記録して、引き算すればよい。
  • より具体的にはこうしてみた。
    • 1ms経過後にtriggerへの出力(2番pin)をHIGHに(event0)
    • その10μs後にtriggerへの出力(2番pin)をLOWに(event1)
    • echo(3番pin)が立ち上がったらその時間を記録(event3)
    • echo(3番pin)が立ち下がったらその時間を記録(event4)
    • 検知できずに30ms経過したら諦める(event2)
    • event4の値からevent3の値 を引いて距離測定。
  • 連続的に距離を測定して、「近いほど速くLチカ」してみる。

サンプルコード

#include "LPC8xx.h"

// SCTの設定
void setSct() {
    //  SWMのclockを有効化
    LPC_SYSCON->SYSAHBCLKCTRL |= (1<<7);

    //  3番pin(PIO_3)にSCT入力の0番(CTIN_0) を設定
    //  UM10601 9.5.6 Pin assign register 5
    LPC_SWM->PINASSIGN5 = 0x03ffffffUL;

    //  2番pin(PIO_4)にSCT出力の0番(CTOUT_0) を設定
    //  UM10601 9.5.7 Pin assign register 6
    LPC_SWM->PINASSIGN6 = 0x04ffffffUL;

    // RESET 以外の無効化
    LPC_SWM->PINENABLE0 = 0xffffffbfUL;

    // UM10601 10.3 Basic configuration
    // に従った準備
    //  SCTのclockを有効化(UM10601 4.6.13 System clock control register)
    LPC_SYSCON->SYSAHBCLKCTRL |= (1<<8);

    // Clear the SCT peripheral reset using the PRESETCTRL register (Table 19).
    LPC_SYSCON->PRESETCTRL &= ~(0x1<<8);
    LPC_SYSCON->PRESETCTRL |= (0x1<<8);

    // 今回は割り込みは使わない
    // UM10601 3.4.1 Interrupt Set Enable Register 0 register
    // NVIC->ISER[0] = (0x1 << 9);

    // 以下、SCTの動作の設定

    // UM10601 10.6.1 SCT configuration register
    // 今回は16bit一本(L)のみを使う。
    // UNIFY = 0でAUTOLIMITも使わないので、 LPC_SCT->CONFIG の設定は不要

    // UM10601 10.6.2 SCT control register
    LPC_SCT->CTRL_L  |= (12 - 1 ) << 5; // PRE_L = 12 -> 12clock(1μ秒)で1カウント

    // match/captureレジスタの3,4番は、capture用に設定。
    // センサからの入力を受け取る。
    // UM10601 10.6.10 SCT match/capture registers mode register
    LPC_SCT->REGMODE_L = 1 << 3  |  1 << 4;

    //  カウンタにmatchする値を定義
    // UM10601 10.6.20 SCT match reload registers 0 to 4 (REGMODEn bit = 0)
    LPC_SCT->MATCH[0].L = 1000; // 開始1ms後(ここで出力をHIGHにする)
    LPC_SCT->MATCH[1].L = 1010; // 開始1ms + 10μs後(ここで出力をLOWにする)
    LPC_SCT->MATCH[2].L = 30000; // 開始30ms(測定のタイムアウト)
    // limitは使わないので、MATCHRELでなく、MATCHそのものに設定している。

    // event0番(match 0、開始1ms後に発生)
    // 10.6.22 SCT event state mask registers 0 to 5
    // 10.6.23 SCT event control registers 0 to 5
    LPC_SCT->EVENT[0].STATE = 0x01; // state0で発生
    LPC_SCT->EVENT[0].CTRL =
        (0 << 0)    // MATCHSEL = 0 -> match0に対応
        |  (0 << 4)    // HEVENT = 0 -> タイマLに対応
        |  (1 << 12);  // COMBMODE = 1 -> match だけに対応

    // event1番(match 1、開始1ms + 10μs後に発生、state1に移行)
    // 10.6.22 SCT event state mask registers 0 to 5
    // 10.6.23 SCT event control registers 0 to 5
    LPC_SCT->EVENT[1].STATE = 0x01; // state0で発生
    LPC_SCT->EVENT[1].CTRL =
        (1 << 0)    // MATCHSEL = 0 -> match0に対応
        |  (0 << 4)    // HEVENT = 0 -> タイマLに対応
        |  (1 << 12)  // COMBMODE = 1 -> match だけに対応
        |  (1 << 14)   // STATELD = 1 -> 状態をSTATEVに設定
        |  (1 << 15);  // STATEV = 1

    // event2番(match 2、タイムアウト。state 0に戻る。)
    // 10.6.22 SCT event state mask registers 0 to 5
    // 10.6.23 SCT event control registers 0 to 5
    LPC_SCT->EVENT[2].STATE = 0x02; // state1で発生
    LPC_SCT->EVENT[2].CTRL =
        (2 << 0)    // MATCHSEL = 2 -> match2に対応
        |  (0 << 4)    // HEVENT = 0 -> タイマLに対応
        |  (1 << 12)  // COMBMODE = 1 -> match だけに対応
        |  (1 << 14)   // STATELD = 1 -> 状態をSTATEVに設定
        |  (0 << 15);   // STATEV = 0

    // event3番(state1で、入力1の立下りで発生)
    LPC_SCT->EVENT[3].STATE = 0x02; // state1で発生
    LPC_SCT->EVENT[3].CTRL =
        (0 << 4)    // HEVENT = 0 -> タイマLに対応
        |  (0 << 6)    // IOSEL 入力1に対応
        |  (1 << 10)   // IOCOND riseに対応
        |  (2 << 12);  // COMBMODE = 2 -> I/O condition だけに対応


    // event2番(state1で、入力1の立下りで発生。state0に戻る。)
    LPC_SCT->EVENT[4].STATE = 0x02; // state1で発生
    LPC_SCT->EVENT[4].CTRL =
        (0 << 4)    // HEVENT = 0 -> タイマLに対応
        |  (0 << 6)    // IOSEL 入力1に対応h
        |  (2 << 10)   // IOCOND fallに対応
        |  (2 << 12)  // COMBMODE = 2 -> I/O condition だけに対応
        |  (1 << 14)   // STATELD = 1 -> 状態をSTATEVに設定
        |  (0 << 15);   // STATEV = 0

    // event3,4番が発生したら、それぞれcaptureの3,4番にタイマの値を記録
    // センサからの入力。
    // UM10601 10.6.21 SCT capture control registers 0 to 4 (REGMODEn bit = 1)
    LPC_SCT->CAPCTRL[3].L = 1 << 3;
    LPC_SCT->CAPCTRL[4].L = 1 << 4;

    // 出力の定義
    // event0で出力0をHIGHにし、1でLOWにする。
    // センサに対するトリガ。
    // UM10601 10.6.24 SCT output set registers 0 to 3
    // UM10601 10.6.25 SCT output clear registers 0 to 3
    LPC_SCT->OUT[0].SET = (1 << 0);
    LPC_SCT->OUT[0].CLR = (1 << 1);

    // event2,4番でhalt
    LPC_SCT->HALT_L = 1 << 4 | 1 << 2;
}

// SCTを使った距離測定
int16_t getDistance() {
    // カウンタをゼロに戻す
    // UM10601 10.6.7 SCT counter register
    LPC_SCT->COUNT_L = 0;

    // capture3,4をゼロに戻す
    // UM10601 10.6.19 SCT capture registers 0 to 4 (REGMODEn bit = 1)
    LPC_SCT->CAP[3].L = 0;
    LPC_SCT->CAP[4].L = 0;

    // haltを解除してstart
    /// UM10601 10.6.2 SCT control register
    LPC_SCT->CTRL_L &= ~(1 << 2);  // HALT_L = 0

    // haltになるまで待つ
    while((LPC_SCT->CTRL_L & (1 << 2)) == 0) {}

    // capture地を読んで距離測定
    uint16_t rise = LPC_SCT->CAP[3].L; // 入力の立ち上がり時間
    uint16_t fall = LPC_SCT->CAP[4].L;  // 入力の立ち下がり時間
    int16_t ret;
    if(fall == 0 || rise == 0) {
        // どちらかゼロなら測定失敗
        ret = -1;
    } else {
        // HIGHだった時間を求める
        ret = fall - rise;
        if(ret < 0) {
            ret = 0; // 何かの間違いで0未満になったら測定失敗扱い
        }  else {
            ret = ret / 58;  // cm単位に変換。58で割ればよいらしい。
        }
    }
    return ret;
}

int main(void) {
    // SCTの設定
    setSct();

    // GPIOに対してclockを有効化
    // UM10601 4.6.13 System clock control register
    LPC_SYSCON->SYSAHBCLKCTRL |= (1<<6);

    // PIO0_0(8番pin)をoutputに設定
    // UM10601 27.1 Packages より、PIO0_0は8番
    // UM10601 7.6.3 GPIO port direction registers
    LPC_GPIO_PORT->DIR0 |= (1<<0);

    while(1)  {
        // SCTで距離測定
        int16_t distance = getDistance();
        if(distance == -1 || distance > 20) { distance = 20; }  // 測定エラーまたは20cm以上は、20cm扱い。

        // 距離が長いほどwaitを長くしてLEDを点滅2回。
        for(int i = 0; i < 4; i++) {
            // PIO0_0の出力をtoggle
            // UM10601 7.6.9 GPIO port toggle registers
            LPC_GPIO_PORT->NOT0 |= (1<<0);
            for(volatile int i = 0; i < 10000 * (distance + 1); i++) {}
        }
    }
    return 0 ;
}

接続

補足/雑感

  • LPC_SCT->REGMODE_Lの設定、'1 << 3 | 1 << 4;'でなくなぜか'1 << 3 | | 1 << 4;'と書いていて気づけず、ものすごくハマった。

みっかぼの無料Androidアプリはこちら。