S3C2440A에 Embedded Linux-Xenomai 포팅

  예전에 PXA270에 Embedded Linux-Xenomai를 포팅하여 휴머노이드로봇의 주 제어용으로 사용했었고, Ethernet용 RTDM을 제작하여 Ethernet을 이용한 실시간 제어 가능성 확인용으로도 활용해 봤었다.
  이번엔 안쓰는 구형 네이비게이터에 장착된 S3C2440A(400MHz)에 Embedded Linux-Xenomai를 포팅해봤다. 궁금해서 기기를 까보니 S3C2440A이 보이길레 한번 올려본 것이다.
  사용한 네비게이터는 엠텍반도체라는 곳에서 2006년에 만든 CARON FDN4000모델이다
[CARON FDN4000]

  뚜껑을 따보니 메인기판 1개와 LCD로 구성된 아주 깔끔한 구조였다.
[분해]


[기판 부품면]

[기판 반대면]
  우선 기본적인 외부 I/O를 확인한다. 다행이 UART가 2.5mm 마이크로 폰잭으로 나와 있었다. 터미널을 연결해 뭐라는지 들어보니 E-Boot와 WinCE 조합이었다.
  시리얼 부팅등 간단한 방법이나 사용 가능한 툴등이 있는지 메뉴얼, 인터넷등을 뒤져 봤는데 없다. JTAG을 붙여서 바닥부터 시작해야 할 듯 하다. JTAG 디버그 포트를 찾아 봤으나 핀헤더나 핀헤더가 있었을 법한 껀덕지는 아무데도 없었다. 여기저기 유심히 보던중 아래 그림처럼 SD카드 슬롯이 있는, 보드 끝자락에 뭔가 컨텍이 있을법한 곳이 눈에 띤다.
  맞다. JTAG디버그 포트가 맞다. 나만의 나름(?) 노하우로 JTAG포트임을 확인하고 TMS, TCK, TDO, TDI등 핀배열을 찾아 냈다. 난 역시 대단해 움하하... 이 때도 역시 일전에 만들어 쓰고있는 나의 보물 USB-JTAG이 아주 큰 도움이 되었다.
  S3C2440A는 ARM9TDMI코어로 JTAG 연동을 위해 Embedded ICE를 구현하면 메모리 읽기 쓰기등이 가능해 진다. 일전에 같은 ARM9TDMI 기반인 KS8695보드 리버스 엔지니어링을 위해 구현했던 코드를 그대로 활용해 S3C2440A용을 만들어 USB-JTAG에 심어 주었다.
[디버깅을 위해 JTAG단자 인출]
  아래와 같이 H/W 개발 환경을 꾸며주었다. 우선 기본 내장된 펌웨어를 모조리 읽어 파일로 저장해 놓은 다음 플래시 메모리를 깨끗하게 지우고 시작 한다.
  부트로더 만들어 올리고, 리눅스 커널 부팅을 위한 ATAG도 만들어 주고, 이더넷 포트가 없으니 커널/램디스크 다운로드용 USB포트도 뚫어 주고, 호스트PC용 USB 디바이스 드라이버도 만들어 주고, 커널 패치하고, 커널 설정 하고, 빌드하고, BusyBox 구성해 주고, 램디스크 만들어주고 보드에 올리면 된다. 참 쉽죠?
[H/W 개발환경]
  사용한 커널 조합은 linux-2.6.28.8과 xenomai-2.6.0-rc이다. ramdisk 마운트 하는데 애를 좀 먹었다. crosstool-ng로 툴체인을 만들어 쓰고 있는데 요놈에 버그가 있었다. 툴체인 버그로 생긴 문제는 도무지 찾기가 힘들다...
  Xenomai가 패치된 커널 메시지는 아래와 같다.
