반응형

1. Title

AVR : Timer 다루기!

 

2. Abstract

개요 : InterruptTImer/Counter에 대한 소개:

-Timer/Counter에 대한 소개

-예제 1 : 정해둔 시간 마다 LEDcircular하게 켜짐

-과제1 : 정확한 초시계 만들기

 

3. Background

1. sREG : AVR status register, 인터럽트의 enable/disable을 결정

(sei() 사용o, cli() 사용x)

EICRx : 인터럽트가 어떨 때 발생이 되는지 설정.(ex rising edge, falling edge)

EIMSK : 어떤 입력을 인터럽트가 걸리는지를 설정

EFIR : INT0~INT7 중에 어떤 인터럽트가 트리거 되었는가?”를 표시함

 

2. 인터럽트 INT0~3EICRA , INT4~7EICRB이고 EICRAEICRB의 각각 Interrupt register는 서로 다르다.

+ 예를 들어, EICRB=0b00001010;로 설정되면, INT4INT5의 인터럽트가 falling edge일 때 발생)

 

3. 그림은 8bit Timer/Counter 블록다이어그램

 

4. TimerMCU내부의 내부클럭을 이용하여 일정시간 간격의 펄스를 만들어 내거나 일정 시간 경과 후에 인터럽트를 발생시키는 기능

=>ATmega1284개의 timer/counter로 구성

=>1. TImer/Counter0 8bit

=>2. TImer/Counter1 16bit

=>3. TImer/Counter2 8bit

=>4. TImer/Counter3 16bit

=>Overflow Interrupt : 카운터 값이 overflow되는 경우 발생

 

5. TCCR0(Timer/Counter Control Register) -동작모드, 분주비(pre-scaler) 설정

=>1. Bit7 : 1로 설정할 경우 강제로 OC0 단자에 출력비교 매치 신호 출력

=>2. Bit 6,3 (파형 출력 모드) : WGM00~01 어떤 파형 출력할지 결정

 

=>3. Bit 5,4 (비교매치 출력 모드) : OC0 단자에 비교매치 출력

 

 

=>4. Bit 2,1,0 (CLK) : Prescaler 설정

 

- Timer예제 t값 정하기: 예를 들어 우리 예제/과제에서 사용할 ‘111’의 경우 clk/1024로 작동을 하게 됩니다. Normal mode 8bit timer interrupt는 매 1/(16000000/256/1024)s 마다 발생할 것입니다. (AVR의 속도 16000000, 8bit timer이기 때문에 = 256, 1024는 ‘111’인 경우)

 

TCNT0(Timer/Counter Register0) : Timer/Counter Register08bit 카운터 값을 저장 하고 있는 레지스터

 

OCR0(Timer/Counter Output Compare Register0) : CTC 모드에서 TCNT0과 비교하여 OC0 단자에 출력신호를 발생하기 위한 8비트 값을 저장 (사용자 지정)

 

ASSR(Asynchronous Status Register) : 타이머/카운터0의 외부 클럭에 의하여 비동기 모드로 동작하는 경우 관련된 기능 수행

 

TIMSK(Timer/Counter Interrupt Mask Register)

=> SREGI-bit1로 설정 될 시

=> 1. OCEIO=1이면 Output Compare Match Interrupt Enable (0x02)

     2. TOEIO=1 이면 Overflow Interrupt Enable (0x01)

 

TIFR(Timer/Counter Interrupt Flag Register)

=> 1. 비교매치가 발생했을 경우 OCF01로 설정 (인터럽트 처리 후 Clear)

     2. Overflow가 발생했을 경우 TOV01로 설정 (인터럽트 처리 후 Clear)

 

6. volatile 변수

- 우선 volatile의 사전적인 의미는 휘발성이다. 이름 그대로 volatile 변수는 외부적 요인에 의해서 언제든 값이 바뀔 수 있음을 의미합니다. 이에 따라, compilervolatile로 선언된 변수에 대해서는 최적화를 수행하지 않습니다. Volatile 변수는 레지스터에 로드된 값을 사용하지 않고, 메모리에서 직접 참조합니다.

- 여기서 최적화란 compiler가 속도 향상을 위해 불필요한 메모리 사용을 없애는 것인데, 하드웨어 조작에 있어서 임의로 코드가 제거/조정된다면 원하는 결과를 가져올 수 없을 것입니다. 따라서 volatile 변수로 선언하면 최적화를 수행하지 않고 메모리를 그대로 사용합니다.

- 보통 I/O Port를 특별한 변수 이름으로 지정해놓고 계속적으로 사용하고자 할 때, volatile 키워드를 붙여서 사용합니다.

 

4. Experimental Results

1). 예제1 _ 정해둔 시간 마다 LEDcircular하게 켜지기

A-1. Source Code (initialization)

(그림1. 인터럽트/타이머 선언 및 초기화)

A-2 . Source Code (main)

(그림2. Main문 )

B-2. Data

 

(그림3. LED가 순차적으로 켜지는 것을 확인)

C-2. Discussion

