「Qtの基礎 - GPIO」の版間の差分

提供:MochiuWiki : SUSE, EC, PCB
ナビゲーションに移動 検索に移動
(文字列「__FORCETOC__」を「{{#seo: |title={{PAGENAME}} : Exploring Electronics and SUSE Linux | MochiuWiki |keywords=MochiuWiki,Mochiu,Wiki,Mochiu Wiki,Electric Circuit,Electric,pcb,Mathematics,AVR,TI,STMicro,AVR,ATmega,MSP430,STM,Arduino,Xilinx,FPGA,Verilog,HDL,PinePhone,Pine Phone,Raspberry,Raspberry Pi,C,C++,C#,Qt,Qml,MFC,Shell,Bash,Zsh,Fish,SUSE,SLE,Suse Enterprise,Suse Linux,openSUSE,open SUSE,Leap,Linux,uCLnux,Podman,電気回路,電子回路,基板,プリント基板 |description={{PAGENAME}} - 電子回路とSUSE Linuxに関する情報 | This pag…)
243行目: 243行目:
<br><br>
<br><br>


{{#seo:
|title={{PAGENAME}} : Exploring Electronics and SUSE Linux | MochiuWiki
|keywords=MochiuWiki,Mochiu,Wiki,Mochiu Wiki,Electric Circuit,Electric,pcb,Mathematics,AVR,TI,STMicro,AVR,ATmega,MSP430,STM,Arduino,Xilinx,FPGA,Verilog,HDL,PinePhone,Pine Phone,Raspberry,Raspberry Pi,C,C++,C#,Qt,Qml,MFC,Shell,Bash,Zsh,Fish,SUSE,SLE,Suse Enterprise,Suse Linux,openSUSE,open SUSE,Leap,Linux,uCLnux,Podman,電気回路,電子回路,基板,プリント基板
|description={{PAGENAME}} - 電子回路とSUSE Linuxに関する情報 | This page is {{PAGENAME}} in our wiki about electronic circuits and SUSE Linux
|image=/resources/assets/MochiuLogo_Single_Blue.png
}}


__FORCETOC__
__FORCETOC__
[[カテゴリ:Qt]]
[[カテゴリ:Qt]]

2024年10月14日 (月) 10:58時点における版

概要

GPIO (General Purpose Input/Output (汎用入出力)) は、マイクロコントローラやシングルボードコンピュータ等のデバイスに搭載されている汎用のピンのことである。
これらのピンは、ソフトウェアにより入力または出力として設定して、制御することができる。

GPIOの主な概念は以下に示す。

  • 入力
    GPIOピンを入力として設定する場合、外部のデバイスやスイッチの状態を読み取ることができる。
    例えば、ボタンが押下されているかどうかを検出するために使用できる。

  • 出力
    GPIOピンを出力として設定する場合、ピンの状態をHIGH (通常は3.3[V]または5[V])、または、LOW (0[V]) に設定できる。
    これにより、LEDの点灯 / 消灯、他のデバイスの制御が可能になる。

  • プルアップ / プルダウン抵抗
    GPIOピンは、プルアップ抵抗またはプルダウン抵抗を内部に持つことができる。
    これらの抵抗は、ピンが明確な状態 (HIGHまたはLOW) を持つようにするために使用される。

  • 割り込み
    一部のGPIOピンは、割り込み機能をサポートしている。
    これにより、ピンの状態が変化した時に、CPUに割り込み信号を送ることができる。
    これは、リアルタイムの応答が必要な場合に使用する。
  • PWM
    一部のGPIOピンは、PWM機能をサポートしている。
    PWMはデューティ比を変化させることにより、LEDの明るさ、モータの速度を制御するために使用される。


GPIOは、ボタン、スイッチ、LED、センサ、ディスプレイ、モータ等の様々な電子部品やデバイスとのインターフェースに使用されている。
GPIOを使用することにより、これらのデバイスとソフトウェアを連携させて、独自のプロジェクトや組み込みシステムを開発することができる。

ただし、GPIOを使用する際は、電気的な特性や制限に注意が必要となる。
不適切な使用は、デバイスの損傷や誤動作につながる可能性があることに注意する。

オンライン上にあるGPIOに関するドキュメントの多くは、ArduinoおよびRaspberry PiのようなSoCを対象にしている。
PCにおいては、SoCとは異なり、GPIOのあるPCはGPIOピンをCPUに直接統合していないが、スーパーI/Oチップ (SIO) を使用している。

Qtを使用して、PCのGPIOピンを直接制御することは一般的ではないが、OSやハードウェアに依存した方法を使用することで可能である。


GPIOへのアクセス

多くのLinuxディストリビューションにおいて、GPIOを扱うことができる。
最近のLinuxでは、/dev/gpiochip[n]キャラクタデバイスをマウントするカーネルモジュールを通して、GPIOを公開している。

GPIOと対話するための推奨ユーティリティは、gpiodである。
これは、GPIOの値の列挙、読み取り、書き込みを行うための一連のCLIを提供するものである。

ただし、/dev/gpiochip[n]キャラクタデバイスが無い場合は、Linuxカーネルがハードウェアのドライバが読み込まれていない。


BIOS / UEFIの設定

BIOS / UEFIにおいて、GPIOへのアクセスを提供する必要がある。

  1. まず、BIOS / UEFIの画面から、[Advanced]タブにある[SIO MISC Configuration]メニューから、[WDT, CASE OPEN, GPIO, DEBUG...]を選択する。
  2. [WDT, CASE OPEN, GPIO, DEBUG...]画面では、ウォッチドッグタイマ、2つのCOMポートモード (RS232、RS485、RS422)、複数のGPIO設定へのアクセス可否の設定がある。
    • GPIOの設定
      各ピンはGPIO[n]という名前である。 (nは0以上の数値)
      モードは入力または出力で設定できる。
      [Output]に設定すると、出力値をLowまたはHighに設定することができる。
      使用する予定の全てのGPIOピンの機能をマッピングすること。


例えば、以下に示すように、GPIOピンはPCケースに表示されているように、左から右に番号が付けられている。
以下の例では、上段は3.3[V]出力、GPIOピン 4本、GNDピン 2本、下段は3.3[V]出力、GPIOピン 4本、12[V]および5[V]出力がある。

 1   2   3   4   5   6   7
3V3  7   6   5   4  GND GND
---------------------------
3V3  3   2   1   0  12V  5V
 1   2   3   4   5   6   7



スーパーI/Oチップ名の確認

BIOS / UEFIには、どのスーパーI/Oチップが使用されているか表示されていない場合もある。
スーパーI/Oチップの名前を確認する場合は、PCケースを開けて、GPIOピンの付近にあるチップの表面に描かれているシルク等で確認する。


スーパーI/Oチップのカーネルモジュールの有効化

まず、付属しているスーパーI/Oチップのカーネルモジュールを知りたい場合は、Linux KernelのGithubにあるgit repoから、スーパーI/Oチップ名で検索する。

以下の例では、スーパーI/Oチップ名が"iTE IT8786E-I"の場合である。

repo:torvalds/linux iTE IT8786E-I


 /* linux/drivers/gpio/gpio-it78.cファイル */
 
 /* gpio-it78.c */
 
 /*
  *  GPIO interface for IT87xx Super I/O chips
  *
  *  Author: Diego Elio Pettenò <flameeyes@flameeyes.eu>
  *  Copyright (c) 2017 Google, Inc.
  *
  *  Based on it87_wdt.c     by Oliver Schuster
  *           gpio-it8761e.c by Denis Turischev
  *           gpio-stmpe.c   by Rabin Vincent
  */


 # linux/drivers/gpio/Makefileファイル
 # ※ "CONFIG_GPIO_IT87"という部分の文字列を控えておくこと
 
 obj-$(CONFIG_GPIO_IT87)			+= gpio-it87.o


# linux/drivers/gpio/Kconfigファイル

Kconfig snippet:

config GPIO_IT87
       tristate "IT87xx GPIO support"
       help
         Say yes here to support GPIO functionality of IT87xx Super I/O chips.

         This driver is tested with ITE IT8728 and IT8732 Super I/O chips, and
         supports the IT8761E, IT8613, IT8620E, and IT8628E Super I/O chips as
         well.

         To compile this driver as a module, choose M here: the module will
         be called gpio_it87.


スーパーI/Oチップ名を確認して、該当するカーネルモジュールがインストールされているかどうかを確認する。

find /lib/modules/$(uname -r)/kernel/drivers/gpio -iname "*gpio*" | grep -i gpio-it87


スーパーI/Oチップ名を確認して、該当するカーネルモジュールが読み込まれているかどうかを確認する。

grep CONFIG_GPIO_IT87 /boot/config-$(uname -r)

# 出力例:
# mと出力される場合、そのカーネルモジュールがデフォルトでは無効であることを意味する
CONFIG_GPIO_IT87=m


スーパーI/Oチップのカーネルモジュールを有効にする。

sudo modprobe gpio-it87


GPIOピンの入出力に関する設定はBIOS / UEFIを通してのみ変更可能であり、GPIOの値を設定 / 取得する場合はスーパーユーザ権限が必要である。

もし、gpiolibのピン番号がBIOS / UEFIのピン番号と一致しない場合は、各ピンをブレッドボード上のLEDに接続して、以下のようなシェルスクリプトを実行する。
以下の例では、チップセット0の全ピンを1秒間ごとにトグルしている。
ただし、実行前に、BIOS / UEFIにおいて、全てのGPIOピンを出力モードに割り当てること。

 for i in $(seq 0 63)
 do
    echo -n "$i on "
    sudo gpioset 0 "$i=1"
    sleep 1
    echo off
    sudo gpioset 0 "$i=0"
 done



エラー

GPIOピンの入力を開放する場合

GPIOピンを入力設定 (pull-downオプションを付加) にして開放する場合、該当ピンの状態を確認するとHighを返す場合がある。

sudo gpioget -B pull-down 0 0

# 出力例:
1


これを解決するには、外付けのプルダウン抵抗を追加することである。
1[K]、10[K]、100[K]のプルダウン抵抗を試して、カット&トライすること。


サンプルコード

以下の例では、Linuxのsysfsインターフェースを使用してGPIO 17を制御している。

  1. まず、exportGpio関数でGPIOをエクスポートして、使用可能にする。
  2. 次に、setGpioDirection関数でGPIOの方向を出力 (out) に設定する。
  3. setGpioValue関数を使用して、GPIOの値を1 (HIGH) 、および、0 (LOW) に交互に設定して、1秒ずつ待機することを5秒繰り返す。
  4. unexportGpio関数でGPIOをアンエクスポートして、GPIOピンの使用を終了する。


ただし、これを実行する場合は、適切な権限 (通常はスーパーユーザ権限) が必要である。
また、使用するGPIOピン番号やパスは、ハードウェアの構成に応じて適宜変更すること。

PCのGPIOピンを制御する場合は、ハードウェアの仕様を確認して、適切な保護回路を使用する等、十分な注意が必要である。

 #include <QCoreApplication>
 #include <QFile>
 #include <QTextStream>
 #include <QThread>
 
 const QString GPIO_PATH = "/sys/class/gpio/";
 
 void exportGpio(int gpio)
 {
    QFile exportFile(GPIO_PATH + "export");
    if (exportFile.open(QIODevice::WriteOnly | QIODevice::Text)) {
        QTextStream out(&exportFile);
        out << gpio;
        exportFile.close();
    }
 }
 
 void unexportGpio(int gpio)
 {
    QFile unexportFile(GPIO_PATH + "unexport");
    if (unexportFile.open(QIODevice::WriteOnly | QIODevice::Text)) {
       QTextStream out(&unexportFile);
       out << gpio;
       unexportFile.close();
    }
 }
 
 void setGpioDirection(int gpio, const QString &direction)
 {
    QFile directionFile(GPIO_PATH + "gpio" + QString::number(gpio) + "/direction");
    if (directionFile.open(QIODevice::WriteOnly | QIODevice::Text)) {
       QTextStream out(&directionFile);
       out << direction;
       directionFile.close();
    }
 }
 
 void setGpioValue(int gpio, int value)
 {
    QFile valueFile(GPIO_PATH + "gpio" + QString::number(gpio) + "/value");
    if (valueFile.open(QIODevice::WriteOnly | QIODevice::Text)) {
       QTextStream out(&valueFile);
       out << value;
       valueFile.close();
    }
 }
 
 int main(int argc, char *argv[])
 {
    QCoreApplication a(argc, argv);
 
    int gpioNumber = 17; // 使用するGPIO番号
 
    exportGpio(gpioNumber);
    setGpioDirection(gpioNumber, "out");
 
    for (int i = 0; i < 5; ++i) {
       setGpioValue(gpioNumber, 1);
       QThread::sleep(1);
       setGpioValue(gpioNumber, 0);
       QThread::sleep(1);
    }
 
    unexportGpio(gpioNumber);
 
    return a.exec();
 }