-------------------------------------------------------------------
Uncompressing Linux... done, booting the kernel.
Linux version 2.6.38.8-xenokang (root@localhost.localdomain) (gcc version 4.7.3 (crosstool-NG 1.19.0) ) #1 PREEMPT Wed Nov 19 16:23:11 KST 2014
CPU: ARM920T [41129200] revision 0 (ARMv4T), cr=c0007177
CPU: VIVT data cache, VIVT instruction cache
Machine: FDN4000
Memory policy: ECC disabled, Data cache writeback
[FDN4000] fdn4000_map_io()
CPU S3C2440A (id 0x32440001)
S3C24XX Clocks, Copyright 2004 Simtec Electronics
S3C244X: core 400.000 MHz, memory 133.333 MHz, peripheral 66.666 MHz
CLOCK: Slow mode (1.500 MHz), fast, MPLL on, UPLL on
Built 1 zonelists in Zone order, mobility grouping on.  Total pages: 16256
Kernel command line: console=ttySAC1,57600 initrd=0x32200000,5M ramdisk=16384 root=/dev/ram0
PID hash table entries: 256 (order: -2, 1024 bytes)
Dentry cache hash table entries: 8192 (order: 3, 32768 bytes)
Inode-cache hash table entries: 4096 (order: 2, 16384 bytes)
Memory: 64MB = 64MB total
Memory: 57636k/57636k available, 7900k reserved, 0K highmem
Virtual kernel memory layout:
    vector  : 0xffff0000 - 0xffff1000   (   4 kB)
    fixmap  : 0xfff00000 - 0xfffe0000   ( 896 kB)
    DMA     : 0xffc00000 - 0xffe00000   (   2 MB)
    vmalloc : 0xc4800000 - 0xf6000000   ( 792 MB)
    lowmem  : 0xc0000000 - 0xc4000000   (  64 MB)
    modules : 0xbf000000 - 0xc0000000   (  16 MB)
      .init : 0xc0008000 - 0xc001f000   (  92 kB)
      .text : 0xc001f000 - 0xc01e0000   (1796 kB)
      .data : 0xc01e0000 - 0xc01f2e00   (  76 kB)
SLUB: Genslabs=13, HWalign=32, Order=0-3, MinObjects=0, CPUs=1, Nodes=1
Preemptable hierarchical RCU implementation.
        RCU-based detection of stalled CPUs is disabled.
        Verbose stalled-CPUs detection is disabled.
NR_IRQS:85
irq: clearing subpending status 000000d2
I-pipe, 11.111 MHz clocksource
I-pipe 1.18-09: pipeline enabled.
Console: colour dummy device 80x30
console [ttySAC1] enabled
Calibrating delay loop... 199.06 BogoMIPS (lpj=497664)
pid_max: default: 32768 minimum: 301
Mount-cache hash table entries: 512
CPU: Testing write buffer coherency: ok
gpiochip_add: gpios 288..303 (GPIOK) failed to register
gpiochip_add: gpios 320..334 (GPIOL) failed to register
gpiochip_add: gpios 352..353 (GPIOM) failed to register
[FDN4000] fdn4000_init()
S3C2440: Initialising architecture
S3C2440: IRQ Support
S3C244X: Clock Support, DVS off
bio: create slab at 0
Trying to unpack rootfs image as initramfs...
rootfs image is not initramfs (no cpio magic); looks like an initrd
Freeing initrd memory: 5120K
I-pipe: Domain Xenomai registered.
Xenomai: hal/arm started.
Xenomai: scheduling class idle registered.
Xenomai: scheduling class rt registered.
Xenomai: real-time nucleus v2.6.0-rc5 (head) loaded.
Xenomai: starting native API services.
Xenomai: starting RTDM services.
io scheduler noop registered
io scheduler deadline registered
io scheduler cfq registered (default)
s3c2440-uart.0: ttySAC0 at MMIO 0x50000000 (irq = 70) is a S3C2440
s3c2440-uart.1: ttySAC1 at MMIO 0x50004000 (irq = 73) is a S3C2440
s3c2440-uart.2: ttySAC2 at MMIO 0x50008000 (irq = 76) is a S3C2440
brd: module loaded
mousedev: PS/2 mouse device common for all mice
RAMDISK: gzip image found at block 0
VFS: Mounted root (ext2 filesystem) on device 1:0.
Freeing init memory: 92K
init started: BusyBox v1.22.1 (2014-11-28 14:19:48 KST)
starting pid 24, tty '': '/etc/init.d/rcS'