- (그림2)에서 Timer/인터럽트 초기화, PORTF(LED) 선언 이후 sei()를 이용해서 인터럽트를 set해줬습니다.

- while(1) , 무한루프를 돌게 만들었고 ISR(TIMER0_OVF_vect)를 통해서 위에서 구한 매 ‘t’ sec마다 인터럽트가 발생하도록 만들었습니다.

- Pre-scaler‘111’로 설정하여 정해진 시간에 LED1bit shift통해 이동하는 것을 확 인하였습니다.

- timer t 구하기 : 분쇠비를 111로 설정해서 clk/1024로 작동을 하게 되는데, Normal mode 8bit timer interrupt는 매 1/(16000000/256/1024)s 마다 발생할 것입니다. (AVR의 속도 16000000, 8bit timer이기 때문에 = 256, 1024‘111’인 경우) 계산해보면 61.0351로 약 61의 값이 나옵니다.

- 때문에 0.016s(=1/t)마다 인터럽트가 발생하므로 temp가 설정한 t와 같아지려 면 61번을 돌아야한다. 따라서 0.016*61=0.976s로 약 1초가 나옴을 확인할 수 있습니다. 1초마다 한번씩 PORTF 1 bit shift를 통해 이동하면서 LED를 제어합니다. LED0b1000_0000일 때는 다음 LED는 처음으로 돌아와야 하기 때문에 PORTF = (PORTF==(0x80))? 0x10:PORTF<<1;를 이용해서 0b0001_0000으로 돌려줍니다.(LED4개밖에 없기 때문)

 

3). 예제2 _ 정확한 초시계(Timer)

A-1. Source Code (initialization)

(그림1. 포트(7_segment)/인터럽트 선언 및 초기화)

C-1. Discussion

- (그림1)에서 7_segment 수업 때 사용했던 SEGNP, SEGWP, SEGPOS를 가져왔습니다. DDRA/E/G를 통해서 LED CTL/DATA 사용(for 7_segment)에 대한 초기화를 했습니다.

- 앞서 설명했던 volatile 키워드를 사용했습니다. Memory에 저장해서 인터럽트 루틴 안에서 ‘cnt’를 사용해서 숫자 counting’을 할 것입니다. 다시 기능에 대한 요약을 하자면 compiler가 최적화 하지 않고 memory에서 변수를 사용하는 것입니다.

- (그림1)volatile 선언부를 보면

volatile int cnt = 0; // 7_segment 숫자

volatile int temp = 0; // volatile 키워드 사용

// volatile을 통해 register가 아닌 memory에 저장

volatile int reset = 0; // 타이머 리셋 (1리셋)

volatile int start = 0; // 타이머 시작 (0정지, 1시작)

volatile int a, b, c, d; // 4자리수 for 7 segment

이렇게 volatile로 선언했습니다. 각각에 대해서 설명 드리자면

- cnt : 우선 7 segment에 띄울 숫자를 카운트 하는 것으로 뒤에서 설명드립 시간 interrupt마다 +1씩 증가시켜 시간을 계산하는 변수가 됩니다.

- temp : 시간을 계산하는데 사용할 것입니다. 앞서 t에 대한 계산에서 매 1/61s 마다 인터럽트가 발생해서 약 1sec가 만들어졌을 때 초시계가 작동하도록 만듭니다.

- reset : reset버튼을 위한 변수로, 1일 때 reset on인 설정입니다.

- start : start버튼으로 0일 때 정지, 1일 때 시작입니다.

- a, b, c, d : 7_segment 각 자리수를 의미하는 변수입니다. 인터럽트 루틴에서 사용되기 때문에 volatile을 이용해서 선언했습니다.

 

A-2 . Source Code (time_check)

(그림2. 소스코드_7 segment에 사용한 time_check() 함수 )

C-2. Discussion

- (그림2)은 저번 7 segment 과제를 수행하면서 사용했던 display용 함수이고, 이번 과제는 정확한 시간을 계산하는 것이 아니기 때문에 delay 함수에 각각 5ms 씩 넉넉하게 시간을 부여했습니다.

 

A-3 . Source Code (timer_setting())

(그림3. Timer Setting)

C-3. Discussion

- (그림3)에서 TCCR0 = 0x02;를 통해서 pre-scaler010 8bit timer로 선언해줍니다. 그러면 t = 1/(16000000/256/8)sec 마다 time interrupt가 들어가게 됩니다. 이를 initialize 할 때 100배를 시켜줘서 약 0.01s마다 interrupt안에서 초시계 기능을 수행하도록 만들었습니다. TIMSK = 0x01;을 통해서 overflow interrupt enable상태로 만들어줍니다.

 

A-4 . Source Code (main)

(그림4. main문 코드 )

B-4. Data

 

(그림5. INT5를 누르면 Reset, INT4를 누르면 Start/Stop)

C-4. Discussion

- 이번 과제는 Timer를 만드는 실험이었습니다. (그림3)의 코드를 보면 우선 포트(segment), 인터럽트에 대한 초기화, 8bit timer로 세팅을 진행하였습니다. Segment에 사용될 a, b, c, d도 각각 volatile int형으로 선언하고 0으로 초기화했습니다.

