CS/마이크로프로세서

[마이크로프로세서] 타이머/카운터 및 예제(2)

nowkoes 2024. 1. 3. 19:08

 

실습(2) CTC 모드, 고속 PWM 모드를 활용한 FND와 LED 제어

 지난 시간엔 오버플로를 이용하여 FND에 디스플레이를 출력하는 실습을 진행하였다. 이번 시간엔 CTC 모드와 고속 PWM 모드를 활용한 예제를 다뤄보도록 하자.


CTC 모드를 활용한 FND 동적 구동

 지난 시간에 오버플로 인터럽트를 이용하여 FND에 디스플레이를 출력했던 부분을 CTC 모드로 작동해 보자. 이때 1ms를 계산하기 위해 다음과 같은 수식을 이용하였다.

 

 

 여기서 분자는 1ms를 계산하기 위한 시스템의 클럭 주기 (1ms/256), 분모는 ATmega128의 시스템 클럭 주기를 의미한다. 따라서 분주비를 64로 설정하였고, 이때 클럭 주기가 4us이므로 오버플로우 주기 1ms와 나눠주면 250개를 계수해야 한다는 식이 나온다.

 

#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>

unsigned char segmentTable[16] =
{
	0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F, 0x77, 0x7C, 0x39, 0x5E, 0x79, 0x71
};

 우선 필요한 헤더파일과, FND에 출력할 숫자를 패턴 테이블로 정의하였다.

ISR(TIMER0_COMP_vect)
{
	static unsigned char index = 0;
	
	PORTG = 0x01 << index;
	PORTC = segmentTable[4 - index];
	
	index = (index == 3) ? 0 : index + 1;
}

 그리고 인터럽트 서비스 루틴을 정의하는데, 우리는 타이머/카운터0를 사용할 예정이므로 벡터를 TIMER_COMP_vect로 설정한다. 또한 1ms마다 FND를 동적으로 출력하게 코드를 작성하였는데, 변수의 소멸 시점을 프로그램 종료로 설정하기 위해 static 키워드를 활용하였다(전역 변수로 사용해도 동작과 메모리 사용량은 동일하다).

 

int main(void)
{
	DDRC = 0xFF;
	DDRG = 0x0F;
	
	TCCR0 = TCCR0 | (1 << WGM01) & ~(1 << WGM00); // CTC 모드 설정
	TCCR0 = TCCR0 & ~(1 << COM01) & ~( 1<< COM00); // 비교 출력 사용 X
	TCCR0 = TCCR0 | (1 << CS02) & ~(1 << CS01) & ~(1 << CS00); // 분주비 64 설정
	TIMSK = TIMSK | (1 << OCIE0); // 타이머/카운터0 출력 비교 인터럽트 허용
	sei(); 
	
	OCR0 = 249;
	
	while(1)
	{
		
	}
	
	return 0;
}

 main 함수에서는 FND를 사용하기 위해 DDRC와 DDRG의 방향을 입/출력으로 설정하였다. 그리고 TCCR0에 있는 WGM 비트를 이용해 CTC 모드를 설정하였고, CS 비트를 이용하여 분주비를 설정하였다. 또한 출력 비교 인터럽트와 전역 인터럽트를 허용한 뒤, OCR0의 값을 앞서 얻은 값으로 설정하여 1ms마다 FND에 숫자가 출력되도록 구현하였다.

 

총합본

더보기
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>

unsigned char segmentTable[16] =
{
	0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F, 0x77, 0x7C, 0x39, 0x5E, 0x79, 0x71
};

ISR(TIMER0_COMP_vect)
{
	static unsigned char index = 0;
	
	PORTG = 0x01 << index;
	PORTC = segmentTable[4 - index];
	
	index = (index == 3) ? 0 : index + 1;
}

int main(void)
{
	DDRC = 0xFF;
	DDRG = 0x0F;
	
	TCCR0 = TCCR0 | (1 << WGM01) & ~(1 << WGM00); // CTC 모드 설정
	TCCR0 = TCCR0 & ~(1 << COM01) & ~( 1<< COM00); // 비교 출력 사용 X
	TCCR0 = TCCR0 | (1 << CS02) & ~(1 << CS01) & ~(1 << CS00); // 분주비 64 설정
	TIMSK = TIMSK | (1 << OCIE0); // 타이머/카운터0 출력 비교 인터럽트 허용
	sei(); 
	
	OCR0 = 249;
	
	while(1)
	{
		
	}
	
	return 0;
}

