레이블이 GPIO인 게시물을 표시합니다. 모든 게시물 표시
레이블이 GPIO인 게시물을 표시합니다. 모든 게시물 표시

2018년 11월 7일 수요일

ESP32 GPIO 레퍼런스

ESP32에는 총 48개의 핀이 있는데 각 핀들은 여러가지 기능을 가지고 있다. 물론 ESP32 개발보드에서 모든 핀을 다 사용할 수 있는것은 아니다. 그래서 여기서는 어떤 핀을 사용해야 하고 어떤 핀을 사용하지 않아야 하는지에 대해 설명하겠다.

ESP32는 다음과 같은 peripheral을 가지고 있다.

* 18 ADC(Analog-to-Digital Converter) channel
* 3 SPI
* 3 UART
* 2 I2C
* 16 PWM
* 2 DAC (Digital-to-Analog Converter)
* 2 I2S
* 10 Capacitive sensing GPIO

ADC와 DAC 기능은 특정 핀에 고정되어 있다. 하지만 UART, I2C, SPI, PWM등의 기능은 어느 핀에 사용할지 결정해서 코드에서 지정해 줘야 한다.

소프트웨어에서 핀의 속성을 정의해 줄 수 있지만, 각 핀들은 디폴트로 지정되어 있는 기능들이 있다. ESP32 DEVKIT V1-DOIT의 경우 디폴트 핀 배열은 다음과 같다.




또한 특정한 기능의 핀들은 프로젝트마다 사용하기에 적합할 수도 그렇지 않을 수도 있다.

아래 그림은 ESP-WROOM-32의 핀 배치이다. 만일 칩만 가지고 자작 보드를 만든다면 아래 그림을 레퍼런스로 삼을 수 있다.



Input Only Pins

GPIO 34부터 39까지는 입력 전용 핀이다. 그리고 이 핀들은 내부적으로 풀업/풀다운 저항이 없다. 출력으로 사용할 수 없으므로 입력으로만 사용해야 한다.

* GPIO 34
* GPIO 35
* GPIO 36
* GPIO 37
* GPIO 38
* GPIO 39

ESP-WROOM-32에 들어 있는 SPI Flash

일부 보드들은 GPIO 6부터 11까지의 핀도 사용할 수 있게 되어 있다. 하지만 이 핀들은 ESP-WROOM-32 칩에 내장된 SPI Flash에 연결되어 있기 때문에 다른 용도로 사용하는걸 권장하지 않는다.

* GPIO 6 (SCK/CLK)
* GPIO 7 (SDO/SD0)
* GPIO 8 (SDI/SD1)
* GPIO 9 (SHD/SD2)
* GPIO 10 (SWP/SD3)
* GPIO 11 (CSC/CMD)

Capacitive Touch GPIO (정전식 터치 입력)

ESP32는 10개의 정전식 터치 입력 센서를 가지고 있다. 이 센서는 사람의 피부 같이 전하를 가진 것들의 전하 변화를 측정할 수 있다. 그러므로 손가락으로 GPIO를 터치할 때 유도되는 변하를 감지할 수 있다. 이 핀들은 전하 패드에 연결해 기계식 버튼을 대치할 수 있다. 정전식 터치 핀은 ESP32를 deep sleep에서 깨어나게 하는데 사용할 수도 있다.

이 내부 터치 센서는 다음의 GPIO에 연결되어 있다.

* T0 (GPIO 4)
* T1 (GPIO 0)
* T2 (GPIO 2)
* T3 (GPIO 15)
* T4 (GPIO 13)
* T5 (GPIO 12)
* T6 (GPIO 14)
* T7 (GPIO 27)
* T8 (GPIO 33)
* T9 (GPIO 32)

