개발자/라즈베리파이4

라즈베리파이 Python 프로그래밍 14: GUI 제어 LED 드라이버

지구빵집 2021. 11. 18. 10:23
반응형

 

 

라즈베리파이 Python 프로그래밍 14: GUI 제어 LED 드라이버 

 

이전의 튜토리얼에서 Tkinter/TTK에 TTK 메뉴, TK에 GUI 툴킷의 레이아웃 관리, 마우스와 키보드 이벤트를 포함하는 내용을 살펴보았습니다. 또한 Python의 다중 스레딩에 대해 논의하고 스레딩 패키지의 우리 사진의 자체 클래스를 만들었습니다.

 

이는 마치 마이크로컨트롤러에서 실행되는 것처럼 정확하게 Raspberry Pi(RPi) 코드를 구현할 수 있음을 의미합니다. 필요할 때마다 스레드를 종료할 수 있도록 스레딩 클래스를 수정했습니다. 이제 RPi와 함께 전자 제품을 사용할 때입니다.

 

우리가 구현할 첫 번째 레시피는 LED 드라이버입니다. 단일 보드 컴퓨터에서 임베디드 전자 장치를 제어하는 ​​것이 마이크로컨트롤러 또는 마이크로컨트롤러 보드와 비교하여 얼마나 유연하고 강력한지 보여줄 수 있도록 GUI 앱을 통해 라즈베리파이 레시피를 구현합니다. 따라서 라즈베리파이 기반 GUI는 LED 드라이버를 제어합니다.

 

RPi 대 마이크로컨트롤러

 

모든 임베디드 컨트롤러는 5가지 방식으로 외부 전자 장치와 인터페이스하고 통신할 수 있습니다.

 

1. 디지털 출력. 컨트롤러는 디지털 HIGH 또는 LOW 신호를 다른 장치로 출력할 수 있습니다. 디지털 신호의 전압 레벨은 CMOS 또는 TTL 로직과 호환될 수 있습니다.

 

2. 디지털 입력. 컨트롤러는 다른 장치에서 디지털 LOW 또는 HIGH 신호를 읽을 수 있습니다. 전압 레벨은 CMOS 또는 TTL 로직과 호환될 수도 있습니다.

 

3. 아날로그 출력. 컨트롤러는 아날로그 신호를 다른 장치로 출력할 수 있습니다. 일반적으로 이 아날로그 신호는 아날로그가 아니라 아날로그 전압 레벨에 가까운 PWM(펄스 폭 변조) 신호입니다.

 

4. 아날로그 입력. 컨트롤러에는 아날로그 전압 레벨을 감지하고 이를 디지털 판독값으로 변환하는 내장 ADC(아날로그-디지털) 채널이 있을 수 있습니다.

 

5. 직렬 통신. 컨트롤러는 UART/USART(피어 투 피어 통신용), I2C/TWI(여러 장치와의 반이중 마스터-슬레이브 직렬 데이터 통신용), 또는 SPI(여러 장치와의 전이중 직렬 데이터 통신용). 다른 직렬 통신 프로토콜은 피어 투 피어, 반이중 또는 전이중일 수 있습니다.

 

대부분의 마이크로컨트롤러에는 5가지 방식의 데이터 및 신호 통신이 모두 장착되어 있으며 타이머/카운터, 하드웨어 인터럽트 및 실시간 클록(RTC)과 같은 추가 하드웨어 기능이 있을 수 있습니다. 보통 이러한 것을 임베디드(애를 밴, 무엇인가를 업은, 가지고 있는 뜻으로)라고 부릅니다.

 

라즈베리파이는 이 5가지 신호 통신 방식 중 4가지를 제공하며 아날로그 입력이 불가능합니다. 디지털 출력, 디지털 입력, 아날로그 출력을 수행하고 USART, I2C 및 SPI를 포함한 공통 직렬 통신 프로토콜을 통해 통신할 수 있습니다. 라즈베리파이는 ADC(아날로그-디지털) 채널이 내장되어 있지 않습니다. 또한 시계 역할을 수행하는 RTC(Real Time Clock)도 가지고 있지 않습니다.

 

ADC(아날로그-디지털) 채널이 내장되어 있지 않습니다.

 

시계 역할을 수행하는 RTC(Real Time Clock)도 가지고 있지 않습니다.

 

아날로그에서 디지털로 변환하려면 직렬 통신을 통해 디지털 판독값을 통신할 수 있는 외부 ADC와 RPi를 인터페이스해야 합니다. RPi에는 또한 일반적인 타이머/카운터로 사용할 수 있는 시스템 타이머와 ARM 타이머가 있습니다. 모든 범용 입/출력(GPIO General Purpose Input Output) 핀을 인터럽트 소스로 구성할 수 있습니다.

 

이러한 컨트롤러별 기능 외에도 Raspberry Pi에는 USB 포트, 이더넷, Wi-Fi, Bluetooth, 온보드 GPU, HDMI 포트, 아날로그 비디오, 오디오 출력, CSI 포트 및 DSI 포트도 ​​있습니다. 최대 4GB의 RAM과 최대 64GB의 MicroSD 카드를 사용하여 부팅할 수 있습니다.

 

이 모든 것이 운영 체제(앱을 모든 고급 언어로 프로그래밍할 수 있는 곳)를 실행할 수 있는 기능과 결합되어 Raspberry Pi를 풍부한 임베디드 컨트롤러로 만듭니다. 이 단일 보드 컴퓨터는 마이크로컨트롤러에서 실행되지 않을 수도 있는 고급 임베디드 애플리케이션에도 사용할 수 있습니다.

 

RPi GPIO

 

RPi가 제공하는 가장 강력한 기능 중 하나는 보드 상단에 40핀 확장 헤더(또는 Pi 1 모델 B+ 이전 모델의 경우 26핀)로 배열된 GPIO 핀입니다. 이 GPIO 행은 전자 장치와 직접 인터페이스하고 임베디드 컨트롤러 역할을 할 수 있음을 의미합니다.

 

