Raspberry pi 에서 python으로 GPIO 사용하기

도구의인간 메이커스 위키
이동: 둘러보기, 검색

RPi.GPIO 라이브러리 사용법

파이썬에서 라즈베리파이의 gpio핀을 제어할 수 있도록 해주는 라이브러리. 라즈비안에 기본 설치 되어있다. 참고: https://sourceforge.net/p/raspberry-gpio-python/wiki/Home/

기본 사용법1(순서)

1. 먼저 모듈을 임포트한다. (필수)

  • RPi.GPIO 모듈은 하드웨어를 건드리기 때문에 슈퍼유저 권한이 필요하다. 실행할 때 $sudo python3 my-gpio-project.py해야한다.
import RPi.GPIO as GPIO

2. 핀 넘버를 부르는 방식을 선택한다.(필수)

GPIO.setmode(GPIO.BOARD)
#or
GPIO.setmode(GPIO.BCM)

GPIO.BOARD 는 라즈베리파이에 배열된 순서대로 핀 이름을 부르겠다는 의미이고, GPIO.BCM은 (Broadcom chip-specific pin numbers) 로 Broadcom SOC 칩에서 사용하는 핀이름을 사용하겠다는 의미. 즉, GPIO.BOARD 모드에서 8번핀은 GPIO.BCM 모드에서 14번 핀과 동일하다. Raspberry Pi GPIO 배치

  • tip: 위 그림에 나온 BCM 핀 배치도는 라즈비안에 기본 설치된 pinout 명령으로 언제든 확인 할 수 있다. (i2c 핀이라든가... 가 표시되지 않아 아쉬움이 있다.) raspberry pi zero 에서 실행한 경우

3. 핀 모드를 설정한다 (입력핀 or 출력핀...). 아두이노에서 pinMode() 와 같은 역할.(필수)

 GPIO.setup(18, GPIO.OUT)
 #or
 GPIO.setup(18, GPIO.IN)

list를 사용해 한번에 여러 핀을 설정 할 수도 있고, output 모드로 설정하는 경우 초기값을 줄 수도 있다.

 GPIO.setup([18, 19, 20], GPIO.OUT, initial=GPIO.HIGH)

4. input, output 등등 원하는대로 사용한다.

5. 프로그램을 종료하기전, 리소스를 반납한다. (필수)

GPIO.cleanup()

결론

종합하자면, 일반적으로 무한루프를 돌며 사용자와 인터렉션 하는 GPIO 활용 프로그램의 특성상 아래와 같은 구조를 갖는 경우가 많다.

# 라이브러리 임포트
import RPi.GPIO as GPIO
...
# GPIO setup
GPIO.setmode(GPIO.BCM)
GPIO.setup(12, GPIO.IN)
GPIO.setup(18, GPIO.OUT)
...
# 메인 쓰레드
try:
    while 1:
        button = GPIO.input(12)
        ...
        GPIO.output(18, GPIO.HIGH)
        ...
# 반드시 클린업
finally:
    GPIO.cleanup()

기본 사용법 2

Digital Output

 import time

 GPIO.output(18, GPIO.HIGH)
 time.sleep(0.1)    # 100 millisecond 딜레이
 GPIO.output(18,GPIO.LOW)

GPIO.HIGH 대신 True1을 써도 좋다. GPIO.LOW 대신에는 False0

Analog Output (PWM)