ADC (Analog-to-Digital Converter)

 ESP32는 18개의 12-bit ADC 채널을 가지고 있다. (ESP8266의 경우는 1개의 10-bit ADC만 가지고 있음) 다음의 GPIO 는 각각 해당 ADC 채널로 사용될 수 있다.

 * ADC1_CH0 (GPIO 36)
 * ADC1_CH1 (GPIO 37)
 * ADC1_CH2 (GPIO 38)
 * ADC1_CH3 (GPIO 39)
 * ADC1_CH4 (GPIO 32)
 * ADC1_CH5 (GPIO 33)
 * ADC1_CH6 (GPIO 34)
 * ADC1_CH7 (GPIO 35)
 * ADC2_CH0 (GPIO 4)
 * ADC2_CH1 (GPIO 0)
 * ADC2_CH2 (GPIO 2)
 * ADC2_CH3 (GPIO 15)
 * ADC2_CH4 (GPIO 13)
 * ADC2_CH5 (GPIO 12)
 * ADC2_CH6 (GPIO 14)
 * ADC2_CH7 (GPIO 27)
 * ADC2_CH8 (GPIO 25)
 * ADC2_CH9 (GPIO 26)

ADC 입력 채널은 12-bit 해살도를 가지고 있다. 즉 아날로그 값을 읽으면 0~4095 사이의 값을 얻을 수 있다. 여기서 0은 0V, 4095는 3.3V를 의미한다. 또한 코드에서 각 채널의 해설도나 ADC 범위를 조정할 수도 있다.

ESP32 ADC 핀은 리니어 한 속성을 가지고 있지 않으므로 주의해야 한다. 0V와 0.1V, 또는 3.2V와 3.3V를 거의 구별하지 못할 것이다. 그러므로 ADC를 사용할 때 이 점을 염두에 두어야만 한다. 아래 그래프와 같은 속성을 가진다.


DAC (Digital-to-Analog Converter)

ESP32에는 2개의 8-bit DAC 채널이 있어 디지털 신호를 아날로그 전압으로 바꿔 출력해 준다.

* DAC1 (GPIO 25)
* DAC2 (GPIO 26)

RTC GPIO

ESP32는 RTC GPIO를 지원한다. ESP32가 deep sleep 모드에 있을 때 RTC의 low-power subsystem에 연결된 GPIO를 사용할 수 있다. 이 RTC GPIO는 ULP(Ultra Low Power) 코프로세서가 실행중일 때 ESP32를 deep sleep에서 깨어나게 하는데 사용된다. 다음의 GPIO들이 외부 wake up 소스로 사용될 수 있다.

* RTC_GPIO0 (GPIO 36)
* RTC_GPIO3 (GPIO 39)
* RTC_GPIO4 (GPIO 34)
* RTC_GPIO5 (GPIO 35)
* RTC_GPIO6 (GPIO 25)
* RTC_GPIO7 (GPIO 26)
* RTC_GPIO8 (GPIO 33)
* RTC_GPIO9 (GPIO 32)
* RTC_GPIO10 (GPIO 4)
* RTC_GPIO11 (GPIO 0)
* RTC_GPIO12 (GPIO 2)
* RTC_GPIO13 (GPIO 15)
* RTC_GPIO14 (GPIO 13)
* RTC_GPIO15 (GPIO 12)
* RTC_GPIO16 (GPIO 14)
* RTC_GPIO17 (GPIO 27)

PWM (Pulse Width Modulation)

ESP32 LED PWM controller는 16개의 독립적인 채널을 가지고 있어 각각 다른 속성의 PWM 신호를 만들어 내도록 설정할 수 있다. 출력으로 사용할 수 있는 모든 핀은 PWM 으로 사용할 수 있다. (즉 GPIO 34~39는 PWM을 만들 수 없다)

PWM 신호를 만들어주려면 코드에서 다음의 파라미터를 정해줘야 한다.

* Signal Frequency
* Duty Cycle
* PWM Channel
* 신호를 출력할 GPIO핀

I2C

아두이노 IDE에서 ESP32를 사용할 때는 다음의 디폴트 핀을 사용해야만 한다.

* GPIO 21 (SDA)
* GPIO 22 (SCL)

SPI

디폴트로 SPI핀은 다음과 같이 매핑되어 있다.

Interrupt

모든 GPIO 핀은 인터럽트로 설정할 수 있다.

Enable (EN)

Enable (EN)은 3.3V 레귤레이터의 enable 핀이다. 이 핀은 풀업이 되어 있으므로, GND에 연결하면 3.3V 레귤레이터가 비활성화 된다. 즉 이 핀을 푸쉬버튼에 연결해 놓으면 ESP32를 재시동하는데 사용할 수 있다.

GPIO current draw

ESP32 데이터쉬트에 있는 "권장하는 동작 조건(Recommended Operating Condition)"에 따르면 GPIO마다 최대 40mA의 전류를 흘릴 수 있다.























