?>

Hard Copy World

HCW

Arduino advanced

Home > Learning >

Arduino advanced

아두이노 Timer 사용하기

페이지 정보

작성자 하드카피 쪽지보내기 메일보내기 자기소개 아이디로 검색 전체게시물 작성일14-11-17 16:50 조회49,100회 댓글9건

본문

주기적으로 반복되는 동작을 요하는 경우에는 정확한 시간 뒤 특정한 코드가 실행될 수 있도록 타이머를 사용하게 됩니다. 타이머는 알람처럼 시간 관련된 이벤트를 위해 설계된 함수입니다.

아래 내용이 어려우시다면 1, 2, 6, 7, 8 번 항목만 참고하세요.

 

1. 아두이노에서의 타이머

 

비록 아두이노 프로그래밍을 할 때 타이머를 사용하는 경우는 흔치 않지만, 역설적이게도 타이머는 흔히 사용되는 함수 중 하나입니다. 왜냐면 아두이노의 시간관련 함수들 - delay(), millis(), micros(), delayMicroseconds(), PWM 함수인 analogWrite() 및 tone(), noTone() 함수들 및 서보모터 라이브러리 등이 내부적으로는 타이머를 사용하기 때문입니다.

타이머는 (혹은 카운터라 불리우는 것들은) 아두이노 컨트롤러에 내장된 하드웨어의 한 구성요소입니다. 그리고 사용자는 특별한 레지스터들을 이용해서 타이머의 동작방식, 주기 등등을 프로그래밍 할 수 있습니다.

아두이노의 Atmel AVR ATmega168, ATmega328 컨트롤러는 3개의 타이머 - timer0, timer1, timer2 를 가지고 있습니다. timer0, timer2 는 8비트 타이머고 timer1은 16비트 타이머입니다. 8비트와 16비트 타이머의 차이점은 해상도입니다. 8비트는 256단계의 값을 가지고 16비트는 65536 단계의 값을 가지고 있습니다. (ATmega1280, ATmega2560 칩은 6개의 타이머를 제공합니다. timer0, timer1, timer2 는 ATmega128/328 과 동일한데 timer3, timer4, timer5는 16비트 타이머로 동작합니다.)

모든 타이머는 아두이노의 시스템 클럭에 종속적입니다. 일반적으로 시스템 클럭이 16MHz 이므로 여기에 맞춰 아래 내용을 보셔야 합니다. Pro mini 보드와 같이 8MHz 로 동작하는 경우는 다르게 적용됩니다.

타이머 하드웨어는 특별한 타이머 레지스터에 의해 설정됩니다. 아두이노 펌웨어에서는 모든 타이머가 1kHz frequency 로 맞춰져 있고, 인터럽트가 활성화되어 있습니다.

 

2. Timer0, Timer1, Timer2

 

Timer0 는 8비트 타이머로 시간관련 함수에 사용되고 있습니다. 따라서 Timer0 레지스터를 변경하면 delay(), millis(), micros() 와 같은 시간관련 함수들도 영향을 받게 됩니다.

Timer1 은 16비트 타이머입니다. 아두이노 UNO 보드의 경우 서보모터 라이브러리(Servo)가 Timer1을 사용합니다. (아두이노 Mega 보드에서는 Timer5 를 사용)

Timer2 는 8비트 타이머로 tone() 함수 등에 사용됩니다.

 

3. 타이머 레지스터 (Timer Register)

 

타이머 레지스터를 통해 타이머 동작을 바꿀 수 있습니다. 레지스터는 아두이노 시스템 영역에서 사용하는 설정 값 정도로 보시면 되겠습니다. 가장 중요한 타이머 레지스터는 TCCRx (Timer/Counter Control Register) 입니다.

tccr1a

tccr1b

  1. TCNTx - Timer/Counter Register. 실질적인 타이머 동작 값이 여기에 저장됩니다.
  2. OCRx - Output Compare Register
  3. ICRx - Input Capture Register (16비트 타이머용)
  4. TIMSKx - Timer/Counter Interrupt Mask Register. 타이머 인터럽트 활성화/비활성화 용도
  5. TIFRx - Timer/Counter Interrupt Flag Register. pending timer interrupt 용도인지 표시

 

 

4. 동작 클럭과 타이머 주기 (Clock select and timer frequency)

 

CPU 동작 클럭에 따라 타이머 주기(timer frequency)는 아래와 같이 결정됩니다.

  1. CPU 동작 주기 = 16MHz
  2. 최대 타이머 카운트 (8비트는 256, 16비트는 65536)
  3. CPU 주기를 선택한 prescaler 로 나눔 (16000000 / 256 = 62500)
  4. 결과값을 다시 원하는 주기로 나눔 (62500 / 2Hz = 31250)
  5. 결과값을 최대 카이머 카운트와 비교 (31250 < 65536 : 적합). 적합하지 않은 경우 더 큰 prescaler 를 선택함

 

 

