컴퓨터를 끄지 않고 오랫동안 계속 사용하다 보면 예상하지 못하게 먹통이 되는 경우가 발생할 수 있다. 데스크탑이나 노트북의 경우 사용자가 사용을 하고 있으니 문제가 생기면 전원을 껐다가 다시 켜거나 리셋을 시켜줄 수 있는데 라즈베리 파이로 만든 장치를 원격지에 놔 두고 장시간 동작을 시켜야 하는 경우 이런 상황은 매우 치명적이다.
라즈베리 파이에도 내장 워치독 타이머가 있지만 매우 불안정해서 악명이 높다. 그래서 해결책으로 외부에 간단한 워치독 타이머를 붙여주는 방법이 있다.
물론 전원을 모니터링 해 주는 전용 칩도 있지만 여기서는 가장 일반적으로 쉽게 구할 수 있는 555 타이머를 사용한다.
위의 회로에서 Hi Pulse 핀은 라즈베리 파이의 GPIO핀 중에 사용하지 않는 핀들 중에 하나에 연결해 주면 된다. 하지만 문제는 40핀 커넥터에는 칩 리셋핀이 없다.
아래는 라즈베리 파이2의 회로도 일부분이다.
회로도처럼 리셋핀은 별도의 커넥터로 나와 있어 위 회로도에서 P6의 두 핀을 쇼트시키면 라즈베리 파이가 리셋이 된다. 그러므로 위의 회로에서 오른쪽의 RESET 핀을 P6의 1번 핀에 연결시켜 주면 된다.
리셋핀이 나온 커넥터는 PCB에 RUN이라고 써 있어 쉽게 구분할 수 있다. 다만 라즈베리 버젼마다 위치가 다르므로 자신이 가지고 있는 라즈베리 파이 기판을 확인해 보면 된다.
라즈베리 파이 2는 아래 사진과 같이 DISPLAY 커넥터 옆쪽에 있다.
라즈베리 파이 3는 USB 커넥터 옆쪽에 있다. 커넥터에 두 핀 중에 네모난 모양의 패드가 1번 핀이라 여기에 리셋 신호를 연결해주면 된다.
위와 같이 회로를 구성해 주면 소프트웨어는 매우 간단하다. 회로 왼쪽의 Hi Pulse 라고 쓰여진 핀에 몇초에 한번씩 L->H 펄스를 만들어 주면 된다. 만일 약 60초 이내에 펄스가 없으면 555 타이머의 3번 핀이 Low가 된다. 이 핀이 캐패시터를 통해 라즈베리 파이의 리셋 핀에 연결되어 라즈베리 파이를 리셋시키게 된다.
만일 60초의 시간을 줄이고 싶으면 R1(1 Mohm)의 값을 줄여주거나, 또는 C1(100 uF)의 값을 줄여주면 된다.
LED는 H 펄스가 공급될 때 마다 깜빡여서 사용자에게 모든것이 정상이라는걸 눈으로 확인할 수 있게 해 준다.
리셋될 때 까지 걸리는 시간을 60초 이상으로 설정한 이유는 라즈베리 파이가 부팅하는데 약 30초 이상 걸리기 때문이다.
2016년 7월 27일 수요일
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
}
...
Labels:
/dev/mem,
라즈베리 파이,
라즈베리 파이 GPIO,
라즈베리 파이2,
GPIO,
GPIO 고속화,
GPIO 레지스터,
mmap,
Raspberry Pi,
Raspberry Pi 2,
Raspberry Pi GPIO,
sysfs
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 권한이 필요하지 않다.
* 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번 핀의 저항을 풀업, 풀다운, 둘 다 사용하지 않는걸로 설정한다.
Labels:
BCM_GPIO,
GPIO,
gpio utility,
Raspberry Pi,
Raspberry Pi GPIO,
wiringPi
피드 구독하기:
글 (Atom)