일반적인 데스크톱 컴퓨터는 GPIO 헤더를 가지고 있지 않고, 임베디드 컴퓨터처럼 동작하지 않습니다. 일반적인 데스크탑 시스템을 임베디드 컨트롤러/컴퓨터로 사용할 수 있는 유일한 방법은 USB-to-GPIO, Bluetooth GPIO 또는 Wi-Fi GPIO 모듈을 사용하는 것입니다. 

 

A Raspberry Pi GPIO expansion header.

 

 

Raspberry Pi 보드 번호는 일반적으로 GPIO 핀을 나타냅니다. 이는 사용 가능한 모든 모델에서 동일하게 유지되는 RPI의 P1 헤더에 있는 핀 번호입니다. 이 핀아웃 다이어그램에 표시된 것처럼 보드의 가장자리에서 시작하여 핀 수를 계산합니다.

 

Raspberry Pi GPIO Pin diagram

 

참고: 모든 핀이 동일한 것은 아닙니다. 일부는 예약되어 있고 다른 일부는 추가 기능을 제공합니다. 다음 그림은 GPIO 헤더의 예약된 핀을 보여줍니다.

 

Simple Guide to the Raspberry Pi GPIO Header https://www.raspberrypi-spy.co.uk/2012/06/simple-guide-to-the-rpi-gpio-header-and-pins/

 

40개의 핀 중:

 

  • 2개의 핀이 Pi의 5V 레일에 연결되어 일정한 +5.0V DC 전원을 제공하도록 예약되어 있습니다.
  • 두 개의 핀이 Pi의 3.3V 레일에 연결되어 있으며 일관된 +3.3V DC 공급을 제공하기 위해 예약되어 있습니다.
  • 8개의 핀이 Pi의 접지에 연결되어 있으며 공통 접지를 제공하기 위해 예약되어 있습니다.
  • 2개의 핀은 HATS EEPROM과의 I2C 통신용으로 예약되어 있습니다.
  • 26개의 핀이 디지털 입력/출력에 사용 가능하며 3V3 핀입니다(즉, 출력은 3.3V로 설정될 수 있고 입력은 3.3V 허용 가능).

 

고정 풀업 저항이 있는 GPIO2(보드 핀 번호 3) 및 GPIO3(보드 핀 번호 5)을 제외하고 내장, 풀업 또는 풀다운 저항을 사용하도록 핀을 구성할 수 있습니다. GPIO 핀은 논리적 HIGH의 경우 3.3V를 출력하고 논리적 LOW의 경우 0V를 출력합니다. 그들은 3.3V를 논리적 HIGH로, 0V를 논리적 LOW로 읽습니다.

 

26개의 모든 GPIO 핀은 소프트웨어 PWM 신호를 생성할 수 있습니다. 하드웨어 PWM 신호는 다음에서만 사용할 수 있습니다.

 

  • GPIO12(보드 핀 번호 32)
  • GPIO13(보드 핀 번호 33)
  • GPIO18(보드 핀 번호 12)
  • GPIO19(보드 핀 번호 35)

 

직렬 통신(USART)의 경우 GPIO14(보드 핀 번호 8)는 직렬 송신기(Tx)로 사용되고 GPIO15(보드 핀 번호 10)는 직렬 수신기(Rx)로 사용됩니다.

 

Pi HATS(Hardware Attached on Top 일종의 쉴드로 보드 위에 연결하여 기능을 수행하는 보드) EEPROM과의 I2C 통신을 위해 GPIO0(보드 핀 번호 27)은 데이터용으로 예약되어 있고 GPIO1(보드 핀 번호 28)은 클록 신호용으로 예약되어 있습니다. 외부 장치와의 I2C 통신을 위해 GPIO2(보드 핀 번호 3)는 데이터용으로 사용되며 GPIO3(보드 핀 번호 5)은 클록 신호용으로 사용됩니다.

 

SPI 통신에는 두 개의 채널이 있습니다. SPI0은 다음을 사용합니다.

 

  • MOSI용 GPIO10(보드 핀 번호 19)
  • MISO용 GPIO9(보드 핀 번호 21)
  • SCLK용 GPIO11(보드 핀 번호 23)
  • CE0용 GPIO8(보드 핀 번호 24)
  • CE1용 GPIO7(보드 핀 번호 26)

 

SPI1(Serial Pheripheral Interface)통신은 다음을 사용합니다.

 

  • MOSI용 GPIO20(보드 핀 번호 38)
  • MISO용 GPIO19(보드 핀 번호 35)
  • SCLK용 GPIO21(보드 핀 번호 40)
  • CE0용 GPIO18(보드 핀 번호 12)
  • CE1용 GPIO17(보드 핀 번호 11)
  • CE2용 GPIO16(보드 핀 번호 36)

 

Python에서 라즈베리파이의 GPIO를 사용하려면

 

Raspberry Pi의 GPIO 핀을 제어하려면 먼저 다음과 같이 RPi.GPIO 모듈을 가져옵니다.

 

import RPi.GPIO as GPIO

 

모듈은 보드 번호와 BCM 번호의 두 가지 방식으로 IO 핀을 참조합니다. 보드 번호는 RPi의 P1 헤더에 있는 핀 번호입니다. 이 수치는 모든 RPi 모델에서 동일하게 유지됩니다. BCM 번호는 Broadcom SoC의 채널 번호를 나타냅니다. 다른 RPi 모델에는 RPi 보드 번호에 연결된 채널 번호가 있을 수 있습니다.

 

따라서 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 핀을 계속 사용하려면 다음과 같이 GPIO 모듈의 setwarnings() 메서드를 사용하여 경고를 False로 설정할 수 있습니다.

 

GPIO.setwarnings(False)

 

단, 핀의 번호 체계를 설정하기 전에 이 작업을 수행해야 합니다. 핀을 디지털 입력 또는 출력으로 사용하는 경우 GPIO 모듈의 setup() 메서드를 사용하여 구성해야 합니다. 핀은 다음과 같이 디지털 출력으로 구성할 수 있습니다.

 