Please press Enter to activate this console.
starting pid 30, tty '': '-/bin/sh'
[root@s3c2440 /]#
[root@s3c2440 /]#
[root@s3c2440 /]# ./xenomai/bin/latency
== Sampling period: 1000 us
== Test mode: periodic user-mode task
== All results in microseconds
warming up...
RTT|  00:00:01  (periodic user-mode task, 1000 us period, priority 99)
RTH|----lat min|----lat avg|----lat max|-overrun|---msw|---lat best|--lat worst
RTD|     -4.681|      3.510|     36.090|       0|     0|     -4.681|     36.090
RTD|     -4.681|      3.600|     40.230|       0|     0|     -4.681|     40.230
RTD|     -4.681|      3.600|     42.930|       0|     0|     -4.681|     42.930
RTD|     -4.681|      3.600|     42.660|       0|     0|     -4.681|     42.930
RTD|     -4.681|      3.600|     42.480|       0|     0|     -4.681|     42.930
RTD|     -4.681|      3.600|     43.920|       0|     0|     -4.681|     43.920
RTD|     -4.681|      3.600|     42.480|       0|     0|     -4.681|     43.920
RTD|     -4.681|      3.690|     47.250|       0|     0|     -4.681|     47.250
RTD|     -4.681|      3.600|     42.660|       0|     0|     -4.681|     47.250
RTD|     -4.681|      3.600|     41.760|       0|     0|     -4.681|     47.250
RTD|     -4.681|      3.600|     42.030|       0|     0|     -4.681|     47.250
RTD|     -4.681|      3.600|     42.660|       0|     0|     -4.681|     47.250
RTD|     -4.681|      3.600|     43.830|       0|     0|     -4.681|     47.250
RTD|     -4.681|      3.600|     42.390|       0|     0|     -4.681|     47.250
RTD|     -4.681|      3.600|     42.660|       0|     0|     -4.681|     47.250
RTD|     -4.681|      3.600|     42.930|       0|     0|     -4.681|     47.250
RTD|     -4.681|      3.600|     43.200|       0|     0|     -4.681|     47.250
RTD|     -4.681|      3.600|     43.110|       0|     0|     -4.681|     47.250
RTD|     -4.681|      3.600|     41.580|       0|     0|     -4.681|     47.250
RTD|     -4.681|      3.600|     42.390|       0|     0|     -4.681|     47.250
RTD|     -4.681|      3.600|     44.280|       0|     0|     -4.681|     47.250
RTT|  00:00:22  (periodic user-mode task, 1000 us period, priority 99)
RTH|----lat min|----lat avg|----lat max|-overrun|---msw|---lat best|--lat worst
RTD|     -4.681|      3.600|     42.840|       0|     0|     -4.681|     47.250
RTD|     -4.681|      3.600|     42.030|       0|     0|     -4.681|     47.250
^C---|-----------|-----------|-----------|--------|------|-------------------------
RTS|     -4.681|      3.600|     47.250|       0|     0|    00:00:23/00:00:23
[root@s3c2440 /]#
[root@s3c2440 /]#
-------------------------------------------------------------------

  실시간 태스크 어플리케이션 동작이 확인되고 커널과 램디스크만 올라간 상태라 이제 시작이긴하다. fb붙여서 LCD도 돌려보고, 주변에 덕지덕지 붙어있는 터치판넬, 오디오, 비디오, GPS, FM 송신칩 등등도 해보면 재미질것 같다. 데이터 시트나 메뉴얼을 구할 수 없는 칩들이 몇 개 있어 좀 제한적이긴 하겠다.
  요놈은 하우징이 깔끔한 완제품이라 어디 좋은데 쓸수 있게 만들면 훌륭할거 같으다.

2014/08/31~09/04 터키여행