2015년 9월 9일 수요일

라즈베리 파이 GPIO를 고속으로 제어하기



라즈베리 파이에서 GPIO 핀을 제어하는 가장 쉬운 방법은 sysfs를 사용하는 것이다.

쉘에서라면 다음과 같은 식으로 GPIO 핀을 제어할 수 있다.

$ echo "4" > /sys/class/gpio/export
$ echo "out" > /sys/class/gpio/gpio4/direction
 
# Set up GPIO 7 and set to input
$ echo "7" > /sys/class/gpio/export
$ echo "in" > /sys/class/gpio/gpio7/direction
 
# Write output
$ echo "1" > /sys/class/gpio/gpio4/value
 
# Read from input
$ cat /sys/class/gpio/gpio7/value 
 
# Clean up
$ echo "4" > /sys/class/gpio/unexport
$ echo "7" > /sys/class/gpio/unexport
 
프로그램 상에서라면 '/sys/class/gpio/' 디렉토리 내의 파일들을 open한 후 read/write로 GPIO를 제어할 수 있다.

이렇게 sysfs를 사용하면 매 비트를 조작할 때 마다 오버헤드가 커서 라즈베리 파이2에서 단순히 GPIO 핀을 H/L로 토글하는데도 6.8KHz로밖에 동작할 수 없다.

하지만 라즈베리 파이2에 들어있는 SoC인 BCM2836의 GPIO레지스터를 직접 조작하면 무려 50MHz의 속도로 GPIO핀을 토글할 수 있게 된다. (약 7350배의 속도 향상) 게다가 sysfs를 사용하는 경우는 한번에 한 비트씩만 조작이 가능하지만 GPIO 레지스터를 조작하면 한번에 여러 비트의 GPIO 조작이 가능하기 때문에 더욱 더 고속 제어가 가능해진다.

BCM2836의 경우 I/O제어 레지스터는 0x3f20 0000 ~ 0x3f20 00b0의 메모리 공간에 매핑되어 있다.

리눅스에서 프로세스는 하드웨어나 물리 주소에 직접 접근이 불가능하다. 그러므로 이 문제를 해결하기 위해 사용되는 것이 '/dev/mem' 디바이스이다. /dev/mem은 메모리 공간에 해당하는 가상 파일이다. 그러므로 이 파일을 오픈해서 파일에 read/write하면 파일이 매핑되어 있는 메모리 공간에 값을 읽고 쓸 수 있게 되는 것이다.

그런데 파일에 값을 read/write 하는건 귀찮기 때문에 등장하는 것이 mmap 함수이다. mmap은 파일의 일부를 메모리처럼 억세스 할 수 있게 해 준다. C에서 말하자면 파일의 특정 장소를 포인터로 지시해 그 포인터를 통해 직접 읽고 쓰기가 가능해진다.

아래 코드가 라즈베리 파이2에서 GPIO 레지스터에 대한 포인터를 얻어오는 함수이다.

unsigned int *get_base_addr()
{
  int fd=open("/dev/mem/", O_RDWR | O_SYNC);
  if (fd<0) {
    printf("can not open /dev/mem\n"); exit(-1);
  }
  #define PAGE_SIZE (4096)
  void *mmaped = mmap(NULL,
                      PAGE_SIZE,
                      PROT_READ | PROT_WRITE,
                      MAP_SHARED,
                      fd,
                      0x3f200000);
  if (mmaped<0) {
    printf("mmap failed\n"); exit(-1);
  }
  close(fd);
  return (unsigned int *)mmaped;
}

 
주의) 라즈베리 파이2는 BCM2836 SoC를 사용하는데 여기서는 GPIO 레지스터가 0x3f20 0000번지에 
매핑되어 있지만 라즈베리 파이1의 BCM2835는 0x2020 0000번지에 매핑되어 있다.
즉 라즈베리 파이1에서 사용하려면 위의 함수에서 0x3f200000을 0x20200000으로 변경 해 줘야만 한다.
 
포인터를 얻었으면 사용하고자 하는 GPIO 핀을 입력 또는 출력으로 사용할 지 설정해 줘야 한다. 
각 I/O 포트의 설정은 3비트 값을 사용한다. 32비트 레지스터에 각각 10개 I/O핀을 설정한다.
 