GPIO.setup(channel, GPIO.OUT)

 

여기서 채널은 보드 또는 BCM 번호를 사용하여 참조하는 핀 번호를 나타냅니다. 디지털 출력으로 구성하는 동안 핀에 의해 출력되도록 초기 값을 설정할 수도 있습니다.

 

GPIO.setup(channel, GPIO.OUT, initial=GPIO.HIGH)

 

핀의 출력을 설정하기 위해 GPIO 모듈의 output() 메서드가 사용됩니다.

 

GPIO.output(channel, state)

 

여기서 상태는 "0" 또는 GPIO.LOW, 논리적 LOW의 경우 False, 논리적 HIGH의 경우 "1" 또는 GPIO.HIGH 또는 True일 수 있습니다. 채널 목록을 인수로 사용하여 동시에 여러 핀에 디지털 신호를 출력하려면 아래와 같은 순서로 프로그램을 구성해야 합니다.

 

gpio_list = [38,40]                             # also works with tuples
GPIO.output(gpio_list, GPIO.LOW)                # sets all to GPIO.LOW
GPIO.output(gpio_list, (GPIO.HIGH, GPIO.LOW))   # sets first HIGH and second LOW

 

핀은 다음 중 하나와 같이 디지털 입력으로 구성할 수 있습니다.

 

GPIO.setup(channel, GPIO.IN)
or
GPIO.setup(channel, GPIO.IN, pull_up_down=GPIO.PUD_UP)
or
GPIO.setup(channel, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)

 

디지털 입력을 읽으려면 GPIO 모듈의 input() 메서드가 사용됩니다.

 

GPIO.input(channel)

 

디지털 입력으로 구성된 핀의 상태는 다음과 같은 조건부 블록을 사용하여 읽을 수 있습니다.

 

if (GPIO.input(channel)):
print(“HIGH”)
else:
print(“LOW”)

 

스크립트가 끝나면 스크립트에서 사용하는 모든 리소스를 정리해야 합니다. 여기에는 IO 핀이 포함됩니다. 스크립트가 정리 없이 종료되면 핀은 RPi에서 계속 사용됩니다. 다른 스크립트가 해당 핀에 액세스하려고 하면 경고가 생성됩니다. 핀이 풀업 또는 풀다운 없이 입력으로 사용된 경우 이러한 핀이 단락으로 인해 손상될 수 있으므로 정리하는 것이 특히 중요합니다.

 

채널은 GPIO 모듈의 cleanup() 메서드를 사용하여 정리할 수 있습니다. 이 방법은 사용 중인 번호 매기기 시스템도 지웁니다.

 

GPIO.cleanup()

 

LED 드라이버

 

발광 다이오드(LED)는 순방향 바이어스 조건에서 가시광선, 적외선 또는 레이저광을 방출할 수 있는 신호 다이오드와 유사합니다. 이들은 장식 목적으로 사용되거나 상호 배타적인 조건의 지표로 사용됩니다. 예를 들어 장치가 켜져 있거나 꺼져 있는지, 옵션/기능이 선택되거나 선택되지 않았는지, 또는 장치가 작동하는지 여부를 나타냅니다.

 

특정 상황에서 동작/이벤트가 진행 중임을 나타내기 위해 LED가 깜박입니다.

 

LED는 회로에서 켜거나 끌 수 있는 2단자 장치입니다. LED는 빛나기 시작할 때 순방향 바이어스 조건에서 켜지고 발광하지 않을 때 역방향 바이어스 조건에서 꺼집니다.

 

LED 드라이버는 LED의 스위치를 제어하는 ​​회로입니다. LED는 작동에 12~30mA의 순방향 전류만 필요한 전류 제어 장치입니다. 모든 마이크로컨트롤러 또는 프로세서의 GPIO 핀은 일반적으로 논리적 HIGH의 출력을 위해 최대 40mA 전류를 공급할 수 있습니다. 그들은 또한 논리적 HIGH의 입력에 대해 동일한 전류를 싱크할 수 있습니다.

 

이것은 LED가 Raspberry Pi를 포함한 CMOS/TTL 칩의 IO 핀에 의해 직접 인터페이스되고 제어될 수 있음을 의미합니다. LED와 LED가 회로에서 어떻게 인터페이스되는지에 대해 자세히 알아보려면 이 Arduino 튜토리얼: Arduino 호환 코딩 04를 확인하십시오 .

 

RPi 기반 GUI 제어 LED 드라이버

 

디지털 IO에 Raspberry Pi의 GPIO 핀을 사용하는 방법과 LED가 회로에서 인터페이스되는 방법을 알았으므로 LED 드라이버를 만들어 보겠습니다.

 

필요한 구성 요소:

 

1. Raspberry Pi 3/4 모델 B x1

2. LED x1

3. 330옴 저항 x1

4. 브레드보드 x1

5. 수-수 점퍼 와이어

 

회로 연결:

 

  • RPi의 GPIO21(보드 핀 번호 36번)을 LED의 양극에 연결합니다.
  • LED의 음극을 330옴 직렬 저항과 연결하고 저항의 다른 쪽 단자를 39번 GND에 연결 합니다.
  • DC 공급 전압 및 접지는 36번과 39번이 아닌 라즈베리파이 핀 번호 2 및 핀 번호 6에서 회로에 제공될 수 있습니다.

 

Raspberry Pi LED 드라이버 회로

 

Raspberry Pi LED driver circuit

 

회로도

 

Raspberry Pi LED 드라이버 회로도의 예

 

프로그래밍 가이드

 

GUI에서 LED의 스위칭 동작을 제어하는 ​​것을 목표로 합니다. 이전 포스팅에서 개발한 GUI 메뉴부터 시작해 보겠습니다 .

 

아래 윈도우 화면과 전체 코드를 나타냈습니다.

 

 

위 윈도우 화면을 나타내는 전체 코드는 아래와 같습니다.

 

from tkinter import *

def donothing():
    pass
    
root = Tk()
root.title("RPi Embedded Electronics Control APP")
root.minsize(480, 480)
root.resizable(0, 0)
main_menu = Menu(root)

