IoT演習第8回、割り込み

# 1. はじめに
[IoT演習シラバス案その２（授業計画表）](https://qiita.com/takashiyamanoue/items/0cf9d58265bff5883698)の第8週に予定している内容の一部です。

（以下の説明で、自分のパソコンの表示が以下の説明の表示と異なる場合があります。例えば、英語で表示されているものが日本語であったり、その逆であったり、C:ドライブの名前が異なったりする場合があります。ソフトウェアのバージョンが異なって、名前が異なる場合もあります。表示が異なっていて、どう操作すれば良いかわからない場合は、他の同様のWebページを参照したり、担当者に聞いたりしてください）

「割り込み」とは、コンピュータの外部の要因により、それまで実行していたプログラムとは別の部分を実行させる（外部要因により「割り込ん」で実行させる）ことです。この部分の実行が終了すると、通常は、もともと実行していたプログラムの実行に戻ります。

割り込みは、データの入出力などによく利用されます。オペレーティングシステム(OS)のマルチタスク（複数のプログラムを同時並行して動作させること）でも利用されることがあります。

データを入力するときに、割り込みがない場合、コンピュータの入力端子等の入力信号があるかないかを検査する命令を、これを含むループ内で繰り返し実行させることになりますが、データの入力があるまで、他の仕事ができなくなります。

割り込みがある場合、プログラムを実行している最中にデータの入力があったときに、割り込みによってデータを入力するプログラムを「割り込んで」実行させ、無駄の少ないプログラムの実行を行うことができます。

https://ja.wikipedia.org/wiki/割り込み_(コンピュータ)

グラフィカルユーザインターフェイス(GUI)のプログラミングでは、「イベント駆動」型プログラミングがよく行われます。これはマウスがクリックされたり、キーボードのキーが押されたりしたとき、これらの「イベント」に対応して、それぞれに対応したプログラムが実行されるようにするものです。
割り込みのプログラミングは、外部要因（イベント）が発生したとき、その要因に対応する処理を割り込んで実行する、という意味で、イベント駆動型プログラミングと似ています。

https://ja.wikipedia.org/wiki/イベント駆動型プログラミング

https://xtech.nikkei.com/atcl/nxt/column/18/02898/072200003/

今回は、マイクロコントローラが持っているタイマーを使い、定期的に割り込みをかけて、LEDを点滅させたり、GPIOの入力に割り込みを設定し、外部入力により割り込みをかけてLEDの点滅を制御したりします。

## 第8回 割り込み
2.タイマー割り込み
3.タイマー割り込みを使ったLチカ
4.演習その１: タイマー割り込みを使ったLチカの実験
5.GPIO入力による割り込み
6.GPIO入力による割り込みを使った発光ダイオードの点滅
7.演習その２：6の実験
8.チャタリングとその緩和
9.チャタリングを緩和した6の実験
10.授業の振り返り


# 2. タイマー割り込み
タイマー割り込みとは、タイマー（時計）により、一定期間ごとに周期的に割り込みをかけることです。Raspberry Pi Pico W (Pico W)は、内部にタイマー割り込みのためのタイマーを持っています。周期的ではなく、一定時間経過したら１回だけ割り込みをかけることもできます。

タイマー割り込みをプログラムで使うには、タイマーに割り込み間隔と、割り込み処理関数を与えます。その後、タイマーが起動したら、時間がその間隔と一致したときに、割り込み処理関数が実行されます。


https://micropython-docs-ja.readthedocs.io/ja/v1.18ja/rp2/quickref.html

タイマーを使う時は、最初に以下のようにタイマーオブジェクトを作成します。ここではタイマーオブジェクトと初期設定を一緒に行なっていますが、タイマーオブジェクトを作った後で初期設定を別に行うこともできます。

```Python
<timer>=Timer(mode=<周期的か？一度きりか？>, freq=<周波数(Hz)>, callback=<割り込み処理関数>)
```
ここで、mode=<周期的か？一度きりか？> は mode=Timer.PERIODIC か mode=Timer.ONE_SHOTを指定します。Timer.PERIODICは周期的に割り込みをかけることを表します。Timer.ONE_SHOTは一度きり割り込みをかけることを表します。freq=<周波数(Hz)>は、この周波数の間隔で割り込みをかけることを表します。freq=<周波数>の代わりに、period=<割り込み間隔(msec)>を指定することもできます。callback=<割り込み処理関数> は、割り込みがかかった時、割り込み処理関数で指定された関数を実行することを表します。
この文を実行直後からタイマーが動き出します。

割り込み処理関数はできるだけ実行時間が短くなるようにすることが求められます。


# 3. タイマー割り込みを使ったLチカ
以下に、タイマー割り込みを使って、１秒ごとにLEDを点けたり消したりする回路とプログラムの例を示します。

回路は、4週目と同じです。タクトスイッチは後で使います。

4週目に示した回路の再掲
![20240629-led-sw-bb-schimatic-01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/585767/9c7ecae7-e229-a1e3-9358-4c40f58eb0b6.png)

以下、プログラムの例です。

```Python
from machine import Pin, Timer
import time

led = Pin(15, Pin.OUT)

def blink(timer):    
    led.value(not led.value())

led.value(0)
timer   = Timer(mode=Timer.PERIODIC, period=1000, callback=blink)
print ("start timer.")
while True:
    time.sleep(1)
```




上のプログラムにおいて、
```Python
def blink(timer):    
    led.value(not led.value())
```
が割り込みが発生したときに実行される割り込み処理関数です。
割り込みが発生するたびに、ledが接続されたGPIOピンの出力値(0か1)を反転しています(0 のnotは1, 1のnotは0)。

```Python
timer   = Timer(mode=Timer.PERIODIC, period=1000,　callback=blink)
```
でタイマー割り込みの設定を行なっています。
mode=Timer.PERIODIC は周期的な割り込みを行うことを示しています。
period=1000 は、1000msec(1秒)ごとに割り込みをかけることを示しています。callback=blinkは、割り込みがかかった時、blink関数が呼び出されることを示しています。


# 4. 演習その１: タイマー割り込みを使ったLチカの実験
3の回路を作り、プログラムを作成し（コピペで良いです）、実際に動かしてみてください。この時のプログラムと回路を撮影し、演習その１の所にUploadしてください。

# 5. GPIO入力による割り込み

GPIO入力がHからLへ、またはLからHへ変わった時に割り込みをかけることができます。

以下に、Pico Wのピンの役割の図を再掲します。

![20240623-pico-w-pinout.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/585767/2176bd80-b906-bdcb-f260-222c74f3c9ac.png)

GPIO(上の図のGP<数字>と書かれた緑の四角がついたピン)を全て割り込みの要因として使うことができます。この<数字>がGPIO番号になります。

GPIOがHからLへ変化するときに割り込みをかける場合は、

```Python
<pin>=Pin(<GPIO番号>,Pin.in, PIN.PULL_UP)
<pin>.irq(trigger=Pin.IRQ_FALLING,  handler=<割り込み処理関数>)
```

を事前に実行します。GPIO入力がLからHへ変わった時に割り込みをかける場合は、

```Python
<pin>=Pin(<GPIO番号>,Pin.in, PIN.PULL_DOWN)
<pin>.irq(trigger=Pin.IRQ_RIZING,  handler=<割り込み処理関数>)
```

を事前に実行します。

# 6. GPIO入力による割り込みを使った発光ダイオードの点滅
タクトスイッチが押されるたびに発光ダイオードの点滅を切り替える回路とプログラムを作ってみます。

回路は、「IoT演習第４回、GPIOと電気回路その２」の「5. 演習1: Pico Wの外部に接続したスイッチを押すと外部のLEDを点灯し、スイッチを離すとそのLEDを消灯する回路とプログラムを作成する」で作ったものと同じものを作ります。（以下に再掲します）

![20240629-led-sw-bb-schimatic-01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/585767/9c7ecae7-e229-a1e3-9358-4c40f58eb0b6.png)


次に以下のプログラムを作成します。

```Python
from machine import Pin
import time

def blink(timer):    
    led.value(not led.value())
led = Pin(15, Pin.OUT)
sw01 = Pin(14, Pin.IN, Pin.PULL_UP)
sw01.irq(trigger=Pin.IRQ_FALLING,  handler=blink)

led.value(0)
print ("start interrupt.")
while True:
    time.sleep(1)

```
このプログラムにおいて、
```Python
def blink(timer):    
    led.value(not led.value())
```
が割り込み処理関数です。 「3. タイマー割り込みを使ったLチカ」の時のプログラムの場合と同じです。
```Python
sw01 = Pin(14, Pin.IN, Pin.PULL_UP)
sw01.irq(trigger=Pin.IRQ_FALLING,  handler=blink)
```
でGPIO sw01 に対する入力割り込みの設定を行っています。最初にPULL_UPの指定をすることで、入力がないときはこの端子の電圧をHigh(1)にしています。trigger=Pin.IRQ_FALLINGを指定することで、
この端子の電圧がLow(0)に「落ちた」とき、割り込みがかかり、割り込み処理関数 blink (handler=blinkの blink)が呼び出されます。


# 7. 演習その２：6の実験
6の回路とプログラムを作って実際に動かしてみてください。この時のプログラムと回路を撮影し、演習その２の所にUploadしてください。

なお、この実験を行っているとき、ボタンを押したけど、点滅の状態が思ったようにならない場合があることを確認してください。


# 8. チャタリングとその緩和
7の実験でタクトスイッチを押したり離したりするとLEDの点滅の関係がおもったように動かない原因の一つは、「チャタリング」という現象が発生しているからです。この場合のチャタリングとは、機械的な接点のON/OFFを行うスイッチの場合、２つの金属の接点を近づけて電流を流したり、接点を離して電流を切ったりするのですが、そのとき、機械的な振動が発生したり火花が出たりして、細かいON/OFFが発生します。
このことにより、意図しない割り込みがかかってしまい、思ったような動作にならないわけです。

https://edn.itmedia.co.jp/edn/articles/1603/22/news028.html

https://www.marutsu.co.jp/pc/static/large_order/1405_311_ph?srsltid=AfmBOoqyMxIf4qJIT2bSVFcxTJEXkYWTCm90dKDYBXDAYXuUmeytMDI5

今回の場合、小さなコンデンサ(0.047μF, 473)を、タクトスイッチと並列に接続することで、チャタリングが緩和します。以下、コンデンサを加えることでチャタリング対策を行った時の回路図です。


![20241108-led-sw-bb-schimatic-03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/585767/b0a05c37-f665-00a1-8a75-1c06663d7ac6.png)


実際の写真を以下に示します。

![20241109-led-sw-c-02.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/585767/3677bb31-56fb-8de6-00a1-7a48e233e9bb.jpeg)


タクトスイッチと並列にコンデンサを接続すると、チャタリングで発生したON/OFFの繰り返しの電流の高周波成分（短い周期の繰り返し）がコンデンサを通じて流れます（コンデンサは電気の流れの交流成分を流します。交流の周波数が高ければ高いほど、よく流れます）。このことにより、端子の方には、この短い繰り返しの電流が流れにくくなります。

# 9. チャタリングを緩和した6の実験
「8. チャタリングとその緩和」で示した、タクトスイッチと並列にコンデンサを接続した回路と、6のプログラムを作って実際に動かしてみてください。この時のプログラムと回路を撮影し、演習その3の所にUploadしてください。

# 補足
以上で示したプログラムは割り込み動作の信頼性を高める手続きや割り込み処理の最中に発生した例外の対策等を省略しています。
より信頼性の高いプログラムを作成するためには、以下のサイト等を参考にしてください。

https://tech-and-investment.com/raspberrypi-pico-14-gpio-interrupt/