본문 바로가기
대한상공회의소 스마트팩토리 교육/IoT 디바이스 개발

[IoT 디바이스 개발] AVR(ATmega128A)«수업-13» : 8비트 타이머 및 카운터 (오버플로 인터럽트 / 비교 일치 인터럽트)

by 나는영하 2022. 2. 16.

※ 주의사항 

본 블로그는 수업 내용을 바탕으로 제가 이해한 부분을 정리한 블로그입니다.
본 내용을 참고로만 보시고, 틀린 부분이 있다면 지적 부탁드립니다!

감사합니다😁

 

안녕하세요!!

오늘은 아래와 같은 내용을 확인해보겠습니다.

 

8비트 타이머/카운터

TCNTn 레지스터

TCCRn 레지스터

TIMSK 레지스터

TIFR 레지스터

OCR0 레지스터

오버플로 인터럽트

비교 일치 인터럽트

millis 함수


1. 8비트 타이머/카운터

1-1. 개요

8비트 타이머/카운터 0번 블록 다이어그램

- 입력 펄스를 세는 장치, 즉 카운터

- 일정한 주기의 펄스를 셈으로써 시간 측정도 가능하다. 즉, 타이머 역할 수행 가능

- 마이크로컨트롤러의 시스템 클록 사용 가능 (외부에서 클록 입력도 가능하다)

 

1) ATmega128A 타이머/카운터

(1) ATmega128A는 총 4개의 타이머/카운터를 제공합니다.

 - 0번과 2번의 8비트 타이머/카운터

 - 1번과 3번의 16비트 타이머/카운터

 

(2) 8비트 타이머/카운터

 - 0 ~ 255까지 세기를 반복한다.

 - 16Mhz 시스템 클록을 사용하는 경우 256개의 클록의 발생 시간은 0.016ms에 해당된다.

 - 분주기를 통해 클록의 속도를 늦춤으로써 보다 긴 시간 측정 가능하다.

※ 분주라는 것은 나눈다는 의미로 2분주는 16Mhz / 2 = 8Mhz에 해당된다.

 

1-2. 구조

8비트 타이머/카운터 오버플로 인터럽트 발생시간

- 내부 클록 16Mhz의 주기는 62.5ns (나노 세컨드)

- 0.0625μs x 256(8비트 타이머/카운터의 레지스터 크기) → 16(μs)

- 즉, 8비트 타이머/카운터에서 0.016ms만큼 타이머(카운트) 가능하다.

(1분주 기준 0.016ms마다 오버플로 인터럽트가 발생한다.)

- 2분주로 주기를 산출할 경우 0.125μs x 256 → 32μs

즉, 8비트 타이머/카운트에서는 0.032ms만큼 타이머(카운트) 가능하다.

- 분주율이 높아질수록 카운트(타이머)할 수 있는 값이 늘어난다.

- 1024분주일 경우 16000000[hz] / 1024 = 약16Khz 

256 / 16K = 1/64[s](즉 256개 펄스를 세었을때 발생하는 오버플로 인터럽트를 32번 체크하면 0.5초의 근사값을 산출할 수 있다.

 


 

2. 8비트 타이머/카운터 관련 레지스터

※ 타이머/카운터 레지스터의경우 1개의 레지스터에 8비트와 16비트 타이머/카운터 관련 비트가 섞여있는 경우가 있습니다. 아래의 레지스터 관련 설명글은 8비트 타이머/카운터 중에서도 0번 타이머에 해당하는 레지스터와 비트만 설명하겠습니다.

2-1. TCNT0 레지스터

TCNT0 레지스터

- 현재까지 센 펄스의 개수를 기록하는 레지스터이다.

- TCNT0의 레지스터의 Default 값은 0이며, 비교일치 인터럽트를 사용할 경우 TCNT0의 값을 0이나 특정 값으로 초기화 해주는것도 중요하다.

 

2-2. TCCR0 레지스터

TCCR0 레지스터

1) 7번 ~ 3번 비트 : 비교일치 인터럽트 중에서 파형 생성 모드와 출력 모드를 설정해주는 레지스터이다.

(비교일치 인터럽트 중에서도 파형을 출력할때 필요한 비트로 설명은 생략..)

 

2) CS00 ~ CS02 비트(0 ~ 2번 비트) : 분주비를 설정하기 위해 사용하는 비트

Default 값은 00으로 타이머/카운터가 멈춰있는 상태이다. 분주비를 선택하는 순간 타이머는 동작한다.

CS00 ~ CS02 비트값에 따른 분주비 설정

- 분주비에 따른 주기와 1ms당 클록수는 아래의 표와 같다.(16Mhz 기준)

분주비 주기(μs) 1ms당 클록수
1 0.0625 16000
8 0.5 2000
32 2 500
64 4 250
128 8 125
256 16 62.5
1024 64 15.6(반올림)

- 16Mhz 1024분주비의 경우 오버플로우 인터럽트의 발생 주기는 0.16384ms에 해당된다.