displays_menu = Menu(main_menu, tearoff=0)
main_menu.add_cascade(label="Displays", menu=displays_menu)
sensors_menu = Menu(main_menu, tearoff=0)
main_menu.add_cascade(label="Sensors", menu=sensors_menu)
modules_menu = Menu(main_menu, tearoff=0)
main_menu.add_cascade(label="Modules", menu=modules_menu)
motors_menu = Menu(main_menu, tearoff=0)
main_menu.add_cascade(label="Motors", menu=motors_menu)
comm_menu = Menu(main_menu, tearoff=0)
main_menu.add_cascade(label="Communication", menu=comm_menu)
help_menu = Menu(main_menu, tearoff=0)
main_menu.add_cascade(label="Help", menu=help_menu)

displays_menu.add_command(label="LEDs", command=donothing)
displays_menu.add_command(label="SSDs", command=donothing)
displays_menu.add_command(label="Character LCD", command=donothing)
displays_menu.add_command(label="Graphic LCD", command=donothing)
displays_menu.add_separator()
displays_menu.add_command(label="Touch Screen", command=donothing)

sensors_menu.add_command(label="LDR", command=donothing)
sensors_menu.add_command(label="IR", command=donothing)
sensors_menu.add_command(label="HC-SR04 Ultrasonic", command=donothing)
sensors_menu.add_command(label="LM35", command=donothing)
sensors_menu.add_command(label="DHT-11", command=donothing)
sensors_menu.add_command(label="Motion Sensor", command=donothing)
sensors_menu.add_command(label="Moisture Sensor", command=donothing)
sensors_menu.add_command(label="Accelerometer", command=donothing)
sensors_menu.add_command(label="Accelero + Gyro", command=donothing)

modules_menu.add_command(label="GPS", command=donothing)
modules_menu.add_command(label="GSM", command=donothing)
modules_menu.add_command(label="Fingerprint", command=donothing)
modules_menu.add_command(label="RFID", command=donothing)
modules_menu.add_command(label="RTC", command=donothing)

motors_menu.add_command(label="DC", command=donothing)
motors_menu.add_command(label="Stepper", command=donothing)
motors_menu.add_command(label="Servo", command=donothing)

comm_menu.add_command(label="RF", command=donothing)
comm_menu.add_command(label="USB", command=donothing)
comm_menu.add_command(label="Ethernet", command=donothing)
comm_menu.add_command(label="Bluetooth", command=donothing)
comm_menu.add_command(label="Wi-Fi", command=donothing)

help_menu.add_command(label="Index", command=donothing)
help_menu.add_command(label="About…", command=donothing)
help_menu.add_command(label="Exit", command=root.quit)

displays_menu.add_separator()


root.config(menu = main_menu)
root.mainloop()

 

LED를 제어하기 위해 "디스플레이" 메뉴에서 "LED" 메뉴 버튼을 클릭할 때 로드되는 앱을 작성했습니다. 앱은 단일 함수 LED_driver()의 실행을 통해 실행됩니다.

 

display_menu에 대한 add_command() 메서드의 명령 인수에 LED_driver() 함수를 할당하고 donothing() 함수를 다음과 같이 대체합니다.

 

displays_menu.add_command(label=”LEDs”, command=LED_driver)

 

스크립트 시작 부분에서 다음 모듈을 가져옵니다.

 

from tkinter import *
from tkinter import ttk
import time
import threading
import sys
import RPi.GPIO as GPIO

 

마이크로 컨트롤러와 같은 무한 루프를 실행할 때 GUI가 정지되도록 앱을 별도의 스레드로 실행할 것이기 때문에 이 스레드를 참조하는 전역 변수를 선언했습니다.

 

스크립트에 사용된 IO 핀을 참조하려면 전역 변수도 필요합니다. 단일 IO 핀이 스크립트에서 사용되므로 "gpio_pin" 변수가 선언됩니다.

 

thread_LED_driver = None
gpio_pin = 40

 

LED_driver() 함수 내에서 사용자 정의된 스레딩 클래스가 정의됩니다. 스레딩 클래스의 수정된 run() 메서드 본문에 있는 무한 While 루프를 사용하면 마치 마이크로컨트롤러에서 실행되는 것처럼 무한 루프에서 코드를 실행할 수 있습니다.

 

사용자 정의 메소드인 stop() 및 stoped()는 스레드를 가상으로 종료하기 위해 작성되었습니다. 스레딩 모듈은 스레드를 종료하는 방법을 제공하지 않습니다. 그래서 우리는 다음과 같은 코딩 트릭을 사용하고 있습니다.

 

class MyThread(threading.Thread):
        def __init__(self, *args, **kwargs):
             super(MyThread, self).__init__(*args, **kwargs)
             self._stop = threading.Event()
         def stop(self):
            self._stop.set()
         def stopped(self):
             return self._stop.isSet()
          def run(self):
              while 1:
                 if self.stopped():
                   print(“Thread Closed”)
                   return
               else:
                    self._target(*self._args, **self._kwargs)

 

LED_driver() 함수 내에서 LED를 제어하기 위해 GUI를 로드하는 코드를 작성해야 합니다.

 

LED_def = Frame(main_ui)
     LED_def.grid()
     LED_def[“padx”]= 25
     LED_def[“pady”]= 10

     label_LED_gpio = Label(LED_def, text = “Select GPIO”)
label_LED_gpio[“padx”]= 25
label_LED_gpio[“pady”]= 10
label_LED_gpio.grid(row = 1, column = 1)

combo_LED_gpio = ttk.Combobox(LED_def, values=[“GPIO 02 (03)”, “GPIO 03 (05)”, “GPIO 04 (07)”, “GPIO 05 (29)”, “GPIO 06 (31)”, “GPIO 07 (26)”, “GPIO 08 (24)”,
                                            “GPIO 09 (21)”, “GPIO 10 (19)”, “GPIO 11 (23)”, “GPIO 12 (32)”, “GPIO 13 (33)”, “GPIO 14 (08)”, “GPIO 15 (10)”
                                            “GPIO 16 (36)”, “GPIO 17 (11)”, “GPIO 18 (12)”, “GPIO 19 (35)”, “GPIO 20 (38)”, “GPIO 21 (40)”, “GPIO 22 (15)”
                                            “GPIO 23 (16)”, “GPIO 24 (18)”, “GPIO 25 (22)”, “GPIO 26 (37)”, “GPIO 27 (13)”,])