- 인터럽트를 사용할 것이기 때문에 cntwhile문 안에서 증가하지 않습니다. while문에서는 display를 위해서 time_check 함수만 계속 실행시키고, 각각 자릿수에 들어갈 숫자들만 계산을 해줍니다. 최고자리수인 1000의 자리는 1000으로 나눈 몫을 통해 구하고, 나머지는 구할 숫자의 값으로 나누고 %10(나머지 연산)을 통해 자릿수의 값을 구했습니다. 추가적으로, 9999를 넘어서면 0으로 보내주고 0에서 더 낮아지면 9999로 보내주기 위해 if문을 사용하였습니다.(이전 과제에서 사용)

- 초시계의 경우 00.00 즉 중간에 dot point를 사용해야하기 때문에 time_check함수에서 3번째 자리 수(1s)에는 SEGWP를 통해서 dot point를 표시하게 만들었습니다.

- 초시계를 제어할 interrupt는 다음 문단에서 설명 드리겠습니다.

 

 

A-5 . Source Code (ISR(TIMER0_OVF_vect))

(그림6. ISR(TIMER0_OVF_vect))

C-5. Discussion

- (그림6)을 보면 인터럽트 발생마다 temp++을 해줍니다. 그리고 if(temp == t)를 통해서 0.01초를 세는 작업을 해줍니다.

- 과제의 경우 예제1과 다르게 t의 값8 bit timer를 통해 구현시켰습니다. 예제에서는 1초를 세는 timer였지만 과제는 0.01sec를 세는 timer이기 때문에 0.01s를 구현하기 위해서 8bit timer를 사용하였고, 8bit timerinterrupt 시간을 구해보면 약 0.0001s가 나옵니다. 이를 100배 시켜줘서 0.01sec마다 시간이 계산되도록 인터럽트 안을 구현했습니다.

- 0.01초가 됐다면 if문 안을 수행합니다. temp를 다시 0으로 만들어주고(또 다른 0.01초를 세기 위해서), reset이 걸렸다면(=1) reset0으로 만들어주고 cnt 값을 0으로 만들어줍니다. 따라서 디스플레이에서 0000이 출력될 것입니다.

- reset이 아니라면 start의 값을 확인합니다. 만약 start 1이라면 카운팅을 통해서 초를 증가시킵니다. 만약 1이 아니라면 cnt = cnt + 1;을 수행하지 않아서 cnt값의 증가가 정지하게 됩니다.

 

 

A-6 . Source Code (ISR(INT4_vect), ISR(INT5_vect))

(그림7. ISR(INT4_vect), ISR(INT5_vect))

C-6. Discussion

- (그림7)INT4, 5에 대한 설명입니다. INT4stop/start에 관련한 버튼이 됩니다. 누를 때 마다 현재상태의 여집합이 나오게 됩니다.

- INT5reset버튼으로 time interrupt에서 reset=0에 대한 조절을 해주기 때문에 INT5 버튼은 reset = 1로 만드는 on 작업만 하게 만들었습니다.

 

5. Analysis

-(예제 1)을 통해 t를 계산하였고 이를 통해 매 1초마다 LED가 돌아가면서 켜지는 예제를 진행하였습니다. 또한, 보드에서 명시된 것과 다르게 schematic을 확인해보면 INT4INT5의 명시가 거꾸로 되어있는 것을 확인할 수 있었습니다.

 

-TIMER 예제의 T : 앞서 설명 드렸던 분석을 다시 정리하자면, pre-scaler‘111’로 설정해서 clk/1024로 작동을 하게 되는데, Normal mode 8bit timer interrupt는 매 1/(16000000/256/1024)s 마다 발생할 것입니다. (AVR의 속도 16000000[hz], 8bit timer이기 때문에 = 256, 1024‘111’인 경우) 계산해보면 61.0351로 약 61의 값이 나옵니다.

 

 

- 위의 값을 사용하면, 1초의 계산이 이루어지는 것을 확인할 수 있었습니다.

- (과제)의 경우 0.01s의 계산을 위해서 pre-scaler ‘010’을 사용했고, t = 1600000/256/8 = 0.0001sec(8bit timerinterrupt time)를 사용해서 t100배한 시간을 이용해 0.01s를 계산하도록 만들었습니다.

 

6. Conclusion

- 이번 실험을 통해 Timer를 만들었습니다. TCCR02:0bit를 정해서 AVR의 세팅된 주파수(16000000hz)를 가지고 Counter에 필요한 t를 만드는 법을 배웠습니다.

 

7. References

- volatile 키워드

https://ko.wikipedia.org/wiki/Volatile_%EB%B3%80%EC%88%98

 

- ATmega128

http://www.kjit.bme.hu/images/stories/targyak/mechatronika_robot_mcu/SH_kepzes/atmel_atmega128_manual.pdf

 

반응형
  • 네이버 블러그 공유하기
  • 네이버 밴드에 공유하기
  • 페이스북 공유하기
  • 카카오스토리 공유하기