고속 PWM 모드를 활용한 LED 조명 Dimming 제어

 PWM 모드는 전압을 조절(펄스의 폭을 변조)하여 디지털 신호를 아날로그처럼 다룰 수 있게 해 준다고 하였다. 이를 이용하여 LED의 밝기를 변화시킬 수 있는데, 이를 디밍(Dimming)이라고 한다. 이번 실습에서는 8개의 LED가 약 10ms 간격으로 Duty Ratio가 0~100% 왕복하도록 구현하도록 하겠다. 이때 OCR0일 때 듀티비가 0%라 가정하고, 분주비는 1024로 사용한다.

 

 

출처: ATmega128 공식 문서

#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>

ISR(TIMER0_COMP_vect)
{
	PORTA = 0x00;
}

ISR(TIMER0_OVF_vect)
{
	PORTA = 0xFF;
}

 LED가 10ms 간격으로 디밍하기 위해 고속 PWM 모드의 그래프의 특징을 이용할 수 있다. 비반전 모드에서 출력 파형을 보면, TCNT0가 OCR0가 되는 순간 LED가 켜지고, TCNT0가 오버플로 되는 순간 LED가 꺼지게 하여 파형의 주기를 조절하여 디밍하게 할 수 있다는 점을 알 수 있다. 따라서 각각에 해당하는 인터럽트 서비스 루틴을 정의하였다.

 

int main(void)
{
	DDRA = 0xFF;
	
	TCCR0 = TCCR0 | (1 << WGM01) | (1 << WGM00); // 고속 PWM 모드 설정
	TCCR0 = TCCR0 & ~(1 << COM01) & ~( 1<< COM00); // 비교 출력 사용 X
	TCCR0 = TCCR0 | (1 << CS02) | (1 << CS01) | (1 << CS00); // 분주비 1024 설정
	TIMSK = TIMSK | (1 << OCIE0); // 타이머/카운터0 출력 비교 인터럽트 허용
	sei(); 
	
	while(1)
	{
		for(unsigned char duty = 0; duty < 255; duty++)
		{
			OCR0 = duty;
			_delay_ms(5);
		}
		
		for(unsigned char duty = 255; duty > 0; duty--)
		{
			OCR0 = duty;
			_delay_ms(5);
		}
	}
	
	return 0;
}

 LED 출력을 위해 DDRA의 방향을 출력으로 설정하였다. 그리고 TCCR0에 WGM 비트를 이용해 고속 PWM 모드를 설정하였고, 분주비 설정을 위해 CS 비트를 설정하였다. 또한 타이머/카운터 출력 비교 인터럽트 및 전역 인터럽트를 허용하였다. 여기서 OCR0의 값을 이용해 디밍을 하고 있으므로 따로 OCR0 값을 설정하진 않았다.

 

OCR0 값에 따른 출력 파형

 그리고 첫 5ms 동안 OCR0 값을 0부터 255까지 증가시키고, 이어지는 5ms 동안은 255부터 0까지 감소시킨다. OCR0 값의 변화에 따라 출력 파형이 위의 사진처럼 변동하며, 이를 통해 LED의 밝기를 조절할 수 있다. 

 

 

총합본

더보기
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>

ISR(TIMER0_COMP_vect)
{
	PORTA = 0x00;
}

ISR(TIMER0_OVF_vect)
{
	PORTA = 0xFF;
}
	
int main(void)
{
	DDRA = 0xFF;
	
	TCCR0 = TCCR0 | (1 << WGM01) | (1 << WGM00); // 고속 PWM 모드 설정
	TCCR0 = TCCR0 & ~(1 << COM01) & ~( 1<< COM00); // 비교 출력 사용 X
	TCCR0 = TCCR0 | (1 << CS02) | (1 << CS01) | (1 << CS00); // 분주비 1024 설정
	TIMSK = TIMSK | (1 << OCIE0); // 타이머/카운터0 출력 비교 인터럽트 허용
	sei(); 
	
	while(1)
	{
		for(unsigned char duty = 0; duty < 255; duty++)
		{
			OCR0 = duty;
			_delay_ms(5);
		}
		
		for(unsigned char duty = 255; duty > 0; duty--)
		{
			OCR0 = duty;
			_delay_ms(5);
		}
	}
	
	return 0;
}
반응형