combo_LED_gpio.current(0)
combo_LED_gpio.grid(row = 2, column = 1)
label_LED_conf = Label(LED_def, text = “LED Configuration”)
label_LED_conf[“padx”]= 25
label_LED_conf[“pady”]= 10
label_LED_conf.grid(row = 1, column = 2)
combo_LED_config = ttk.Combobox(LED_def, values=[“Source Mode”, “Sink Mode”])
combo_LED_config.current(0)
combo_LED_config.grid(row = 2, column = 2)
label_LED_op = Label(LED_def, text = “Operation”)
label_LED_op[“padx”]= 25
label_LED_op[“pady”]= 10
label_LED_op.grid(row = 1, column = 3)

combo_LED_op = ttk.Combobox(LED_def, values=[“Switch ON”, “Switch OFF”, “Blink”])
combo_LED_op.current(0)
combo_LED_op.grid(row = 2, column = 3)
label_LED_duration = Label(LED_def, text = “Blink Duration (Milliseconds)”)
label_LED_duration[“padx”]= 25
label_LED_duration[“pady”]= 10
label_LED_duration.grid(row = 1, column = 4)
entry_LED_duration = Entry(LED_def)
entry_LED_duration.insert(0, “500”)
entry_LED_duration[‘state’] = DISABLED
entry_LED_duration.grid(row = 2, column = 4)
button_LED_signal = Button(LED_def, text = “Generate Signal”, command = thread_LED_signal)
button_LED_signal.grid(row = 3, column = 2, padx = 5, pady = 5)
button_LED_shutdown = Button(LED_def, text = “Shutdown Signal”, command = shutdown_LED_signal)
button_LED_shutdown.grid(row = 3, column = 3, padx = 5, pady = 5)
combo_LED_op.bind(‘<<ComboboxSelected>>’, active_blink)

 

gui

 

 

GUI는 메뉴 아래의 'LED_def' 프레임에 로드됩니다. 여기에는 LED가 인터페이스될 수 있는 GPIO 핀을 선택하는 콤보 상자, LED를 켜기 위해 핀이 전류 소스 또는 전류 싱크로 작동해야 하는지 여부를 선택하는 콤보 상자, 원하는 LED 작동을 선택하는 콤보 상자(스위치 ON, 스위치 OFF 또는 Blink) 및 밀리초 단위의 깜박임 간격 값을 로드하는 항목입니다. 각 콤보 상자의 상단에는 레이블 위젯이 있으며 목적을 나타내는 항목 위젯이 있습니다. 항목은 기본적으로 비활성화되어 있으며 'Blink' 작업을 선택하면 상태가 정상으로 변경됩니다.

 

이것은 <<ComboboxSelected>>를 사용하여 수행됩니다. Combobox 위젯의 가상 이벤트입니다. 이 이벤트는 Tkinter/TTK의 bind() 메서드를 사용하여 콤보박스에 바인딩되며 이벤트 발생 시 active_blink() 함수가 실행됩니다. active_blink() 함수에서 콤보박스의 현재 인덱스를 읽어 2(깜박임 동작에 해당)이면 진입 위젯의 상태를 정상으로 설정한다.

 

def active_blink(event):
        x = combo_LED_op.current()
        if x == 2:
        entry_LED_duration[‘state’] = NORMAL

 

LED 드라이버 회로를 위한 Raspberry Pi의 임베디드 UI.

 

 

 

GUI에는 두 개의 버튼이 있습니다. 하나는 LED를 구동하는 신호 생성을 시작하고 다른 하나는 중지합니다. 적절한 신호를 생성하는 버튼은 thread_LED_signal() 함수에 바인딩되어야 합니다. 신호를 중지하는 버튼은 shutdown_LED_signal() 함수에 바인딩되어야 합니다.

 

thread_LED_signal() 함수에서:

  • 전역 변수는 스레드가 정의되었음을 참조하도록 선언됩니다. 
  • generate_LED_signal() 함수는 스레드 의 대상 호출 가능으로 할당됩니다. 스레드는 스레딩 모듈의 호출 start() 메서드에 의해 시작됩니다.
def thread_LED_signal():
       global thread_LED_driver
       thread_LED_driver = MyThread(target = generate_LED_signal)
       thread_LED_driver.start()

 

generate_LED_signal() 함수에서

  • 위젯의 get() 메서드를 사용하여 콤보 상자의 현재 값을 읽습니다.
  • 선택한 GPIO 핀의 값은 RPI의 보드 핀 번호에 해당하는 정수로 디코딩됩니다.
  • 깜박임 간격 동안 사용자가 입력한 값은 정수로 검증됩니다. 그렇지 않으면 500밀리초가 기본 깜박임 간격으로 설정됩니다.
  • GPIO 경고는 False로 설정해야 하며 RPi에 대해 보드 번호 지정 시스템을 선택해야 합니다. 선택한 GPIO 핀도 출력으로 설정됩니다.

다음으로, LED 구동 방식에 대해 다른 경우와 함께 선택된 LED 동작에 대한 조건이 일치된다. 따라서 선택된 GPIO 핀은 논리적 HIGH 또는 논리적 LOW로 설정되어 선택한 LED 구동 방법에서 작동을 수행합니다.

 

LED를 깜박이려면 선택한 IO 핀의 상태가 사용자가 입력한 지연에 의해 토글됩니다. 지연을 제공하기 위해 Time 모듈의 sleep() 메서드가 사용됩니다. 이 메서드는 초 단위의 지연을 허용합니다.

 

