パルス検出をCPUによるポーリング手法で行なうと、240nsec以下のパルス幅では取りこぼしが発生した。そこで、GPIOの割込みを利用した2つのパルス検出手法で検討を進める。
1系統GPIOでLOWエッジとHIGHエッジの両方を検出する手法と、2系統GPIOでLOWエッジとHIGHエッジをそれぞれ検出する手法である。共通の割込みハンドラー、1系統制御関数、2系統制御関数を以下に示す。
なお、3つのパルス入力ピンは、ラズピコ実装のブレッドボード上で接続されている。
#define PICO_PULSE_OUT_PIN 1 // パルス出力ピン
#define PICO_PULSE_IN_PIN 2 // パルス入力ピン
#define PICO_PULSE_IRQ1_PIN 4 // パルス入力ピン(GPIO割込み1)
#define PICO_PULSE_IRQ2_PIN 5 // パルス入力ピン(GPIO割込み2)
#define PICO_PULSE_DETECT_PIN 15 // パルス検出ピン
#define HIGH 1
#define LOW 0
bool loEdgeFlag = false; // LOWエッジ検出フラグ
bool hiEdgeFlag = false; // HIGHエッジ検出フラグ
// GPIOエッジ割込みハンドラー
void gpio_callback(uint gpio, uint32_t events) {
// 1系統GPIO
if (gpio == PICO_PULSE_IN_PIN && events == GPIO_IRQ_EDGE_FALL) {
loEdgeFlag = true; // LOWエッジ検出
}
else if (gpio == PICO_PULSE_IN_PIN && events == GPIO_IRQ_EDGE_RISE) {
hiEdgeFlag = true; // HIGHエッジ検出
}
// 2系統GPIO
else if (gpio == PICO_PULSE_IRQ1_PIN && events == GPIO_IRQ_EDGE_FALL) {
loEdgeFlag = true; // LOWエッジ検出
}
else if (gpio == PICO_PULSE_IRQ2_PIN && events == GPIO_IRQ_EDGE_RISE) {
hiEdgeFlag = true; // HIGHエッジ検出
}
}
// 1系統GPIO割込み動作開始・停止
void control_GPIOIRQ_1(bool state) {
// LOWエッジとHIGHエッジ設定
gpio_set_irq_enabled_with_callback(PICO_PULSE_IN_PIN, GPIO_IRQ_EDGE_FALL | GPIO_IRQ_EDGE_RISE,
state, &gpio_callback);
}
// 2系統GPIO割込み動作開始・停止
void control_GPIOIRQ_2(bool state) {
// LOWエッジ設定
gpio_set_irq_enabled_with_callback(PICO_PULSE_IRQ1_PIN, GPIO_IRQ_EDGE_FALL,
state, &gpio_callback);
// HIGHエッジ設定
gpio_set_irq_enabled_with_callback(PICO_PULSE_IRQ2_PIN, GPIO_IRQ_EDGE_RISE,
state, &gpio_callback);
}
以下は、1系統GPIO割込みによるパルス検出のコア部分。タイマー割り込みなどに関する部分は、前項「ポーリング手法」を参考にして欲しい。
パルス幅を1024nsecから16nsec単位で減少させていく。最初は1秒間の周期1msecのパルス数1,000を維持しているがが、576nsecから取りこぼしが発生し、512nsecで割込みが停止する。1つのGPIOにLOWエッジとHIGHエッジのコールバックを設定した場合は、一定間隔以上のパルス幅でないと内部処理が間に合わないようだ。従って、「一瞬の変化を捉える」という目的には、1系統GPIO割込みは使えないことが判明した。
uint32_t detect_number = 0; // 1秒間のパルス検出回数
control_GPIOIRQ_1(true); // 1系統GPIO割込み動作開始
while (true) {
if (loEdgeFlag && hiEdgeFlag) { // パルス検出
gpio_put(PICO_PULSE_DETECT_PIN, HIGH); // パルス検出ピンHIGH
detect_number++; // パルス検出数インクリメント
if (oneSecFlag) { // 1秒経過
gpio_put(PICO_PULSE_DETECT_PIN, HIGH); // パルス検出ピンHIGH
multicore_fifo_push_blocking((uint32_t)detect_number); // 表示更新(実測150nsec)
detect_number = 0; // パルス検出数クリア
oneSecFlag = false; // 1秒経過フラグクリア
update_PWM(PICO_PULSE_OUT_PIN); // パルス幅更新(1024nsecから16nsec循環)
}
sleep_us(1); // 1usec休止(オシロスコープ確認用)
gpio_put(PICO_PULSE_DETECT_PIN, LOW); // パルス検出ピンLOW
loEdgeFlag = false; // LOWエッジ検出フラグクリア
hiEdgeFlag = false; // HIGHエッジ検出フラグクリア
}
// パルス幅512nsec以下の割込み発生停止に対する例外処理
else if (oneSecFlag) {
multicore_fifo_push_blocking((uint32_t)detect_number);
detect_number = 0;
oneSecFlag = false;
update_PWM(PICO_PULSE_OUT_PIN);
}
}
576nsecから取りこぼし発生、512nsecで割込み停止
以下は、2系統GPIO割込みによるパルス検出のコア部分。実験結果から、パルス幅が最小の16nsecになっても、パルス検出数1,000を維持できた。2系統GPIO割込みは有用であることが判明したが、1つの信号に対して、2つのGPIOを割り当てるというデメリットがある。
uint32_t detect_number = 0; // 1秒間のパルス検出回数
control_GPIOIRQ_2(true); // 2系統GPIO割込み動作開始
while (true) {
if (loEdgeFlag) { // LOWエッジ検出
gpio_put(PICO_PULSE_DETECT_PIN, HIGH); // パルス検出ピンHIGH
if (hiEdgeFlag) { // HIGHエッジ検出
detect_number++; // パルス検出数インクリメント
if (oneSecFlag) { // 1秒経過
multicore_fifo_push_blocking((uint32_t)detect_number); // 表示更新(実測150nsec)
detect_number = 0; // パルス検出数クリア
oneSecFlag = false; // 1秒経過フラグクリア
update_PWM(PICO_PULSE_OUT_PIN); // パルス幅更新(1024nsecから16nsec循環)
}
sleep_us(1); // 1usec休止(オシロスコープ確認用)
gpio_put(PICO_PULSE_DETECT_PIN, LOW); // パルス検出ピンLOW
loEdgeFlag = false; // LOWエッジ検出フラグクリア
hiEdgeFlag = false; // HIGHエッジ検出フラグクリア
}
}
}