임베디드월드

글: 라영호 | ratharn@naver.com / 2012-08-08


[연재 차례]

1. 안드로이드 시스템의 역사 및 동향
2. 안드로이드 시스템과 리눅스
3. 안드로이드 플랫폼의 이해
4. 안드로이드 바인더(Binder)의 이해
5. 안드로이드 서비스
6. 안드로이드 SurfaceFlinger와 프레임버퍼 드라이버
7. 안드로이드 User Interface와 ADK2012
8. Linux Sound Device와 안드로이드 사운드 시스템
9. 안드로이드 카메라 시스템
10. 안드로이드 카메라와 멀티미디어 프레임워크
11. 안드로이드 카메라와 멀티미디어 프레임워크 ②
12. 안드로이드 시스템 디버깅 및 기타



안드로이드 시스템 개발자를 위한 안드로이드 시스템의 분석 및 이해 ⑦
안드로이드 User Interface와 ADK2012


8월호에서는 안드로이드에서 사용자 인터페이스를 관리하는 터치 및 입력 드라이버에 관하여 살펴 보도록 하겠다. 지금까지는 안드로이드의 전체적인 운영 방법에 대해 살펴 봤고 이제부터는 각각의 모듈이 실제 어떻게 운영되는지 확인해 보는 시간이다.

사용자가 화면을 터치하거나 버튼을 눌러 안드로이드 시스템에 전달되기까지의 과정을 살펴 보면서 시스템에 대한 전반적인 이해를 하도록 하자. 또한 Google I/O 2012년에 발표된 ADK2012에 대해서 살펴 보도록 하겠다.
ADK2012는 안드로이드 시스템용 부가 장치 개발 킷트이다. 안드로이드 스마트폰에 연결되는 센서 및 장치들을 만들 수 있게 하는 하드웨어 및 소프트웨어이다.

안드로이드 입력 시스템
리눅스 Kernel 2.6이 되면서 모든 입력장치들은 입력 장치(input device)는 이벤트 인터페이스(event interface)로 통합되게 된다. 모든 입력장치는 입력 장치(input device)의 형태로 나타나도록 구성된다.
안드로이드 시스템에서의 입력 장치는, 키보드: /dev/input/event0, 터치 스크린: /dev/in put/event1, 스위치 드라이버: /dev/input/event?는 쿼티 형태의 스마트 폰에서 슬라이드를 감지하기 위해 사용했다.
실제로는 device의 구분이 중요하지는 않고, device의 내용이 중요하다. 즉, /dev/input/event0 하나로 처리해도 된다. 모든 입력장치는 다음과 같은 구조체 형태로 모든 발생 event를 생성한다.

struct input_event {
  struct timeval time;
  __u16 type;
  __u16 code;
  __s32 value;
};

#define EV_SYN 0x00 : event synchronize 특성
#define EV_KEY 0x01 : keyboard/button 입력
#define EV_REL 0x02 : mouse와 같은 상대좌표 특성
#define EV_ABS 0x03 : touch와 같은 절대좌표
#define EV_SW 0x05 : switch event


각 입력 장치(input device)는 여러 개의 속성을 동시에 사용할 수 있음

예> touchscreen: EV_SYN | EV_ABS | EV_KEY
Touch screen으로의 pen down/pen up은 key입력으로 볼 수 있음

안드로이드 시스템은 커널에서 올라오는 입력장치를 EventHub에서 처리하고, 안드로이드 시스템의 키 레이아웃 파일(key layout file)과 비교한 후 App.Framework쪽으로 해당 키 값을 올려주게 된다.

// include/linux/input.h

#define KEY_BOOKMARKS 156 /* AC Bookmarks */
#define KEY_COMPUTER 157
#define KEY_BACK 158 /* AC Back */
#define KEY_FORWARD 159 /* AC Forward */

#if 0
#define KEY_HOME 102
#define KEY_BACK 158 /* AC Back */
#define KEY_MENU 139 /* Menu (show menu) */
#define KEY_SEND 231 /* AC Send */
#define KEY_END 107
#endif