이번엔 터키를 다녀 왔다.
원래 파리를 가기로 했으나 당일 터키로 갑자기 변경해 출발한 여행이라 아무런 정보가 없어 거의 패닉 상태로 개고생 했다.
일정 때문에 24시간을 깨어 있었던적도 있었다.
말이 안통해 새벽 5시즘엔 앙카라에서 미아가 될뻔도 했다.
그래도 참 재밌었던 여행이다.
외국사람이 한국말로 자기나라를 소개하는 가이드 투어라는것도 처음 해봤다.
노란 풍선을 타고 하늘도 날았다.




































아웃사이더, 어떤이의 꿈 - 봄여름가을겨울





한참 노래방이 유행하던 대학시절,
개강모임, 종강모임 등등으로 얼큰하게 취해 들어가면 목이 터져라 부르던 노래들...
저거 뭔 노래야?... 하는 표정으로 아무도 호응을 해주지 않았지만
나 혼자 열심히 돼지 멱따는 소리로 고성방가를 했었지... 움흐흐...
그래서 친구가 없나?...

간단한 OS 자작 (SOS - Simple OS)


아주 오래전 80C196(16bit)과 TMS320C6416T(32bit) 컨트롤러에 uC/OS-II를 포팅하여 사용한적이 있다. 이번엔 ARM 기반 컨트롤러에 포팅해보려고 자료를 뒤져 봤는데 uC/OS-II가 오래전 유료화 되었댄다.
아... 이 청천벽력 같은 소식을 우째야 하나...
뭐 열심히 만든 SW를 편히 쓰게 해준것 만으로도 감지덕지다.
참 잘만든 SW가 유료화 되는것은 어쩌면 지극히 당연한 것이라 본다.
대신 지금 나에겐 대안이 필요하다.
아주 간단하고 무료로 쓸만한 OS를 찾아보니 요샌 FreeRTOS가 대세인듯 하다.
FreeRTOS 포팅 하려고 자료를 찾던중 문득

'요놈도 나중에 유료화 될려나?...'
'Free를 붙여놨는데 설마...'
'요놈에 의지하다 또 당하면?...'
'그냥 하나 만들어 버릴까?...'

이런 생각이 들었다.

예전에 이런일이 있었다.
MATLAB이라는 아주 좋은 툴에 완전히 의존해 개발을 하다보니 라이센스를 비롯해, 메모리, 저장장치 공간등의 문제로 MATLAB 라이브러리들을 못쓰게 되는 상황이 발생되니 사람이 바보가 되더라.
그 뒤로는 실개발에 필요한 라이브러리들 (FFT해석, 물리엔진 시뮬레이션용 적분기, 필터등등)을 C로 자체 개발해 사용하고 있다.

그런데, 비슷한 상황이 또 발생 되었다.
중대형 프로세서 기반의 Embedded System에서 자유롭게 사용가능한 OS가 필요할 경우 역시 Embedded Linux이다.
여기에 실시간성이 필요할 경우 Embedded Linux-Xenomai가 좋은 솔루션이 될수 있다.
소형 프로세서기반의 Embedded System에서 OS가 필요할 경우 uC/OS를 점찍어 놓고 있었는데 유료화 되면서 다른 대안이 필요한 상황이다.
또 바보가 되지 않기 위해 그냥 하나 만들기로 했다.

우선 소형 Embedded System을 목표로 하는 OS이므로 최대한 간단하게 구성하기로 한다.
OSless 펌웨어로 하기엔 좀 부족하고, OS를 넣자니 거대한 커널과 복잡한 메모리 관리, 태스크 관리, IPC툴등은 부담스러운, 그런 애매한 상황에 필요한 솔루션.
그래서 간단하게 만들어 보기로 했다. 뭐 거창한 OS만들 능력도 안된다. 크크크...
소형 Embedded System은 주로 주기적인 제어가 목적이므로 Periodic Task를 기본 지원하는 커널로 한다 (uC/OS나 FreeRTOS에서 Periodic Task를 구성하기가 좀 까다로운듯 하다).