5. 레지스터와 타이머 동작

 

타이머 모드 (Timer modes)

타이머는 여러가지 타이머 모드로 설정할 수 있습니다. 예를 들어, PWM 모드에서는 OCxy output 값이 PWM 신호를 생성하기 위해 사용됩니다. CTC(Clear timer On Compare match) 모드에서는 타이머 카운터가 compare match register 값에 도달할 때 타이머가 삭제됩니다.

레지스터에 따라 변화되는 모드는 아래 표를 참고하세요.

waveform-gen

 

타이머 인터럽트 (Timer interrupts)

인터럽트에 대한 기본내용은 아래 링크를 참고하세요.

http://www.hardcopyworld.com/gnuboard5/bbs/board.php?bo_table=lecture_pract&wr_id=2

Pending interrupt 가 ISR(Interrupt Service Routine)을 실행하기 위해서는 아래 조건이 활성화 되어야 합니다.

  • 인터럽트가 활성화 되어야 함
  • 관련된 인터럽트 마스크가 활성화 되어야 함.

인터럽트는 interrupts(), noInterrupts() 함수로 활성화/비활성화 할 수 있습니다. 아두이노의 기본설정은 인터럽트 활성화 상태입니다. 인터럽트 마스크는 interrupt mask register(TIMSKx) 에서 setting/clearing bit 를 설정함으로써 활성화/비활성화 할 수 있습니다.

인터럽트가 발생할 때 interrupt flag register(TIFRx) 값이 설정됩니다. 이 값에 따라 ISR(Interrupt Service Routine)이 실행되고, 실행 후 자동으로 해제됩니다. 혹은 수동으로 값을 변경할 수 있습니다.

일반적으로 코드에서 attachInterrupt(), detachInterrupt()  함수에 의해 설정되는 인터럽트는 외부 인터럽트 핀에의해 활성화 됩니다. 이것들은 다른 인터럽트 소스이므로 여기서는 논외로 합니다.

타이머는 여러종류의 타이머 인터럽트를 발생시킬 수 있습니다. 레지스터와 비트 설정 값은 아두이노의 프로세서 데이터 시트를 참고하세요. (Atmega328 or Atmega2560). suffix x 는 타이머 숫자 (0~5)를 의미하고 suffix y 는 출력 값 (A, B, C)을 의미합니다.  예를들어 TIMSK1 = timer1 interrupt mask register, OCR2A = timer2 output compare register A 입니다. 아래에서 연관된 설명이 이어집니다.

 

Timer overflow

타이머 오버플로우는 타이머 카운트가 최대값에 도달함을 의미합니다. 타이머가 오버플로우되면 인터럽트가 발생하고 timer overflow bit (TOVx) 값이 interrupt flag register(TIFRx) 에 설정됩니다. 만약 interrupt mask register(TIMSKx)에 timer overflow interrupt enable bit(TOIEx) 값이 설정되어 있다면 interrupt service routine - ISR(TIMERx_OVF_vect) 함수가 호출될 것입니다.

 

Output Compare Match

OCFxy 플래그가 interrupt flag register(TIFRx) 에 설정되면 Output compare match interrupt 가 발생합니다. Output compare interrupt 가 발생하면 interrupt mask register(TIMSKx) 에 output compare interrupt enable bit(OCIExy) 값이 설정됩니다. 그리고 output compare match interrupt service - ISR(TIMERx_CAPT_vect) 함수가 호출될 것입니다.

 

Timer input capture

Interrupt flag register(TIFRx)에 input capture flat bit (OCFxy) 가 설정되어 있으면 Timer input capture interrupt 가 발생합니다. Interrupt mask register(TIMSKx) 에 input capture interrupt enable bit (ICIEx) 가 설정되면 timer input capture interrupt service routine - ISR(TIMERx_CAPT_vect) 함수가 실행됩니다.

 

PWM 과 timer

타이머와 PWM 핀은 밀접하게 연관되어 있습니다. 프로세서의 핀아웃 문서나 데이터 시트를 보면 OCRxA, OCRxB, OCRxC 로 표기된 PWM 핀이 있습니다. (x는 타이머 숫자 0~5) Mega를 제외한 일반적인 아두이노는 3개의 타이머와 6개의 PWM 핀이 있으며 아래와 같이 연관되도록 설정되어 있습니다.

  • Pin 5, 6 : timer0 에 의해 컨트롤
  • Pin 9, 10 : timer1 에 의해 컨트롤
  • Pin 11, 3 : timer2 에 의해 컨트롤

 

 

6. 주의사항

 