def generate_LED_signal():
global gpio_pin
gpio_pin = combo_LED_gpio.get()
LED_config = combo_LED_config.get()
LED_op = combo_LED_op.get()

if gpio_pin == “GPIO 02 (03)”:
      gpio_pin = 3
elif gpio_pin == “GPIO 03 (05)”:
      gpio_pin = 5
elif gpio_pin == “GPIO 04 (07)”:
      gpio_pin = 7
 elif gpio_pin == “GPIO 14 (08)”:
      gpio_pin = 8
elif gpio_pin == “GPIO 15 (10)”:
     gpio_pin = 10
elif gpio_pin == “GPIO 18 (12)”:
     gpio_pin = 12
elif gpio_pin == “GPIO 17 (11)”:
     gpio_pin = 11
elif gpio_pin == “GPIO 27 (13)”:
     gpio_pin = 13
elif gpio_pin == “GPIO 22 (15)”:
     gpio_pin = 15
elif gpio_pin == “GPIO 23 (16)”:
     gpio_pin = 16
elif gpio_pin == “GPIO 24 (18)”:
     gpio_pin = 18
elif gpio_pin == “GPIO 10 (19)”:
     gpio_pin = 19
elif gpio_pin == “GPIO 09 (21)”:
     gpio_pin = 21
elif gpio_pin == “GPIO 11 (23)”:
     gpio_pin = 23
elif gpio_pin == “GPIO 25 (22)”:
     gpio_pin = 22
elif gpio_pin == “GPIO 08 (24)”:
     gpio_pin = 24
elif gpio_pin == “GPIO 07 (26)”:
     gpio_pin = 26
elif gpio_pin == “GPIO 05 (29)”:
     gpio_pin = 29
elif gpio_pin == “GPIO 06 (31)”:
     gpio_pin = 31
elif gpio_pin == “GPIO 12 (32)”:
     gpio_pin = 32
elif gpio_pin == “GPIO 13 (33)”:
     gpio_pin = 33
elif gpio_pin == “GPIO 19 (35)”:
     gpio_pin = 35
elif gpio_pin == “GPIO 16 (36)”:
     gpio_pin = 36
elif gpio_pin == “GPIO 26 (37)”:
     gpio_pin = 37
elif gpio_pin == “GPIO 20 (38)”:
     gpio_pin = 38
elif gpio_pin == “GPIO 21 (40)”:
     gpio_pin = 40
else:
     gpio_pin = 40

LED_duration = entry_LED_duration.get()
if LED_duration.isdigit():
    LED_duration = int(LED_duration)
else:
    LED_duration = 500

GPIO.setwarnings(False)
GPIO.setmode(GPIO.BOARD)
GPIO.setup(gpio_pin, GPIO.OUT)

if LED_op == “Switch ON”:
   if LED_config == “Source Mode”:
      GPIO.output(gpio_pin, GPIO.HIGH)
   elif LED_config == “Sink Mode”:
      GPIO.output(gpio_pin, GPIO.LOW)
elif LED_op == “Switch OFF”:
    if LED_config == “Source Mode”:
       GPIO.output(gpio_pin, GPIO.LOW)
elif LED_config == “Sink Mode”:
    GPIO.output(gpio_pin, GPIO.HIGH)
elif LED_op == “Blink”:
    GPIO.output(gpio_pin, GPIO.HIGH)
    time.sleep(LED_duration/1000)
    GPIO.output(gpio_pin, GPIO.LOW)
     time.sleep(LED_duration/1000)

 

shutdown signal button을 누를 때 스레드를 닫아야 합니다. 따라서 shutdown_LED_signal() 함수에는 스레드를 참조하도록 선언된 전역 변수가 정의되어 있습니다. 그런 다음 수정된 스레딩 클래스의 stop() 메서드를 사용하여 스레드를 닫습니다.

 

마지막으로 신호를 종료하는 동안 GPIO 핀 정리가 수행됩니다.

 

def shutdown_LED_signal():
       global thread_LED_driver
       thread_LED_driver.stop()
       GPIO.cleanup()

 

모든 함수와 GUI 본문 문은 LED_driver() 함수에 포함되어 있습니다. 이 단일 기능은 GUI를 로드하고 임베디드 LED 드라이버를 구현합니다.

 

여기까지 작업한 소스코드를 아래에 나타냅니다. 제대로 동작하지 않습니다. LED_driver 함수가 정의되지 않았다고 나오는데 아무래도 소스에 문제가 있는 듯 보입니다. 여하튼 참고하세요. 이거 문제를 찾아야 하는데 아직 실력이 안 되나 봅니다. ㅠ.ㅠ

 

from tkinter import *
from tkinter import ttk
import time
import threading
import sys
import RPi.GPIO as GPIO

thread_LED_driver = None
gpio_pin = 40

def donothing():
    pass
    
root = Tk()
root.title("RPi Embedded Electronics Control APP")
root.minsize(480, 480)
root.resizable(0, 0)
main_menu = Menu(root)

displays_menu = Menu(main_menu, tearoff=0)

main_menu.add_cascade(label="Displays", menu=displays_menu)
sensors_menu = Menu(main_menu, tearoff=0)
main_menu.add_cascade(label="Sensors", menu=sensors_menu)
modules_menu = Menu(main_menu, tearoff=0)
main_menu.add_cascade(label="Modules", menu=modules_menu)
motors_menu = Menu(main_menu, tearoff=0)
main_menu.add_cascade(label="Motors", menu=motors_menu)
comm_menu = Menu(main_menu, tearoff=0)
main_menu.add_cascade(label="Communication", menu=comm_menu)
help_menu = Menu(main_menu, tearoff=0)
main_menu.add_cascade(label="Help", menu=help_menu)

displays_menu.add_command(label="LEDs", command=LED_driver)
displays_menu.add_command(label="SSDs", command=donothing)
displays_menu.add_command(label="Character LCD", command=donothing)
displays_menu.add_command(label="Graphic LCD", command=donothing)
displays_menu.add_separator()
displays_menu.add_command(label="Touch Screen", command=donothing)

