본문 바로가기

NDS 홈브루(Homebrew)/홈브루 Tutorial

06 키패드(KeyPad) 및 터치스크린(Touch Screen) 제어

06 키패드(KeyPad) 및 터치스크린(Touch Screen) 제어

원문 : http://kkamagui.springnote.com/pages/422698


들어가기 전에...



 키패드는 4개의 방향키와 4개의 일반 버튼, 그리고 2개의 Start/Select 버튼, 2개의 어깨버튼으로 되어있다. 이중에서 Gameboy에서 사용했던 2개의 일반 버튼 X/Y는 ARM7에만 연결되어있으며, Touch Screen 또한 ARM7에만 연결되어있다.


1.ARM9/ARM7 키패드(Keypad)

 ARM9과 ARM7에서 다 접근할 수 있는 키는 http://nocash.emubase.de/gbatek.htm#keypadinput 에서 정보를 찾아볼 수 있다.

The built-in GBA gamepad has 4 direction keys, and 6 buttons.

4000130h - KEYINPUT - Key Status (R)

Bit   Expl.
  0     Button A        (0=Pressed, 1=Released)
  1     Button B        (etc.)
  2     Select          (etc.)
  3     Start           (etc.)
  4     Right           (etc.)
  5     Left            (etc.)
  6     Up              (etc.)
  7     Down            (etc.)
  8     Button R        (etc.)
  9     Button L        (etc.)
  10-15 Not used
It'd be usually recommended to read-out this register only once per frame, and to store the current state in memory. As a side effect, this method avoids problems caused by switch bounce when a key is newly released or pressed.

 위에서 보면 각 비트별로 키가 눌러졌으면 0, 눌러지지 않았으면 1로 설정되고 주의할 점은 한 프레임별로 한번만 읽어서 저장하라고 되어있다. 이렇게 하지 않으면 스위치가 튀는 현상이 생긴단다. 주의하도록 하자.


4000132h - KEYCNT - Key Interrupt Control (R/W)

The keypad IRQ function is intended to terminate the very-low-power Stop mode, it is not suitable for processing normal user input, to do this, most programs are invoking their keypad handlers from within VBlank IRQ.

Bit   Expl.
  0     Button A        (0=Ignore, 1=Select)
  1     Button B        (etc.)
  2     Select          (etc.)
  3     Start           (etc.)
  4     Right           (etc.)
  5     Left            (etc.)
  6     Up              (etc.)
  7     Down            (etc.)
  8     Button R        (etc.)
  9     Button L        (etc.)
  10-13 Not used
  14    IRQ Enable Flag (0=Disable, 1=Enable)
  15    IRQ Condition   (0=Logical OR, 1=Logical AND)
In logical OR mode, an interrupt is requested when at least one of the selected buttons is pressed.
In logical AND mode, an interrupt is requested when ALL of the selected buttons are pressed.

In 8bit gameboy compatibility mode, L and R Buttons are used to toggle the screen size between normal 160x144 pixels and stretched 240x144 pixels.
The GBA SP is additionally having a * Button used to toggle the backlight on and off, as far as I know there's no way to detect the current button or backlight state by software.

 인터럽트 컨트롤 레지스터는 키가 눌리면 인터럽트가 바로 발생하여 어떤 처리를 할 수 있도록 해준다. Logical AND와 Logical OR는 위에서 보는 것과 같이 인터럽트 플래그가 설정된 키가 모두 눌러지는가 or 하나만 눌러지는가에 따라서 인터럽트를 발생시키는 것이다.

 위의 설명에 따르면 일반적으로 User Input을 처리하는 방식은 Frame 별로 한번 읽어서 처리하는 방식이라 인터럽트를 사용해서 처리하는 방식은 Low Power Mode에서 깨어나게 하는 용도로 사용된다고 되어있다. 나중에 참고하도록 하자.


2.ARM7 키패드(Keypad)