RPi는 기본적으로 2개의 hardware PWM 채널을 가지고 있는데, 아쉽게도 RPi.GPIO에서는 사용할 방법이 없다. (C로 wiringPi를 사용하는경우 1개 채널(GPIO18) 사용가능.) 대신 software PWM을 어느 핀에서든 사용할 수 있다. 동시에 여러개도 가능하다. (참고:https://www.raspberrypi.org/forums/viewtopic.php?f=44&t=31714)

#swPWM 초기화
myPwm = GPIO.PWM(18, 1000) # pin, frequency
myPwm.start(50) #dutycycle (0~100사이 값). 아두이노로 치면 analogWrite(18, 128)과 동일.

# 출력값 변경 (0~100%)
myPwm.ChangeDutyCycle(75)

# Frequency  변경 (Hz)
myPwm.ChangeFrequency(1500)

#swPWM 정지
myPwm.stop()

Digital Input

pin_read = GPIO.input(18)   #True / False
  • tip: 출력핀에 GPIO.input(outputPin) 함수를 사용할 수도 있는데, 현재 GPIO.HIGH가 출력되고 있는지, GPIO.LOW가 출력되고 있는지 알아보는 용도.
# toggle button
# 12 번 핀이 GPIO.OUT으로 setup 되어있는 상태에서...
GPIO.output(12, not GPIO.input(12))

Analog Input

안타깝게도 라즈베리파이는 analog input이 불가능하다. ADC(analog-digital converter)회로가 필요하다. MCP3008 을 사용하도록 하자. (참고: https://learn.adafruit.com/reading-a-analog-in-and-controlling-audio-volume-with-the-raspberry-pi/overview) 혹은 간이로 만들수도 있다(참고: https://www.allaboutcircuits.com/projects/building-raspberry-pi-controllers-part-5-reading-analog-data-with-an-rpi/)

고급 사용법

내장 pull-down, pull-up 저항 사용

스위치에 pull-down, pull-up 회로를 만들어주는게 별 거아니지만 귀찮을 때가 많다. 그럴 줄 알고 라즈베리파이 내부에 풀다운/풀업 저항을 만들어 놓고 sw로 활성화 할 수 있도록 되어있다.

GPIO.setup(18, GPIO.IN, pull_up_down = GPIO.PUD_UP) # 스위치 안눌렸을 때 on, 눌렸을 때 off
#or
GPIO.setup(18, GPIO.IN, pull_up_down = GPIO.PUD_DOWN) # 스위치 안눌렸을 때 off, 눌렸을 때 on

interrupt 사용

전체 코드 중 'GPIO.input(channel)'이 실행되는 그 순간에만 스위치가 눌렸는지를 알 수 있기 때문에 코드의 다른 부분이 실행되는 때에 스위치를 누르면 컴퓨터가 감지하지 못하고 넘어가버리는 일이 종종 있다. 이럴 때 사용하는게 인터럽트. 인터럽트라는게 별게 아니고 별도의 쓰레드에서 스위치가 눌렸는지만 아주 짧은 주기로 계속 보고있다가 스위치가 눌리면 메인 스레드로 이를 알려주어 callback 함수가 동작하도록 하는 것. GUI sw 개발할 때의 event-driven 과 같은 것이라고 보면 된다.

RPi.GPIO에서는 인터럽트 구현을 위해 GPIO.wait_for_edge(channel, edge_type) 함수와 GPIO.add_event_detect(channel, edge_type), GPIO.event_detected(channel) 함수가 준비되어있다.

GPIO.RISING, GPIO.FALLING, GPIO.BOTH 를 감지할 수 있다.

대표적인 사용법:

def my_callback(channel):
    print('Edge detected on channel %s'%channel)

GPIO.add_event_detect(channel, GPIO.RISING, callback=my_callback)  # add rising edge detection on a channel
...the rest of your program...
# gpio-interrupt-test.py
# GPIO12에 입력이 들어오면 문장을 출력한다.

# 라이브러리 불러오기
import RPi.GPIO as GPIO
import time

# 스위치 눌렸을 때 콜백함수
def switchPressed(channel):
    print('channel %s pressed!!'%channel)

# GPIO setup
GPIO.setmode(GPIO.BCM)
GPIO.setup(12, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
# interrupt 선언
GPIO.add_event_detect(12, GPIO.RISING, callback=switchPressed)
# 메인 쓰레드
try:
    while 1:
        print(".")
        time.sleep(0.1)
finally:
    GPIO.cleanup()

스위치 debounce

버튼을 한번 눌렀는데 두번 눌린 것으로 인식하는 현상을 '튐, bounce' 라고 한다. 위 그림은 스위치 눌리는 순간의 전류흐름을 보여주는 것인데, 기대처럼 한번에 깔금하게 0v -> 3.3v 가 되지는 않음을 보여준다. 이 때문에 바운스 현상 발생한다. 해결책으로는...

  • add a 0.1uF capacitor across your switch.
  • software debouncing (기본적으로 스위치가 눌린 직후의 수 millisecond 를 무시하는 것이다.)
  • a combination of both

RPi.GPIO에서 software debouncing을 활성화 하기 위해서는,

GPIO.add_event_detect(channel, GPIO.RISING, callback=my_callback, bouncetime=200)
#or
GPIO.add_event_callback(channel, my_callback, bouncetime=200)

참고

RPi.GPIO wiki: https://sourceforge.net/p/raspberry-gpio-python/wiki/BasicUsage/ https://learn.sparkfun.com/tutorials/raspberry-gpio http://studymake.tistory.com/498