int keybutton_keycode[] =
{
  KEY_HOME, KEY_MENU, KEY_BACK, KEY_END, KEY_SEND
};


다음은 안드로이드 시스템에서 사용하는 키 레이아웃 파일의 예다. 레이아웃 파일의 주요 용도는 입력된 하드웨어 키 값을 시스템이 사용할 수 있는 키 값으로 전달하는데 있다. 또한 키의 언어나 배열을 재 배치하는 용도로도 사용된다.

// Android의 Rootfs의 /system/usr/keylayout/qwerty.kl

key 158 BACK   WAKE_DROPPED
key 230 SOFT_RIGHT   WAKE
key 60 SOFT_RIGHT   WAKE
key 107 ENDCALL   WAKE
key 88 ENDCALL   WAKE
key 229 MENU   WAKE_DROPPED
key 228 POUND
key 227 STAR
key 231 CALL   WAKE_DROPPED
key 61 CALL   WAKE_DROPPED
key 232 DPAD_CENTER   WAKE_DROPPED
key 108 DPAD_DOWN   WAKE_DROPPED
key 103 DPAD_UP   WAKE_DROPPED
key 105 DPAD_LEFT   WAKE_DROPPED
key 106 DPAD_RIGHT   WAKE_DROPPED
key 115 VOLUME_UP
key 114 VOLUME_DOWN
key 116 POWER   WAKE
key 66 HOME   WAKE


안드로이드 터치 드라이버
안드로이드 시스템에서는 일반적인 입력 장치로 터치 스크린을 사용하고 있다. 터치 스크인은 화면의 터치 했을 때의 위치를 시스템에서 인식할 수 있는 논리적인 위치로 변경해 주어야 하기 때문에 터치 위치에 대한 캘리브레이션 작업이 필요하다. 기존의 저항막 방식의 터치스크린의 경우는 캘리브레이션 작업을 따로 되어야 하는 구조이지만 요즘 사용되는 정전식 방식에서는 별도 캘리브레이션 작업을 하지 않는다. 안드로이드 시스템의 터치 드라이버의 경우 터치스크린의 절대 값을 입력 받아 스크린상의 좌표로 변환하도록 구성되었다. 정전 터치 패널의 경우는 정전 터치의 좌표를 안드로이드 시스템에 맞도록 매핑하여 전달하고, 압력 터치 패널의 경우에는 캘리브레이션 공식을 대입하여 안드로이드 시스템에 맞도록 매핑하여 전달하도록 구성된다.

[그림 1]은 LCD 화면에서 터치를 했을 때 인식되는 터치 좌표를 보여준다.

[그림 2]는 안드로이드 시스템에서 터치 정보의 처리 과정을 보여준다.
아나로그 형태의 터치 위치 정보는 ADC(Analog to Digital Converter)를 거쳐 터치 위치에 대한 정보로 변환된다. 터치 드라이버를 거쳐, 터치 위치를 계산하기 위한 TS 라이브러리를 통해 안드로이드 시스템으로 전달되게 된다.
커널 드라이버에서 발생된 터치 정보는 앞에서 살펴본 입력 장치드라이버와 마찬가지로 이벤트 허브(EventHub)에 관리되고, 이 정보는 KeyInputQueue를 통해 안드로이드 시스템에 전달되어 사용자의 입력을 처리하고, 시스템을 관리하게 된다.

다음 소스는 터치 스크린 드라이버에서 터치 정보를 계산하는 소스의 일부분이다.

