Raspberry Pi Pico Wのパルス幅変調(PWM)とMicroPythonでLEDの輝度制御をしてみよう。

2024年8月6日

【 当サイトには広告リンクが含まれています。 】


ラズパイPicoW(Raspberry Pi Pico W)に、LEDとタクトスイッチを接続し、タクトスイッチを押すごとに、パルス幅変調(PWM)を利用してLEDを蛍の光のように点滅させます。



実験準備、機器/部品の実装

実験準備および機器/部品の実装は、「ラズパイPicoW:スイッチでデジタル入力。」と同じですので、下記のリンクより確認してください。


プログラミング

Raspi4BのThonnyを起動し、次のコードを「エディタ」に入力するか、リストをコピーしてペーストします。


Thonnyの「エディタ」には直接、日本語が入力できませんので、「Text Editor」に入力後、コピー・ペーストします。


#モジュールの読み込み(インポート)
from machine import Pin, Timer, PWM
from time import sleep


#グローバル変数 LEDの輝度制御開始(True)/停止(False)フラグを設定します。
led_st = False


#GPピン28をPWMとする、クラスPinのインスタンス「led」を作成し、
#周期を1000Hz(1kHz)に設定します。
led= PWM(Pin(28), freq=1000)


#GPピン27を入力とする、クラスPinのインスタンス「sw」を作成します。
sw = Pin(27, Pin.IN)


#タクトスイッチが押された後、チャタリングを吸収するタイマーとして、
#クラスTimerのインスタンス「tm_sht」を作成します。
tm_sht = Timer()
  
    
#LEDの輝度制御開始/停止を選択する関数「chtrng_absrb」を定義します。
def chtrng_absrb(t1):
    
    #グローバル変数として宣言します。
    global led_st
    
    #LEDの輝度制御開始(True)/停止(False)フラグを反転させます。 
    led_st = not led_st
         

#タクトスイッチの割り込みを処理する関数「itr_sw_req」を定義します。
def itr_sw_req(p):
    
    #200ms経過後に、一回だけ「chtrng_absrb」を呼び出すように、
    #チャタリングを吸収するタイマーを設定します。
    tm_sht.init(period=200, mode=Timer.ONE_SHOT, callback=chtrng_absrb)


#タクトスイッチの割り込み処理を設定します。
sw.irq(trigger=Pin.IRQ_RISING,  handler=itr_sw_req)


#
# メイン処理
#

#無限ループ処理

while (True):
    
    if led_st == True:
        
        #LEDの輝度制御開始
        
        #明るさを徐々に上げていきます。
        for duty in range(65025):
            led.duty_u16(duty)
            sleep(0.0001)
            
        #明るさを徐々に下げていきます。    
        for duty in range(65025, 0, -1):
            led.duty_u16(duty)
            sleep(0.0001)        

    else:
        #LEDの輝度制御停止
        led.deinit()


Thonnyの「F5」キーを押して、現在のスクリプトを実行します。

タクトスイッチを押すたびに、LEDが蛍の光のように点滅、消灯します。



確認後、Thonnyの「Ctrl + F2」キーを押して、停止します。


任意のファイル名でラズパイPicoWに保存します。(ここでは「3_4pwmcnt.py」で保存しました。)


プログラムの説明

Thonnyを起動し、ラズパイPicoWに保存したプログラムファイル(3_4pwmcnt.py)を読み込みます。


#モジュールの読み込み(インポート)
from machine import Pin, Timer, PWM
from time import sleep


#グローバル変数 LEDの輝度制御開始(True)/停止(False)フラグを設定します。
led_st = False


#GPピン28をPWMとする、クラスPinのインスタンス「led」を作成し、
#周期を1000Hz(1kHz)に設定します。
led= PWM(Pin(28), freq=1000)


#GPピン27を入力とする、クラスPinのインスタンス「sw」を作成します。
sw = Pin(27, Pin.IN)


#タクトスイッチが押された後、チャタリングを吸収するタイマーとして、
#クラスTimerのインスタンス「tm_sht」を作成します。
tm_sht = Timer()
  
    
#LEDの輝度制御開始/停止を選択する関数「chtrng_absrb」を定義します。
def chtrng_absrb(t1):
    
    #グローバル変数として宣言します。
    global led_st
    
    #LEDの輝度制御開始(True)/停止(False)フラグを反転させます。 
    led_st = not led_st
         

#タクトスイッチの割り込みを処理する関数「itr_sw_req」を定義します。
def itr_sw_req(p):
    
    #200ms経過後に、一回だけ「chtrng_absrb」を呼び出すように、
    #チャタリングを吸収するタイマーを設定します。
    tm_sht.init(period=200, mode=Timer.ONE_SHOT, callback=chtrng_absrb)


#タクトスイッチの割り込み処理を設定します。
sw.irq(trigger=Pin.IRQ_RISING,  handler=itr_sw_req)


#
# メイン処理
#

#無限ループ処理

while (True):
    
    if led_st == True:
        
        #LEDの輝度制御開始
        
        #明るさを徐々に上げていきます。
        for duty in range(65025):
            led.duty_u16(duty)
            sleep(0.0001)
            
        #明るさを徐々に下げていきます。    
        for duty in range(65025, 0, -1):
            led.duty_u16(duty)
            sleep(0.0001)        

    else:
        #LEDの輝度制御停止
        led.deinit()