sensors_menu.add_command(label="LDR", command=donothing)
sensors_menu.add_command(label="IR", command=donothing)
sensors_menu.add_command(label="HC-SR04 Ultrasonic", command=donothing)
sensors_menu.add_command(label="LM35", command=donothing)
sensors_menu.add_command(label="DHT-11", command=donothing)
sensors_menu.add_command(label="Motion Sensor", command=donothing)
sensors_menu.add_command(label="Moisture Sensor", command=donothing)
sensors_menu.add_command(label="Accelerometer", command=donothing)
sensors_menu.add_command(label="Accelero + Gyro", command=donothing)

modules_menu.add_command(label="GPS", command=donothing)
modules_menu.add_command(label="GSM", command=donothing)
modules_menu.add_command(label="Fingerprint", command=donothing)
modules_menu.add_command(label="RFID", command=donothing)
modules_menu.add_command(label="RTC", command=donothing)

motors_menu.add_command(label="DC", command=donothing)
motors_menu.add_command(label="Stepper", command=donothing)
motors_menu.add_command(label="Servo", command=donothing)

comm_menu.add_command(label="RF", command=donothing)
comm_menu.add_command(label="USB", command=donothing)
comm_menu.add_command(label="Ethernet", command=donothing)
comm_menu.add_command(label="Bluetooth", command=donothing)
comm_menu.add_command(label="Wi-Fi", command=donothing)

help_menu.add_command(label="Index", command=donothing)
help_menu.add_command(label="About…", command=donothing)
help_menu.add_command(label="Exit", command=root.quit)

displays_menu.add_separator()


root.config(menu = main_menu)
root.mainloop()



def LED_driver():

    class MyThread(threading.Thread):
        def __init__(self, *args, **kwargs):
            super(MyThread, self).__init__(*args, **kwargs)
            self._stop = threading.Event()
        def stop(self):
            self._stop.set()
        def stopped(self):
            return self._stop.isSet()
        def run(self):
            while 1:
                if self.stopped():
                    print("Thread Closed")
                    return
                else:
                    self._target(*self._args, **self._kwargs)
    
    LED_def = Frame(main_ui)
    LED_def.grid()
    LED_def["padx"]= 25
    LED_def["pady"]= 10

    label_LED_gpio = Label(LED_def, text = "Select GPIO")
    label_LED_gpio["padx"]= 25
    label_LED_gpio["pady"]= 10
    label_LED_gpio.grid(row = 1, column = 1)

    combo_LED_gpio = ttk.Combobox(LED_def, values=["GPIO 02 (03)", "GPIO 03 (05)", "GPIO 04 (07)", "GPIO 05 (29)", "GPIO 06 (31)", "GPIO 07 (26)", "GPIO 08 (24)",
                                            "GPIO 09 (21)", "GPIO 10 (19)", "GPIO 11 (23)", "GPIO 12 (32)", "GPIO 13 (33)", "GPIO 14 (08)", "GPIO 15 (10)"
                                            "GPIO 16 (36)", "GPIO 17 (11)", "GPIO 18 (12)", "GPIO 19 (35)", "GPIO 20 (38)", "GPIO 21 (40)", "GPIO 22 (15)"
                                            "GPIO 23 (16)", "GPIO 24 (18)", "GPIO 25 (22)", "GPIO 26 (37)", "GPIO 27 (13)",])

    combo_LED_gpio.current(0)
    combo_LED_gpio.grid(row = 2, column = 1)
    label_LED_conf = Label(LED_def, text = "LED Configuration")
    label_LED_conf["padx"]= 25
    label_LED_conf["pady"]= 10
    label_LED_conf.grid(row = 1, column = 2)
    combo_LED_config = ttk.Combobox(LED_def, values=["Source Mode", "Sink Mode"])
    combo_LED_config.current(0)
    combo_LED_config.grid(row = 2, column = 2)
    label_LED_op = Label(LED_def, text = "Operation")
    label_LED_op["padx"]= 25
    label_LED_op["pady"]= 10
    label_LED_op.grid(row = 1, column = 3)

    combo_LED_op = ttk.Combobox(LED_def, values=["Switch ON", "Switch OFF", "Blink"])
    combo_LED_op.current(0)
    combo_LED_op.grid(row = 2, column = 3)
    label_LED_duration = Label(LED_def, text = "Blink Duration (Milliseconds)")
    label_LED_duration["padx"]= 25
    label_LED_duration["pady"]= 10
    label_LED_duration.grid(row = 1, column = 4)
    entry_LED_duration = Entry(LED_def)
    entry_LED_duration.insert(0, "500")
    entry_LED_duration['state'] = DISABLED
    entry_LED_duration.grid(row = 2, column = 4)
    button_LED_signal = Button(LED_def, text = "Generate Signal", command = thread_LED_signal)
    button_LED_signal.grid(row = 3, column = 2, padx = 5, pady = 5)
    button_LED_shutdown = Button(LED_def, text = "Shutdown Signal", command = shutdown_LED_signal)
    button_LED_shutdown.grid(row = 3, column = 3, padx = 5, pady = 5)
    combo_LED_op.bind('<<ComboboxSelected>>', active_blink)
    
    def active_blink(event):
        x = combo_LED_op.current()
        if x == 2:
            entry_LED_duration['state'] = NORMAL
    
    def thread_LED_signal():
        global thread_LED_driver
        thread_LED_driver = MyThread(target = generate_LED_signal)
        thread_LED_driver.start()
    
    def generate_LED_signal():
        global gpio_pin
        gpio_pin = combo_LED_gpio.get()
        LED_config = combo_LED_config.get()
        LED_op = combo_LED_op.get()

        if gpio_pin == "GPIO 02 (03)":
            gpio_pin = 3
        elif gpio_pin == "GPIO 03 (05)":
            gpio_pin = 5
        elif gpio_pin == "GPIO 04 (07)":
            gpio_pin = 7
        elif gpio_pin == "GPIO 14 (08)":
            gpio_pin = 8
        elif gpio_pin == "GPIO 15 (10)":
            gpio_pin = 10
        elif gpio_pin == "GPIO 18 (12)":
            gpio_pin = 12
        elif gpio_pin == "GPIO 17 (11)":
            gpio_pin = 11
        elif gpio_pin == "GPIO 27 (13)":
            gpio_pin = 13
        elif gpio_pin == "GPIO 22 (15)":
            gpio_pin = 15
        elif gpio_pin == "GPIO 23 (16)":
            gpio_pin = 16
        elif gpio_pin == "GPIO 24 (18)":
            gpio_pin = 18
        elif gpio_pin == "GPIO 10 (19)":
            gpio_pin = 19
        elif gpio_pin == "GPIO 09 (21)":
            gpio_pin = 21
        elif gpio_pin == "GPIO 11 (23)":
            gpio_pin = 23
        elif gpio_pin == "GPIO 25 (22)":
            gpio_pin = 22
        elif gpio_pin == "GPIO 08 (24)":
            gpio_pin = 24
        elif gpio_pin == "GPIO 07 (26)":
            gpio_pin = 26
        elif gpio_pin == "GPIO 05 (29)":
            gpio_pin = 29
        elif gpio_pin == "GPIO 06 (31)":
            gpio_pin = 31
        elif gpio_pin == "GPIO 12 (32)":
            gpio_pin = 32
        elif gpio_pin == "GPIO 13 (33)":
            gpio_pin = 33
        elif gpio_pin == "GPIO 19 (35)":
            gpio_pin = 35
        elif gpio_pin == "GPIO 16 (36)":
            gpio_pin = 36
        elif gpio_pin == "GPIO 26 (37)":
            gpio_pin = 37
        elif gpio_pin == "GPIO 20 (38)":
            gpio_pin = 38
        elif gpio_pin == "GPIO 21 (40)":
            gpio_pin = 40
        else:
            gpio_pin = 40

        LED_duration = entry_LED_duration.get()
        if LED_duration.isdigit():
            LED_duration = int(LED_duration)
        else:
            LED_duration = 500

        GPIO.setwarnings(False)
        GPIO.setmode(GPIO.BOARD)
        GPIO.setup(gpio_pin, GPIO.OUT)

        if LED_op == "Switch ON":
            if LED_config == "Source Mode":
                GPIO.output(gpio_pin, GPIO.HIGH)
            elif LED_config == "Sink Mode":
                GPIO.output(gpio_pin, GPIO.LOW)
        elif LED_op == "Switch OFF":
            if LED_config == "Source Mode":
                GPIO.output(gpio_pin, GPIO.LOW)
        elif LED_config == "Sink Mode":
            GPIO.output(gpio_pin, GPIO.HIGH)
        elif LED_op == "Blink":
            GPIO.output(gpio_pin, GPIO.HIGH)
            time.sleep(LED_duration/1000)
            GPIO.output(gpio_pin, GPIO.LOW)
            time.sleep(LED_duration/1000)
        
    def shutdown_LED_signal():
        global thread_LED_driver
        thread_LED_driver.stop()
        GPIO.cleanup()

 