static void s5pv310_ts_process_data(touch_process_data_t *ts_data)
{
// 중략

if((ts_data->x1 = ((s5pv310_ts.rd[3] << 8) | s5pv310_ts.rd[2]))){
  ts_data->x1 = (ts_data->x1 * 134) / 100;
  /* flip X & resize */
  ts_data->x1 = TS_ABS_MAX_X - ts_data->x1;
}

/* resize */
if((ts_data->y1 = ((s5pv310_ts.rd[5] << 8) | s5pv310_ts.rd[4]))){
  ts_data->y1 = (ts_data->y1 * 134 ) / 100;
}
if(ts_data->finger_cnt > 1) {
  /* flip X & resize */
  if((ts_data->x2 = ((s5pv310_ts.rd[7] << 8) | s5pv310_ts.rd[6]))) {
  ts_data->x2 = (ts_data->x2 * 134) / 100;
  ts_data->x2 = TS_ABS_MAX_X - ts_data->x2;
  }
  /* resize */
  if((ts_data->y2 = ((s5pv310_ts.rd[9] << 8) | s5pv310_ts.rd[8]))){

  ts_data->y2 = (ts_data->y2 * 134 ) / 100;
// 중략


안드로이드 배터리 드라이버
안드로이드 시스템에서 사용하는 배터리의 잔량을 표시하기 위해서는 배터리 드라이버를 탑재해야 한다. 본 연재에서는 간단한 배터리 드라이버를 통해 어떠한 식으로 안드로이드 시스템이 관리를 하는지 살펴 보도록 하겠다.

(1) dummy_battery.c 파일 복사
Dummy_battery.c 파일을 driver/power에 복사한다.
# cp -a dummy_battery.c (kernel_source)/driver/power/


(2) kernel configuration에 항목을 추가
drivers/power/Kconfig

config BATTERY_DUMMY
  tristate "Dummy battery driver"
  help
  Say Y to include dummy battery driver.
  It always returns battery information as fully charged.

endif # POWER_SUPPLY


(3) 실제 컴파일이 되도록 Makefile 수정
drivers/power/Makefile

obj-$(CONFIG_BATTERY_DUMMY)+= dummy_battery.o


(4) 안드로이드 시스템에서 배터리 관련 정보를 인식하기 위한 수정작업
drivers/power/power_supply_sysfs.c
static struct device_attribute power_supply_attrs[] = {
  POWER_SUPPLY_ATTR(status),
  POWER_SUPPLY_ATTR(health),
  POWER_SUPPLY_ATTR(present),
  POWER_SUPPLY_ATTR(online),
  POWER_SUPPLY_ATTR(technology),
//…
  POWER_SUPPLY_ATTR(batt_vol), // POWER_SUPPLY_ATTR(voltage_now),
//…
  POWER_SUPPLY_ATTR(capacity),
  POWER_SUPPLY_ATTR(batt_temp), // POWER_SUPPLY_ATTR(temp),
//..
};


(5) Kernel configuration 설정
make menuconfig
Device Drivers --->
  <*> Power supply class support --->
  <*> Dummy battery driver



(6) Kernel에서 상태값 확인
/
sys/class/power_supply/ac
-rw-r--r-- root root 4096 uevent
lrwxrwxrwx root root subsystem -> ../../power_supply
lrwxrwxrwx root root device -> ../../../devices/platform/dummy-battery.0
-r--r--r-- root root 4096 type
-r--r--r-- root root 4096 online

/sys/class/power_supply/battery
-rw-r--r-- root root 4096 uevent
lrwxrwxrwx root root subsystem -> ../../power_supply
lrwxrwxrwx root root device -> ../../../devices/platform/dummy-battery.0
-r--r--r-- root root 4096 type
-r--r--r-- root root 4096 status
-r--r--r-- root root 4096 health
-r--r--r-- root root 4096 present
-r--r--r-- root root 4096 technology
-r--r--r-- root root 4096 capacity
-r--r--r-- root root 4096 voltage_now
-r--r--r-- root root 4096 temp


배터리 드라이버의 내부
배터리 드라이버는 커널 디바이스 모델(Kernel device model)기반의 디바이스 드라이버다. 따라서, 리눅스 디바이스 드라이버가 가져야 할 기본적인 정보를 등록해 주어야 한다. dummy_battery_probe, dummy_battery_remove와 같은 함수를 통해 기본적인 등록 및 제거 과정이 호출되게 된다.

static struct platform_device dummy_battery_device = {
  .name = "dummy-battery",
};

static struct platform_driver dummy_battery_driver = {
  .probe = dummy_battery_probe,
  .remove = dummy_battery_remove,
  .driver = {
  .name = "dummy-battery" }
};

static int __init dummy_battery_init(void) {
  int ret;
  ret = platform_device_register(&dummy_battery_device);
  //…
  ret = platform_driver_register(&dummy_battery_driver);
  //…
  return 0;
}


power_supply 범주에 AC, Battery 각각 등록

static int dummy_battery_probe(struct platform_device *pdev)
// AC
data->ac.properties = dummy_ac_props; // AC 관련 항목 리스트
data->ac.num_properties = ARRAY_SIZE(dummy_ac_props);
data->ac.get_property = dummy_ac_get_property; // 상태값 반환 함수
data->ac.name = "ac";
data->ac.type = POWER_SUPPLY_TYPE_MAINS;
ret = power_supply_register(&pdev->dev, &data->ac);

// Battery
data->battery.properties = dummy_battery_props; // battery 관련 항목 리스트
data->battery.num_properties = ARRAY_SIZE(dummy_battery_props);
data->battery.get_property = dummy_battery_get_property; // 상태값 반환 함수
data->battery.name = "battery";
data->battery.type = POWER_SUPPLY_TYPE_BATTERY;
ret = power_supply_register(&pdev->dev, &data->battery);


AC/Battery 항목
“enum power_supply_property” 타입의 배열 변수를 기반으로 각각의 항목을 생성(관련 상수값은 include/li nux/power_supply.h 참조)

AC의 경우 전원 연결 상태 정보만 인식하여 제공한다.
static enum power_supply_property dummy _ac_props[] 참조

Battery의 경우, 충전여부, 충전상태(mV), 충전최대치(%), 타입 등의 정보를 제공한다.
static enum power_supply_property dummy_ battery_props[] 참조
각 항목에 대하여 get_property()함수를 통해 현재 상태 값이나 정보를 실수나 문자열로 제공한다.
Dummy_battery의 경우, 고정된 가상의 값으로 대체된다.

AC 상태값 목록 및 상태값 제공 루틴
AC의 경우 전원 연결 상태값만 제공한다.
(관련 상수값은 include/linux/power_supply.h 참조)

static enum power_supply_property dummy_ac_props[] = {
  POWER_SUPPLY_PROP_ONLINE,
};

static int dummy_ac_get_property(…) {
//…
  int ret = 0;
  switch (psp) {
    case POWER_SUPPLY_PROP_ONLINE: val->intval = 1; // 1:online, 0:offline
    break;
    default:
    ret = -EINVAL;
    break;
  }
  return ret;
}


Battery 상태값 목록
(관련 상수값은 include/linux/power_supply.h 참조)

static enum power_supply_property dummy_battery_props[] = {
  POWER_SUPPLY_PROP_STATUS,
  POWER_SUPPLY_PROP_HEALTH,
  POWER_SUPPLY_PROP_PRESENT,
  POWER_SUPPLY_PROP_TECHNOLOGY,
  POWER_SUPPLY_PROP_CAPACITY,
  POWER_SUPPLY_PROP_VOLTAGE_NOW,
  POWER_SUPPLY_PROP_TEMP,
};

static int dummy_battery_get_property(…) {
  switch (psp) {
  case POWER_SUPPLY_PROP_STATUS:
  val->intval = POWER_SUPPLY_STATUS_CHARGING;
  break;
//…
}


Android에서 battery 상태 값을 읽어가는 항목
frameworks/base/services/jni/com_android_server_BatteryService.cpp

#define AC_ONLINE_PATH "/sys/class/power_supply/ac/online"
#define USB_ONLINE_PATH "/sys/class/power_supply/usb/online"
#define BATTERY_STATUS_PATH "/sys/class/power_supply/battery/status"
#define BATTERY_HEALTH_PATH "/sys/class/power_supply/battery/health"
#define BATTERY_PRESENT_PATH "/sys/class/power_supply/battery/present"
#define BATTERY_CAPACITY_PATH "/sys/class/power_supply/battery/capacity"
#define BATTERY_VOLTAGE_PATH "/sys/class/power_supply/battery/batt_vol"
#define BATTERY_TEMPERATURE_PATH "/sys/class/power_supply/battery/batt_temp"
#define BATTERY_TECHNOLOGY_PATH "/sys/class/power_supply/battery/technology"


구글의 두번째 시도, ADK2012
구글은 왜 2011년에 이어 ADK2012를 안드로이드 플랫폼의 액서서리 인터페이스를 위한 플랫폼으로 다시 발표하였다. ADK2011의 경우 아두이노 프로젝트의 오픈 소스/오픈 하드웨어를 바탕으로 탄생하였고, ADK2012의 경우는 ARM의 32비트 Cortex M3 마이크로 프로세서를 기반으로 설계되었다.

ADK에 대한 오픈 소스/오픈 하드웨어 정책은 구글의 안드로이드 운영 정책과 부합하게 되어 ADK가 탄생하게 된 것이다. 하지만 실제로 ADK의 구조 그대로 부가장치가 만들어지고 제품화로 출시될 것인지는 아직까지는 의문이다. 또한 ADK의 방향에 대해서 실패를 점치는 개발자들의 목소리도 들리고 있다. USB를 이용한 인터페이스 방식에 회의를 갖는 것이다. 자세한 사항은 뒤에서 살펴 보기 바란다. ADK의 향후 방향이나 발전 방향에 대한 전망은 아직 시기 상조이다. 필자와 같이 전자 회로를 만들기 좋아하고 취미로 하드웨어 프로그래밍을 해보는 사람들에게 안드로이드 폰을 이용하여 자바로 하드웨어를 제어하는 새로운 플랫폼이 생겼다는 것에 즐거울 뿐이다.

ADK와 같이 안드로이폰을 통하여 하드웨어를 제어하고자 하는 시도는 이전에도 있었다. 우연에 일치인지는 모르지만 스파크펀(SparkFun)사이트에서 IOIO라는 보드를 통해 ADK와 유사한 개발 방법을 2011년 5월에 소개 했었다

IOIO가 먼저인지 구글에서 만든 ADK가 먼저인지는 사실 자세히는 할 수 없다. 다만 같은 생각을 가지고 뭔가를 해보려는 새로운 시도를 하고 있다는 것이 중요할 뿐이다. 또한 저렴한 방법으로 하드웨어를 구성하고 로봇 제어와 같은 일을 이제 공식적인 ADK를 가지고 할 수 있다는 것뿐이다. IOIO의 경우 독자적인 펌웨어와 프레임워크를 사용해야 했다.

구글의 ADK나 IOIO는 동일한 구조 및 동작 방식을 가진다. 구글의 경우 아두이노 하드웨어 플랫폼을 이용했다는 것이고 IOIO의 경우 안드로이드에서 정식으로 지원하지 않기 때문에 디버깅의 위해 사용했던 ADB를 이용했다는 것뿐이지 하드웨어의 구성이나 방향은 거의 동일하다 보면 된다. 그럼 지금부터 알아보려고 하는 ADK의 구조 및 내부에 대해 살펴 보도록 하자.

ADK는 구글 안드로이드 개발자 사이트 (http://devel oper.android.com/guide/topics/usb/adk.html)에서 직접 다운로드 받을 수 있으며 하드웨어 구성을 위한 하드웨어 정보와 소프트웨어 패키지로 구성되어 있다. 현재 5월 12일자 ADK 패키지를 다운로드 받을 수 있으며 구성 및 내용은 다음과 같다.

ADK를 구성
ADK는 하드웨어 및 소프트웨어 패키지로 구성이 되어 있다. 즉 다시 말하면 하드웨어 설계시 필요한 하드웨어 기판 설계 정보와 제작 정보, ADK 하드웨어의 구동에 필요한 소프트웨어, 안드로이드 운영체제에서 ADK 하드웨어와 연결하여 테스트 해 보기 위한 응용 프로그램으로 구성이 되어 있다.

● 하드웨어
ADK를 구성하는 하드웨어는 미국 아트멜사의 ATM EGA2560 프로세서를 탑재한 MEGA2560이라는 보드와 이 보드를 통해 온도 센서, LED, 스위치 등을 제어 테스트 할 수 있도록 수성된 쉴드 보드로 구성되었다.

ADK 주 보드는 쉴드 보드와 연결할 수 있는 연결 커넥터를 제공하고 USB를 통해 안드로이드 운영체제를 탑재한 장치와 연결할 수 있도록 구성되었다.

쉴드 보드를 통해서 제어할 수 있는 기능은 캡센스(CapSene)라는 터치 장치를 통한 터치 인터페이스, I2C 를 통한 하드웨어 제어, 서보 모터 제어, SPI 통신을 통한 장치 제어등과 같은 다양한 하드웨어를 제어 테스트 해 볼 수 있다. 따라서 로봇 및 센서를 이용한 다양한 장치 등을 개발할 수 있는 기능을 제공하고 있다.

● 하드웨어 펌웨어
하드웨어 보드에 탑재되는 펌웨어 프로그램은 demok it.pde라는 이름으로 제공된다. pde 확장자는 아두이노 통합 개발 환경에서 사용되는 확장자이다.
demokit.pde는 AndroidAccessory, USB_Host _Shield라는 라이브러리 폴더에 있는 라이브러를 통하여 ADK 보드의 각종 기능 및 센서를 제어할 수 있도록 해준다.

● 안드로이드 라이브러리
안드로이드 3.1(API 레벨 12) 버전에는 ADK에 관련된 라이브러리가 탑재되어 나올 것이지만 안드로이드 2.3.4(API 레벨 10) 버전에서는 우선 애드온(Add-on) 형태의 라이브러리를 설치하여 사용할 수 있다.

ADK2012
ADK2012는 프로세서의 변화뿐만 아니라 각종 센서 및 불루투스에 대한 지원도 제공하고 있다. 기존의 ADK2011가 가지고 있던 USB 연결 방식에서 벗어날 수 있는 방법을 제공한 것이다. 자세한 ADK2012에 대한 내용은 추후에 더 다룰 수 있도록 하겠다.

끝으로
지금까지 안드로이드 시스템에서 사용자 입력을 처리하는 입력 장치 및 ADK에 대해 살펴봤다. 화면에 대한 출력을 처리하는 프레임 버퍼 드라이버와 입력 장치는 안드로이드 시스템에서 꼭 필수적인 중요 기능들이다. 이러한 기본적인 기능들을 보다 잘 이해한다면 좀 더 안드로이드 시스템에 대해 접근할 수 있을 것이다. ADK는 안드로이드 시스템을 사용하기 위한 부가장치 플랫폼이지만 스마트 폰을 다양하게 확장할 수 있도록 해 준다는 장점이 있다. 하지만 아직까지는 기술적인 제약 및 활용도가 떨어지는 문제 때문에 ADK를 이용한 제품의 출시가 많지 않았다. ADK2012를 계기로 새로운 부가장치 시장이 형성되지 않을까 기대하면서 8월호 연재를 마친다.



/필/자/소/개/

필자

라영호

국내 스마트폰의 초창기인 Cellvic에서부터 스마트폰을 개발하였고 윈도 모바일 및 다양한 임베디드 시스템을 개발하고 있다. 현재는 안드로이드 시스템 포팅 및 임베디드 시스템 개발, 컨설팅, 교육 등을 진행하는 회사를 운영하고 있다. 개인 블로그(www.embeddedce.com)를 통해 임베디드 시스템개발에 대한 다양한 생각과 방법론을 함께 생각해 보고자 노력 중이다.

※ 본 내용은 (주)테크월드(http://www.embeddedworld.co.kr)의 저작권 동의에 의해 공유되고 있습니다.
    Copyright ⓒ Techworld, Inc. 무단전재 및 재배포 금지

맨 위로
맨 위로