Pin Class
Pin class란?
Pin Class란 machine 모듈에서 제공하는 클래스이며, GPIO의 핀을 제어할 수 있다.
Pin의 파라미터
Pin class의 파라미터는 다음과 같이 이루어져 있다고 볼 수 있다.
class machine.Pin(id, mode=-1, pull=-1, *, value=None, drive=0, alt=-1)
id
id는 GPIO의 핀을 입력 받는 것이다.
위에서 봤다시피 다른 파라미터는 값이 이미 지정되어 있다. 핀 번호만 Pin class에게 넘겨주어도 상관 없다.
대신, 아래에서 배울 mode, pull, value, drive, alt의 의미를 알아야만 할 것이다.
mode
mode는 지정해줬던 핀을 어떤 방식으로 사용할 것인지 정하는 것이다.
우선 -1은 아무 모드도 지정하지 않겠다는 뜻이다.
이제 본격적으로 모드들에 대해서 나열하고 설명하겠다.
Pin.IN과 Pin.OUT은 입력과 출력이다. 입력은 보통 외부의 값을 읽어올 때 사용하고, 출력은 LED같은 불빛을 출력할 때 사용한다.
Pin.OPEN_DRAIN은 핀을 오픈 드레인으로 설정하는 것이다.
오픈 드레인을 이해하기 위해서는 먼저 푸시풀을 이해해야 한다.
푸시풀이란?
푸시풀이란 마이크로프로세서가 직접 HIGH와 LOW를 제어할 수 있는 방식이다.
푸시풀 회로는 다음과 같다.
3.3V
|
위 트렌지스터
|
신호선
|
아래 트렌지스터
|
GND
Pin.value()를 이용하여 트렌지스터를 마이크로컨트롤러로 제어할 수 있다.
만약 Pin.value(1)이라면 위 트렌지스터가 닫히고, 아래 트렌지스터가 열리면서 3.3V-위 트렌지스터-신호선만 연결되며, 신호선에서는 3.3V 전압이 걸리게 된다. 이는 신호선이 HIGH로 인식한다.
오픈 드레인이란?
오픈 드레인은 마이크로프로세서가 LOW로만 회로를 제어하는 방식이다. HIGH라는 개념이 없다. 그럼 의문이 들 것이다. HIGH를 어떻게 표현할 것인가? 우선 아래의 회로도를 먼저 보자.
3.3V
|
풀업저항
|
신호선
|
아래 트렌지스터
|
GND
푸시풀 방식과는 다르게 오픈 드레인은 위 트렌지스터가 없다. 대신 풀업 저항이라는 것이 붙어있다. 풀업저항은 대개 높은 저항을 가져, 위 트렌지스터가 없는 회로도에서 전류가 흘렀을 때, 회로가 고장나는 것을 막아주는 역할을 한다.
Pin.value(1)은 아래 트렌지스터를 열어 신호선과 아래 트렌지스터 사이의 통로를 끊는다. 그 때, 해당 선에는 풀업저항으로 높은 저항을 가지게 되어 높은 전압이 흐르게 된다. 이는 전압 분배 공식에 근거하여, 약 3.3V가 신호선에 흐르게 되고 HIGH로 인식하게 되는 것이다.
참고) 전압 분배 공식
\[V_{\text{signal}} = 3.3\text{V} \times \frac{R_{\text{아래}}}{R_{\text{위}} + R_{\text{아래}}}\]Pin.value(0)을 하게 되면 아래 트렌지스터를 닫아 통로를 연결시키고, 아래 트렌지스터의 매우 약한 저항으로 인해 전압도 거의 0V로 작아지게 되고 신호선은 LOW로 인식할 수 있는 것이다.
그럼 왜 오픈 드레인을 사용하는가?
이유는 간단하다. 2개 이상의 회로가 하나의 선을 공유할 때 문제가 발생한다.
푸시풀 방식을 사용할 경우 여러 개의 회로가 여러 개의 신호를 만들어 낼 수 있으므로 충돌이 발생한다.
MCU A MCU B
3.3V_A 3.3V_B
│ │
[A 위 스위치] [B 위 스위치]
│ │
└───────────┐ ┌───────────────┘
│ │
●──────●
공통 신호선
│ │
┌───────────┘ └───────────────┐
│ │
[A 아래 스위치] [B 아래 스위치]
│ │
GND_A GND_B
└──────────── 공통 GND ────────────┘
* MCU = 마이크로컨트롤러
위 상황일 때 발생할 수 있는 경우의 수는 두 가지이다.
첫 번째, A와 B가 모두 HOW이거나 LOW인 경우.
이 때, 신호선에서는 하나의 신호만 처리하기 때문에 문제가 없어 보이지만 실제로 내부 전압은 다를 수 있다. 예를 들어, A는 3.28V일 수 있고 B는 3.27V일 수 있다. 전류는 높은 곳에서 낮은 곳으로 흐르므로, 예기치 않은 전류 순환으로 인해 버그가 발생할 수 있는 것이다. 푸시풀 방식에서 2개 이상의 회로를 하나의 선으로 연결하는 방식은 권고하지 않는 이유가 바로 이 것이다.
두 번째, A와 B의 신호가 다를 경우.
만약, A의 위 스위치가 닫히고, B의 아래 스위치가 닫히게 되면 A는 3.3V의 전압을 걸게 되고, B는 GND가 열리게 되면서 다음과 같은 상황이 발생한다.
A의 3.3V - 공통 신호선 - B의 GND
이 때, 풀업 저항처럼 큰 저항을 따로 두지 않는다면 트렌지스터의 저항만 존재할 것이고 신호선에는 3.3V, 0V도 아닌 애매한 중간의 저항값이 흐르게 될 것이다. 소프트웨어에서는 이를 어떤 신호값으로 판단해야할지 어려워지고 애초에 충돌이 발생하는 것이기 때문에 정상적인 상황이 아니다. 또한 큰 전류가 흐를 수 있기 때문에 회로도가 망가질 수 있다. 풀업 저항같은 큰 저항을 설치한다고 하더라도 애초의 충돌을 해결할 수 있는 방안은 아니다.
그래서 오픈 드레인을 사용하는 것이다.
오픈 드레인 회로도는 다음과 같다.
공통 3.3V
│
4.7kΩ(풀업 저항)
│
●──────────── Vsignal
│
┌────────────────┴────────────────┐
│ │
MCU A 내부 MCU B 내부
│ │
[A LOW 스위치] [B LOW 스위치]
[아래 트랜지스터] [아래 트랜지스터]
│ │
└────────────────┬────────────────┘
│
공통 GND
아까의 두 가지 경우의 수를 다시 봐보자.
첫 번째, A와 B의 신호가 같을 경우. A와 B가 모두 LOW 스위치가 열려 있거나 닫혀 있다면 풀업 저항으로 인해 Vsignal은 HIGH 또는 LOW로 찍힐 것이다. 3.3V가 하나이므로 저항 차이로 인해 전류 순환이 발생하지 않으니 문제가 발생하지 않는다.
두 번째, A와 B의 신호가 다를 경우.
A의 LOW 스위치가 열려 있고, B의 LOW 스위치가 닫혀 있는 상황을 가정해보자. 이는 A가 Pin.value(1)이고 B가 Pin.value(0)임을 의미한다.
이 때, 조금 단순하게 보면 공통 3.3V - 풀업 저항 - Vsignal - A = B - B의 GND 로 연결이 되어 있기 때문에 충돌이 되지 않고 Vsignal에는 전압 분배 법칙으로 인해 거의 0V에 가까운 전압이 걸리고 LOW값이 찍힐 것이다.
결론적으로 말하면 AND회로와 같다고 보면 된다.
이를 위해 오픈 드레인 모드를 사용하는 것이고, 풀업 저항을 따로 설계하여 부착해야 한다.
Pin.ALT는 GPIO의 핀을 마이크로컨트롤러가 직접 제어하는 것이 아니라 칩 내부의 UART, I^2C, SPI, PWM 같은 전용 하드웨어에 맡기는 모드이다.
하드웨어를 설계하다 보면 하나의 핀을 하나의 마이크로컨트롤러가 제어하는 것은 불가능에 가깝다. 여러 전용 하드웨어를 번갈아가며 사용하게 되는데 이를 가능케 하는 모드이다.
이 기능을 동시에 수행하는 것이 아니라 MUX같은 컨트롤러가 해당 하드웨어를 선택한다.
┌─ 일반 GPIO 제어 회로
├─ UART1 TX 회로
피코 내부 회로 ── 선택기 ┼─ I2C0 SDA 회로 ─── GP4 핀 ─── 외부 장치
├─ SPI0 RX 회로
└─ PWM2 A 회로
Pin.ALT_OPEN_DRAIN이란 Pin.ALT와 Pin.OPEN_DRAIN을 합쳐 놓은 모드이다.
Pin.ANALOG는 핀을 아날로그 입력용으로 설정하는 모드이다.
ADC 객체가 바로 아날로그를 사용하는 예시이다. 디지털처럼 1과 0으로 표현되지 않고 연속적인 숫자들로 표현된다.
pull
pull이란 입력 상태일 때, 핀에 아무 것도 연결되지 않았을 때 대신 값을 확정해주는 역할을 한다.
자세하게 설명해보면, 다음과 같이 설정했다고 가정해보자.
button = Pin(14, Pin.IN, None)
이후 핀에 아무 것도 연결하지 않았다고 가정했을 때, GP14에 전압을 결정해줄 대상이 없다. 이런 상태를 플로팅이라고 한다.
주변 전선, 사람의 손 등으로 인해 프로그램에서 0과 1이 불규칙하게 나올 수 있다.
이를 해결하기 위해서는 pull 파라미터를 사용해야 한다.
Pin.PULL_UP
Pin.PULL_UP이란 내부 풀업 저항을 위쪽으로 설정하는 것이다.
이렇게 되면, 아래와 같이 GP14는 3.3V로 끌려가게 된다. 코드는 다음과 같다고 가정해보자.
button = Pin(14, Pin.IN, Pin.PULL_UP)
3.3V
│
[풀업 저항]
│
GP14
│
버튼 열림
│
GND
결국 버튼을 누르지 않았을 때는 HIGH값이 찍히게 된다. 이 때, 버튼을 누르게 되면 열렸던 버튼이 닫히게 되면서 아래의 낮은 저항으로 인해 GP14에는 LOW가 찍히게 된다.
PULL_DOWN도 마찬가지 원리로 생각하면 될 것이다.
pull=None은 무엇인가?
이는 풀업도 아니고 풀다운도 아니라는 뜻이다. 즉, 외부에서 확실하게 신호를 읽게 해주는 것을 보장해준다는 뜻이다. 예를 들면, 센서를 생각해보면 실시간으로 계속 센서 값을 보내기 때문에 무조건 신호를 읽게 되는 상황을 보장할 수 있다. 그러므로 센서 사용 시, None을 많이 사용한다.
pull=-1은 무엇인가?
-1은 기존의 pull 설정 값을 변경하지 않겠다는 뜻이다. 먼저 GP14에 다음과 같은 설정값을 지정한다.
pin = Pin(14, Pin.IN, Pin.PULL_UP)
그 후 init()으로 다시 설정값을 초기화할 때 -1을 주면 기존의 설정값을 유지한다.
pin = Pin(14, Pin.IN, Pin.PULL_UP)
결과는 다음과 같다.
GP14: 입력 모드
pull: 여전히 PULL_UP
*
*은 Python의 기본 문법이고 * 이후에 나오는 파라미터를 사용 시, 무조건 파라미터명=값을 준수하라는 뜻이다.
value
value 파라미터는 아까 입력 상태일 때 pull 값을 지정하여 입력을 받지 아니할 때 값을 정해주는 방식이 있었는데, value는 출력 상태일 때 초기 값을 정해주는 것이다.
다음과 같은 상황일 때 필요할 수 있다.
led = Pin(15, Pin.OUT)
이렇게 명시하면 None으로 하드코딩 된 값이 value에 들어가게 된다. 미리 말하자면, None은 버퍼에 저장되어 있는 value 값을 가져온다. 즉, 프로그래머가 원하는 초기의 값을 설정해주고 싶다면 value값을 꼭 원하는 값으로 설정해주어야 한다.
value=None 일 때
앞서 말했듯이, None으로 설정하게 되면 버퍼에 저장되어 있던 값을 가져오게 된다.
처음 실행시키거나 RESET 되게 되면 제조사가 정해놓은 리셋 기본값을 가져오게 된다. RP2040 기준 0이 버퍼에 설정될 것이다.
그게 아니라면 버퍼에 특정 값이 남아있을 것이고 해당 값이 출력될 것이다.
value가 1이거나 0으로 설정하면 HIGH 또는 LOW로 출력될 것이다. 이는 어렵지 않다.
drive
drive는 핀의 출력 구동 강도를 지정한다. 약한 구동 강도가 있고 강한 구동 강도가 있다.
약한 구동 강도는 내부 저항이 높아 원하는 전압을 올리거나 내릴 때 시간이 걸린다. 하지만 강한 구동 강도는 내부 저항이 낮아 원하는 전압을 올리거나 내릴 때 빠르게 처리할 수 있다.
RP2040 기준 구동 강도는 다음과 같다.
| RP2040의 DRIVE 값 | 구동 강도 |
| ————–: | —-: |
| 0 (00) | 2mA |
| 1 (01) | 4mA |
| 2 (10) | 8mA |
| 3 (11) | 12mA |
오해하지 말아야 할 것은, 고정 출력 전류를 설정하는 것이 아니라 해당 전류를 강하게 흘려 보내 순간적인 힘을 얻도록 하는 것이다.
보통 구동 강도가 세게 필요한 경우는 많은 입력 핀을 사용했을 때나, 출력 전압 부하, 신호 전환이 너무 느릴 때를 생각해볼 수 있다.
alt
alt는 아까 mode에서 이야기했던 Pin.ALT의 설정값이다. 아무것도 지정하지 않으면 -1 값으로 설정된다. -1은 유추할 수 있듯이 어떤 ALT 하드웨어도 설정하지 않겠다는 뜻이다.
이어서 설명해보자면, 아까 mode에서 이야기했듯이 ALT는 전용 하드웨어가 해당 GPIO를 제어하도록 하는 것이다.
┌─ 일반 GPIO 제어 회로
├─ UART1 TX 회로
피코 내부 회로 ── 선택기 ┼─ I2C0 SDA 회로 ─── GP4 핀 ─── 외부 장치
├─ SPI0 RX 회로
└─ PWM2 A 회로
여기서 많은 회로 중 선택을 하도록 하는 것이고 조금 더 자세하게 적으면 다음과 같다.
┌─ 일반 GPIO 제어 회로
├─ UART 회로
├─ I2C 회로
RP2040 내부 ─ 선택기 ┼─ SPI 회로 ───── 외부 GPIO 핀
├─ PWM 회로
├─ PIO0 회로
└─ PIO1 회로
마이크로파이썬에서 사용할 수 있는 ALT 상수를 다음과 같이 정리할 수 있으며, 해당 값을 alt파라미터에 입력하면 된다.
Pin.ALT_SPI
Pin.ALT_UART
Pin.ALT_I2C
Pin.ALT_PWM
Pin.ALT_SIO
Pin.ALT_PIO0
Pin.ALT_PIO1
Pin.ALT_GPCK
Pin.ALT_USB