2011년9월9일_ARM_AT91SAM7S256 타이머/카운터 비교매치 인터럽트를 응용하여 초음파센서SRF05에서 나오는 Echo출력의 펄스폭을 측정해보자. (펄스폭은 센서와 물체 사이에 떨어진 거리)



image

우리가 거리측정에 사용할 센서는 SRF05 초음파센서이고 SRF04보다 발전된 모듈로 비용, 성능측면에서 발전된 제품이다.
SRF04와 완벽한 호환이 되고 범위는 3m에서 4m로 늘어났으며 새로운 모드가 추가되어 핀(포트) 수를 절약할 수 있다.
SRF05는 기본 우표(?)와 명령에서 펄스를 실행하는 picaxe시간으로 속도가 느린 컨트롤러를 제공하기 위해 에코펄스 전에 작은 지연이 포함되어 있다고 한다. (구글번역이라 완벽한 번역은 아니나 센서출력신호에 대해 알 수 있다.)



image

SRF05초음파센서에서 신호를 입력 받아 펄스폭을 측정하기 위해 AT91SAM7S256 MCU 내부의 타이머/카운터(TC0)를 사용하고,
그 값을 표시하기 위해 CLCD와 DBGU를 사용해 RS232케이블 통해 PC로 데이터를 전송한다. 

  

image

image
SRF05테스트에 필요한 하드웨어의 블록다이어그램은 상기와 같고,
초음파센서의 출력 Echo Output은 ARM Board의 PA8에,
초음파센서의 입력 Trigger Input은 PA7에 연결한다.
(회로도와 비슷하게 전원핀은 표시하지 않았다.)

CLCD전과 같이 PA11을 RS, PA12을 R/W, PA13 EN에 연결한다.
PA16 ~ PA23까지 8개의 핀을 데이터버스로 사용하여 LCD에 명령과 데이터를 전송해 센서에서 얻은 값을 표시할 것이다.

PA9와 PA10은 ARM Board내부적으로 MAX3232 IC와 연결되어,
CMOS레벨의 직렬 디지털 신호 데이터를 RS232신호레벨로 바꾸어 PC(개인용 컴퓨터)의 COM1단자에 연결한다.

이렇게 연결 후 각 핀들을 아래와 같이 C Code에서 정의한다.



   1:  // SRF05 초음파센서
   2:  #define    TRG_PIN     7        //출력
   3:  #define    ECHO_PIN    8        //인터럽트핀
   4:   
   5:  // 16*2 CLCD
   6:  #define    CLCD_RS    (1 << 11)            //LCD레지스터 선택 (1 = DATA, 0 = INST)
   7:  #define    CLCD_RW    (1 << 12)            //LCd읽기/쓰기 선택( 1 = READ, 0 = WRITE)
   8:  #define    CLCD_EN    (1 << 13)            //LCD활성.
   9:  #define    CLCD_BS    (0xFF << 16)         //데이터버스 I/O

잘 보면 센서와 LCD의 핀 정의가 다른데,
LCD는 PIOA_CODR과 PIOA_SODR레지스터에 바로 대입할 수 있도록 시프트연산을 하였으나,
SRF05는 시프트연산을 하지 않아 해줘야 한다.
I/O핀 정의를 시프트연산이라 어렵게 생각하지 말고 해당 비트 번호를 적어 주면 된다.
시프트연산을 하여도 이를 컴파일러가 알아서 최적화해 해당 수치로 바꾸어 주니,
연산을 여러 번하여 느려진다고 걱정할 필요도 없다.
(우리는 많은 사소한 것에 대해 걱정하고 산다.)

image

센서의 스펙(사용설명서, 사양서)을 보면 상기와 같은 타이밍도를 볼 수 있다.
Trigger input핀으로 최소 10us의 펄스를 인가하면,
SRF05센서로부터 초음파가 발생하여 그 초음파가 물체에 부딪혀 반사되어 돌아오면,
SRF05센서의 Echo output핀으로 100us에서 25ms사이의 펄스폭을 가진 신호가 출력된다.
만약 물체를 찾지 못 하였다면(반사가 없으면) 30ms의 타임아웃에 걸려 30ms의 신호가 나온다.

그런데 이 타이밍도에서 누락된 사실이 있는데,
반사되어 돌아온 시간외에 50us이하의 짧은 펄스신호가 앞에 포함되어 있다는 것이다.
이 펄스신호는 무시하고 뒤에 오는 펄스폭만 측정해야 한다.
그래서 아래와 같이 uiFlag라는 변수를 하나 더 써서 두 번째 인터럽트시에만 폭을 측정하도록 한다.

   1:      static volatile unsigned int uiFlag = 0;    
   2:   
   3:      isr = PIOA_ISR;    //신호 들오온 장치 번호 저장  , 읽어야 인터럽트 처리.
   4:                      //(한번 읽으면 레지스터내용은 지워지므로 변수에 저장)
   5:      
   6:      if(0 != uiFlag)
   7:      {
   8:          uiCnt = ICNT;        //ICNT값을 확보.
   9:          TC0_CCR = (1 << CLKDIS);        //TC0에 클록이 공급되지 않도록.
  10:   
  11:          LCD_Cursor(1, 0);
  12:          printf_LCD("%dcm", uiCnt, 0, 0);            //LCD에 표시
  13:          printf_DBGU("%dcm\n", uiCnt, 0, 0);
  14:   
  15:          uiFlag = 0;
  16:      }
  17:      else
  18:      {
  19:          uiFlag = 1;
  20:      }

좀 더 효율적인 코드도 짤 수 있어야 하나 급하니 이대로 하고 후에 더 생각해보자.


● 전체코드
▷ main.c만 미리보기

   1: /*********************************************************
   2: 
   3:     제    목: 초음파센서 SRF05
   4:             2011.09.09
   5:     
   6: *********************************************************/
   7: #include "PIOA.h"
   8: #include "DBGU.h"
   9: #include "LCD.h"
  10: #include "PMC.h"
  11: #include "LED.h"
  12: #include "UserFunc.h"
  13: #include "Timer.h"
  14: #include "Ultra.h"
  15:  
  16: int main(void)
  17: {
  18:     PORT_INIT();
  19:     LED_INIT();
  20:     LCD_INIT();
  21:     Ultra_INIT();
  22:     DBGU_INIT();
  23:     //Timer0_INIT(TIMER_CLOCK5, 49 , MyCounter);
  24:     
  25:     Send_String("\n\r초음파센서 SRF-05 test\n\r");
  26:     LCD_String("Test SRF-05");
  27:  
  28:     
  29:     while(1)
  30:     {
  31:         Recv_Char();        //키보드 입력 대기만
  32:         Ultra_TRG();            //초음파 발생.        
  33:     }
  34:     
  35:     while(1);            //return되면 안됨. 돌아갈 곳이 없어. 집이 없어. 
  36:  
  37:     return 0;
  38: }
  39:  













image_thumb1[1]

image_thumb4[1]