서보 라이브러리(Servo library)는 Timer1 을 사용합니다. 서보 라이브러리를 사용할 경우 9, 10번 PWM 은 사용할 수 없습니다. 아두이노 Mega 보드의 경우는 좀 더 복잡해서 사용하는 서보의 갯수에 따라 제한이 바뀝니다. 아래 링크를 참고하세요.

http://letsmakerobots.com/content/arduino-101-timers-and-interrupts

tone() 함수는 적어도 timer2 를 사용합니다. 이 경우 3, 11번 핀의 PWM을 사용할 수 없습니다. Mega 보드의 경우는 9, 10번 핀을 사용할 수 없습니다.

 

7. 라이브러리

 

타이머 레지스터를 수정해가며 타이머를 사용한다는 것은 굉장히 소모적이고 복잡한 방법입니다. 따라서 보다 간편하게 사용할 수 있는 아래 라이브러리를 추천합니다. 물론 세부적으로 원하는 컨트롤을 요하는 경우 위와 같은 방법을 사용할 수도 있겠지만요.

Timer1,  Timer3 라이브러리 : ATmega128/328 의 16비트 타이머(Timer1)를 설정해주는 라이브러리입니다. 일반적인 라이브러리 설정경로와는 다르게 (Arduino/hardware/libraries/) 에 설치하셔야 합니다. Mega 보드에서는 Timer3 라이브러리를 사용하시길 추천합니다.

타이머를 이용한 IR 리모트 컨트롤 예제 및 라이브러리

SW Timer 라이브러리SimpleTimerMetro :  아두이노에 built-in 된 hardware 타이머를 직접 사용하는 방식이 아니라 단순히 millis() 함수를 이용해서 타이머를 구현한 라이브러리. 단순하고 하드웨어적인 정밀도와 제어가 필요치 않은 경우 유용.

 

8. 예제

 

아래 링크 하단부에 타이머를 직접 설정해서 사용하는 예제가 있습니다.

http://letsmakerobots.com/content/arduino-101-timers-and-interrupts

 

 

참고자료 :

아두이노 공식 페이지 : http://letsmakerobots.com/content/arduino-101-timers-and-interrupts,  http://playground.arduino.cc/Code/Timer1

타이머 설정 방법(instructables.com) : http://www.instructables.com/id/Arduino-Timer-Interrupts/

Timer and Interrupts : http://letsmakerobots.com/content/arduino-101-timers-and-interrupts

 


하드카피 쪽지보내기 메일보내기 자기소개 아이디로 검색 전체게시물

모든 전통이 한 때는 오해를 면치 못했다. 마찬가지로 모든 아이디어는 한 때는 비웃음을 면치 못했다. -홀브룩 잭슨 ::::: 느닷없이 떠오르는 생각이 가장 귀중한 것이며, 보관해야할 가치가 있는 것이다. -베이컨 ::::: 미리 숙고하면 힘든 일도 그저 실행에 옮기는 순간 쉬워진다. - 로버트 M. 피어식

추천 0
  • 페이스북으로 보내기
  • 트위터로 보내기
  • 구글플러스로 보내기

댓글목록

김진팔님의 댓글

김진팔 이름으로 검색 작성일

안녕하세요 . 좋은 정보 감사합니다.
제가 msTimer 2 라는 라이브러리를 사용하여 뭔가를 만들었는데 최대 설정가능시간이 32초 라는 것을 발견했습니다.
이 시간을 더 늘릴 수는 없나요?  밀리초 단위까지 사용할 필요는 없습니다. 최소 초 단위만 써도 되는데
방법이 없나요?

arsviator님의 댓글

arsviator 이름으로 검색 작성일

ISR에 카운터 변수를 두면 됩니다.

 void isr()
{
  static int cnt = 0;

  cnt++;
  if (cnt == 10) {
    cnt = 0;
    // 여기에 실행할 코드를 넣음
  }
  ...
}

예를 들어 100초로 설정하려면 위의 코드를 10초마다 호출되도록 설정하면 되겠죠.

김진팔님의 댓글

김진팔 이름으로 검색 댓글의 댓글 작성일

빠르고 친절하신 답변 정말 감사합니다. 방법이 없는 줄 알고 어제 밤세 뒤척이면서 이런 궁리 저런 궁리를 했지만 뾰족한 수가 없었는데 이렇게 한방에 해결 되는 군요.
하지만 제가 워낙 무식한 초보라서 저 예제를 가지고 원하는 결과를 얻으려면 한달정도 고생해야 할 것 같습니다.
그래서 허락하신다면, 부끄러움을 무릅쓰고 한번만 더 질문을 하고싶습니다.
제가 작성한 소스코드는 다음과 같은 간단한 것입니다.
선생님이 알려주신 코드는 어디쯤에 어떻게 적용하는 게 좋을까요?
한번만 더 귀하신 가르침 부탁드립니다.

