[電子工作]LPC810メモ:超音波距離センサHC-SR04をSCTで利用するクラス
SCTの例その3(超音波距離センサ)で実験した距離測定を改良してクラス化。
どんなセンサであるかは上記ページで書いたので省略。
前提とする環境はこちら→LPC810メモ 共通の準備
基本的なサンプルについてはこちら→LPC810みっかぼ自作サンプル集
工夫した点
- 非同期で測定するが、割り込み不使用。一度測定開始すると、SCTの機能だけで定期的に(0.1秒毎などで)測定し、必要な時だけ測定結果を取り出す。
- 割り算不使用。タイマの進み方を71clockで1カウントと設定していて、これは12MHzだと音速で約2mm(往復距離なら1mm)に相当するため、カウント数がmm数となる。
以下はSCTの状態遷移表的なもの。
イベント | 発生条件 | state0 | state1 |
---|---|---|---|
event0 | カウンタリセット | 出力rise | 出力rise |
event1 | 10μs経過 | 出力fall | 出力fall |
event2 | 入力rise | カウンタをcapture2に記録 | - |
event3 | 入力rise | - | カウンタをcapture3に記録 |
event4 | 入力fall | カウンタをcapture4に記録、state1に遷移 | - |
event5 | 入力fall | - | カウンタをcapture4に記録、state0に変更 |
上記の趣旨。
- event0,1(10μsのhigh)でセンサ起動。
- state0の時は、event2でriseをcapture2に記録、event4でfallをcapture4に記録する。その後state1に遷移するため、state1である間はcapture4 - capture2で距離が算出可能。
- state1の時は、event3でcaprure3,event5でcapture4になる以外は同様。
- state0,1でcapture4を共用しているのは、match/captureがあわせて5つしか使えないため。event0,1でmatchを2つ使ってしまったので、captureは2,3,4の3つしか使えない。たぶん矛盾はおきてないつもり。しかしeventは6個定義できるのに、なぜmatech/captureが5個しか定義できないのか・・・
テンプレート引数
- template
- TrigPort HCSR04のTriggerにつなぐGPIOの番号
- EchoPort HCSR04のEchoにつなぐGPIOの番号
- Cycle 1秒間に計測する回数
- static void init()
- 初期化。最初に必ず呼ぶ。
- static void start()
- 測定開始。以降、測定しつづける。
- static void stop()
- 測定停止。次にstart()を呼ぶまで止まる。
- static int16_t getDistance()
- 距離をmm単位で返す。負値になったり、5000mmを超えた場合には、失敗とみなして-1を返す。
サンプルコード
sample.cpp(クラスを使う側)
#include "LPC8xx.h" #include "HCSR04.h" using HCSR04 = HCSR04Base<4,3>; // TriggerにGPIO4、2番pin // EchoにGPIO3、3番pin int main(void) { HCSR04::init(); // 初期化 HCSR04::start(); // LED光らせる準備。。 // GPIOに対してclockを有効化して、 // PIO0_0(8番pin)をoutputに設定 // UM10601 4.6.13 System clock control register // UM10601 27.1 Packages より、PIO0_0は8番 // UM10601 7.6.3 GPIO port direction registers LPC_SYSCON->SYSAHBCLKCTRL |= (1<<6); LPC_GPIO_PORT->DIR0 |= (1<<0); while(1) { int16_t distance = HCSR04::getDistance(); // 距離測定 if(distance < 20) { distance = 20; } // 測定エラーと20mm以下は20mm扱い else if(distance > 200) { distance = 200; } // 200mm以上は便宜上200mm扱い // 距離が長いほど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 < 1000 * (distance + 1); i++) {} } } return 0 ; }
HCSR04.h(超音波距離センサHC-SR04をSCTで利用するクラス、今回の主題)
#ifndef HCSR04_H_ #define HCSR04_H_ // テンプレート引数 // TrigPort -> Triggerに使うGPIOの番号 // EchoPort -> Echoに使うGPIOの番号 // Cycle -> 1秒に何回計測するか。多分20くらいが限界、デフォルト10とする。 // 例) using HCSR04 = HCSR04Base<4,3>; template<uint8_t TrigPort, uint8_t EchoPort, uint8_t Cycle = 10> class HCSR04Base { public: static void init(); // 最初に必ず呼ぶ static void start(); // 測定開始 static void stop(); // 測定停止 static int16_t getDistance(); // 距離をmm単位で返す、失敗は-1 }; template<uint8_t TrigPort, uint8_t EchoPort, uint8_t Cycle> void HCSR04Base<TrigPort, EchoPort, Cycle>::init() { // SWMのclockを有効化 LPC_SYSCON->SYSAHBCLKCTRL |= (1<<7); // EchoPortのpinにSCT入力の0番(CTIN_0) を設定 // UM10601 9.5.6 Pin assign register 5 LPC_SWM->PINASSIGN5 = 0x00ffffffUL | (EchoPort << 24); // 2番pin(PIO_4)にSCT出力の0番(CTOUT_0) を設定 // UM10601 9.5.7 Pin assign register 6 LPC_SWM->PINASSIGN6 = 0x00ffffffUL | (TrigPort << 24); // RESET 以外の無効化 LPC_SWM->PINENABLE0 = 0xffffffbfUL; // UM10601 10.3 Basic configuration に従った準備 // SCTのclockを有効化(UM10601 4.6.13 System clock control register) if(!(LPC_SYSCON->SYSAHBCLKCTRL & (0x1<<8))) { 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); // 以下、SCTの動作の設定 // UM10601 10.6.1 SCT configuration register // 16bit一本(L)のみを使う。 LPC_SCT->CONFIG = (0 << 0 ) // UNIFY = 0 -> 16bitのタイマ2本(L,H)で利用する | (1 << 17) // AUTOLIMIT_L = 1 -> matchの0番(後で定義)を、カウントアップの上限とする ; // UM10601 10.6.2 SCT control register LPC_SCT->CTRL_L |= (71 - 1 ) << 5; // PRE_L = 71 -> 71clock(音が2mm進む時間)で1カウント // match/captureレジスタの2,3,4番は、capture用に設定。 // センサからの入力を受け取る。 // UM10601 10.6.10 SCT match/capture registers mode register LPC_SCT->REGMODE_L = 1 << 2 | 1 << 3 | 1 << 4; // カウンタにmatchする値を定義 // UM10601 10.6.20 SCT match reload registers 0 to 4 (REGMODEn bit = 0) // 10.6.22 SCT event state mask registers 0 to 5 // 10.6.23 SCT event control registers 0 to 5 // UM10601 10.6.21 SCT capture control registers 0 to 4 (REGMODEn bit = 1) // event0番 MATCHRELでreloadされた際に発生 // 出力を立ち上げる LPC_SCT->MATCHREL[0].L = 12000000 / 71 / Cycle; // 秒間10回 LPC_SCT->EVENT[0].STATE = 0x03; // state0,1にかかわらず発生 LPC_SCT->EVENT[0].CTRL = (0 << 0) // MATCHSEL = 0 -> match0に対応 | (0 << 4) // HEVENT = 0 -> タイマLに対応 | (1 << 12); // COMBMODE = 1 -> match だけに対応 LPC_SCT->OUT[0].SET = (1 << 0); // event1番 カウント1で発生。reload後、約10μs、のつもり。 // 出力を立ち下げる LPC_SCT->MATCHREL[1].L = 1; // 立ち上げ後約 10μs後に立下げる。カウンタ的に10μsぴったり(ここで出力をLOWにする) ★まずは+2で。 LPC_SCT->EVENT[1].STATE = 0x03; // state0,1にかかわらず発生 LPC_SCT->EVENT[1].CTRL = (1 << 0) // MATCHSEL = 1 -> match2に対応 | (0 << 4) // HEVENT = 0 -> タイマLに対応 | (1 << 12); // COMBMODE = 1 -> match だけに対応 LPC_SCT->OUT[0].CLR = (1 << 1); // event2番 state0で、入力1の立ち上がりで発生 // CAPCTRL[2]にその時点でのカウンタを記録 LPC_SCT->EVENT[2].STATE = 0x01; // state0で発生 LPC_SCT->EVENT[2].CTRL = (0 << 4) // HEVENT = 0 -> タイマLに対応 | (0 << 6) // IOSEL 入力1に対応 | (1 << 10) // IOCOND riseに対応 | (2 << 12); // COMBMODE = 2 -> I/O condition だけに対応 LPC_SCT->CAPCTRL[2].L = 1 << 2; // event3番 state1で、入力1の立ち上がりで発生 // CAPCTRL[3]にその時点でのカウンタを記録 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 だけに対応 LPC_SCT->CAPCTRL[3].L = 1 << 3; // event4,5番 // それぞれstate0,1で入力1の立下りで発生。 // CAPCTRL[4]にその時点でのカウンタを記録 // それぞれstate1,0に遷移する LPC_SCT->EVENT[4].STATE = 0x01; // state0で発生 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に設定 | (1 << 15); // STATEV = 1 LPC_SCT->EVENT[5].STATE = 0x02; // state1で発生 LPC_SCT->EVENT[5].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 LPC_SCT->CAPCTRL[4].L = 1 << 4 | 1 << 5; } // captureを読んで距離測定 template<uint8_t TrigPort, uint8_t EchoPort, uint8_t Cycle> int16_t HCSR04Base<TrigPort, EchoPort, Cycle>::getDistance() { volatile uint16_t rise, fall; auto getRiseAndFall = [&] { rise = LPC_SCT->CAP[LPC_SCT->STATE_L == 0 ? 3 : 2].L; // 立ち下がりcapture fall = LPC_SCT->CAP[4].L; // 立ち上がりのcapture }; volatile uint8_t state = LPC_SCT->STATE_L; // rise,fallを持ってくる getRiseAndFall(); // この一瞬でstateが変わってるかもしれないから、その場合には取り直し if(state != LPC_SCT->STATE_L) { getRiseAndFall(); }; // fall,riseの差分をとって距離とする int16_t distance = fall - rise; if(distance < 0 || distance > 5000) { distance = -1; } // 最大4mまで測れるらしいので、とりあえず5m超えたらエラーとする return distance; } // haltを解除して計測開始 template<uint8_t TrigPort, uint8_t EchoPort, uint8_t Cycle> void HCSR04Base<TrigPort, EchoPort, Cycle>::start() { /// UM10601 10.6.2 SCT control register LPC_SCT->CTRL_L &= ~(1 << 2); // HALT_L = 0 } // haltを設定してstop計測停止 template<uint8_t TrigPort, uint8_t EchoPort, uint8_t Cycle> void HCSR04Base<TrigPort, EchoPort, Cycle>::stop() { /// UM10601 10.6.2 SCT control register LPC_SCT->CTRL_L |= 1 << 2; // HALT_L = 1 } #endif
接続
- 左上のが超音波距離センサ
- 上から、VDD,trigger,echo,GND
- trigger,echoは、それぞれLPC810の2,3番に接続。
- http://akizukidenshi.com/catalog/g/gM-08762/
- 上の方は無駄に複雑だが昇圧コンバータで5vを作っているだけ
- http://akizukidenshi.com/catalog/g/gM-03451/
- 電源onで測定開始、手を近づけるとLEDの点滅が早くなる