LoginSignup
0
0

More than 3 years have passed since last update.

RaspberryPi で ValueError: A different mode has already been set!

Posted at

RaspberryPi で、1602LCD用 Adafruit_CharLCD ライブラリ と ICカードリーダ用 pirc522 ライブラリを同時に使うと、


ValueError: A different mode has already been set!

エラーが出ます。

環境

  • Raspbian GNU/Linux 10 (buster)
  • Raspberry Pi 3 Model B
  • Adafruit_CharLCD ライブラリ
  • pirc522 ライブラリ
  • Python 3.7.3

コード


from pirc522 import RFID
import Adafruit_CharLCD as LCD
rdr = RFID()

発生するエラー



Traceback (most recent call last):
  File "read_once_with_key_LCD.py", line 4, in <module>
    rdr = RFID()
  File "/usr/local/lib/python3.7/dist-packages/pi_rc522-2.2.1-py3.7.egg/pirc522/rfid.py", line 88, in __init__
ValueError: A different mode has already been set!

原因

これは、 Adafruit_CharLCD.py で BCM番号、pirc522 で BOARD番号でGPIOを指定しているからです。

エラーの再現


pi@raspberrypi:~ $ python3
Python 3.7.3 (default, Dec 20 2019, 18:57:59) 
[GCC 8.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import RPi.GPIO as GPIO
>>> GPIO.setmode(GPIO.BCM)
>>> GPIO.setmode(GPIO.BOARD)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: A different mode has already been set!

再現する。

cleanup でエラー回避


>>> import RPi.GPIO as GPIO
>>> GPIO.setmode(GPIO.BCM)
>>> GPIO.setup(11,GPIO.IN)
>>> GPIO.cleanup()
>>> GPIO.setmode(GPIO.BOARD)

cleanup するとエラーが出なくなる。ちなみに cleanup する前に一度適当なIO操作をしておかないとうまくいかない。上記の例ではGPIO.setup(11,GPIO.IN)
となっているところ。

cleanup の問題

cleanup すると GPIO.setmode がクリアされる。
それぞれのライブラリを呼び出すごとに cleanup すればと思ったが、ライブラリで使う setmode が消えてしまうので別の方法を考える。

pirc522 ライブラリ

読んでみました


class RFID(object):
    pin_rst = 22
    pin_ce = 0
    pin_irq = 18

    mode_idle = 0x00
・
・
・
    def __init__(self, bus=0, device=0, speed=1000000, pin_rst=def_pin_rst,
            pin_ce=0, pin_irq=def_pin_irq, pin_mode = def_pin_mode):
        self.pin_rst = pin_rst
        self.pin_ce = pin_ce
        self.pin_irq = pin_irq

        self.spi = SPIClass()
        self.spi.open(bus, device)
        if board == RASPBERRY:
            self.spi.max_speed_hz = speed
        else:
            self.spi.mode = 0
            self.spi.msh = speed

        if pin_mode is not None:
            GPIO.setmode(pin_mode)


となっていて、pin_mode は以下のようになっています。


import threading

RASPBERRY = object()
BEAGLEBONE = object()
board = RASPBERRY
try:
    # Try with Raspberry PI imports first
    import spidev
    import RPi.GPIO as GPIO
    SPIClass = spidev.SpiDev bus=0, device=0, speed=1000000, pin_rst=def_pin_rst,
            pin_ce=0, pin_irq=def_pin_irq, pin_mode = def_pin_mode):
    def_pin_rst = 22
    def_pin_irq = 18
    def_pin_mode = GPIO.BOARD

Adafruit_CharLCDライブラリ

読んでみました


class Adafruit_CharLCD(object):
    """Class to represent and interact with an HD44780 character LCD display."""

    def __init__(self, rs, en, d4, d5, d6, d7, cols, lines, backlight=None,
                    invert_polarity=True,
                    enable_pwm=False,
                    gpio=GPIO.get_platform_gpio(),
                    pwm=PWM.get_platform_pwm(),
                    initial_backlight=1.0):

となっていますね。gpio=GPIO.get_platform_gpio()って何だ?

https://github.com/adafruit/Adafruit_Python_GPIO/blob/master/Adafruit_GPIO/GPIO.py
を見てみると、


def get_platform_gpio(**keywords):
    """Attempt to return a GPIO instance for the platform which the code is being
    executed on.  Currently supports only the Raspberry Pi using the RPi.GPIO
    library and Beaglebone Black using the Adafruit_BBIO library.  Will throw an
    exception if a GPIO instance can't be created for the current platform.  The
    returned GPIO object is an instance of BaseGPIO.
    """
    plat = Platform.platform_detect()
    if plat == Platform.RASPBERRY_PI:
        import RPi.GPIO
        return RPiGPIOAdapter(RPi.GPIO, **keywords)
    elif plat == Platform.BEAGLEBONE_BLACK:

となっていて、


class RPiGPIOAdapter(BaseGPIO):
    """GPIO implementation for the Raspberry Pi using the RPi.GPIO library."""

    def __init__(self, rpi_gpio, mode=None):
        self.rpi_gpio = rpi_gpio
        # Suppress warnings about GPIO in use.
        rpi_gpio.setwarnings(False)
        # Setup board pin mode.
        if mode == rpi_gpio.BOARD or mode == rpi_gpio.BCM:
            rpi_gpio.setmode(mode)
        elif mode is not None:
            raise ValueError('Unexpected value for mode.  Must be BOARD or BCM.')
        else:
            # Default to BCM numbering if not told otherwise.
            rpi_gpio.setmode(rpi_gpio.BCM)
        # Define mapping of Adafruit GPIO library constants to RPi.GPIO constants.
        self._dir_mapping = { OUT:      rpi_gpio.OUT,
                              IN:       rpi_gpio.IN }
        self._pud_mapping = { PUD_OFF:  rpi_gpio.PUD_OFF,
                              PUD_DOWN: rpi_gpio.PUD_DOWN,
                              PUD_UP:   rpi_gpio.PUD_UP }
        self._edge_mapping = { RISING:  rpi_gpio.RISING,
                               FALLING: rpi_gpio.FALLING,
                               BOTH:    rpi_gpio.BOTH }

となっています。

それぞれ、デフォルトだとpirc522 ライブラリ
GPIO.BCM、 Adafruit_CharLCD(objeだと、GPIO.BORAD になってますね。

BCMに統一することにします。以下のように呼び出したらうまくいきました。


import sys
from pirc522 import RFID
import RPi.GPIO as GPIO
#rdr = RFID()  # サンプルプログラムの書き方
#rdr = RFID(0,0,1000000,22,0,18,GPIO.BOARD) # GPIO.BOARD での指定
rdr = RFID(0,0,1000000,25,0,24,GPIO.BCM) # GPIO.BCM での指定


おまけ

上記は GPIO ライブラリを使う時の問題でした。

pirc522 では GPIO ライブラリを内部で使用、
Adafruit_CharLCD.py では Adafruit_GPIO ライブラリを使っていますがそこから更に GPIO ライブラリを呼んでいてそこで GPIO.setmode の競合が起こっているということでした。

あくまでこれは GPIO ライブラリの内部保持している情報に関わることで、RaspberryPiのハードウェアに由来する制限ではありません。 なので、上記のプログラムに更に WiringPi を混ぜ、異なるGPIOナンバリングを使うのは全然問題がありませんでした。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0