#include <Servo.h>  // 서보 라이브러리 포함
#include <MsTimer2.h> //타이머 라이브 러리 포함(인터넷 다운 받아야 함.)


Servo myservo;  // create servo object to control a servo  // myservo라는 오브젝트를 만들어야함.
                // a maximum of eight servo objects can be created  8개까지 만들 수 있음.
 
int pos = 0;    // variable to store the servo position // 서보 포지션을 저장하기 위한 변수

void cutPower(){ //  cutPower라는 인터럽트 함수를 만듦.(정해진 시간마다 이곳의 내용을 실행)
 

// 90도에서 180도까지 움직이되 1단계씩 움직이라는 명령

  for(pos = 90; pos < 180; pos += 1)  // goes from 90 degrees to 180 degrees
  {                                  // in steps of 1 degree
    myservo.write(pos);              // tell servo to go to position in variable 'pos'
    delay(15);                      // waits 15ms for the servo to reach the position
  }
 
 
}
void setup() {
 pinMode(8,INPUT); // 8번핀으로 PIR센서값을 입력받는다.
 MsTimer2::set(1000*5 , cutPower); // 1000곱하기 5 밀리초 =5초 동안 타이머가 돈다.
 MsTimer2::start(); // 타이머 시작

  myservo.attach(9);  // 9번핀에 서보 신호선을 꽂는다.

}
 
 
void loop() {
  int val = digitalRead(8); // 8번핀으로 PIR센서 신호가 들어오는지 값을 읽는다.

  if(val == HIGH){  // 만일 그 값이 HIGH 이면
    MsTimer2::stop();  // 타이머를 멈춘다.
    MsTimer2::start();  // 타이머를 시작한다.
  }

}

arsviator님의 댓글

arsviator 이름으로 검색 작성일

void cutPower(){ //  cutPower라는 인터럽트 함수를 만듦.(정해진 시간마다 이곳의 내용을 실행)
  static int cnt = 0;

  cnt++;
  if (cnt == 10) {
    cnt = 0;
    // 90도에서 180도까지 움직이되 1단계씩 움직이라는 명령
    for(pos = 90; pos < 180; pos += 1)  // goes from 90 degrees to 180 degrees
    {                                  // in steps of 1 degree
      myservo.write(pos);              // tell servo to go to position in variable 'pos'
      delay(15);                      // waits 15ms for the servo to reach the position
    }
  }
}

cutPower 함수를 이런 식으로 바꾸시면 됩니다.

김진팔님의 댓글

김진팔 이름으로 검색 댓글의 댓글 작성일

정말 감사합니다. 이렇게 빨리 답변을 해 주시다니.....
다음에 혹시 만난다면 꼭 맥주를 한잔 사드리겠습니다.^^

김진팔님의 댓글

김진팔 이름으로 검색 작성일

지난 번에 알려주신 방법으로 문제가 잘 해결 되어 원하는 동작을 얻었습니다.
그래서 static 변수라는 것에 대해 인터넷을 찾아봤는데  대략적인 지식을 알 수 있었습니다.
그러나 다른 곳에서 알 수 없는 것은 
질문) 한 프로그램 내에서 static 변수를 몇번이나 선언할 수 있느냐? 입니다.

한 프로그램 내에서 한번만 선언 가능한지?
아니면 원하는 만큼 무한히 선언 가능한지?

알려주시면 감사하겠습니다.

arsviator님의 댓글

arsviator 이름으로 검색 댓글의 댓글 작성일

변수의 종류를 결정하는것 뿐이고 메모리 용량이 허용하는 한 갯수에 대한 제한은 없습니다.

LJG님의 댓글

LJG 이름으로 검색 작성일

안녕하세요 타이머에 대해 잘 배웠습니다. 그러나 아직 잘와닿지가 않네요
제가 아두이노에서 servo.h와 tone.h를 같이사용하려는데 타이머에 문제가 있다고 하네요..
같은 타이머를 쓴다고 하는데 혹시 해결방안을 알 수 있을까요?

최고관리자님의 댓글

최고관리자 쪽지보내기 메일보내기 홈페이지 자기소개 아이디로 검색 전체게시물 댓글의 댓글 작성일

PWM 신호를 직접 만드는 방법이 있습니다. 아래 서보모터 가이드의 첫 예제처럼 아주 짧은 시간 간격동안 PWM 신호를 만들어주는 방법입니다. 버저도 유사하게 사용할 수 있습니다.
http://www.hardcopyworld.com/gnuboard5/bbs/board.php?bo_table=tech_motor&wr_id=3

잘 동작하지 않는 경우 유사한 소스를 구글 검색해서 사용해보세요.