各出典元の皆様、大変参考になりました。ありがとうございます。
RaspberryPiOS・GPIOピン制御手順
出典:
■ライブラリのインストール
ピン番号やコマンドが理解しやすそうな(クセが少なそうな)RPi.GPIOを適用
pip install RPi.GPIO
■基本的な制御手順
1:GPIOピン使用宣言
# GPIO番号指定(BCMはBroadcomの略)
GPIO.setmode(GPIO.BCM)
もしくは#240128追記
GPIO.setmode(11)
# ボードピン番号指定
GPIO.setmode(GPIO.BOARD)
もしくは#240128追記
GPIO.setmode(10)
2:GPIO入出力指定
GPIO.setup(ピン番号, GPIO.OUT) # 出力指定
GPIO.setup(ピン番号, GPIO.IN) # 入力指定
3:GPIO入出力
出力の設定例
GPIO.output(ピン番号, 1) # ピンの出力を3.3Vにする
GPIO.output(ピン番号, 0) # ピンの出力を0Vにする
入力読出例
GPIO.input(ピン番号) # ピンの電圧状態読み取る。この関数を呼んだ戻り値の処理が必要!.
print GPIO.input(ピン番号) # ピンの電圧状態を表示する
4:GPIO設定リセット
GPIO.cleanup() # GPIO設定をリセット
使用終了宣言せずに電源OFFすると初期状態にリセットされる。
■内部プルダウン / プルアップ抵抗指定
GPIO.setup(SWITCH_PIN, GPIO.IN, pull_up_down=GPIO.PUD_DOWN) : 内部プルダウン抵抗を設定
GPIO.setup(SWITCH_PIN, GPIO.IN, pull_up_down=GPIO.PUD_UP) : 内部プルアップ抵抗を設定
GPIO.setup(SWITCH_PIN, GPIO.IN) : 内部プルダウン / プルアップ抵抗を未設定
■GPIO開放
GPIO.cleanup() : 全ピンを解放
GPIO.cleanup([12,13]) : GPIO 12, 13ピンを解放(特定ピンを開放する例)
注意:GPIO解放で全ピン設定リセットされて入力モードになり、内部プルダウン / プルアップ抵抗無効になる。
■GPIO出力値切替えコマンド
GPIO.output(<GPIO番号>, True) : 出力をHigh(3.3V)に設定
GPIO.output(<GPIO番号>, False) : 出力をLow(0V)に設定
■PWM使用例
GPIO.setmode(GPIO.BOARD)
GPIO.setup(12, GPIO.OUT)
p = GPIO.PWM(12, 50) #channel=12 frequency=50Hz
■要注意の事項
1:GPIOを壊さないために・・・
・電流値: 1ピンあたり16mA以下、合計50mA以下にする(できれば7割程度を目安に)
・電圧値: 3.5V以下にする
・回路: スイッチ切替時、モード切替時のショートに注意!→プルダウン / プルアップ抵抗や電流制限抵抗が有効
2:GPIOのピン
・内部プルダウン / プルアップ抵抗の固定されたピンがある
・ハードウェアPWMに対応したピンがある
・シリアル通信(UART, I2C, SPI)用のピンがある
■GPIOのピン番号指定
Raspberry Piのピンには、wiringPi、BCM、Boardという3つの命名方法がある。
ピン番号対応表参照!
GPIO.BOARD:The first is using the BOARD numbering system.
GPIO.BCM:This is a lower level way of working
とのこと。
例:
Raspberry Pinoutのピンの12番=PCM18(PWM0)は、
「Physical pin 12」であり「BCM pin 18」となる。
すわなち、BOARD指定はRaspberryPiのGPIOヘッダピン40本の各ピンの番号が「BOARDピン番号」であり、それらに繋がっているBroadcomCPUのピン番号が「BCMピン番号」となる・・・ですね。
kabayon考察:
・ラズパイを使うならBOARDピン番号一択ですが、ハード固有「数値」になる。
・プログラムの初期設定部分にて、
ピン番号を表す変数にハード固有値を設定、
以後はその変数を使用
といった構造にすべきである。
★240123追記
・I2CデバイスのドライバではWiringPiを使うことがあるが、
WiringPiは非推奨になり、RPi.GPIOを使え、となっているそうです。
ドライバがRPi.GPIOを使っている場合、そのソースを確認し、
BOARDとBCMのどちらを使っているかを判別しなければならない。
★240128追記
出典:
↑この解説がわかりやすかったです。
■ピン番号対応表
もしくは下記出典参照!
WiringPi、BCM、ラズパイBoardのピンアサイン対比図
https://raspberry-pi.ksyic.com/bypass/files/GPIO.svgz#PinN
■RaspberryPi3B+でGPIO専用のピン番号変数設定の例(GPIO_BOARDを適用)
#GPIOxx is for RPi.GPIO #Pxx is for wiring Pi
ーーーーーーーーーーーーーーーーー
pin_GPIO17 = 11 #pin_P0 = 11
pin_GPIO27 = 13 #pin_P2 = 13
pin_GPIO22 = 15 #pin_P3 = 15
pin_GPIO23 = 16 #pin_P4 = 16
pin_GPIO24 = 18 #pin_P5 = 18
pin_GPIO25 = 22 #pin_P6 = 22
pin_GPIO5 = 29 #pin_P21 = 29
pin_GPI06 = 31 #pin_P22 = 31
pin_GPIO16 = 36 #pin_P27 = 36
pin_GPIO26 = 37 #pin_P25 = 37
■割り込み処理について(出典コピペ&短文化)
出典:
ポーリングよりも割込処理の方がいい理由
1.CPU資源の有効利用
周辺機器が処理を行っている間、CPUが他の処理を行ったほうが効率がよい。
周辺機器の処理終了をCPUで定期的チェック(ポーリング)するのは、他の処理の効率を落とす。
そこで、周辺機器の側から割込みによって処理の終了を通知する方法がとられる。
2.応答性の向上
ユーザインターフェースは、入力の遅延や入力漏れが致命的な欠陥になる。
割り込みを使ってユーザからの入力を確実に処理する必要がある。
コンピュータフリーズ時にマウスカーソルだけ反応するのは、これは割込み処理が機能しているから。
3.例外処理の効率化
周辺機器に障害が生じた場合、割込みでプログラム側に障害を速やかに伝えることが可能になる。
このときプログラム上でも例外処理を本来の処理と分離しての記述が容易になる。
■割込み処理の具体的方法1 【callback関数】(出典コピペ&短文化)
出典:
スイッチを押した時やセンサーが反応したときに、プログラムを実行するには?? そういうときは、割り込みという機能を使えば実現できます。信号を検知したときに、割り込んで処理をしてくれるので、割り込みです。
Raspberry PiにはGPIOピンの立ち上がり、立ち下がりエッジを検知した時に、指定の関数(コールバック関数)を呼び出すしくみがあります。
import RPi.GPIO as GPIO
import time
class CallBack:
def __init__(self):
# 4番pinを入力、プルアップに設定
pin = 4
GPIO.setmode(GPIO.BCM)
GPIO.setup(pin, GPIO.IN, GPIO.PUD_UP)
# 割り込みイベント設定
GPIO.add_event_detect(pin, GPIO.RISING, bouncetime=1000)
# コールバック関数登録
GPIO.add_event_callback(pin, self.my_callback_one)
GPIO.add_event_callback(pin, self.my_callback_two)
def my_callback_one(self, channel):
print('Callback one')
def my_callback_two(self, channel):
print('Callback two')
def callback_test(self):
while True:
time.sleep(1)
cb = CallBack()
cb.callback_test() # 割り込みイベント待ち
◇割込み検出について(出典コピペ&短文化)
出典:
#割り込みイベント設定
#GPIO.add_event_detectメソドで、割り込みを検知するpin番号、エッジ、バウンスタイムを設定
GPIO.add_event_detect(pin, GPIO.RISING, bouncetime=1000)
#pin ⇒ GPIOピン(例:4)
#GPIO.FALLING ⇒ イベント検出を立下りエッジに指定
#bouncetime = 1000 ⇒ 割り込み検知後、1000ms割込みマスク(割込み検知しない。チャタリング防止)
□イベント検出の設定パラメータ
GPIO.FALLING → 立ち下がりエッジ
GPIO.RISING → 立ち上エッジ
GPIO.BOTH → 両エッジ
■割込み処理の具体的方法2 【wait_for_edge】による例:
(出典からコピペ、短文化)
出典:
スイッチを押した時やセンサーが反応したときに、プログラムを実行するには?? そういうときは、割り込みという機能を使えば実現できます。信号を検知したときに、割り込んで処理をしてくれるので、割り込みです。
タイマーで設定した時間とGPIOピン(汎用入出力)の立ち上がり、立ち下がりエッジで割り込み処理をしてくれるwait for edge関数という便利な関数があります。
import RPi.GPIO as GPIO
class CallBack:
def __init__(self):
# 4番pinを入力、プルアップに設定
self.pin = 4
GPIO.setmode(GPIO.BCM)
GPIO.setup(self.pin, GPIO.IN, GPIO.PUD_UP)
def callback_test(self):
while(True):
# 立下りエッジイベント、タイムアウトイベント待ち
isr = GPIO.wait_for_edge(self.pin, GPIO.FALLING, timeout=5000, bouncetime=1000)
if isr is None:
# 5秒間割り込みがないときに実行される
print("5秒間割り込みなし")
else:
# 割り込みがあったときに実行される
print("割り込みあり")
cb = CallBack()
cb.callback_test()
wait for edge関数を使うと、この関数のところでイベントが来るまで、待っていてくれます。
タイムアウト設定した時間経過したときと、割り込みが来た時にイベント待ちが解除されます。
■ポーリングによる制御
出典:
ポーリング制御は「もしボタンAが押されたら、この処理を実行」「もしボタンBが押されたら、この処理を実行」のように、条件判定と対応する処理を1回のループの中で行う方法です。処理と処理の同期のようなシステム全体のバランスを取りやすいのが特徴です。
毎回のループの中ではsleep()関数を使用したプログラムの短時間休止が必要です。これを行わないとCPUの負荷が大きくなりラズパイの動作が遅くなるなどのトラブルの原因となります。
OSが乗っていないArduinoのようなマイコンボードでは不要ですが、複数のタスクの実行をOSが管理するラズパイではsleep()関数の呼び出しが必要です。
ポーリング制御はループの中にチェックしたい状態の数だけif文を並べるイメージのコーディング手法なので記述としてはシンプルな制御方法です。
一方で判定する状態が増えればループの処理も大きくなり、可読性が悪くなるという欠点もあります。
■アナログ出力(出典からコピペ、短文化)
出典:
アナログ出力では周波数とデューティ比を指定してPWM制御できる。
RaspberryPi3+ではGPIO18がPWMで機能する。
1:ピンに対して周波数設定、pwmオブジェクトを取得
pwm = GPIO.PWM([チャンネル], [周波数(Hz)])
2:pwmオブジェクトに対しデューティ比指定、出力開始
pwm.start([デューティ比])
例:
・ピン18に周波数1KHz、デューティ比50%でPWM出力
pwm = GPIO.PWM(18, 1000)
pwm.start(50)
・途中で周波数変更
pwm.ChangeFrequency([周波数(Hz)])
・途中でデューティ比変更
pwm.ChangeDutyCycle([デューティ比])
・PWM出力を停止
pwm.stop()
■SPI通信(出典からコピペ、短文化)
SPIはデバイスに応じたコードになるので、コード例は「手順の参考」として!
出典:https://masaeng.hatenablog.com/entry/2020/05/10/234342
ライブラリ
$ sudo apt-get install python-pip
$ sudo pip3 install spidev
ーーーーーーーーーーーーーーーーーーーーーーーーーーーーーー
コード例:
ーーーーーーーーーーーーーーーーーーーーーーーーーーーーーー
import spidev
read_addr = 0x00
write_addr = 0x1E
write_data = 0x08
#初期設定
spi = spidev.SpiDev()
spi.open(0,0)
spi.mode = 3 #このデバイスはSPI mode3で動作
spi.max_speed_hz = 1000000
#アドレス"read_addr "の値を読み出す
read_data = spi.xfer2([0x80 | read_addr,0x00])
print('read_data = 0x{:02x}'.format(read_data[1])) # デバイスID 11100101 が読めるはず
# read_data = 0xe5
#アドレス"write_addr "に対してwrite_dataを書き込む
spi.xfer2([write_addr, write_data])
#正しく書き込めたことを確認
read_data = spi.xfer2([0x80 | write_addr,0x00])
print('write_data = 0x{:02x}'.format(read_data[1]))
# write_data = 0x08
ーーーーーーーーーーーーーーーーーーーーーーーーーーーーーー
出典:
SPI (Serial Peripheral Interface)は、マイクロコントローラとその周辺デバイスとの接続に使用されるインターフェイスの一種です。各種センサー、A/Dコンバータ、D/Aコンバータ、SRAMなどとマイクロコントローラを接続するのに幅広く採用されています。
SPIは、同期式/全二重のマスター・スレーブ型のインターフェイスです。マスターまたはスレーブからのデータは、クロックの立ち上がりエッジまたは立ち下がりエッジによって、同期がとられます。また、マスターとスレーブは、同時にデータを送信することも可能です。
SPIには、3線式のものと4線式のもがあります。ここでは、一般的な 4 線式のSPIについて記載します。4線式のSPIデバイスには、次の4つの信号があります。
クロック(SPI CLK, SCLK)
チップ・セレクト(CS)
マスター出力/スレーブ入力(MOSI:Master Output Slave Input)
マスター入力/スレーブ出力(MISO:Master Input Slave Output)
2種類のデバイスのうち、クロック信号を生成する方が、マスターと呼ばれます。マスターとスレーブ間で送信されるデータは、マスターによって生成されるクロック信号に同期して転送されます。SPIデバイスは、I2Cインターフェイスよりもはるかに高いクロック周波数に対応していますので、高速なデータ転送を行うことができます。
SPIに対応するデバイスを使用する場合は、データシートを参照しSPIのクロック周波数に関する仕様を確認してください。
SPIでは、1つのマスターに対して、複数のスレーブを接続することができます。マスターとスレーブ間の接続は、標準モードまたはデイジーチェーンモードで接続します。
出典:
ーーーーーーーーーーーーーーーーーーーーーーーーーーーーーー
コード例:
ーーーーーーーーーーーーーーーーーーーーーーーーーーーーーー
import spidev
import time
# SPIの設定です
spi = spidev.SpiDev()
spi.open(0,0)
spi.max_speed_hz = 100000
#
# ChipSelectコマンド内部で自動的にしてくれるので、
# 別途GPIOを操作する必要はありません。
#
# 表示のクリア
writeData = [ 0x76,]
spi.xfer2(writeData)
time.sleep(1)
# 数値の表示
writeData = [ 0x1, 0x2, 0x3, 0x4 ]
spi.xfer2(writeData)
# コロンの表示
writeData = [ 0x77, 0x10]
spi.xfer2(writeData)
print("done")
ーーーーーーーーーーーーーーーーーーーーーーーーーーーーーー
■A/D変換(出典からコピペ、短文化)
出典:
I2CかSPIのAD変換器を使うこと。
A/D変換器には次の2種類がある。
1:逐次比較(ちくじひかく)方式;
比較的高速に変換する。分解能は多くても16から18ビット。インターフェースはSPIバスが多い
2:ΔΣ(デルタ・シグマ)方式;
変換時間は中低速。分解能は様々で24ビットまである。インターフェースはI2Cバスが多い
■A/D変換器MCP3208の使用例
出典:
MCP3208はSPIで接続。
■A/D変換器MCP3208の使用例
出典:
ADS1015やADS1115はI2Cで接続。
ADS1015:12 ビット、3.3kSPS、4 チャネル
ADS1115:16 ビット、860SPS、4 チャネル・デルタ・シグマ ADC
以上