라즈베리파이 Python 프로그래밍 15: 푸시 버튼 및 디지털 입력 인터페이스
앞선 포스팅에서 라즈베리파이에 디지털 출력을 사용하는 방법을 논의했습니다. Python과 같은 고급 언어(HLL)와 같은 단일 보드 컴퓨터 기능이 임베디드 애플리케이션을 제어할 수 있는 방법을 보여주는 GUI 제어 LED 드라이버를 설계했습니다.
마이크로컨트롤러 기반 임베디드 애플리케이션은 일반적으로 어셈블리 언어나 임베디드 C와 같은 저수준 언어로 프로그래밍됩니다. 일반적으로 정교한 소프트웨어 백엔드가 부족합니다.
제한된 하드웨어 리소스와 전반적인 복잡성 때문에 마이크로컨트롤러와 저수준 프로그래밍을 사용하여 정교한 임베디드 애플리케이션을 설계하는 것은 어렵습니다. 라즈베리파이와 같은 단일 보드 컴퓨터는 HLL(High Level Language)로 구현하는 정교한 사용자 인터페이스와 기능을 임베디드 애플리케이션에서 사용할 수 있는 솔루션을 제공합니다.
이것은 GUI 제어 LED 드라이버를 설계하기 위해 그래픽 프로그래밍과 멀티스레딩을 사용한 이전 튜토리얼과 유사합니다.
임베디드 애플리케이션 인터페이스에 사용되는 모든 컨트롤러 또는 프로세서는 5가지 방식으로 임베디드 전자 장치와 상호 작용합니다.
1. 디지털 출력
2. 디지털 입력
3. 아날로그 입력
4. 아날로그 출력
5. 직렬 통신
우리는 이미 이 시리즈에서 Raspberry Pi의 디지털 출력을 사용하는 방법을 다루었습니다. RPi의 범용 입/출력(GPIO)은 3.3V와 호환됩니다.
이 튜토리얼에서는 Raspberry Pi 에서 디지털 입력을 사용하는 방법에 대해 설명합니다 . 그렇게 하기 위해 푸시 버튼을 RPi의 GPIO에 인터페이스하고 여기에서 디지털 입력을 감지합니다. 또한 Python의 IDLE(통합 개발 및 학습 환경) 콘솔에서 이 디지털 입력을 모니터링합니다.
디지털 입력 및 라즈베리파이
다음을 위해 컨트롤러/프로세서에 디지털 또는 논리 입력(High or Low)이 필요합니다.
- 주변기기에서 데이터 입력
- 인터페이스된 외부 장치와 상호 작용
- 인간-컴퓨터 인터페이스 설계
결국 전자적 관점에서 볼 때 모든 컴퓨터(범용 프로세서, 전용 프로세서 또는 컨트롤러)는 데이터를 입력하고 입력을 처리하고 데이터 또는 전자 신호를 출력할 수 있는 디지털 전자 회로입니다. 입력. "입력-처리-출력"이 주어지면 컴퓨터는 지정된 작업이나 작업을 수행할 수 있습니다.
라즈베리파이는 단일 보드 컴퓨터로서 출력과 입력도 가능합니다. GPIO 핀은 3.3V TTL 내전압성이므로 그에 따라 논리 신호를 출력 및 입력할 수 있습니다. RPi의 GPIO에 대한 입력 신호는 순간 유형 스위치를 통한 데이터 입력에 사용할 수 있습니다.( Arduino를 사용하여 푸시 버튼을 인터페이스 하는 방법과 동일합니다).
RPi의 GPIO
이전 튜토리얼에서 언급했듯이 Raspberry Pi는 40핀 헤더와 함께 제공되므로 임베디드 컴퓨터가 됩니다. 임베디드 전자 장치와 인터페이스하고 디지털 입출력, PWM 및 다양한 직렬 통신 프로토콜(UART/USART, I2C 및 SPI 포함)을 통해 통신할 수 있기 때문입니다.
40핀 헤더에는 디지털 신호를 출력하고 디지털 입력을 받을 수 있는 26개의 핀이 있습니다. 이 디지털 입/출력 핀(GPIO)은 3.3V TTL 논리 신호와 호환됩니다.
고정 풀업 저항이 있는 GPIO2(보드 핀 번호 3) 및 GPIO3(보드 핀 번호 5)을 제외하고 내장된 풀업 또는 풀다운 저항을 사용하도록 핀을 구성할 수 있습니다. GPIO 핀은 논리적 HIGH의 경우 3.3V를 출력하고 논리적 LOW의 경우 0V를 출력합니다. 그들은 3.3V를 논리적 HIGH로, 0V를 논리적 LOW로 읽습니다.
Pull-up/Pull-down 저항
GPIO 핀이 내장된 풀업 또는 풀다운 저항(사용자 프로그램에서)을 사용하도록 구성되지 않은 경우 외부에서 수행해야 하며 그렇지 않으면 부동 상태로 유지됩니다. GPIO는 매우 민감하며 약간의 변경에도 영향을 받을 수 있습니다. 예를 들어 사용자의 손가락, 공기 또는 브레드보드 연결에서 표유 정전 용량을 선택할 수 있습니다. 따라서 VCC(또는 RPi의 경우 3.3V) 또는 접지에 영구적으로 연결되어야 합니다.
위 회로가 의미하는 것은 디지털 입력 핀을 1과 0,으로 고정되게 만들어 준다는 의미이다. 1일 때는 확실히 1로 만들어 주기 위해 Pull-up 저항을, 0일 때는 확실히 0을 만들어 주기 위해 Pull-down 저항을 사용하는 이유다. 어렵게 생각할 필요가 없다.
LOW 신호를 디지털/논리 입력으로 감지하도록 핀을 구성해야 하는 경우 HIGH로 당겨야 합니다. 그렇게 하려면 스위치의 한쪽 끝을 접지에 연결해야 하고 다른 쪽 끝은 풀업 저항을 통해 VCC(3.3V)에 유선으로 연결해야 합니다. 이 구성에서 핀(기본값)은 HIGH 논리 신호를 수신합니다. 버튼이나 스위치를 누르면 논리적 LOW가 수신됩니다.
HIGH 신호를 디지털/논리 입력으로 감지하도록 핀을 구성해야 하는 경우 LOW로 당겨야 합니다. 이렇게 하려면 스위치의 한쪽 끝을 VCC(3.3V)에 연결하고 다른 쪽 끝을 풀다운 저항을 통해 접지에 고정 배선해야 합니다. 이 구성에서 핀(기본값)은 LOW 논리 신호를 수신합니다. 버튼이나 스위치를 누르면 논리적 HIGH가 수신됩니다.
이 두 경우 모두 풀업 또는 풀다운 저항은 잠재적으로 핀을 손상시킬 수 있는 전압 서지 또는 표유 커패시턴스로부터 채널(GPIO 핀)을 보호합니다.
푸시 버튼
푸시 버튼은 가장 단순한 순간 유형의 스위치입니다. 이들은 2단자 또는 4단자 스위치일 수 있습니다.
4단자 스위치는 양쪽 단자가 짧습니다. 이것은 두 개의 회로 분기가 동시에 전환될 수 있음을 의미합니다. 그러나 2단자 푸시 버튼은 회로의 단일 분기만 전환할 수 있습니다.
푸시 버튼 인터페이스
푸시 버튼 은 Raspberry Pi 의 GPIO 핀과 쉽게 인터페이스할 수 있습니다 . GPIO2(보드 핀 번호 3) 및 GPIO3(보드 핀 번호 5)을 제외한 모든 GPIO 핀은 내부 풀업 또는 풀다운을 사용하도록 구성할 수 있습니다.
위에서 언급했듯이 그렇지 않은 경우 핀은 외부 풀업 또는 풀다운 저항으로 구성되어야 합니다. 추가 안전을 위해 전류 제한 저항을 직렬 스위치와 연결할 수도 있습니다.
사용자 프로그램이 논리적 HIGH를 읽도록 설계된 경우 내부 또는 외부 풀다운이 발생해야 합니다. 사용자 프로그램이 논리적 LOW를 읽도록 설계된 경우 외부 풀업이 수행되어야 합니다. "읽기 논리 레벨"은 사용자 프로그램에 따라 데이터 값 또는 제어 신호로 해석될 수 있습니다.
디지털 입력 감지
컨트롤러/프로세서가 디지털 입력을 감지하는 방법에는 여러 가지가 있습니다. 이러한 방법은 사용자 프로그램에서 구현됩니다.
논리적 입력을 감지하는 가장 기본적인 방법은 특정 시점의 입력값을 확인하는 것이다. 이것을 "투표"라고 합니다. 그러나 이 방법에서 컨트롤러/프로세서는 사용자 프로그램이 잘못된 시간에 값을 읽는 경우 입력 읽기를 놓칠 수 있습니다. 폴링의 경우 스위치의 상태는 "조건부인 경우"에 의해 확인되고 루프에서 확인됩니다. 그러나 이것은 프로세서 집약적인 작업입니다.
그렇게 하는 또 다른 방법은 단순히 인터럽트 또는 에지 감지를 사용하여 입력을 감지하는 것입니다. 이 방법에서 사용자 프로그램은 GPIO 핀에서 HIGH에서 LOW로의 전환(하강 에지) 또는 LOW에서 HIGH로의 전환(상승 에지)을 기다립니다.
Python을 사용하여 GPIO 핀 을 입력 으로 설정하기
Python을 사용하여 GPIO 핀 을 입력 으로 설정하는 것은 매우 간단합니다. 그러나 Python에서 Raspberry Pi의 GPIO 핀을 제어하려면 먼저 다음과 같이 RPi.GPIO 모듈을 가져와야 합니다.
import RPi.GPIO as GPIO
모듈은 보드 번호와 BCM 번호의 두 가지 방식으로 IO 핀을 참조합니다. 보드 번호는 RPI의 P1 헤더에 있는 핀 번호입니다. 이 수치는 모든 RPi 모델에서 동일하게 유지됩니다.
BCM 번호는 Broadcom SoC의 채널 번호를 나타냅니다. RPi 모델에는 다른 방식으로 RPi 보드 번호에 연결된 채널 번호가 있을 수 있습니다. 따라서 BCM 번호를 사용하는 경우 특정 보드의 회로도를 사용하여 작업해야 합니다. BCM 번호를 사용하는 경우 스크립트가 한 모델에서는 작동하고 다른 모델에서는 중단될 수 있습니다.
핀의 번호 체계는 GPIO 모듈의 setmode() 메서드를 사용하여 설정할 수 있습니다. 다음은 Python 스크립트에서 IO 핀의 번호 매기기 시스템을 설정하는 코드입니다.
GPIO.setmode(GPIO.BOARD)
or
GPIO.setmode(GPIO.BCM)
IO 핀의 번호 매기기 방식이 어떤 식으로 되어 있는지 알아보려면 GPIO 모듈의 getmode() 메서드를 다음과 같이 호출할 수 있습니다.
mode = GPIO.getmode()
or
print(GPIO.getmode())
이 메서드는 GPIO.BOARD, GPIO.BCM 또는 NONE(번호 체계가 설정되지 않은 경우)을 문자열로 반환합니다. 따라서 Python 스크립트가 중단 없이 모든 모델에서 실행될 수 있도록 보드 번호 매기기 시스템을 사용하는 것이 좋습니다.
또한 세션에서 둘 이상의 스크립트를 실행할 수 있습니다. 한 스크립트가 이미 IO 핀을 사용하고 있고 동일한 IO 핀도 사용하는 다른 스크립트가 실행되면 채널이 이미 사용 중이라는 경고가 표시됩니다.
이 경고를 무시하고 현재 스크립트의 IO 핀을 계속 사용하려면 경고를 "False"로 설정할 수 있습니다. 이렇게 하려면 다음과 같이 GPIO 모듈의 setwarnings() 메서드를 사용합니다.
GPIO.setwarnings(False)
핀을 디지털 입력으로 구성하려면 다음 코드를 사용하십시오.
GPIO.setup(channel, GPIO.IN)
그러나 내부 풀업을 사용하도록 핀을 구성해야 하는 경우 다음과 같이 설정합니다.
GPIO.setup(channel, GPIO.IN, pull_up_down=GPIO.PUD_UP)
내부 풀다운을 사용하도록 핀을 구성해야 하는 경우 다음과 같이 설정합니다.
GPIO.setup(channel, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
폴링 Polling 방식으로 디지털 입력 감지
스위치의 상태는 다음과 같이 "if conditional"을 사용하여 GPIO 핀에서 테스트할 수 있습니다.
if (GPIO.input(channel)):
print(“HIGH”)
else:
print(“LOW”)
같은 코드를 while 루프를 사용하여 RPi의 GPIO에서 스위치 상태를 테스트할 수도 있습니다.
while(GPIO.input(channel) == GPIO.LOW): print(“LOW”)
or
while(GPIO.input(channel) == GPIO.HIGH): print(“HIGH”)
"if 조건부"는 스위치 상태를 읽을 때 코드 블록을 한 번 실행하고 루프가 코드 블록을 계속 실행하는 동안(적어도 스위치 상태가 변경될 때까지) 사용자 프로그램의 수명 주기 동안 스위치 상태를 모니터링해야 하는 경우 사용자 프로그램은 자체적으로 반복해야 합니다.
이전 튜토리얼에서는 GUI를 사용하여 LED 드라이버를 제어했기 때문에 사용자 프로그램을 반복하기 위해 멀티 스레딩을 사용했습니다. 우리는 또한 스레드를 죽이는 트릭을 고안했습니다. 이 튜토리얼에서는 GUI를 사용하지 않기 때문에 try-exception 메소드를 사용하여 사용자 프로그램을 반복할 것입니다.
이벤트 구동 GPIO 입력
GPIO 핀에서 디지털 입력을 감지하는 보다 효율적인 방법을 에지 감지라고 합니다. 논리 신호가 LOW에서 HIGH(상승 에지) 또는 HIGH에서 LOW(하강 에지)로 변경되면 채널(핀)에서 이벤트(하드웨어 인터럽트)로 간주됩니다.
Python은 상승 또는 하강 에지가 감지될 때까지 코드 실행을 차단하는 wait_for_edge() 함수를 제공합니다.
함수의 구문은 다음과 같습니다.
GPIO.wait_for_edge(channel, GPIO.RISING)
<Code to execute after detection of rising edge>
or
GPIO.wait_for_edge(channel, GPIO.FALLING)}
<Code to execute after detection of falling edge>
or
GPIO.wait_for_edge(channel, GPIO.BOTH)
<Code to execute after detection of rising or falling edge>
이 함수는 또한 시간 초과 인수를 허용하므로 CPU가 GPIO 핀에서 에지를 감지하기 위해 특정 기간 동안 기다리도록 강제할 수 있습니다. 시간 초과는 밀리초 단위로 지정됩니다. 아래는 코드로 구현한 예입니다.
channel = GPIO.wait_for_edge(channel, GPIO_RISING, timeout=5000)
if channel is None:}
<Code to execute if no edge is detected>
else:
<Code to execute if edge is detected>
wait_for_edge() 함수를 사용하는 이점은 CPU가 GPIO 핀에서 논리 신호 감지를 놓치지 않는다는 것입니다. 그러나 이 기능은 프로그램의 정상적인 흐름을 일시적으로 중단하고 사용자 프로그램을 중지합니다. 이를 피하기 위해 GPIO에서 이벤트 기반 에지 감지를 수행할 수 있습니다.
Python은 다음 구문을 가진 add_event_detect() 함수를 사용할 때 하드웨어 이벤트를 추가할 수 있습니다.
GPIO.add_event_detect(channel, GPIO.RISING)
<Code to execute normally in the user-program>
if GPIO.event_detected(channel):
<Code to execute after detection of edge at GPIO>
다시 말하지만, 에지는 GPIO.RISING, GPIO.FALLING 또는 GPIO.BOTH일 수 있습니다. 이벤트 리스너를 추가하면 사용자 프로그램의 정상적인 흐름을 방해하지 않고 RPi 채널의 논리적 신호(에지) 감지에 대한 후속 조치를 실행할 수 있습니다.
add_event_callback() 함수를 사용하여 에지 감지 이벤트를 하나 이상의 콜백 함수와 바인딩할 수도 있습니다. 다음과 같은 구문이 있습니다.
GPIO.add_event_detect(channel, GPIO.RISING)
GPIO.add_event_callback(channel, callback_function_one)
GPIO.add_event_callback(channel, callback_function_two)
둘 이상의 콜백 함수가 에지 감지에 바인딩된 경우 이러한 함수는 동시에 실행되지 않고 순차적으로 실행됩니다. 콜백 함수는 사용자 프로그램에서 호출된 순서대로 실행됩니다. add_event_callback() 함수를 사용하여 하나의 콜백 함수만 호출할 수 있기 때문입니다. 그러나 콜백 함수를 사용하면 코드(사용자 프로그램)가 더 모듈화되고 관리하기 쉬워집니다.
때때로 스위치 바운스로 인해 콜백 함수가 두 번 이상 실행됩니다. 이것은 스위치에 0.1uF의 커패시터를 연결하거나 소프트웨어 디바운싱을 사용하여 수정할 수 있습니다. 소프트웨어 디바운싱의 경우, 바운스타임 매개변수는 add_event_callback() 함수에 인수로 포함될 수 있습니다.
바운스 시간은 밀리초 단위로 지정됩니다. 다음은 예입니다.
GPIO.add_event_detect(channel, GPIO.RISING, callback=callback_func, bouncetime=250)
더 나은 결과를 위해 두 가지 방법(스위치에 커패시터 연결 및 소프트웨어 디바운싱)도 적용할 수 있습니다.
try-exception을 사용하여 사용자 코드를 무한 반복하기
이전 포스팅에서 멀티 스레딩을 사용하여 사용자 프로그램을 무한 루프로 실행했습니다. 우리는 또한 필요할 때마다 스레드를 죽이는 트릭을 사용했습니다.
해당 응용 프로그램이 GUI를 사용하고 있기 때문에 멀티 스레딩이 필요했습니다. 멀티 스레딩을 적용하지 않으면 GUI가 멈췄을 것입니다.
Python 애플리케이션에 임베디드 전자 장치를 관리하기 위한 GUI가 포함되어 있지 않고 전자 장치를 제어하는 백그라운드 스크립트로 실행되는 경우 간단한 try-exception 문으로 마이크로컨트롤러와 같은 무한 코드 실행을 구현할 수 있는 솔루션이 있습니다.
예를 들어:
if __name__ == ‘__main__’:
setup()
try:
loop()
except KeyboardInterrupt:
endprogram()
이 예에서 setup()은 한 번 실행되는 사용자 정의 함수입니다(Arduino 프로그래밍의 setup() 함수와 같이). loop()는 try 문에 의해 실행되고 키보드 인터럽트가 수신될 때까지 계속 반복되는 함수입니다.
endprogram()은 RPi가 키보드 인터럽트를 수신할 때 호출되는 예외 처리를 위한 함수입니다. 이 함수는 리소스를 정리하고 사용자 프로그램이 종료될 때 Python 스크립트를 안전하게 닫는 데 사용할 수 있습니다.
레시피: RPi와 푸시 버튼 인터페이스
이 레시피에서는 푸시 버튼을 Raspberry Pi의 GPIO에 인터페이스하고 Python IIDLE 콘솔에서 버튼 누르기를 감지합니다.
필요한 부품
1. Raspberry Pi 3/4 모델 B x1
2. 푸시 버튼 x1
3. 10K Ohms 저항 x1
4. 브레드보드 x1
5. 암수 점퍼 와이어
회로 연결은 아래 그림과 같습니다.
테스트를 위한 전체 스크립트 코드는 아래와 같습니다.
import RPi.GPIO as GPIO
import time
button = 10
def setup():
GPIO.setmode(GPIO.BOARD)
GPIO.setup(button, GPIO.IN)
def loop():
while True:
button_state = GPIO.input(button)
if button_state == False:
print(“Button Pressed…”)
while GPIO.input(button) == False:
time.sleep(0.2)
def endprogram():
GPIO.cleanup()
if __name__ == ‘__main__’:
setup()
try:
loop()
except KeyboardInterrupt:
print(“keyboard interrupt detected”)
endprogram()
먼저 RPi의 GPIO21(보드 핀 번호 10)을 푸시 버튼의 한 단자에 연결합니다. 그런 다음 같은 단자에 10K의 저항을 연결하고 저항의 다른 쪽 끝을 3.3V에 연결합니다. 푸시 버튼의 다른 쪽 단자를 접지에 연결합니다. 이제 푸시 버튼이 풀 하이 구성으로 연결되어야 합니다. RPi 보드의 1번 핀과 6번 핀에서 각각 브레드보드에 조립된 회로에 DC 전원 전압과 접지를 공급할 수 있습니다.
Python 스크립트 프로젝트 작업 푸시 버튼은 외부 풀업과 함께 GPIO21(보드 핀 번호 10)에서 인터페이스됩니다. 기본적으로 핀 10은 HIGH 로직(3.3V)을 수신합니다. 푸시 버튼을 누르면 LOW 논리 신호(접지)가 핀에 적용됩니다.
사용자 프로그램은 핀 10에서 논리적 LOW를 감지하고 이 메시지를 콘솔에 인쇄하도록 설계되었습니다. "Button Pressed…." 사용자 프로그램은 Python IDLE에 작성되었으며 버튼을 눌렀을 때 수신된 메시지는 IDLE의 콘솔에서 모니터링됩니다.
프로그래밍 가이드
스크립트는 GPIO 및 시간 라이브러리를 가져오는 것으로 시작합니다. 푸시 버튼이 외부 풀업과 연결된 핀 10을 나타내기 위해 전역 변수가 정의됩니다.
import RPi.GPIO as GPIO
import time
button = 10
핀 번호 지정 시스템이 BOARD로 설정되고 핀 10이 입력으로 설정되는 사용자 정의 함수 setup()이 설정됩니다. 이 기능은 한 번만 실행됩니다.
def setup():
GPIO.setmode(GPIO.BOARD)
GPIO.setup(button, GPIO.IN)
"조건부"에서 스위치의 상태를 모니터링하는 사용자 정의 함수 loop()도 설정됩니다. 스위치가 눌러지고 핀 40에서 논리적 LOW가 판독되면 "Button Pressed..." 메시지가 콘솔에 인쇄됩니다.
버튼이 계속 눌러져 있으면 사용자 프로그램은 time.sleep() 함수를 사용하여 0.2초 동안 기다려야 합니다. 이 기능은 무한히 반복되어 스위치의 상태를 지속적으로 모니터링합니다.
def loop():
while True:
button_state = GPIO.input(button)
if button_state == False:
print(“Button Pressed…”)
while GPIO.input(button) == False:
time.sleep(0.2)
그런 다음 스크립트가 종료될 때 사용자 정의 함수 endprogram()가 실행됩니다. 이 기능은 GPIO를 정리하도록 설정됩니다.
def endprogram():
GPIO.cleanup()
try-exception 문은 setup() 함수를 한 번 실행하고, loop() 함수는 setup() 함수 다음에 무한히 실행하고, endprogram() 함수는 키보드 인터럽트 수신 시 스크립트를 종료하는 데 사용됩니다.
if __name__ == ‘__main__’:
setup()
try:
loop()
except KeyboardInterrupt:
print(“keyboard interrupt detected”)
endprogram()
다음 포스팅에서 라즈베리 파이에서 PWM 신호 (아날로그 출력)를 생성하는 방법을 설명합니다. 고생하셨습니다.
'개발자 > 라즈베리파이4' 카테고리의 다른 글
라즈베리파이 Python 프로그래밍 17: UART 프로토콜을 사용한 직렬 통신 (0) | 2021.11.24 |
---|---|
라즈베리파이 4 IoT(사물인터넷) 2강 IoT 기반기술 (0) | 2021.11.23 |
라즈베리파이 Python 프로그래밍 16: 아날로그 출력 및 소프트웨어 PWM (0) | 2021.11.22 |
라즈베리파이 4 IoT(사물인터넷) 1강 IoT와 웨어러블 디바이스 (0) | 2021.11.20 |
라즈베리파이 Python 프로그래밍 14: GUI 제어 LED 드라이버 (0) | 2021.11.18 |
라즈베리파이 4 기반 IoT(사물인터넷) 설계 5강 스마트 IoT 설계 시스템 (0) | 2021.11.17 |
라즈베리파이 Python 프로그래밍 13: TTK 메뉴, 레이아웃 관리, 이벤트 및 멀티스레딩 (0) | 2021.11.17 |
라즈베리파이 Python 프로그래밍 12: Tkinter 및 TTK 위젯 (0) | 2021.11.16 |
더욱 좋은 정보를 제공하겠습니다.~ ^^