07 문쉘(Moon shell)의 터치스크린(Touch Screen) 소스

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

 

들어가기 전에...

 

0.시작하면서...

 libnds의 터치스크린 소스는 값이 튀는 문제가 있다. 물론 소프트웨어 적으로 처리하는 방법이 있지만 완전하지 못해서 가장 널리 쓰이는 문쉘(Moon shell)의 소스를 받아서 터치스크린쪽 소스를 봤다. http://mdxonline.dyndns.org/archives/nds/에 가면 NDS의 문쉘소스를 받을 수 있다.

 

 실제로 이 소스를 이용해서 완전하게 터치스크린의 문제를 해결한 소스는 참고. 터치스크린(Touch Screen)의 튐 현상 해결방안 문서를 참고하면 된다.

 

1.libnds의 touch.c

 \devkitPro\libnds\source\source\arm7 폴더의 touch.c 파일을 열어보면 굉장히 복잡한 소스들이 널려있다. 그중에서 터치 스크린에서 값을 읽는 핵심적인 소스만 추리면 아래와 같다.

  1. static bool touchInit = false;
    static s32 xscale, yscale;
    static s32 xoffset, yoffset;
  2. //---------------------------------------------------------------------------------
    int16 readTouchValue(uint32 command, int16 *dist_max, u8 *err){
    //---------------------------------------------------------------------------------
     int16 values[5];
     int32 aux1, aux2, aux3, dist, dist2, result = 0;
     u8 i, j, k;
  3.  *err = 1;
  4.  SerialWaitBusy();
  5.  REG_SPICNT = SPI_ENABLE | SPI_BAUD_2MHz | SPI_DEVICE_TOUCH | SPI_CONTINUOUS;
     REG_SPIDATA = command;
  6.  SerialWaitBusy();
  7.  for(i=0; i<5; i++){
      REG_SPIDATA = 0;
      SerialWaitBusy();
  8.   aux1 = REG_SPIDATA;
      aux1 = aux1 & 0xFF;
      aux1 = aux1 << 16;
      aux1 = aux1 >> 8;
  9.   values[4-i] = aux1;
  10.   REG_SPIDATA = command;
      SerialWaitBusy();
  11.   aux1 = REG_SPIDATA;
      aux1 = aux1 & 0xFF;
      aux1 = aux1 << 16;
  12.   aux1 = values[4-i] | (aux1 >> 16);
      values[4-i] = ((aux1 & 0x7FF8) >> 3);
     }
  13.  REG_SPICNT = SPI_ENABLE | SPI_BAUD_2MHz | SPI_DEVICE_TOUCH;
     REG_SPIDATA = 0;
     SerialWaitBusy();
  14.  dist = 0;
     for(i=0; i<4; i++){
      aux1 = values[i];
  15.   for(j=i+1; j<5; j++){
       aux2 = values[j];
       aux2 = abs(aux1 - aux2);
       if(aux2>dist) dist = aux2;
      }
     }
  16.  *dist_max = dist;
  17.  for(i=0; i<3; i++){
      aux1 = values[i];
  18.   for(j=i+1; j<4; j++){
       aux2 = values[j];
       dist = abs(aux1 - aux2);
  19.    if( dist <= range ){
        for(k=j+1; k<5; k++){
         aux3 = values[k];
         dist2 = abs(aux1 - aux3);
  20.      if( dist2 <= range ){
          result = aux2 + (aux1 << 1);
          result = result + aux3;
          result = result >> 2;
          result = result & (~7);
  21.       *err = 0;
  22.       break;
         }
        }
       }
      }
     }
  23.  if((*err) == 1){
      result = values[0] + values[4];
      result = result >> 1;
      result = result & (~7);
     }
  24.  return (result & 0xFFF);
    }
  25. //---------------------------------------------------------------------------------
    // reading pixel position:
    //---------------------------------------------------------------------------------
    touchPosition touchReadXY() {
    //---------------------------------------------------------------------------------
  26.  int16 dist_max_y, dist_max_x, dist_max;
     u8 error, error_where, first_check, i;
  27.  touchPosition touchPos = { 0, 0, 0, 0, 0, 0 };
  28.  if ( !touchInit ) {
  29.   xscale = ((PersonalData->calX2px - PersonalData->calX1px) << 19) / ((PersonalData->calX2) - (PersonalData->calX1));
      yscale = ((PersonalData->calY2px - PersonalData->calY1px) << 19) / ((PersonalData->calY2) - (PersonalData->calY1));
  30.   xoffset = ((PersonalData->calX1 + PersonalData->calX2) * xscale  - ((PersonalData->calX1px + PersonalData->calX2px) << 19) ) / 2;
      yoffset = ((PersonalData->calY1 + PersonalData->calY2) * yscale  - ((PersonalData->calY1px + PersonalData->calY2px) << 19) ) / 2;
      touchInit = true;
     }
  31.  uint32 oldIME = REG_IME;
  32.  REG_IME = 0;
  33.  first_check = CheckStylus();
     if(first_check != 0){
      error_where = 0;
  34.   touchPos.z1 =  readTouchValue(TSC_MEASURE_Z1 | 1, &dist_max, &error);
      touchPos.z2 =  readTouchValue(TSC_MEASURE_Z2 | 1, &dist_max, &error);
  35.   touchPos.x = readTouchValue(TSC_MEASURE_X | 1, &dist_max_x, &error);
      if(error==1) error_where += 1;
  36.   touchPos.y = readTouchValue(TSC_MEASURE_Y | 1, &dist_max_y, &error);
      if(error==1) error_where += 2;
  37.   REG_SPICNT = SPI_ENABLE | SPI_BAUD_2MHz | SPI_DEVICE_TOUCH | SPI_CONTINUOUS;
      for(i=0; i<12; i++){
       REG_SPIDATA = 0;
  38.    SerialWaitBusy();
      }
  39.   REG_SPICNT = SPI_ENABLE | SPI_BAUD_2MHz | SPI_DEVICE_TOUCH;
      REG_SPIDATA = 0;
  40.   SerialWaitBusy();
  41.   if(first_check == 2) error_where = 3;
  42.   switch( CheckStylus() ){
      case 0:
       last_time_touched = 0;
       break;
      case 1:
       last_time_touched = 1;
  43.    if(dist_max_x > dist_max_y)
        dist_max = dist_max_x;
       else
        dist_max = dist_max_y;
  44.    break;
      case 2:
       last_time_touched = 0;
       error_where = 3;
  45.    break;
      }
  46.   s16 px = ( touchPos.x * xscale - xoffset + xscale/2 ) >>19;
      s16 py = ( touchPos.y * yscale - yoffset + yscale/2 ) >>19;
  47.   if ( px < 0) px = 0;
      if ( py < 0) py = 0;
      if ( px > (SCREEN_WIDTH -1)) px = SCREEN_WIDTH -1;
      if ( py > (SCREEN_HEIGHT -1)) py = SCREEN_HEIGHT -1;
  48.   touchPos.px = px;
      touchPos.py = py;

  49.  }else{
      error_where = 3;
      touchPos.x = 0;
      touchPos.y = 0;
      last_time_touched = 0;
     }
  50.  UpdateRange(&range, dist_max, error_where, last_time_touched);
  51.  REG_IME = oldIME;

  52.  return touchPos;
  53. }

 음.. 일단 소스가 좀 복잡하고 뭔가 잘하려고 노력한것 같은데, 문제가 좀 있어보인다. readTouchValue() 소스를 일단 보면 값을 여러번 읽어서 그 값을 평균내는 것 비슷하게 동작하는 것 같은데...  더 문제는 값을 다 읽고 난 다시 CheckStylus() 함수를 이용해서 터치스크린의 상태를 얻어온다는 점이다.

 만약 위에서 값을 읽었을 때 정상적인 터치스크린의 값이 들어왔는데, 뒤에 CheckStylus() 함수에서 체크할 때는 터치가 떨어진 상태라면...? 약간의 시간차가 있는데 이 시간차 때문에 문제가 있어 보인다(실제로도 문제가 있다. ㅡ,.ㅡ;;;)

 

