티스토리 뷰

입문

타이머/카운터 - TCNT

Just4Fun 2016. 4. 9. 15:00

잠을 자려고 누웠는데, 잠이 쉬이 오지 않는다.  평소에는 잘 듣지 못했던 벽에 걸린 시계에서 탁, 탁, 탁 초침 소리가 일정한 간격으로 들려온다.  하나, 둘, 셋 ... 소리가 들릴때마다 숫자를 더해간다. 어느덧 300까지 세었다.  그런데 어느 순간부터 일정하게 들려오던 초침 소리가 불규칙한 간격으로 들리기 시작하였다. 

소리가 일정한 간격으로 들려 올 때에는, 1초에 한번씩 소리가 난다는 것을 알고 있으므로 300번 세었으면 5분의 시간이 흘렀다는 것을 알 수 있다.  그러나 불규칙한 간격으로 소리가 들린다면 소리가 몇번 났는지는 알 수 있으나 그 다음부터는 얼마나 시간이 흘렀는지는 알 수 없다.

AVR에 내장되어 있는 타이머/카운터는 위의 얘기와 비슷한 원리로 동작한다.  AVR 뿐만 아니라 대부분의 CPU에 들어있는 타이머는 이런 방식으로 동작한다.  카운터로 들어오는 신호가 일정한 간격을 유지하고, 간격 사이의 시간이 얼마인지 정확히 알고 있으면 타이머로도 동작 가능하다는 것이다.

실제로 Quartz라고 부르는 전자 시계도 결국은 시계 안에 있는 크리스탈이 몇번 진동하는지 카운트하다가 일정한 값에 도달하면 초침을 한번 움직이게 만든 장치에 불과하다.  초침이 60번 움직여 원래 출발했던 자리로 돌아가면 분침이 한칸 옮겨 가 있는것이다.  이를 보고 사람들은 1분의 시간이 경과된 것을 알게 되는 것이다. 

타이머/카운터에 대한 원리에 대해서 간단히 설명하였으므로 실제 AVR에 있는 타이머/카운터를 동작 시켜 보도록 하겠다.

AVR에는 CPU모델에 따라 여러개의 8비트 타이머와 16비트 타이머를 가질 수 있다.  각각의 타이머가 동작되는 방법은 비슷하므로 그 중에서 8비트 타이머를 가지고 설명하도록 한다.

AVR의 타이머/카운터가 어떻게 생겼는지 알아보기 위하여 블럭도를 먼저 살펴본다.

블럭도를 보면 Databus에 다섯개의 레지스터가 연결되어 있는것을 알 수 있다.  카운터로 들어오는 입력은 Tn과 Prescaler이고, 출력으로는 OCnA/B가 있고, CPU로 보낼수 있는 인터럽트 신호로 TOVn, OCnA/B가 있다는 것을 알 수 있다.

Datasheet 문서에 타이머의 동작에 대한 설명이 상세하게 나와 있으므로 직접 한 번 읽어 보는것이 좋다.  인터넷에 올라와 있는 설명들 중에는 정확하지 않거나 잘못 설명되어 있는것들도 제법 많기 때문에 가장 확실한 것은 CPU제조사에서 제공하는 문서를 공부하는 것이다.  어쩌면 이 블로그에서 설명하는것도 사실과 다를수 있으므로 설명을 제대로 하고 있는지 어쩐지 확인해 보기 바란다.

아주 간단하게 타이머/카운터의 동작 흐름을 설명하자면, 블럭도 위쪽에 있는 'Control Logic' 은 "clkTn"으로 되어 있는 입력 신호가 들어 올 때마다 'TCNT'레지스터의 값을 1 증가/감소 시킨다.  TNCT의 값이 'OCRnA/B'레지스터에 있는 값과 같게 될 경우 'OCnA/B'의 출력을 제어할 수 있다.

타이머/카운터의 동작을 확인하기 위하여 다음과 같은 회로를 구성한다.

회로도에서 전원과 UART 연결은 생략 하였다.  이전 글을 참조하여 UART연결을 추가하여야 한다.

Atmel Studio에서 새로운 프로젝트를 만들고 프로젝트 배너메시지를 출력하는 시작프로그램을 만든다.


일단 위와 같은 상태에서 프로그램을 시작하도록 하겠다.

타이머/카운터 레지스터를 제어하기 위한 구조체를 만든다.

Datasheet에서 I/O register memory map을 찾아보면 위와 같이 8비트 TIMER/COUNTER0 레지스터들을 볼 수 있다.

문서를 참조하여 타이머 구조체는 위의 코드처럼 만들수 있다.

처음으로 확인해보고 싶은 것은 타이머블럭으로 몇번의 신호가 들어왔는지 알수 있는 TCNT레지스터의 값을 출력해 보는것이다.  loop()함수에 다음과 같은 코드를 추가한다.