즉, 해당 인터럽트를 몇번 반복해도 정확히 0.5초를 산출하지 못한다. (근사값에 해당됨)

 

- 하지만 64분주비나 128분주비를 사용해서 해당 클록을 250번 또는 125번을 측정하면 정확한 0.5초를 산출할 수 있다.

(ex) 64분주로 설정하면 1clock은 4μs이고 펄스의 개수를 1 ~ 250까지 측정하면 1ms마다 인터럽트를 발생시킬 수 있다. 이러한 인터럽트를 500번 측정하면 0.5초라는 타이머의 값을 산출할 수 있다. 

 

2-3. TIMSK 레지스터

1) OCIE2 비트 (7번 비트) : Output Comapre match Interrupt Enable

2) TOIE2 비트 (6번 비트) : Overflow Interrupt Enable

TIMSK 레지스터 7번,6번 비트 (2번 카운터)

3) 2 ~ 5번 비트 : 1번 타이머/카운터의 설정관련 비트 (16비트 타이머)

TIMSK 레지스터 2 ~ 5번 비트 (1번 카운터)

4) OCIE0 비트 (1번 비트) : Output Comapre match Interrupt Enable

- 0번 타이머/카운터의 비교 일치 인터럽트 활성화 비트

5) TOIE0 비트 (0번 비트) : Overflow Interrupt Enable

- 0번 타이머/카운터의 오버플로 인터럽트 활성화 비트 

TIMSK 레지스터 0 ~ 1번 비트 (0번 카운터)

※ 8비트 타이머/카운터 인터럽트 벡터이름 및 벡터 번호 (0번, 2번 타이머)

2번 타이머/카운터 인터럽트
0번 타이머/카운터 인터럽트

2-4. TIFR 레지스터

- TIFR 레지스터는 해당 비트가 세트되었을때 TIMSK 레지스터의 해당 비트 역시 세트된 상태라면 실제로 인터럽트가 발생한다. (2번 ~ 7번 비트는 다른 타이머/카운터 관련 비트이다.)

※ 타이머/카운터를 사용하기 위해 꼭 필요한 레지스터는 아니므로 필수설정과는 관계가 멀다.

TIFR 레지스터 0번, 1번 비트 구조

1) TOV0 비트 (0번 비트) : 오버플로가 발생한 경우 세트되는 비트

2) OCF0 비트 (1번 비트) : 비교 일치가 발생한 경우 세트되는 비트

 

2-5. OCR0 레지스터

OCR0 레지스터 구조

- 비교 일치 인터럽트를 사용하기 위해 비교값을 설정해주는 레지스터

 


 

3.  타이머/카운터 인터럽트

3-1. 오버플로 인터럽트 vs 비교일치 인터럽트

오버플로 인터럽트 vs 비교일치 인터럽트

 

1) 오버플로 인터럽트

 - 최대로 셀 수 있는 펄스의 수를 넘어설 때 TCNTn 레지스터가 0으로 바뀌면서 '오버플로 인터럽트' 발생

 - 8비트 타이머의 경우 최대로 셀 수 있는 펄스의 수는 0 ~ 255이다.

 

2) 비교일치 인터럽트

 - TCNTn의 값이 미리 설정된 OCRn 레지스터의 값과 일치하는 경우 '비교일치 인터럽트' 발생

 - 비교 일치 인터럽트가 발생할때 OCn 핀을 통해 파형 출력 가능

3-2. 관련 예제 : 오버플로 인터럽트를 이용한 LED 점멸

오버플로 인터럽트를 사용한 0.5초 주기 LED 점멸

1) 코드 설명 

- Line 4 : 오버플로 인터럽트의 횟수를 카운팅 하기 위한 변수

- Line 5 : LED의 현재 상태를 저장하기 위한 변수

- Line 7 ~ 16 : 0번 타이머의 오버플로 ISR에 해당되는 함수부

  Line 9 : 오버플로 인터럽트 발생할때마다 1씩 카운팅

  Line 10 : 오버플로 인터럽트가 32회 발생하면 if문 실행

  Line 11 : 오버플로 인터럽트의 횟수를 카운팅하는 변수 초기화

  Line 12 : LED 현재 상태를 저장하는 변수값 반전
  (즉 오버플로 인터럽트가 32회 발생하면 LED의 상태가 반전된다)

  Line 13 : State 값이 1이면 A포트의 0번핀에 연결되어있는 LED가 점등된다. 

  Line 14 : state 값이 0이면 A포트의 0번핀에 연결되어있는 LED가 소등된다.

- Line 24 : 0번 타이머의 분주율을 1024로 설정한다.

- Line 25 : 0번 타이머 오버플로 인터럽트를 허용한다.(Enable)

- Line 26 : 전역 인터럽트를 허용한다.(Enable)

 

2) 문제점 

- 위와 같은 프로그래밍의 문제점은 정확히 0.5초마다 점멸하지 않는다는 점이다.

(정확히는 0.524288s 마다 LED가 점등되었다 소등되었다 반복한다.)