예를 들어 GPIO0의 설정 레지스터는 0x3f20 0000 번지의 32비트 값 중 비트 2~0, GPIO11의 
설정 레지스터는 0x3f20 0004 번지의 32비트 값 중 비트 5~3이 된다.
공식화 하면 GPIO p의 설정 레지스터는 0x3f200000+(p/10) 번지의 비트 (p%10)*3+2~(p%10)*3가 된다.
 
아래 코드는 포트 설정을 위한 함수이다. 
 
void gpio_mode(unsigned int *addr, int port, int mode)
{
  if (0<port || port >31) {
    printf("port out of range: %d\n", port);
    exit(-1);
  }
  unsigned int *a = addr + (port/10);
  unsigned int mask = ~(0x7 << ((port%10) * 3));
  *a &= mask;
  *a |= (mode & 0x7) << ((port%10) * 3);
}  
 
포트 설정이 되었으면 이제 포트 값을 H/L로 변경할 수 있다. 
여기서는 GPIO핀을 H로 만들때 사용하는 레지스터와 L로 만들때 사용하는 레지스터가 따로 있다. 
즉 H로 만들 때 사용하는 레지스터에 '1'을 써 넣은 포트만 값이 H로 바뀌고 나머지 포트는 값이 
그대로 유지된다.
마찬가지로 L로 만들 때 사용하는 레지스터에 '1'을 써 넣은 포트만 값이 L가 되고 나머지 비트는 
값에 변화가 없게 된다.
예를 들어 GPIO0의 값을 H로 하고 싶으면 0x3f20 001c 번지에 0x0000 0001을 써 주면 GPIO0만 
H가 되고 나머지 GPIO는 값의 변화가 없다.
동일하게 GPIO1의 값을 L로 하고 싶으면 0x3f20 0028 번지에 0x0000 0002를 써 주면 된다. 
이렇게 레지스터를 이용하면 한번에 여러 비트를 H로 만들거나, 여러 비트를 L로 만들어 줄 수 있다.
 
void gpio_set(unsigned int *addr, int port)
{
  if (0<port || port>31) {
    printf("set: port out of range: %d\n", port);
    exit(-1);
  }
  *(addr+7) = 0x1 << port;
}

void gpio_clear(unsigned int *addr, int port)
{
  if (0<port || port>31) {
    printf("clear: port out of range: %d\n", port);
    exit(-1);
  }
  *(addr+10) = 0x1 << port;
} 
 

위의 함수들을 사용해 GPIO0를 제어하는 코드의 틀은 다음과 같다.
 

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/mmap.h>
#include <unistd.h>

#define GPIO_IN     0
#define GPIO_OUT    1

unsigned int *get_base_addr();
void gpio_mode(unsigned int *, int, int);
void gpio_set(unsigned int *, int);
void gpio_clear(unsigned int *, int);

int main(int argc, char **argv)
{ 
  volatile unsigned int *addr = get_base_addr();
 
  ... 
  gpio_mode(addr, 0, GPIO_OUT);
  ...
  ...
  gpio_set(addr, 0);
  ... 
  gpio_clear(addr, 0);
  ...
}
 

참고로 라즈베리 파이2에서 모든 함수 호출이나 딜레이 없이 가장 빨리 GPIO 포트를 토글하는 경우 
최대 50MHz로 스위칭이 가능하다.
 
  ...
  for (;;) {
    *(0x3f200000+7) = 1<<0;   // GPIO 0 set
    *(0x3f200000+10) = 1<<0;  // GPIO 0 clear
  } 
  ...
 

2015년 4월 2일 목요일

Raspberry Pi gpio 유틸리티

WiringPi는 gpio라는 이름의 유틸리티를 포함하고 있다. 이 프로그램은 GPIO 핀을 조작하기 위한 스크립트에도 사용될 수 있다. 심지어 쉘 스크립트로 gpio 명령을 사용해 전체 프로그램을 작성할 수도 있다. 물론 이 방법은 효율적이지 않아 크게 권장하는 방법은 아니다. 또한 C/C++에서 system() 함수를 사용해 gpio 프로그램을 호출할 수도 있다.

