2. GPIO の応用 - LED 制御と割り込み
GPIO Python ライブラリのインポート
開発ボードには GPIO Python ライブラリ Hobot.GPIO がプリインストールされています。以下のコマンドで GPIO ライブラリをインポートできます。
sunrise@ubuntu:~$ sudo python3
Python 3.8.10 (default, Mar 15 2022, 12:22:08)
Type "help", "copyright", "credits" or "license" for more information.
>>> import Hobot.GPIO as GPIO
>>> GPIO.VERSION
'0.0.2'
>>> GPIO.model
'RDK_X5'
ピン番号方式の設定
開発ボードのピン番号方式には 4 つのモードがあります。
- BOARD:物理ピン番号で、開発ボードのシルク印刷の番号と一対一で対応します。
- BCM:Broadcom SoC で定められた GPIO 命名規則に基づきます。
- CVM:数字の代わりに文字列を使用し、CVM / CVB コネクタの信号名に対応します。
- SOC:チップ内部の GPIO ピン番号に対応します。
本書では BOARD 番号方式の使用を推奨します。番号方式の設定方法は以下のとおりです。
注意:番号方式は一度しか設定できません。再設定したい場合は、GPIO.cleanup() を実行してから設定し直してください。
GPIO.setmode(GPIO.BOARD)
# or
GPIO.setmode(GPIO.BCM)
# or
GPIO.setmode(GPIO.CVM)
# or
GPIO.setmode(GPIO.SOC)
番号方式の確認:
GPIO.getmode()
プログラムは BOARD, BCM, CVM, SOC or None のいずれかの結果を出力します。
警告メッセージ
以下の場合にコードを実行すると警告ログが出力されますが、正常な機能には影響しません。
- 使用しようとしている GPIO が、他のアプリケーションですでに使用されている場合
- モードとチャネルを設定する前に、
GPIO.cleanupを呼び出してピンをクリーンアップしようとした場合
警告メッセージを非表示にするには、以下のコマンドを使用します。
GPIO.setwarnings(False)
ピンの設定
GPIO ピンは使用前に、以下のように設定する必要があります。
入力に設定する方法:
GPIO.setup(channel, GPIO.IN)
出力に設定する方法:
GPIO.setup(channel, GPIO.OUT)
出力チャネルに初期値を指定することもできます。例:
GPIO.setup(channel, GPIO.OUT, initial=GPIO.HIGH)
また、複数の出力チャネルを同時に設定することもできます。例:
# set gpio(18,12,13) to output
channels = [18, 12, 13]
GPIO.setup(channels, GPIO.OUT)
入力操作
チャネルの値を読み取るには、以下を使用します。
GPIO.input(channel)
戻り値は 0 または 1 です。0 は GPIO.LOW、1 は GPIO.HIGH を表します。
出力操作
チャネルの出力値を設定するには、以下を使用します。
GPIO.output(channel, state)
state には GPIO.LOW または GPIO.HIGH を指定できます。
ピン占有のクリーンアップ
プログラム終了前に、チャネルのクリーンアップを行うことを推奨します。以下を使用してください。
GPIO.cleanup()
特定のチャネルのみをクリーンアップする場合は、以下を使用します。
# 清除单个通道
GPIO.cleanup(channel)
# 清除一组通道
GPIO.cleanup((channel1, channel2))
GPIO.cleanup([channel1, channel2])
ピン状態の確認
この機能で、対応する GPIO チャネルの機能を確認できます。
GPIO.gpio_function(channel)
この関数は IN または OUT を返します。
エッジ検出と割り込み
エッジとは、電気信号の ローからハイ(立ち上がりエッジ)または ハイからロー(立ち下がりエッジ)への変化です。この変化はイベントの発生とみなすことができ、CPU の割り込み信号のトリガーとして利用できます。
情報
RDK Ultraプラットフォームでは、40pinのうち特定のピンのみを割り込みピンとして使用できます。BOARD番号方式での番号は 13、16、18、22、27、28、32、33、37 です。ピン定義については ピン設定と定義 をご参照ください。
GPIO ライブラリには、入力イベントを検出する方法が 3 つ用意されています。
wait_for_edge() 関数
この関数は、対応するエッジの変化を検出するまで呼び出し元スレッドをブロックします。呼び出し方は以下のとおりです。
GPIO.wait_for_edge(channel, GPIO.RISING)
第 2 引数には検出するエッジを指定し、指定可能な値は GPIO.RISING、GPIO.FALLING または GPIO.BOTH です。待ち時間を指定したい場合は、タイムアウトを設定できます。
# 超时以毫秒为单位
GPIO.wait_for_edge(channel, GPIO.RISING, timeout=500)
タイムアウト時間内に外部信号が変化した場合、関数は検出したチャネル番号を返します。タイムアウトした場合は None を返します。
event_detected() 関数
この関数は、前回の呼び出し以降にイベントが発生したかどうかを定期的に確認するために使用できます。設定と呼び出しは以下のように行います。
# 在通道GPIO上设置上升沿检测
GPIO.add_event_detect(channel, GPIO.RISING)
if GPIO.event_detected(channel):
print("Rising edge event detected")
GPIO.RISING、GPIO.FALLING、GPIO.BOTH のイベントを検出できます。
エッジイベント検出時にコールバック関数を実行する
この機能はコールバック関数の登録に使用できます。コールバック関数は独立した処理スレッドで実行されます。使い方は以下のとおりです。
# define callback function
def callback_fn(channel):
print("Callback called from channel %s" % channel)
# enable rising detection
GPIO.add_event_detect(channel, GPIO.RISING, callback=callback_fn)
必要に応じて、以下の方法で複数のコールバックを追加することもできます。
def callback_one(channel):
print("First Callback")
def callback_two(channel):
print("Second Callback")
GPIO.add_event_detect(channel, GPIO.RISING)
GPIO.add_event_callback(channel, callback_one)
GPIO.add_event_callback(channel, callback_two)
すべてのコールバック関数は同じスレッド上で実行されるため、異なるコールバックは同時ではなく順番に実行されます。
複数のイベントを 1 つにまとめてコールバック関数が何度も呼び出されるのを防ぐため、デバウンス時間を設定できます。
# bouncetime unit is ms
GPIO.add_event_detect(channel, GPIO.RISING, callback=callback_fn, bouncetime=200)
割り込みの無効化
エッジ検出が不要になった場合は、以下のように削除できます。
GPIO.remove_event_detect(channel)
テストプログラム
/app/40pin_samples/ ディレクトリに主なテストプログラムが用意されています。
| テストプログラム名 | 説明 |
|---|---|
| simple_out.py | 単一ピンの 出力 テスト |
| simple_input.py | 単一ピンの 入力 テスト |
| button_led.py | 1 つのピンをボタン入力、1 つのピンを LED 制御用出力として使用 |
| test_all_pins_input.py | 全ピンの 入力テスト コード |
| test_all_pins.py | 全ピンの 出力テスト コード |
| button_event.py | ピンの立ち上がり/立ち下がりエッジイベントを検出 |
| button_interrupt.py | 割り込み方式でピンの立ち上がり/立ち下がりエッジイベントを処理 |
- GPIO を
出力モードに設定し、1 秒ごとに出力レベルを切り替えます。LED の点滅制御に使用できます。テストコードsimple_out.py:
#!/usr/bin/env python3
import sys
import signal
import Hobot.GPIO as GPIO
import time
def signal_handler(signal, frame):
sys.exit(0)
# 定义使用的GPIO通道为37
output_pin = 37 # BOARD 编码 37
def main():
# 设置管脚编码模式为硬件编号 BOARD
GPIO.setmode(GPIO.BOARD)
# 设置为输出模式,并且初始化为高电平
GPIO.setup(output_pin, GPIO.OUT, initial=GPIO.HIGH)
# 记录当前管脚状态
curr_value = GPIO.HIGH
print("Starting demo now! Press CTRL+C to exit")
try:
# 间隔1秒时间,循环控制LED灯亮灭
while True:
time.sleep(1)
GPIO.output(output_pin, curr_value)
curr_value ^= GPIO.HIGH
finally:
GPIO.cleanup()
if __name__=='__main__':
signal.signal(signal.SIGINT, signal_handler)
main()
- GPIO を
入力モードに設定し、ビジーポーリングでピンのレベル値を読み取ります。テストコードsimple_input.py:
#!/usr/bin/env python3
import sys
import signal
import Hobot.GPIO as GPIO
import time
def signal_handler(signal, frame):
sys.exit(0)
# 定义使用的GPIO通道为37
input_pin = 37 # BOARD 编码 37
GPIO.setwarnings(False)
def main():
prev_value = None
# 设置管脚编码模式为硬件编号 BOARD
GPIO.setmode(GPIO.BOARD)
# 设置为输入模式
GPIO.setup(input_pin, GPIO.IN)
print("Starting demo now! Press CTRL+C to exit")
try:
while True:
# 读取管脚电平
value = GPIO.input(input_pin)
if value != prev_value:
if value == GPIO.HIGH:
value_str = "HIGH"
else:
value_str = "LOW"
print("Value read from pin {} : {}".format(input_pin, value_str))
prev_value = value
time.sleep(1)
finally:
GPIO.cleanup()
if __name__=='__main__':
signal.signal(signal.SIGINT, signal_handler)
main()
- GPIO を入力モードに設定し、ピンの立ち上がり/立ち下がりエッジイベントを検出します。テストコード
button_event.pyでは、37 番ピンの立ち下がりエッジを検出し、31 番ピンの出力を制御します。
#!/usr/bin/env python3
import sys
import signal
import Hobot.GPIO as GPIO
import time
def signal_handler(signal, frame):
sys.exit(0)
# 定义使用的GPIO通道:
# 31号作为输出,可以点亮一个LED
# 37号作为输入,可以接一个按钮
led_pin = 31 # BOARD 编码 31
but_pin = 37 # BOARD 编码 37
# 禁用警告信息
GPIO.setwarnings(False)
def main():
# 设置管脚编码模式为硬件编号 BOARD
GPIO.setmode(GPIO.BOARD)
GPIO.setup(led_pin, GPIO.OUT) # LED pin set as output
GPIO.setup(but_pin, GPIO.IN) # button pin set as input
# Initial state for LEDs:
GPIO.output(led_pin, GPIO.LOW)
print("Starting demo now! Press CTRL+C to exit")
try:
while True:
print("Waiting for button event")
GPIO.wait_for_edge(but_pin, GPIO.FALLING)
# event received when button pressed
print("Button Pressed!")
GPIO.output(led_pin, GPIO.HIGH)
time.sleep(1)
GPIO.output(led_pin, GPIO.LOW)
finally:
GPIO.cleanup() # cleanup all GPIOs
if __name__ == '__main__':
signal.signal(signal.SIGINT, signal_handler)
main()
- GPIO を入力モードに設定して GPIO 割り込み機能を有効にし、ピンの立ち上がり/立ち下がりエッジイベントに応答します。テストコード
button_interrupt.pyでは、37 番ピンの立ち下がりエッジを検出し、16 番ピンのハイ/ローレベルを高速に 5 回切り替えます。
#!/usr/bin/env python3
import sys
import signal
import Hobot.GPIO as GPIO
import time
def signal_handler(signal, frame):
sys.exit(0)
# 定义使用的GPIO通道:
# 15号作为输出,可以点亮一个LED
# 16号作为输出,可以点亮一个LED
# 37号作为输入,可以接一个按钮
led_pin_1 = 15 # BOARD 编码 15
led_pin_2 = 16 # BOARD 编码 16
but_pin = 37 # BOARD 编码 37
# 禁用警告信息
GPIO.setwarnings(False)
# 按下按钮时 LED 2 快速闪烁 5 次
def blink(channel):
print("Blink LED 2")
for i in range(5):
GPIO.output(led_pin_2, GPIO.HIGH)
time.sleep(0.5)
GPIO.output(led_pin_2, GPIO.LOW)
time.sleep(0.5)
def main():
# Pin Setup:
GPIO.setmode(GPIO.BOARD) # BOARD pin-numbering scheme
GPIO.setup([led_pin_1, led_pin_2], GPIO.OUT) # LED pins set as output
GPIO.setup(but_pin, GPIO.IN) # button pin set as input
# Initial state for LEDs:
GPIO.output(led_pin_1, GPIO.LOW)
GPIO.output(led_pin_2, GPIO.LOW)
# 把blink函数注册为按钮下降沿事件的中断处理函数
GPIO.add_event_detect(but_pin, GPIO.FALLING, callback=blink, bouncetime=10)
# 开始测试, Led1 缓慢闪烁
print("Starting demo now! Press CTRL+C to exit")
try:
while True:
# blink LED 1 slowly
GPIO.output(led_pin_1, GPIO.HIGH)
time.sleep(2)
GPIO.output(led_pin_1, GPIO.LOW)
time.sleep(2)
finally:
GPIO.cleanup() # cleanup all GPIOs
if __name__ == '__main__':
signal.signal(signal.SIGINT, signal_handler)
main()
hb_gpioinfo ツールの紹介
hb_gpioinfo は X5 向けの GPIO 補助ツールで、現在の開発ボードの PinName と PinNum の対応関係を確認できます。
hb_gpioinfo の構成
hb_gpioinfo ツールはドライバーとアプリケーションの 2 つで構成されています。ドライバーは pinmux-gpio.dtsi を解析して pinnode と pinname の情報を debugfs に出力し、hb_gpioinfo アプリケーションがそれを解析してターミナルに表示します。
ドライバーのコードパス:kernel/drivers/gpio/hobot_gpio_debug.c
hb_gpioinfo の使用例
- PinName:SoC 上のピン名で、回路図上の X5 SoC のピン名と一致します
- PinNode:デバイスツリー内の PinNode 情報です
- PinNum:X5 の実際の対応する GPIO 番号です
root@ubuntu:~# hb_gpioinfo
gpiochip0 - 8 lines: @platform/31000000.gpio: @GPIOs 498-505
[Number] [Mode] [Status] [GpioName] [PinName] [PinNode] [PinNum]
line 0: unnamed input AON_GPIO0_PIN0 aon_gpio_0 498
line 1: unnamed input AON_GPIO0_PIN1 aon_gpio_1 499
line 2: unnamed input active-low GPIO Key Power AON_GPIO0_PIN2 aon_gpio_2 500
line 3: unnamed input interrupt AON_GPIO0_PIN3 aon_gpio_3 501
line 4: unnamed input AON_GPIO0_PIN4 aon_gpio_4 502
line 5: unnamed input id AON_ENV_VDD aon_gpio_5 503
line 6: unnamed input id AON_ENV_CNN0 aon_gpio_6 504
line 7: unnamed input AON_ENV_CNN1 aon_gpio_7 505
gpiochip1 - 31 lines: @platform/35060000.gpio: @GPIOs 466-496
[Number] [Mode] [Status] [GpioName] [PinName] [PinNode] [PinNum]
line 0: unnamed input HSIO_ENET_MDC hsio_gpio0_0 466
line 1: unnamed input HSIO_ENET_MDIO hsio_gpio0_1 467
line 2: unnamed input HSIO_ENET_TXD_0 hsio_gpio0_2 468
line 3: unnamed input HSIO_ENET_TXD_1 hsio_gpio0_3 469
line 4: unnamed input HSIO_ENET_TXD_2 hsio_gpio0_4 470
line 5: unnamed input HSIO_ENET_TXD_3 hsio_gpio0_5 471
line 6: unnamed input HSIO_ENET_TXEN hsio_gpio0_6 472
line 7: unnamed input HSIO_ENET_TX_CLK hsio_gpio0_7 473
line 8: unnamed input HSIO_ENET_RX_CLK hsio_gpio0_8 474
....
ドキュメント