予測困難な瞬間ノイズなど、極めて短い時間に起きる変化をラズピコはどこまで捉えられるだろうか? ここでの「短い時間」とはナノ秒単位を想定している。まずは、ラズピコのPWMによりパルスを発生させ、そのパルス幅を変動させながら限界値を見極めることにした。
関数start_PWMに出力ピンとパルス幅初期値を指定して、PWM動作を開始。周期1msecで16nsec単位のLOWパルスを発生。さらに、タイマー割り込みによって1秒毎に関数update_PWMでパルス幅を1024nsecから16nsecまで循環させている。パルス幅の変数pulseWidthは、現在の値を小型有機LEDに表示する関数で用いる。
uint16_t pulseWidth = 1024; // ナノ秒パルス幅初期値(16nsec単位)
pwm_config slice_config; // PWMコンフィグレーション
// PWM動作開始関数
void start_PWM(uint PIN, int width) {
uint slice_num;
gpio_set_function(PIN, GPIO_FUNC_PWM);
slice_num = pwm_gpio_to_slice_num(PIN);
slice_config = pwm_get_default_config();
pwm_config_set_wrap(&slice_config, 62500); // 周期1msec = 16nsec x 62500
pwm_config_set_clkdiv(&slice_config, 2); // 分周比2
pwm_config_set_output_polarity(&slice_config, true, true); // 極性反転
pwm_init(slice_num, &slice_config, true);
pwm_set_gpio_level(PIN, (slice_config.top * 0.000001 * width));
}
// PWMパルス幅更新(1024nsecから16nsec循環)
void update_PWM(uint PIN) {
pulseWidth -= 16;
if (pulseWidth < 16) pulseWidth = 1024;
pwm_set_gpio_level(PIN, (slice_config.top * 0.000001 * pulseWidth));
}
CPUでパルス出力ピンを常時監視するポーリング手法から始める。有機LEDの表示更新は、実測で約27msecを要した。この処理中はパルスを取りこぼしてしまうため、表示更新処理はコア1に委ねた。なお、表示関数update_DISPLAYの内容については、ここでは割愛する。
また、ラズピコ外部からのパルス入力に対応するため、パルス出力ピンとパルス入力ピンは別としている。この実験では、ラズピコを実装したブレッドボード上で出力ピンと入力ピンを接続する。
#define PICO_PULSE_OUT_PIN 1 // パルス出力ピン
#define PICO_PULSE_IN_PIN 2 // パルス入力ピン
#define PICO_PULSE_DETECT_PIN 15 // パルス検出ピン
#define HIGH 1
#define LOW 0
bool oneSecFlag = false; // 1秒経過フラグ
static repeating_timer_t timer; // タイマー割込み構造体
// 1秒タイマー割り込み
bool timer_callback(repeating_timer_t *rt) {
oneSecFlag = true;
return (true);
}
// コア1メインプログラム(小型有機LED表示更新)
void core1_main() {
uint32_t number = 0;
while (true) {
number = multicore_fifo_pop_blocking();
update_DISPLAY((int)number); // この関数の内容は割愛
}
}
// メインプログラム
int main() {
// ラズピコ初期化
stdio_init_all();
// パルス出力ピン設定(初期値HIGH)
gpio_init(PICO_PULSE_OUT_PIN);
gpio_set_dir(PICO_PULSE_OUT_PIN, GPIO_OUT);
gpio_put(PICO_PULSE_OUT_PIN, HIGH);
// パルス入力ピン設定
gpio_init(PICO_PULSE_IN_PIN);
gpio_set_dir(PICO_PULSE_IN_PIN, GPIO_IN);
gpio_pull_up(PICO_PULSE_IN_PIN);
// パルス検出ピン設定(初期値LOW)
gpio_init(PICO_PULSE_DETECT_PIN);
gpio_set_dir(PICO_PULSE_DETECT_PIN, GPIO_OUT);
gpio_put(PICO_PULSE_DETECT_PIN, LOW);
// 1秒タイマー割り込み設定
add_repeating_timer_ms(-1000, &timer_callback, NULL, &timer);
// PWM動作開始
start_PWM(PICO_PULSE_OUT_PIN, pulseWidth);
// コア1実行開始
multicore_launch_core1(core1_main);
// ポーリングによるパルス検出
while (true) {
... // 次項に表記
}
}
以下は、ポーリング手法によるパルス検出のコア部分。1秒間の検出パルス数が有機LEDに表示される。周期1msecパルスのため、取りこぼしがなければ1,000となる。
uint32_t detect_number = 0; // 1秒間のパルス検出回数
bool loEdgeFlag = false; // パルスLOWエッジ検出フラグ
bool pulseStateNow = true; // パルス現信号 初期値HIGH
bool pulseStatePre = true; // パルス前信号 初期値HIGH
while (true) {
pulseStateNow = gpio_get(PICO_PULSE_IN_PIN); // パルス信号取得
if (!pulseStateNow && pulseStatePre) loEdgeFlag = true; // LOWエッジ検出
else if (pulseStateNow && loEdgeFlag) { // LOWエッジ検出後HIGH
gpio_put(PICO_PULSE_DETECT_PIN, HIGH); // パルス検出ピン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エッジ検出フラグクリア
}
pulseStatePre = pulseStateNow; // パルス取得信号更新
}
パルス幅を1024nsecから16nsec単位で減少させていくと、240nsecから取りこぼしが発生し、80nsecでは検出パルス数が600を下回る。CPUによる処理は240nsec程度が限界値と言える。
以下に、パルス幅に対しての検出パルス数の実験結果を掲載する。有機LEDの上部左が検出数、下部左がナノ秒パルス幅。上部右は検出手法を示す数値。

パルス幅608nsec:検出パルス数1,000(取りこぼし無し)

パルス幅80nsec:検出パルス数591(取りこぼし約4割)