실행 결과는 위의 그림처럼 나올것이다.

printf() 안에 들어가 있는 내용이 아마 생소한 부분이 있을것이다.  "\x1B[15D"라는 것이 있는데 이 내용은 아주 옛날 은행이나 관공서 같은곳에서 사용하던 '더미터미널(Dummy Terminal)'이라는 것에서 사용하던 제어용 문자이다.  'VT100' 호환 UART 터미널을 사용하면 이러한 제어 문자를 지금도 사용할 수 있다.  "\x1B["를 escape 문자라고 하고, 뒤따라 오는 '15D'는 커서를 현재 위치에서 15번 왼쪽으로 이동하라는 명령어이다.  이렇게 되면 커서를 제일 왼쪽으로 옮긴 다음 'TCNT =   0'를 출력한다.  이때 커서는 0 바로 다음에 위치하게 된다.  다음 loop()가 수행될 때 다시 15번 왼쪽으로 옮긴 다음 'TCNT =   0'을 출력하므로, 눈으로 보기에는 숫자 부분만 갱신 되는걸로 보이게 된다.   다른 제어용 문자를 보려면 'VT100'으로 검색하면 많은 정보를 얻을 수 있다.

TCNT의 값이 계속 변함없이 0으로 나올 것이다.   그 이유는 타이머 블럭이 disable 되어 있기 때문이다.  타이머를 동작 시키려면 TCCRB레지스터의 'Clock Select' 값을 0이 아닌 값으로 설정해 주어야 한다.  문서에는 다음과 같이 설명되어 있다.

앞 부분에서 간단히 언급했는데, 타이머 블럭으로 들어오는 신호는 T0로 들어오는 신호와 AVR내부 클럭을 선택할 수 있다.  지금은 T0를 이용하기로 하겠다.  외부 신호를 사용할 때는 신호의 falling edge에서 카운트를 할지, rising edge에서 할지를 선택할 수 있다. 

문서에서 T0는 PD4에 있는것으로 나온다.  회로도에서 보면 스위치 'S1'이 연결되어 있다.  이 스위치를 누를때마다 TCNT의 값이 증가 될 것으로 예상이 된다.  'S1'이 GND에 연결되어 있으므로 falling edge에서 카운트 되도록 하는게 좋을것 같다.

timer_init()이라는 함수를 만들어 clock selection 코드를 넣어보자.

timer_init()에 있는 코드는 TCCRB레지스터에서 다른 비트값들은 변경하지 않고 clock select에 해당되는 세비트의 값만 다른값으로 변경시키는 코드이다.  이 부분에 대한 설명은 푸쉬 버튼 감지 1에서 설명하였으므로 참고하기 바란다.

timer_init()을 추가한 후 다시 컴파일 하여 결과를 확인해 보자.  원래는 S1스위치를 누를때에만 TCNT값이 증가하여야 되는데, 스위치를 누르지 않았음에도 불구하고 TCNT의 값이 계속 증가되는것으로 보일것이다.   그 이유는 T0핀이 입력으로 설정되었지만, pull-up이나 pull-down상태가 아니고 들떠 있는 상태이기 때문에 그렇다.  따라서 T0핀을 idle 상태에서는 항상 high 값을 가질 수 있도록 핀을 pull-up이 되도록 하여야 한다.  외부에 pull-up 저항을 달아주어도 되지만, 푸쉬버튼 프로젝트에서 AVR 내부 pull-up을 사용하는 방법에 대해서 설명하였으므로 AVR내부 pull-up 저항을 연결시키도록 하겠다.

timer_init()에 다음과 같은 코드를 추가한다.

portd->port |= EXT_TO;

새로 컴파일 하여 결과를 확인해보자.

이제는 TCNT의 값이 0으로 유지되고 있을 것이다.  S1 스위치를 누를때마다 TCNT 레지스터의 값이 증가 되는 것이 보일 것이다.   그러나 스위치가 눌리워질때 미세한 접촉문제로 순간적으로 여러번의 신호가 카운터로 들어가는 수가 있어서 TCNT의 값이 1씩 증가되지 않고 불규칙하게 증가될수 있다.  이를 방지하기 위하여 좋은 스위치를 사용하거나 디바운스 회로를 추가하여야 한다. 



main.c


입문 과정 목차

'입문' 카테고리의 다른 글

타이머/카운터 - PWM(Normal Mode)  (2) 2016.04.10
타이머/카운터 - OC(Output Compare) match  (3) 2016.04.09
인터럽트 - 시작과 끝  (0) 2016.04.07
인터럽트 - UART  (0) 2016.04.06
인터럽트 - 개념  (1) 2016.04.04
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
TAG
more
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함