プログラム全体の流れ


「パルス幅変調(PWM)でLEDの輝度制御」プログラムは次の処理を行います。


実装順流 れ説 明
モジュールの読み込み**machineモジュールのクラスPin、クラスTimer、クラスPWM及びtimeモジュールの関数sleepを読み込みます。
LEDのGPピンを設定**LEDのGPピンをPWM、出力する周期を1kHZに設定します。
タクトスイッチのGPピンを設定**タクトスイッチの状態を検出するGPピン番号を設定します。
タイマーの設定**タクトスイッチが押された後、チャタリング時間を吸収するタイマーを設定します。
LEDの輝度制御の開始/停止を選択する関数4LEDを蛍の光のように点滅させるか、消灯させるかを判断するフラグを設定します。
タクトスイッチの割り込みを処理する関数3タクトスイッチのチャタリングにより発生した複数の割り込みを吸収するため、200msの一回限りのタイマーを設定し、「LEDの輝度制御の開始/停止を選択する関数」を呼び出します。
タクトスイッチの割り込み処理2タクトスイッチが押された時に割り込みを発生させ、「タクトスイッチの割り込みを処理する関数」を呼び出します。
メイン処理1タクトスイッチが押され、フラグがTrueになった場合、輝度制御が開始がされます。

A.デューティ比を設定した値まで増加させ、明るさを徐々に上げていきます。

B.デューティ比を設定した値まで減少させ、明るさを徐々に下げていきます。

タクトスイッチが再び押されるまで、A,Bを繰返します。
タクトスイッチが押され、フラグがFalseになった場合、PWMを停止しLEDを消灯させます。


モジュールの読み込み(インポート)ついては、「ラズパイPicoW:デジタル出力でLED点灯・消灯。」を参照して下さい。

タクトスイッチのGPピンを設定、タイマーの設定、タクトスイッチの割り込みを処理する関数、タクトスイッチの割り込み処理ついては、「ラズパイPicoW:スイッチでデジタル入力。」を参照して下さい。


LEDのGPピンを設定


GPピン28をPWM状態に設定し、1000Hz(1kHz)の周期に中でパルス幅を変えていきます。


led= PWM(Pin(28), freq=1000)
ledクラスPinを使うため、インスタンス名「led」に代入します。
PWM(Pin(28), freq=1000)呼出されるコンストラクタの引数として、GPピンを28(LEDを接続)、状態をPWM(パルス幅変調)、周期を1000Hzに設定します。


LEDの輝度制御開始/停止を選択する関数(chtrng_absrb)


クトスイッチが押され度に、グローバル変数 LEDの輝度制御開始/停止フラグ(led_st)を開始、停止に切り替えます。


global led_st
グローバル変数led_stを関数の中で変更できるように、global宣言します。


led_st = not led_st
グローバル変数led_stを反転させます。


メイン処理


LEDの明るさを徐々に上下させる処理と、LEDを消灯させる処理を、「グローバル変数 LEDの輝度制御開始/停止フラグ」の状態に応じて選択ます。

LEDの輝度制御(LEDの明るさを徐々に上下させる処理)は「while」文及び「for」文で繰り返し処理を行います。

「while」文及び「for」文は関数の記述と同じように、ブロックを4個の半角スペース(空白)でインデント(字下げ)します。


▶️無限ループ処理


LEDの輝度制御の開始/停止を無限ループの中で繰り返します。

while (True):
Thonnyの「Ctrl + F2」キーを押して強制停止させるまで、while以下のブロックを無限に繰り返します。


▶️LEDの輝度制御を開始します。【 if led_st == True: 】


🔸明るさを徐々に上げていきます。

for duty in range(65025):
  led.duty_u16(duty)
  sleep(0.0001)
for duty in range(65025):for文はrange関数が0~65025の範囲で0から1ずつ増加させる値を、変数dutyに設定し、for以下のブロックをduty が65024になるまで繰り返し実行します。
led.duty_u16(duty)PWM 出力とし設定された「led」のデューティ比を、duty / 65535(符号なし16ビット値) の比率で設定します。
sleep(0.0001)輝度制御の速さを調整するため、0.0001秒(0.1ms)間、一時停止します。


🔸明るさを徐々に下げていきます。

for duty in range(65025, 0, -1):
  led.duty_u16(duty)
  sleep(0.0001)
for duty in range(65025, 0, -1):for文はrange関数が65025~0の範囲で65025から1ずつ減少させる値を、変数dutyに設定し、for以下のブロックをduty が1になるまで繰り返し実行します。
led.duty_u16(duty)PWM 出力とし設定された「led」のデューティ比を、duty / 65535(符号なし16ビット値) の比率で設定します。
sleep(0.0001)輝度制御の速さを調整するため、0.0001秒(0.1ms)間、一時停止します。


▶️LEDの輝度制御を停止します。【 else: 】


led.deinit()
PWM 出力とし設定された「led」を無効にします。



まとめ

ラズパイPicoWのGPピンにLEDをパルス幅変調(PWM)として接続し、LEDの輝度制御(LEDの明るさを徐々に上下させる処理)を行いました。

パルス幅変調(PWM)は、パルスの周期とHighの時の幅(時間)の比率であるデューティ比で制御されることを学びました。

for文とrange()関数を使いデューティ比を変えることで、LEDの輝度制御ができることを学びました。