* gpio 명령은 setuid 프로그램으로 설치되도록 만들어져 있기 때문에 일반 사용자가 sudo 명령 없이 (또는 root 권한 없이) 실행할 수 있다.

gpio 프로그램을 사용하면 GPIO 핀을 제어하는것 뿐 아니고 다음의 기능들을 수행할 수 있다.

* /sys/class/gpio 인터페이스를 통한 핀들의 export/unexport
* /sys/class/gpio 인터페이스를 통해 edge-trigger 인터럽트를 활성화
* SPI, I2C 모듈을 로드하여 read/write
* SPI 버퍼 사이즈나 I2C 속도를 설정
* Gertboard DAC로 값 출력
* Gertboard ADC 값 입력
* Raspberry Pi 보드 하드웨어 리비젼 확인

기본 사용법

$ gpio -v

버젼을 출력한다

$ gpio -g ...

-g 플래그를 사용하면 핀 번호를 wiringPi의 핀 번호 대신 BCM_GPIO 핀 번호로 인식하게 함

표준 입/출력 명령

$ gpio mode <pin> in/out/pwm/up/down/tri

핀의 모드를 설정한다. 설정 가능한 모드는 입력(in), 출력(out), PWM(pwm)이고 추가로 내부 저항의 풀업(up), 풀다운(down), Tri-state(tri) 설정이 가능하다.

$ gpio write <pin> 0/1

핀의 출력 레벨을 HIGH(1) 또는 LOW(0)으로 설정한다.

$ gpio read <pin>

핀의 레벨을 읽어 화면에 출력해준다. LOW면 '0', HIGH면 '1'이 출력된다.

$ gpio readall

정상적으로 억세스 가능한 모든 핀들을 읽어 테이블 형태로 핀 번호(wiringPi 번호 및 BCM_GPIO)와 모드, 현 입력값을 화면에 출력해 준다.

모듈 로드 명령

$ gpio load spi [buffer size in KB]

SPI 커널 모듈을 로드하고 옵션으로 내부 버퍼 사이즈를 KB 단위로 설정한다. 디폴트 사이즈는 4KB이고 거의 대부분의 경우 충분한 크기이다.
/dev/spi* 엔트리는 gpio 프로그램을 사용하는 사람이 소유한것으로 설정되기 때문에 다음 프로그램을 실행할 때 root 권한이 필요하지 않다.

$ gpio load i2c [baud rate in Kb/sec]

I2C 커널 모듈을 로드하고 옵션으로 Kb/sec 단위로 통신속도를 지정한다. 디폴트는 100Kb/sec이다.
/dev/i2c* 엔트리는 gpio 프로그램을 사용하는 사람이 소유한것으로 설정되기 때문에 다음 프로그램을 실행할 때 root 권한이 필요하지 않다.

/sys/class/gpio 모드 명령

$ gpio export <pin> in/out

주어진 핀 번호(BCM_GPIO 핀 번호)를 input 또는 output으로 export한다.

$ gpio unexport <pin>

주어진 핀 번호의 export를 제거한다.

$ gpio unexportall

/sys/class/gpio의 모든 export를 제거한다.

$ gpio exports

/sys/class/gpio 인터페이스를 통해 export 된 모든 gpio 핀의 목록과 모드를 화면에 출력한다.

$ gpio edge <pin> rising/falling/both/none

주어진 핀의 인터럽트 트리거를 활성화 시킨다. 

주의) sys 모드 명령의 핀 번호는 항상 BCM_GPIO 핀 번호이다.

예제

$ gpio mode 0 out
$ gpio write 0 1

위의 명령은 wiringPi 핀번호 0번 핀을 출력으로 설정하고 출력값을 HIGH로 설정한다.

$ gpio -g mode 0 in
$ gpio -g read 0

위의 명령은 BCM_GPIO 핀번호 0번을 입력으로 설정하고 핀의 입력값을 읽는다.

Internal pull up/down

GPIO 핀은 내부 풀업/풀다운 저항을 가지고 있어 핀이 입력모드인 경우 소프트웨어로 어떤 저항을 사용할 지 제어할 수 있다.

$ gpio mode 0 up
$ gpio mode 0 down
$ gpio mode 0 tri

위의 명령은 각각 순서대로 WiringPi 0번 핀의 저항을 풀업, 풀다운, 둘 다 사용하지 않는걸로 설정한다.