2.문쉘(Moon Shell)의 _touch.c

 문쉘의 터치스크린 소스는 의외로 간단하다. 전체 소스는 아래와 같다.

  1. #include <nds.h>
    #include <nds/jtypes.h>
    #include <nds/system.h>
    #include <nds/arm7/touch.h>
    #include <nds/registers_alt.h>
  2. #include <stdlib.h>
  3. #include "_touch.h"
  4. //---------------------------------------------------------------------------------
    __attribute__((noinline)) static uint16 _touchRead(uint32 command) {
    //---------------------------------------------------------------------------------
     uint16 result;
     SerialWaitBusy();
  5.  // Write the command and wait for it to complete
     REG_SPICNT = SPI_ENABLE | SPI_BAUD_2MHz | SPI_DEVICE_TOUCH | SPI_CONTINUOUS; //0x0A01;
     REG_SPIDATA = command;
     SerialWaitBusy();
  6.  // Write the second command and clock in part of the data
     REG_SPIDATA = 0;
     SerialWaitBusy();
     result = REG_SPIDATA;
  7.  // Clock in the rest of the data (last transfer)
     REG_SPICNT = SPI_ENABLE | 0x201;
     REG_SPIDATA = 0;
     SerialWaitBusy();
  8.  // Return the result
     return ((result & 0x7F) << 5) | (REG_SPIDATA >> 3);
    }

  9. //---------------------------------------------------------------------------------
    uint32 _touchReadTemperature(int * t1, int * t2) {
    //---------------------------------------------------------------------------------
     *t1 = _touchRead(TSC_MEASURE_TEMP1);
     *t2 = _touchRead(TSC_MEASURE_TEMP2);
     return 8490 * (*t2 - *t1) - 273*4096;
    }

  10. static bool touchInit = false;
    static s32 xscale, yscale;
    static s32 xoffset, yoffset;

  11. //---------------------------------------------------------------------------------
    __attribute__((noinline)) static s32 readTouchValue(int measure, int retry , int range) {
    //---------------------------------------------------------------------------------
     int i;
     s32 this_value=0, this_range;
  12.  s32 last_value = _touchRead(measure | 1);
  13.  for ( i=0; i < retry; i++) {
      this_value = _touchRead(measure | 1);
      this_range = abs(last_value - this_value);
      if (this_range <= range) break;
     }
     
     if ( i == range) this_value = 0;
     return this_value;
  14. }
  15. static int _MaxRetry = 5;
    static int _MaxRange = 30;
  16. void _touchReadXY_AutoDetect(void)
    {
      xscale = ((PersonalData->calX2px - PersonalData->calX1px) << 19) / ((PersonalData->calX2) - (PersonalData->calX1));
      yscale = ((PersonalData->calY2px - PersonalData->calY1px) << 19) / ((PersonalData->calY2) - (PersonalData->calY1));
     
      xoffset = ((PersonalData->calX1 + PersonalData->calX2) * xscale  - ((PersonalData->calX1px + PersonalData->calX2px) << 19) ) / 2;
      yoffset = ((PersonalData->calY1 + PersonalData->calY2) * yscale  - ((PersonalData->calY1px + PersonalData->calY2px) << 19) ) / 2;
     
      touchInit = true;
    }
  17. // reading pixel position:
    //---------------------------------------------------------------------------------
    touchPosition _touchReadXY() {
    //---------------------------------------------------------------------------------
  18.  if(touchInit==false){
       REG_IME=0;
       while(1);
     }
  19.  touchPosition touchPos;
  20. /*
      if((xscale<128)||(yscale<128)||(xoffset<128)||(yoffset<128)){
     touchPos.px = 0;
     touchPos.py = 0;
  21.  return touchPos;
      }
    */
     
     touchPos.x = readTouchValue(TSC_MEASURE_X, _MaxRetry, _MaxRange);
     touchPos.y = readTouchValue(TSC_MEASURE_Y, _MaxRetry, _MaxRange);
  22.  s16 px = ( touchPos.x * xscale - xoffset + xscale/2 ) >>19;
     s16 py = ( touchPos.y * yscale - yoffset + yscale/2 ) >>19;
  23.  if ( px < 0) px = 0;
     if ( py < 0) py = 0;
     if ( px > (SCREEN_WIDTH -1)) px = SCREEN_WIDTH -1;
     if ( py > (SCREEN_HEIGHT -1)) py = SCREEN_HEIGHT -1;
  24.  touchPos.px = px;
     touchPos.py = py;
  25.  return touchPos;
  26. }

 libnds에서 봤던 평균을 낸다던지 하는 소스는 여기서 보이지 않는다. 아주 간단하게 처음 터치 스크린의 값을 읽고 그값과 다음 읽은 값과 비교하여 차이가 적당한 범위 내이면 읽은 값이 정확하다고 판단하여 리턴하는 형식이다. 참으로 간단하면서도 명확한 방법이 아닐 수 없다. 만약 첫번째 읽은 값이 잘못된 값이면 touchPos.x와 touchPos.y의 값이 문제가 있을 것이므로 이것을 이용해서 판단하면 될 듯 하다.

 실제 libnds를 이용해서 touch.x, touch.y의 값을 같이 사용해서 판단하고 있으나 제대로 동작하지 않는데, 문쉘의 방법은 괜찮을 듯 하다(실제로도 문쉘은 잘 동작한다.)

 

3.마치며...

 간단한 방법이 제일 좋은 듯하다. libnds의 경우 여러가지 경우를 너무 생각해서 소스가 복잡하고 제대로 동작하지 않는 것 같다. 나중에 소스를 손봐서 새로 테스트 해봐야 겠다.

 

 

 

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

+ Recent posts