akira.kei が 2026年09月27日12時38分52秒 に編集
コメント無し
本文の変更
[<前の記事](https://elchika.com/article/9c031513-2021-42c7-87d0-a742eb357277/) : 次の記事>
@[youtube](https://youtube.com/shorts/JnaMV6ei8Ao)
@[youtube](@[youtube](https://www.youtube.com/watch?v=lJIrF4YjHfQ))
## サンプリング周期一定のADC [前の記事](https://elchika.com/article/9c031513-2021-42c7-87d0-a742eb357277/)では8連LEDの点灯制御をメインループで、タッチスイッチの監視を割り込みで処理していた。これに加えADCも一定サンプリング周期で読み込んでLEDが流れる速度を変更してみる。ついでにLEDのタイミング制御もタイマーにしておいたので、二つのインターバルタイマーを使うことになる。 ```cpp: #include "pico/stdlib.h" #include "hardware/timer.h" #include "hardware/adc.h" ``` インクルードはいつも必須の```pico/stdlib.h```の他に、ハードウェア固有の```hardware/timer.h```と```hardware/adc.h```だ。高機能SDKというかAPIの場合、こういう作法を覚えるのが一苦労だ。PICのような低機能APIだと自分で書く羽目になるわけだからあまり差がないかもしれないけど。 ## スイッチ割り込み [前の記事](https://elchika.com/article/9c031513-2021-42c7-87d0-a742eb357277/)と同じように、スイッチの昇りエッジと降りエッジの両方で割り込めるようにしてある。割り込みGPIOの14番は何度も出てくるので```#define```で定義すべきかも。 ```cpp: bool dir=false; void gpio_callback(uint gpio, uint32_t events) { if (gpio == 14) { if (events & GPIO_IRQ_EDGE_RISE) dir=true; if (events & GPIO_IRQ_EDGE_FALL) dir=false; } } ``` ## インターバルタイマー 用意するインターバルタイマーは```repeating_timer```と```repeating_timer_adc```だ。この```repeating_timer```の方は[前の記事](https://elchika.com/article/9c031513-2021-42c7-87d0-a742eb357277/)と同じになっていて、```bool dir```の値に従って右シフトか左シフトかを切り替えている。 ```cpp: repeating_timer_t timer,adc_timer; bool repeating_timer_callback(struct repeating_timer *t) { static uint32_t cnt = 1; gpio_put_masked(0xff << 6, cnt<< 6); cnt=(dir)?(cnt<<1)&0xff:(cnt>>1)&0xff; if(cnt==0) cnt=(dir)?1:0x80; return true; } ``` ADCの方のタイマーは以下のように、LED点灯制御の流れる速度を決めている。処理性能に余裕があるので変数を気兼ねなく```float```に設定できる。ADCの値と速度の関係はリニアになっているが指数的とか対数的じゃないと自然な速度変更にならないようだ。ゆっくりの場合はボリュームをまわしてもいつまでもゆっくりで、急に速度が速くなる。 ```cpp: float result=0; float result_prev=0; float mint=25.0f; float maxt=250.0f; bool repeating_timeradc_callback(struct repeating_timer *t) { result_prev=result; result = (float)(127-(adc_read()>>5)); if(result!=result_prev) { float msval=mint+(maxt-mint)*result/127.0f; cancel_repeating_timer(&timer); add_repeating_timer_ms((int)-msval, repeating_timer_callback, NULL, &timer); } return true; } ``` ## 初期化 ピン関連の初期化ルーチンは別途```settup```関数にまとめてみた。Arduino等でよくある形である。ADCを使うためには```adc_init```、```adc_gpio_init```および```adc_select_input```の3行が必要なところはちょっと長すぎないか。 ```cpp: void setup(void) { for(int i=6;i<14;i++) { gpio_init(i); gpio_set_dir(i, GPIO_OUT); } gpio_init(14); gpio_set_dir(14, GPIO_IN); adc_init(); adc_gpio_init(26); adc_select_input(0); } ``` ## メイン関数 メインはやはり[前の記事](https://elchika.com/article/9c031513-2021-42c7-87d0-a742eb357277/)とほとんど同じだが、ADCのタイマーの呼び出し(割り込み登録)の部分が異なる。ループ部分は```while(true);```の無限ループだが中身が空っぽなので、もしかしたら```__wfe();```を使う方がいいかもしれない。 ```cpp: int main() { setup(); gpio_set_irq_enabled_with_callback( 14, GPIO_IRQ_EDGE_RISE | GPIO_IRQ_EDGE_FALL, true, &gpio_callback ); add_repeating_timer_ms(125, repeating_timer_callback, NULL, &timer); add_repeating_timer_ms(250, repeating_timeradc_callback, NULL, &adc_timer); while (true) ; } ```