UMich에서 아주 재미있는 프로젝트가 진행되었다. HiJack이라고 아이폰의 헤드폰 단자에 꼽아 외부 주변기기와 통신을 할 수 있는 디바이스를 만든 것이다.
아이폰의 30핀 독 커넥터에도 시리얼 포트가 있기 때문에 외부 디바이스를 연결할 수 있지만 이 경우 탈옥을 하지 않은 아이폰/터치팟의 경우 애플의 인증칩이 붙어 있어야만 동작하고 관련 자료를 구하는것도 개발자 프로그램에 가입해야만 가능하기 때문에 제약이 많았다.
물론 탈옥을 하고 나면 모든 제한이 사라지고 리눅스에서 시리얼 포트 사용하는것과 동일하게 사용할 수 있게 된다.
하지만 HiJack을 사용하면 탈옥을 하지 않은 순정 아이폰/터치팟에 시리얼포트 뿐 아니고 digital I/O를 제공해주기 때문에 여러가지 외부 디바이스를 손쉽게 아이폰에 인터페이스 할 수 있다.
당연히 지금까지 이런 방식을 사용하지 않았던건 아니다.
이미 위의 사진같이 Square라는 회사에서 아이폰용 신용카드 리더를 만들어 제공하고 있는데 이 역시 기본적으로 동일한 방식을 사용해서 통신을 하는 것이다.
단 차이는 사진의 리더기는 완성된 제품인데 비해 HiJack은 모든 관련자료와 필요한 소스코드까지 완전히 공개되어 있다는 점이다.
그리고 이 회로의 훌륭한 점은 헤드폰 잭은 스테레오라는걸 이용해서 아이폰에서 HiJack으로 데이터 전송은 왼쪽 모노 채널을 통해 보내고 오른쪽 모노 채널로는 지속적으로 22KHz 톤을 보내 HiJack에서는 그 톤은 energy harvesting회로를 통해 자신에게 전원을 공급하는데 사용한다.
약 47%의 변환효율을 보여 7.4mW의 전력을 공급한다.
데이터 전송 속도는 Bell 202 FSK modulation을 사용할 때는 300bps, Manchester encoding된 디지털 신호를 직접 사용하는 경우는 8.82kbps가 된다.
시리얼 포트 뿐 아니고 MSP430의 A/D나 digital I/O신호도 사용할 수 있기 때문에 아래 사진은 HiJack에 EKG모니터를 연결한 경우이다.
ECG Extension for HiJack from Thomas Schmid on Vimeo.
Hijacking Power and Bandwidth from the Mobile Phone's Audio Interface - Integrated Prototype from Thomas Schmid on Vimeo.
Hijacking Power and Bandwidth from the Mobile Phone's Audio Interface from Thomas Schmid on Vimeo.
SoilJack - A Low-Cost Phone Soil Moisture Meter from Thomas Schmid on Vimeo.
회로와 관련 소스는 http://code.google.com/p/hijack-main/ 에서 다운받을 수 있다.
좀 더 자세한 정보와 관련 논문은 프로젝트 홈페이지인 http://eecs.umich.edu/~prabal/projects/hijack/ 를 참조하면 된다.
2011년 2월 26일 토요일
2010년 6월 12일 토요일
아뒤노에서 포트를 직접 억세스 (Direct Port Manipulation in Arduino)
아뒤노에서도 포트 레지스터를 사용하면 마이크로 컨트롤러의 I/O핀을 로우레벨에서 고속으로 조작할 수 있다. 아뒤노 보드(ATmega8과 ATmega168)에 사용된 칩은 3개의 포트를 가지고 있다.
- B (digital pin 8~13)
- C (analog input pins)
- D (digital pin 0~7)
각 포트는 3개의 레지스터로 제어되고 레지스터들은 아뒤노에 변수로 정의되어 있다. DDR 레지스터는 각 핀이 INPUT 또는 OUTPUT인지를 결정한다. PORT레지스터는 핀의 출력값을 HIGH또는 LOW로 제어한다. PIN레지스터는 INPUT모드인 핀의 상태값을 읽는다.
DDR과 PORT레지스터는 읽고 쓰기가 가능하다. PIN레지스터는 입력 상태에 대응하며 읽기 전용이다.
PORTD는 아뒤노의 digital pin 0~7에 매핑된다.
- DDRD - 포트 D 데이터 방향 레지스터 (R/W)
- PORTD - 포트 D 데이터 레지스터 (R/W)
- PIND - 포트 D 입력 핀 레지스터 (R)
PORTB는 아뒤노의 digital pin 8~13에 매핑된다. 상위 2 비트는 crystal핀에 매핑되고 사용할 수 없다.
- DDRB - 포트 B 데이터 방향 레지스터 (R/W)
- PORTB - 포트 B 데이터 레지스터 (R/W)
- PINB - 포트 B 입력 핀 레지스터 (R)
PORTD는 아뒤노의 analog pin 0~5에 매핑된다. 핀 6, 7은 아뒤노 미니에서만 억세스할 수 있다.
- DDRC - 포트 C 데이터 방향 레지스터 (R/W)
- PORTC - 포트 C 데이터 레지스터 (R/W)
- PINC - 포트 C 입력 핀 레지스터 (R)
이 레지스터들의 각 비트는 핀 하나에 대응한다. 아뒤노 핀 번호와 포트/비트간의 전체 매핑은 칩의 다이어그램을 보면 된다. (포트의 일부 비트는 I/O이외의 용도로 사용될 수도 있기 때문에 그에 해당하는 레지스터 비트 값을 바꾸지 않도록 주의해야 한다.)
예제
위의 핀 맵을 참조하면 PortD 레지스터는 아뒤노 디지털 핀 0~7을 제어한다.
하지만 핀 0과 1은 아뒤노 프로그래밍과 디버깅을 위한 시리얼 통신에 사용되기 때문에 시리얼 입/출력 함수를 위해 필요하지 않는 이상 이 핀을 바꾸는건 피하는게 좋다. 이 값을 잘못 바꾸면 프로그램 다운로드나 디버깅을 방해받을 수 있다.
DDRD는 포트 D의 방향 레지스터이다. 레지스터의 각 비트는 포트 D의 핀이 INPUT 또는 OUTPUT으로 설정되도록 제어한다.
DDRD = B11111110; // 아뒤노 핀 1~7은 출력으로, 핀 0는 입력으로 설정
DDRD = DDRD | B11111100; // 더 안전한 방법. Rx/Tx로 사용되는 핀0과 1의 값은 변경하지 않고 핀 2~7을 출력으로 설정
PORTD는 출력 상태를 위한 레지스터이다.
PORTD = B10101000; // 디지털 핀 7,5,3을 HIGH로 만듬
DDRD레지스터 또는 pinMode()를 사용해 저 핀들을 OUTPUT으로 설정해 놓은 경우 핀에서 5V가 출력되는걸 볼 수 있다.
PIND는 입력 레지스터 변수로 모든 디지털 입력 핀의 값을 동시에 읽는다.
포트 조작 팁
#define B0 B00000001
#define B1 B00000010
#define B2 B00000100
#define B3 B00001000
#define B4 B00010000
#define B5 B00100000
#define B6 B01000000
#define B7 B10000000
이렇게 비트를 정의해 놓으면 여러 핀을 동시에 H로 만들 경우는
PORTD |= B7 | B5 | B4; // 디지털 핀 7, 5, 4를 HIGH로
여러 핀을 동시에 L로 만들 경우는
PORTD &= ~(B7 | B5 | B4); // 디지털 핀 7, 5, 4를 LOW로
여러 핀의 값을 토글(현재값이 H면 L로, 현재값이 L이면 H로) 시키는 경우는
PORTD ^= B7 | B5 | B4; // 디지털 핀 7, 5, 4의 값을 토글
Port manipulation을 사용하는 이유
일반적으로 이런식으로 포트를 직접 조작하는건 별로 좋은 생각이 아니다. 그 이유는
- 코드 디버깅 및 관리하기가 더 힘들어지고 다른 사람이 이해하기 힘들다. 마이크로 프로세서가 코드를 실행하는건 몇 마이크로초밖에 안 걸리지만 그 코드가 왜 동작하지 않는가 찾아내고 수정하는데는 몇시간이 걸린다. 시간이 중요하지 않은가? 컴퓨터의 시간은 사용전력으로 따져보면 매우 싸다. 가능한 가장 확실한 방법으로 코드를 작성하는게 좋다.
- 코드의 이식성이 떨어진다. digitalRead()와 digitalWrite()를 사용하면 모든 Atmel 마이크로 컨트롤러에서 실행시키는게 훨씬 쉽지만 컨트롤과 포트 레지스터는 각 마이크로컨트롤러마다 다를 수 있어 코드를 바로 사용하기 힘들수 있다.
- 직접 포트 레지스터를 억세스하다 보면 의도하지 않은 문제를 만들기 쉬워진다. 위의 예제에서 DDRD = B11111110; 에서처럼 핀 0은 입력으로 남겨둬야만 한다. 핀 0는 시리얼 포트의 Rx이다. 하지만 실수로 핀 0을 OUTPUT으로 만들어버리면 갑자기 시리얼포트가 동작하지 않게 만들기 쉽다. 갑자기 시리얼 포트에서 데이터를 받을 수 없으면 매우 혼란스러울 것이다.
그럼에도 불구하고 왜 포트 레지스터를 직접 조작해야 하는가? 포트 레지스터를 직접 억세스하면 다음의 장점이 있다.
- 핀의 출력을 H/L로 매우 빠르게(몇 나노초 이내에) 바꾸는게 필요한 경우가 있다. lib/targets/arduino/wiring.c 소스코드를 보면 digitalRead()와 digitalWrite()는 각각 몇십줄의 코드로 된 걸 볼 수 있는데 이는 컴파일되면 상당한 양의 기계어 명령어가 된다. 각 기계어 명령어는 16MHz 클럭사이클에서 한 클럭사이클이 필요한데 시간에 민감한 어플리케이션에서는 상당한 부담이 된다. 그에 비해 포트를 직접 억세스하면 훨씬 적은 클럭 사이클로 동일한 작업을 할 수 있다.
- 때로는 여러개의 출력핀의 값을 정확히 동시에 바꿔줘야 할 필요가 있다. digitalWrite(10, HIGH); 다음에 digitalWrite(11, HIGH)를 호출하면 먼저 핀 10이 HIGH로 바뀌고 몇 마이크로초 후에 핀 11이 HIGH로 바뀌게 된다. 때로는 외부의 시간에 민감한 디지털 회로의 동작에 혼란을 줄 수 있다. 이 경우 PORTB |= B1100; 을 사용하면 동시에 두 핀을 HIGH로 바꿔줄 수 있다.
- 프로그램 메모리가 부족한 경우 이 방식을 사용하면 코드 크기를 작게 줄일 수 있다. 루프를 사용해 각 핀을 독립적으로 조작하는것에 비해 포트 레지스터를 통해 동시에 여러 하드웨어 핀에 값을 쓰는게 훨씬 작은 명령어를 사용하게 된다. 때로는 이 방식을 사용여부가 프로그램이 플래쉬 메모리에 들어갈 수 있는가 못들어가는가를 결정하는 차이가 될 수도 있다.
Labels:
디지털 입출력,
아두이노,
아뒤노,
아듀이노,
포트 제어,
arduino,
ATmega,
ATMEGA168,
ATMEL,
digital i/o,
digitalRead,
digitalWrite,
direct port manipulation,
Embedded,
Embedded System,
port register
피드 구독하기:
글 (Atom)