Periodic Task와 관련하여 부연 설명을 좀 하자면 이렇다.
보통 디지털 제어기를 코딩할때 중요한 것이 바로 샘플링 시간 (Ts)이다.
아날로그 제어기법을 디지털 제어로 변경하면서 필연적으로 이 Ts라는 변수가 생기게 되는데 좋은 제어를 위해서는 이 Ts가 일정하게 유지되는 것이 중요하다.
이 Ts를 일정하게 유지하기 위해 보통 OSless 펌웨어에서는 하드웨어 타이머를 사용하던지 아니면 RTOS를 사용하게 된다.
Ts가 100ms인 Non-Periodic Task를 예로 보면 다음과 같다.

void NonPeriodicTask( void )
{
  for( ;; ) {
    Sensor();
    Control();
    Sleep( 100 );
  }


센서에서 값을 읽고 그 값에 따라 제어기를 돌린다.
그리고 100ms를 쉰다음 같은일을 반복한다.
디지털 Feedback 제어의 가장 단순한 예시이다.
Sensor()함수와 Control()함수를 수행하는데 시간 소모가 제법 있고 이 시간이 항상 일정 하다면 그 시간만큼 뺀 시간 동안 Sleep을 하면 된다.
그러나 보통 그 시간을 측정해 적용하는 일은 아주 드물며(바보같은 일) 수행시간이 매번 변한다면 Ts가 일정치 않아 정확한 제어가 되지 않을 것이다.
이를 위해 Periodic Task가 필요하다.

void PeriodicTask( void )
{
  for( ;; ) {
    Sensor();
    Control();
    SleepPeriod();
  }


void main( void )
{
  ...
  CreateTask( priority=10, period=100, task=PeriodicTask );
  ...
  StartKernel();
}

위와같이 Periodic Task를 커널에서 지원 한다면 Sensor()함수와 Control()함수의 수행시간과 상관 없이 (둘이 합쳐 100ms이내 종료된다는 조건) for루프는 매 100ms마다 반복된다.
커널이 Realtime을 지원 한다면 RTOS가 되는 것이다.
Periodic Task는 얼핏 타이머와 비슷하지만 다른 개념이다.
uC/OS에서 이 Periodic Task를 구현 하려면 커널 타이머와 이벤트, 세마포어등을 결합해 사용해야 한다 (지금은 커널에서 지원하는지 모르겠다).
Linux-Xenomai의 경우 커널 옵션에서 Periodic Task를 지원하는 커널로 설정하여 빌드하면 위와같이 사용 가능하다.

어찌 되었건 Periodic Task를 지원하도록 만들면 제어기 개발시 많이 편리하다.
스케쥴링 방식은 실시간성 보장을 위해 우선순위 기반과 Preemptive 방식으로 구현하고 다른 기능들은 넣지 않는다 (정확히는 넣을 능력이 안된다 크크크). 
기타 OS 커널에서 스케쥴링시 고려되어야 하는 많은 이론적 발생 가능한 문제점들을 응용 개발자에게 모두 떠넘겨 버리면 스케쥴러 구조는 아주 쉬어질 수 있다 움하하.
간단한 예로 우선순위 기반 스케쥴러에서 발생하기 쉬운 Starvation문제는 어플리케이션 개발시 주의하면 충분히 해결되거나 발생되지 않게 만들수 있다.
타겟역시 소형 Embedded System용 이므로 공룡 그림 그려진 OS이론 책에서 설명하는 기능들은 많은 부분이 필요 없다.
Task역시 많아야 10개 내외이고 ISR은 직접 구성하여 사용하면 된다.
태스크간 그리고 태스크-ISR간 동기화에는 공유 메모리와 Flag를 사용하면 소형 Embedded System환경에서의 어플리케이션은 대부분 개발 가능할 것이다.
뮤텍스나 세마포어, 이벤트등도 있으면 좋겠지만 스케쥴러를 최대한 간단하게 구성하기 위해 구현하지 않았다.
뮤텍스와 이벤트를 구현하여 기본 동작하는 것은 확인 하였으나 스케쥴러가 복잡해지고 잠재적 버그를 많이 가지고 있을수 있어 테스트만 해보고 넣지 않았다.
큰 그림으로 보자면 OSless 펌웨어와 OS-based 펌웨어의 어중간한 중간즘?...
커널 구조가 워낙 간단해 하루이틀 정도 코딩만으로 완성 되었다.
테스트는 일전에 Reverse Engineering으로 사용 가능해진 S3C3410(ARM7TDMI) 기반 무선전화기 보드를 활용했다.


[S3C3410(ARM7TDMI) 기반 무선전화기 보드 - 11MHz 동작]

잠재적 버그의 수를 줄이고 재사용 편의를 위해 간결한 코드 유지목적으로 다음 제한사항을 걸어 두었다.

<> 실시간성 확보를 위해 우선순위 기반의 Preemptive 스케쥴러로 구성
<> 필요한 파일수는 헤더 파일(h)과 구현 파일(c) 달랑 2개
<> 위 2개 파일 이외 추가 의존성 없이 구현
<> CPU의존 코드를 커널 파일안에 포함: 커널 관련 코드보다 CPU의존 코드가 더 많음(주객전도)
<> 인터럽트 컨텍스트 스위칭 주기는 1ms로 고정: 시간관리가 편해지고 추가 나눗셈 곱셈 연산이 필요 없어짐
<> 컨텍스트 스위칭 ISR은 여유 레지스터가 많은 FIQ사용

만들어 놓고 보니 OS라는 타이틀을 붙이기에는 너무 심하게 뭔가 없다. 흐흐...


[8개 태스크 동작 시험]

위 그림은 자작한 OS의 실제 동작 시험 결과 이다.
총 8개 태스크를 만들어 동시 구동 시켰으며 이중 우선순위가 가장 낮은 T7 태스크는 Non-Periodic Task로 Idle Task가 된다. 즉, 절대 Sleep이 일어나지 않도록 한다.
T0부터 T5까지는 각각 10ms, 20ms, 40ms, 80ms, 160ms로 두배씩 증가하는 주기로 설정하여 내부 카운터를 증가 시키고, T6는 200ms주기로 위 상태 정보 문자열을 출력한다.
T7은 Idle Task로 남는 시간동안 CPU 사용률을 계산한다.
위 출력 메시지의 맨 좌측값이 시스템 시간으로 1ms단위로 측정된다.
태스크 카운터 값을 보면 T0의 경우 시스템 타임의 1/10로 증가되며 나머지 태스크는 각 1/2씩 작은 값으로 증가되어 설정한 주기로 모든 태스크가 정확히 수행되는 것을 볼 수 있다.
T6의 경우 메시지 출력을 위해 200ms 주기로 설정되어 있으므로 매 메시지가 200ms 간격으로 출력되고 있는것을 볼 수 있다.
우선순위 기반 스케줄링 이므로 우선순위가 높은 태스크일 수록 실시간성이 잘 보장된다.
Idle Task를 제외한 T6의 우선순위가 가장 낮음에도 커널이 안정된 이후 정확한 200ms 간격을 보여 주고 있다.
해당 보드가 32bit기반 ARM임에도 약 11MHz정도 저속으로 동작하는데 태스크 8개와 1ms 스케쥴러에 약 20%정도 CPU사용량을 보인다. 

아직 실 어플리케이션에 적용한게 없어 어떤 상황에서 어떤 버그가 생겨날지 모른다.
추후 사용해 가면서 다듬어 봐야 겠다.

OS기반의 소규모 펌웨어 개발에 필요한 솔루션이 얼추 만들져 기쁘다.
의존성이 없어 아주 홀가분하기도 하다. 움하하...


<> 2014/08/21 내용 추가
CPU 사용률 측정에 버그가 있었다.
위 이미지에서는 11MHz S3C3410(ARM7) CPU 사용률이 약 20%정도이나 버그 수정후 재측정결과 약 95~96%정도 CPU 사용률을 보여 주었다.
8개 태스크와 시간 소모가 많은 printf()문에다 1ms 컨텍스트 스위칭 주기를 설정했는데 제아무리 32비트 코어라도 11MHz로 동작하는데 20%대는 무리인듯 하다.
추가로 CPU코어가 33MHz로 동작하는 S3C3400(ARM7) 코어에 똑같은 상황에서 측정해 보았는데 CPU 사용률은 약 48~49%대를 보여 주었다.

자작 적외선 리모컨 (무선 전화기 개조)

거실 TV에 PC가 연결되어 있는데 PC로 동영상 볼려면 TV 리모컨과 PC용 적외선 무선 키보드 2개를 조작해야 한다.
PC용 키보드는 파일 선택을 위한 트랙볼과 재생 위치 및 볼륨 조절용 방향키만 주로 사용한다.
이걸 한개로 통합한 리모컨을 만들까 하다가 회로도 그리고 땜질하고 만들기도 귀찮고, 또 자작한 하드웨어는 언제나 마무리(하우징)가 아쉬어 미루고 있었다.
운좋게 적외선 리모컨 기능이 내장된 무선 전화기를 하나 구했다.
LG에서 만든 GP-9138이라는 모델이고 KT ann용 무선 전화기 이다.


[요렇게 생긴 놈이다]
배를 갈라보니 LPC2210이라고 마킹된 메인 컨트롤러가 보인다.
요건 또 뭘까? 구글신께 문의 시작...
메인 컨트롤러는 ARM7TDMI 기반이고 다행히 LCD를 제외한 메인 컨트롤러 및 주변 부품들의 메뉴얼과 데이터 시트를 구할 수 있었다.
컬러 LCD와 전화기용 키패드가 있어 리모컨 만들기에 아주 딱이다.
요걸로 만들면 되겠다 싶어 시작 했다.


[메인 컨트롤러]
우선 컨트롤러의 리셋 벡터와 부트 모드를 확인해 보았다.
JTAG핀을 쓸까 하다가 UART 시리얼 부트모드와 제작사에서 제공하는 'LPC2000 Flash Utility'라는 툴을 이용하면 부트로더 주입이 가능해 보여 이걸로 시작 하기로 했다.
부트모드 변경 및 UART연결을 위한 와이어 작업을 한다.
하는김에 다른 기기들 조작을 위해 리모컨 발신 분석용 IR 수신기도 달아 준다.
수화스피커를 떼낸 공간에 딱 맞게 들어 간다.


[대가리쪽에 달아준 IR 수신기]
[오.. 딱 맞게 들어간다]
필요 없는 RF 및 모뎀 관련 부품들은 소모전류를 줄이기 위해 모두 들어낸다.
LPC2210에는 내장 플래시 메모리가 없어 'LPC2000 Flash Utility'툴 사용이 가능할지 의문이었는데 내부 램 R/W와 내부 램으로 점프도 가능하게 구성되어 있다. 탱크유소우머취.
내부 램에 1차 부트로더를 올리고 외부 플래시 메모리에 2차 부트로더를 버닝하면 된다.
안되면 JTAG을 붙일려고 자작해서 쓰고 있는 USB-JTAG 하드웨어를 옆에 대기 시켜놨는데 철수해도 되겠다. 흐흐흐... 철수중에 철수는 역시 배철수지. 크크크...


[부트모드 변경용 버튼과 리셋버튼 그리고 UART 연결용 케이블 제작]
간단히(?) 부트로더를 만들어 올리고 기본 펌웨어는 백업받아 파일로 저장해 놓는다.
회로 구성상 UART를 전혀 쓰지 않은 것으로 보인다.
터미널 연결도 없이 코딩을 하다니...
아주 좋은 디버깅 하드웨어를 썼거나 아니면 터미널 없이도 개발이 가능한 대단한 개발자 이거나...

PLL, 타이머, 인터럽트, UART, 키패드, LED, LCD 백라이트, 외부메모리 인터페이스등 기본 기능들 테스트를 완료하고 LCD부터 시작.

LCD에 마킹된 문자열로 검색해 봤는데 구글이 모른댄다. 나오는 데이터가 없어 인터넷으로 구할 수 있는 컬러 LCD제어기 칩들을 모아 일일히 메뉴얼대로 구현해 인터페이스 테스트를 해봤다.
안된다. 젠장...
read 스트로브 신호를 쓰지 않아 칩 상태를 읽어 볼 수가 없다.
아 피곤하군...
백업 받은 바이너리 파일을 디스어셈블한다.
LCD에 접근하는 코드를 찾아낸다.
LCD에 접근하는 함수가 약 3개 정도 보이는거 같다.
역시나 LCD상태를 읽어보는 코드는 보이지 않는다.
한개는 LCD 초기화 함수 같고 한개는 초기화시 호출하는 함수 인데 플래시에서 상수를 읽어 LCD레지스터에 쓴다. 뭐하는 함수인지 모르겠다.
나머지는 픽셀제어용 함수인듯 보인다. 4개 인수를 받아 좀 복잡한 수식 계산을 하는데 그래픽 램 어드레스 설정이나 포인터 설정등 뭐 이런거 하는거 같다. 어셈블리로된 수치연산 코드 해석은 골아프다. 일단 스킵...
초기화 함수로 추정되는 코드를 따라 하드 코딩된 8bit 상수 데이터를 뽑아내 초기화 해보니 오우~~ 된다. 나머지 함수들도 C로 구현해 이리 저리 돌려보며 설정할 수 있게 되었다.
이틀간 삽질로 LCD제어가 완료 되었다 (참 쉽죠?).
초기화시 호출하는 함수는 LCD 콘트라스트 설정이고, RGB포멧은 픽셀당 4:4:4 12bit 포멧이다 (2픽셀당 3바이트).
인터페이스 방식이 기존 인터넷에서 구했던 컬러LCD 제어칩들과 완전히 다르다.


[LCD 테스트중]
기본 IR 송신과 수신기능을 테스트하고 기기 기능들을 구현해 넣는다.
동작 모드는 총 3가지로, RC모드 (Remote Control Mode), TX모드 (Transmission Mode), RX (Receive Mode)모드이다.
RC모드는 알고있는 기기들의 송신코드를 미리 넣어 해당 기기를 조작하는 기능을 구현시켰다.
TX모드는 리모컨이 없는 기기에 임의의 IR코드를 주입시켜 동작테스트를 해볼 목적으로 구현해 넣었다.
RX모드는 IR리모컨의 발신 신호를 받아 분석할 수 있게 기능을 구현했다.

사용중인 IR제어 기기들의 코드를 읽어 내장시켰다.

대부분 NEC코딩 방식이라 쉽게 읽어볼 수 있었으나 IR 무선 키보드는 자체 개발 포멧인지 데이터 분석이 불가하여 키별 타이밍값을 하드코딩하여 넣어 버렸다.
기존 장착된 Ni-Cd배터리는 맛이가서 리튬이온 배터리로 달아 줬다.


[맛간 Ni-Cd 배터리를 걷어내고 안쓰는 휴대폰 배터리를 개조해서 넣어줌]
[동작 모드별 LCD화면]
LCD표기는 위 그림의 좌측부터 RC모드, RC모드에서 키보드 선택, TX모드, RX모드에서 수신대기, RX모드에서 수신완료 상태이다.
RX모드에서는 IR수신 완료후 NEC 포멧으로 분석하여 헤더 타이밍, 8bit-ID, 8bit-DATA값을 표기해 주도록 하였다. 나머지 수치는 수신된 타이밍값을 us단위로 모두 표기해 NEC포멧이 아닐 경우 수동으로 해독할 수 있게 했다.
소프트파워 버튼이 하드웨어로 구현되어 있어 일정시간내 키 조작이 없을 경우 자동 OFF시켜 꽤 오래 쓸 수 있을듯 하다.


[최종 완성]