- 이는 16Mhz를 분주할때 1ms당 클록수가 정수값으로 떨어지지 않기 때문에 발생한다.

(위의 2-2 레지스터의 표 참고)

- 따라서 8비트의 카운터/타이머를 이용할경우 64분주나 128분주를 사용하면 해결된다.

 

3) 문제점 해결

- Line 88 : 주석을 수정하지 못했지만 분주율은 64분주로 설정되었다.

- Line 73 : 클록을 카운팅하는 레지스터의 초기값을 6으로 설정하면서 250번 펄스를 셀때마다 오버플로 인터럽트를 1회 발생하도록 설정한다. 

- Line 75 : 오버플로 인터럽트가 500회 발생하면 if문을 수행하도록 한다.(0.5초 주기)

 ※ 왜?? 위의 코드가 정확히 0.5초 주기인가??
- 16[Mhz] / 64[분주] = 250[Khz]
- 주파수 : 250[Khz] = 주기 : 4μs
- 4μs x 250 = 0.001s (6 ~ 255 = 250회, TCNT0(레지스터 초기값) = 6)
- 0.001s x 500 = 0.5s (count값이 500이면 if문 수행) 

 

3-3. 관련 예제 : 비교 일치 인터럽트를 이용한 LED 점멸

1) 코드 설명

- Line 41 : 타이머 카운터 레지스터의 초기값을 0으로 설정한다.

즉, 비교 일치 인터럽트가 실행되면 타이머 카운터 레지스터의 값을 0으로 설정한다.

- Line 43 : 비교 일치 인터럽트가 64회 실행되면 if문을 수행한다.

- Line 57 : 분주율 1024로 설정

- Line 58 : 비교 일치 인터럽트에 사용되는 비교값을 128로 저장한다.

(TCNT0의 값이 128이 될때마다 비교 일치 ISR을 실행한다.)

 

2) 동작 설명

- 3-2의 오버플로 인터럽트를 사용한 LED 점멸 코드 동작과 동일하다.

- 주의해야할 점은 1024분주로 분주율은 동일하나 TCNT0의 값이 256번 마다 ISR을 실행하는 오버플로 인터럽트와는 달리 128번 마다 ISR을 실행하기 때문에 인터럽트를 카운팅하는 변수의 목표값을 달리 설정해야 한다.

(count 변수의 값이 달라진 점 확인 필요)


 

4. 8비트 타이머 관련 응용문제

4-1. millis함수 : 실행 시간 알아내기

- 분주비를 64로 설정한 경우 0번 타이머/카운터의 오버플로 인터럽트가 발생하기까지 64 x 256 = 16,384개의 클록이 필요하다.

- 16Mhz 클록에서 분주비 64로 설정한 타이머의 오버플로 인터럽트 발생 시간은 (64 x 256) / 16000000 = 0.001024s이다.

- 즉, 오버플로 인터럽트는 1ms 24μs 마다 발생한다는 점을 이용하여 실행시간을 알아내는 코드를 짰다.

ISR 함수부(좌) / millis 함수부(중) / main 함수부(우)

1) 코드 설명

- Line 105 ~ 119 : 오버플로 ISR 함수부 / ms와 μs를 구분해서 데이터를 저장해주는것이 포인트!!

- Line 121 ~ 135 : millis 함수부 / 즉, 현재 시간의 값을 리턴해주는 함수부분에 해당된다.

(ISR 함수부에서 현재시간을 timer0_millis 값에 저장하고 그값을 millis 함수부분에서 m값에 저장해서 해당 변수값을 리턴해준다.)

- Line 137 ~ 142 : 0번 타이머 인터럽트 및 분주비 관련 설정부

- Line 144 ~ 172 : main 함수부 / millis 함수의 리턴값을 time_current 값에 저장하고 이전에 저장한 값과 비교해서 1초가 지났을때 마다 PORTA의 상태를 반전시켜준다.

 

4-2. 연습문제 : 오버플로 인터럽트를 활용한 LED 순차 점멸

1) 코드 설명

- ISR내에 사용되는 변수를 Volatile 명령어를 붙혀주어서 변수를 가변적으로 만들어 준다.

- 8개의 LED가 순차적으로 점멸하는 동작을 하도록 동작 패턴을 배열로 만든다.

- 64분주율로 설정하고 카운터 레지스트의 초기값(TCNT0)을 6으로 설정한 후 250번 반복하면 ISR이 실행된다.

(64[분주] x 250 / 16000000[hz] = 0.001s, 1ms)

- 1ms를 500번 반복(count == 500)하면 0.5초에 한번 value[i] 패턴 배열이 바뀌도록 프로그래밍

 

※ 본 과정에서 비교 일치 인터럽트 수행시 파형을 출력하거나 내부 클록(16Mhz) 대신 ASSR 레지스터를 통해 외부 클록을 사용할 수 있는 내용도 있었지만 생략하거나 간략하게 집고 넘어간 부분으로써 본 내용에는 생략하였습니다. 😥

댓글