프로젝트 작업

 

LED는 전류가 공급될 때 순방향 바이어스되도록 브레드보드에서 프로토타입됩니다. GUI 앱은 Raspberry Pi와 인터페이스하는 방식에 관계없이 LED를 제어할 수 있습니다.

 

GUI 앱은 LED가 RPi와 인터페이스하는 방법에 대한 사용자의 지시를 받고 앱은 LED를 켜거나 끄거나 깜박이도록 적절하게 IO 핀의 출력을 설정하도록 프로그래밍됩니다.

 

Raspberry Pi의 임베디드 UI에서 LED를 켭니다.

 

이제 마이크로 컨트롤러와 비교하여 단일 보드 컴퓨터를 사용하는 것의 위력을 인식하는 것이 가능합니다. Raspberry Pi를 사용하여 GUI 앱에서 인터페이스하고 구동할 수 있는 방식으로 LED를 제어하고 구동했습니다.

 

Raspberry Pi의 임베디드 UI에서 LED를 끕니다.

 

이러한 유연성은 일반적으로 마이크로컨트롤러 기반 애플리케이션에서 아끼지 않습니다. 마이크로컨트롤러는 이러한 기능을 구현하기 위해 디지털 입력과 결합하는 인터페이스 버튼이 필요합니다. 또한 깜박임 지속 시간을 설정하려면 숫자 키패드를 연결해야 합니다. 선택한 깜박임 지속 시간을 표시하려면 별도의 디스플레이 장치(SSD 또는 LCD)가 필요했으며 여기에는 많은 하드웨어가 포함됩니다.

 

Raspberry Pi를 사용하면 단일 IO 핀을 사용하고 GUI 앱에서 다른 기능을 구현하기만 하면 됩니다.

 

Tkinter/TTK 제한

 

Tkinter/TTK는 정적 GUI만 생성할 수 있습니다. 동적 GUI를 지원했다면 추가 LED를 즉석에서 인터페이스하는 옵션을 추가하고 가변 어레이를 사용하여 동시에 여러 LED를 제어할 수 있었을 것입니다.

 

Try-it-yourself

 

Tkinter/TTK는 동적 인터페이스를 지원하지 않습니다. 그러나 모든 RPi의 GPIO 핀에 대한 옵션이 있는 GUI를 미리 로드할 수 있었습니다. 그런 다음 해당 옵션에 대해 grid() 메서드를 호출하여 GUI 버튼을 사용하여 이러한 옵션을 채울 수 있었습니다(사용자가 새 LED에 대한 드라이버를 추가하라는 메시지를 표시할 때).

 

다음 튜토리얼에서 라즈베리 파이의 디지털 입력을 사용하고 버튼을 인터페이스하여 카운터 응용 프로그램을 만드는 방법을 배울 수 있습니다. 그런 다음 다중화 기술을 사용하여 동일한 응용 프로그램을 Raspberry Pi용 숫자 키패드로 확장합니다.

 

참고

GUI-controlled LED driver

 

 

밤에도 아름답구나

 

 

 

반응형