4000136h - ARM7 - EXTKEYIN - Key X/Y Input (R)

0      Button X     (0=Pressed, 1=Released)
  1      Button Y     (0=Pressed, 1=Released)
  3      DEBUG button (0=Pressed, 1=Released/None such)
  6      Pen down     (0=Pressed, 1=Released/Disabled)
  7      Hinge/folded (0=Open, 1=Closed)
  2,4,5  Unknown / set
  8..15  Unknown / zero
The Hinge stuff is a magnetic sensor somewhere underneath of the Start/Select buttons, it will be triggered by the magnet field from the right speaker when the console is closed. The hinge generates an interrupt request (there seems to be no way to disable this, unlike as for all other IRQ sources), however, the interrupt execution can be disabled in IE register (as for other IRQ sources).
The Pen Down is the /PENIRQ signal from the Touch Screen Controller (TSC), if it is enabled in the TSC control register, then it will notify the program when the screen pressed, the program should then read data from the TSC (if there's no /PENIRQ then doing unneccassary TSC reads would just waste CPU power). However, the user may release the screen before the program performs the TSC read, so treat the screen as not pressed if you get invalid TSC values (even if /PENIRQ was LOW).
Not sure if the TSC /PENIRQ is actually triggering an IRQ in the NDS?
The Debug Button should be connected to R03 and GND (R03 is the large soldering point between the SL1 jumper and the VR1 potentiometer).
Interrupts are reportedly not supported for X,Y buttons.

 X/Y 버튼은 ARM7에 연결되어있으므로 ARM9에서는 바로 읽을 수 없고 이것을 ARM7에서 읽어 ARM9으로 넘겨주는 식으로 해야 한다. Hinge 같은 경우는 NDS가 접혔을때 1로 설정되고, Pen down은 Touch Screen이 눌러졌을 때 0으로 설정된다. 하지만 위에 설명에서 나왔듯이 저 값이 1로 설정되었을 때 CPU가 SPI를 통해 Touch Screen에서 값을 읽게 되는데, 읽기 전에 Touch Screen에서 Release되면 잘 못된 값을 읽을 수 있다. 즉 완전하지는 못한 것 같다. Debug 레지스터는 크게 중요하지 않으므로 넘어간다.


3.터치 스크린(Touch Screen)

 NDS의 터치스크린에 대한 문서는 http://nocash.emubase.de/gbatek.htm#dstouchscreencontrollertsc에서 찾아볼 수 있다.



  VCC  1|o       |16 DCLK
  X+   2|        |15 /CS
  Y+   3|  TSC   |14 DIN
  X-   4|  2046  |13 BUSY
  Y-   5|        |12 DOUT
  GND  6|        |11 /PENIRQ
  VBAT 7|        |10 IOVDD
  AUX  8|________|9  VREF


 현재 ( 2007/08/18 14:24:40 )까지 테스트한 결과로 아직까지 Touch Screen의 튀는 현상이 완전히 없어지지는 않고 있는데, 그 이유가 여기서 잠깐 나온다. ARM7에서 SPI를 통해서 값을 읽어야 하는데, 위에서 잠깐 언급했듯이 값을 읽는 동안에 release가 되면 어떻게 할 수 없다는 것이다. 결국 반응속도를 높여서 1로 설정되었을 때 빨리 읽어줘야 한다는 것인데.... 애매하기 짝이없다.

 튀는 현상을 줄이기 위해서는 이 부분에 대해서 libnds 라이브러리를 수정하여 튜닝을 좀 해야 할 것 같다.

Texas Instruments TSC2046
The Touch Screen Controller is accessed via SPI bus,
DS Serial Peripheral Interface Bus (SPI)

 SPI에 대한 내용은  07 Serial Peripheral Interface(SPI) 를 참조하도록 하자.


Control Byte (transferred MSB first)

  0-1  Power Down Mode Select
  2    Reference Select (0=Differential, 1=Single-Ended)
  3    Conversion Mode  (0=12bit, max CLK=2MHz, 1=8bit, max CLK=3MHz)
  4-6  Channel Select   (0-7, see below)
  7    Start Bit (Must be set to access Control Byte)

 Control Byte는 위와 같이 갖가지 모드를 사용할 수 있다. 채널은 아래에서 나오는데, 컨트롤러로부터 원하는 값을 읽을때 사용된다.


0 Temperature 0 (requires calibration, step 2.1mV per 1'C accuracy)
  1 Touchscreen Y-Position  (somewhat 0B0h..F20h, or FFFh=released)
  2 Battery Voltage         (not used, connected to GND in NDS, always 000h)
  3 Touchscreen Z1-Position (diagonal position for pressure measurement)
  4 Touchscreen Z2-Position (diagonal position for pressure measurement)
  5 Touchscreen X-Position  (somewhat 100h..ED0h, or 000h=released)
  6 AUX Input               (connected to Microphone in the NDS)
  7 Temperature 1 (difference to Temp 0, without calibration, 2'C accuracy)

All channels can be accessed in Single-Ended mode.
In differential mode, only channel 1,3,4,5 (X,Z1,Z2,Y) can be accessed.

 채널은 위와 같이 구성되며 각 채널은 해당 채널의 값을 리턴한다. 터치패드의 X값을 읽고 싶으면 Control Byte에 5를 설정하면 읽을 수 있다.

 오늘( 2007/08/21 03:27:23 ) 테스트 결과 NDS의 Z축 데이터는 정상적으로 수신되지 않는 것을 발견했다.


Power Down Mode

Mode /PENIRQ   VREF  ADC   Recommended use
  0    Enabled   Auto  Auto  Differential Mode (Touchscreen, Penirq)
  1    Disabled  Off   On    Single-Ended Mode (Temperature, Microphone)
  2    Enabled   On    Off   Don't use
  3    Disabled  On    On    Don't use
Allows to enable/disable the /PENIRQ output, the internal reference voltage (VREF), and the Analogue-Digital Converter.

 Power Down Mode는  데이터를 어떤식으로 받을 것인가 설정하는 부분인데, 아래에 설명이 나온다.


Reference Voltage (VREF)
VREF is used as reference voltage in single ended mode, at 12bit resolution one ADC step equals to VREF/4096. The TSC generates an internal VREF of 2.5V (+/-0.05V), however, the NDS uses as external VREF of 3.33V (sinks to 3.31V at low battery charge), the external VREF is always enabled, no matter if internal VREF is on or off. Power Down Mode 1 disables the internal VREF, which may reduce power consumption in single ended mode. After conversion, Power Down Mode 0 should be restored to re-enable the Penirq signal.

Sending the first Command after Chip-Select
Switch chipselect low, then output the command byte (MSB first).

Reply Data
The following reply data is received (via Input line) after the Command byte has been transferred: One dummy bit (zero), followed by the 8bit or 12bit conversion result (MSB first), followed by endless padding (zero).
Note: The returned ADC value may become unreliable if there are longer delays between sending the command, and receiving the reply byte(s).

Sending further Commands during/after receiving Reply Data
In general, the Output line should be LOW during the reply period, however, once when Data bit6 has been received (or anytime later), a new Command can be invoked (started by sending the HIGH-startbit, ie. Command bit7), simultanously, the remaining reply-data bits (bit5..0) can be received.
In other words, the new command can be output after receiving 3 bits in 8bit mode (the dummy bit, and data bits 7..6), or after receiving 7 bits in 12bit mode (the dummy bit, and data bits 11..6).
In practice, the NDS SPI register always transfers 8 bits at once, so that one would usually receive 8 bits (rather than above 3 or 7 bits), before outputting a new command.

 명령을 보내고 난뒤 응답을 수신하는 주기에서 바로 다시 새로운 커맨드를 보내면 데이터를 수신할 수 있다고 한다. 보내는 즉시 6bit를 수신할 수 있으므로 명령을 보내고 Ready가 됬을 때 명령을 날리는 것도 괜찮은 것 같다. 실제 NDS의 터치스크린 코드를 보면 그렇게 구현되어 있다.

 데이터 2Byte로 잘라보내며, ADC가 12bit의 값을 가지는데 6bit씩 잘라서 보내는 것 같다. 데이터를 보낼때 첫비트는 더미 비트로 보내고 나머지 8bit or 12bit가 데이터 그리고 남은 비트는 모두 0으로 체워지므로 2byte를 수신했을 때 아래와 같은 모습이 될 것이다.

  1. bit   76543210 76543210
  2. data  01111111 11111000

 이 값을 정상적인 12Bit로 만들려면 첫번째 byte를 << 5를 하고 두번째 Byte를 >>3 하면 된다.


Touchscreen Position
Read the X and Y positions in 12bit differential mode, then convert the touchscreen values (adc) to screen/pixel positions (scr), as such:

scr.x = (adc.x-adc.x1) * (scr.x2-scr.x1) / (adc.x2-adc.x1) + (scr.x1-1)
  scr.y = (adc.y-adc.y1) * (scr.y2-scr.y1) / (adc.y2-adc.y1) + (scr.y1-1)

The X1,Y1 and X2,Y2 calibration points are found in Firmware User Settings,
DS Firmware User Settings
scr.x1,y1,x2,y2 are originated at 1,1 (converted to 0,0 by above formula).

Touchscreen Pressure
To calculate the pressure resistance, in respect to X/Y/Z positions and X/Y plate resistances, either of below formulas can be used,

Rtouch = (Rx_plate*Xpos*(Z2pos/Z1pos-1))/4096
  Rtouch = (Rx_plate*Xpos*(4096/Z1pos-1)-Ry_plate*(1-Ypos))/4096

The second formula requires less CPU load (as it doesn't require to measure Z2), the downside is that one must know both X and Y plate resistance (or at least their ratio). The first formula doesn't require that ratio, and so Rx_plate can be set to any value, setting it to 4096 results in

touchval = Xpos*(Z2pos/Z1pos-1)

Of course, in that case, touchval is just a number, not a resistance in Ohms.

Touchscreen Notes
It may be impossible to press locations close to the screen borders.
When pressing two or more locations the TSC values will be somewhere in the middle of these locations.
The TSC values may be garbage if the screen becomes newly pressed or released, to avoid invalid inputs: read TSC values at least two times, and ignore BOTH positions if ONE position was invalid.

  터치 스크린 컨트롤러 같은 경우, 값이 쓰레기가 될 수 있기 때문에, 여러번 읽으라고 되어있다. 그래서 하나의 포지션이라도 유효하지 않으면 둘다 무시하면 된다고 한다. 쓰레기 값이 어떤 값인지 값을 한번 체크해 봐야겠다.


Microphone / AUX Channel
Observe that the microphone amplifier is switched off after power up, see:
DS Power Management

Temperature Calculation
TP0 decreases by circa 2.1mV per degree Kelvin. The voltage difference between TP1 minus TP0 increases by circa 0.39mV (1/2573 V) per degree Kelvin. At VREF=3.33V, one 12bit ADC step equals to circa 0.8mV (VREF/4096).
Temperature can be calculated at best resolution when using the current TP0 value, and two calibration values (an ADC value, and the corresponding temperature in degrees kelvin):

K = (CAL.TP0-ADC.TP0) * 0.4 + CAL.KELVIN
Alternately, temperature can be calculated at rather bad resolution, but without calibration, by using the difference between TP1 and TP0:
K = (ADC.TP1-ADC.TP0) * 8568 / 4096
To convert Kelvin to other formats,
Celsius:     C = (K-273.15)
  Fahrenheit:  F = (K-273.15)*9/5+32
  Reaumur:     R = (K-273.15)*4/5
  Rankine:     X = (K)*9/5
The Temperature Range for the TSC chip is -40'C .. +85'C. According to Nintendo, the DS should not be exposed to "extreme" heat or cold, the optimal battery charging temperature is specified as +10'C..+40'C.
The original firmware does not support temperature calibration, calibration is supported by nocash firmware (if present). See Extended Settings,
DS Firmware Extended Settings

 위의 내용은 온도 센서와 마이크에 관한 내용이므로 그냥 넘어가자.



4.1 키 패드(Keypad) 제어

 키 패드 레지스터를 통해 접근가능한 키들은 아래와 같이 비교적 같단하게 읽어올 수 있다. 각 매크로들은 system.h에 있다.


    // Key Interrupt를 발생하도록 한다.
    REG_KEYCNT = 0x7FFF;


    // IRQ_KEYS 발생했는지 체크
    if(REG_IF & IRQ_KEYS)
        REG_IF |= IRQ_KEYS;



    usButton = ~( REG_KEYINPUT );

    if( usButton & KEY_A)

    { ..... }

 위 처리 방식은 Interrupt를 이용한 방식이므로 04 인터럽트 제어(Interrupt Control) 문서를 참조하면 추가적인 사용법을 알 수 있다.

 ARM7에서만 접근 가능한 X/Y 버튼과 터치스크린은 libnds에서 IPC라는 구조체를 사용해서 넘기도록 되어있다. 실제 읽어 들이는 부분은 ARM7에서 처리하고 저장을 IPC 영역에 하여 ARM9에서 읽어 들일 수 있게한다. 자세한 구현은 실제 libnds를 살펴보도록 하자.


4.2 SPI를 통한 터치 스크린(Touch Screen) 제어

 SPI를 이용한 터치스크린의 값을 읽는 코드는 \devitPro\libnds\source\source\arm7 폴더에 touch.c 파일에서 찾을 수 있다. 파일을 열면 touchRead()라는 함수가 있는데, 이것이 터치스크린에서 값을 읽어들이는 함수이다.

  1. //---------------------------------------------------------------------------------
    uint16 touchRead(uint32 command) {
     uint16 result, result2;
  2.  uint32 oldIME = REG_IME;
  3.  REG_IME = 0;
  4.  // Write the command and wait for it to complete
     REG_SPIDATA = command;
  5.  // Write the second command and clock in part of the data
     REG_SPIDATA = 0; <== 데이터를 즉시 받기 위해 Dummy Command를 날림
     result = REG_SPIDATA;
  6.  // Clock in the rest of the data (last transfer)
     REG_SPICNT = SPI_ENABLE | 0x201; <== 마지막 데이터 보내는 부분. SPI_CONTINUOUS가 없음
     REG_SPIDATA = 0; <== 데이터를 즉시 받기 위해 Dummy Command를 날림
  7.  SerialWaitBusy();
  8.  result2 = REG_SPIDATA >>3;
  9.  REG_IME = oldIME;
  10.  // Return the result
     return ((result & 0x7F) << 5) | result2; <== 위에서 설명했던 2Byte를 12bit Value로 변환하는 부분

 SPI 같은 경우 버스를 사용하는 통신이므로 상태란 것이 존재한다. 만약 위의 과정에서 중간에 인터럽트가 발생하여 다른 곳에서 다시 SPI 버스를 사용하게 된다면 낭패가 될 것이다. 그래서 SPI를 사용하는 부분은 인터럽트 불가를 설정하여 그런 일을 방지한다.



 아직 터치스크린쪽은 devkitPro의 기본 Library를 이용했을 때, 튀는 문제가 많은 편이다. 여러가지가 맞물려 있어서 그런 것이겠지만 추후 수정을 좀 해야 할 부분 같다.



이 글은 스프링노트에서 작성되었습니다.