본문 바로가기
전자공학/마이크로프로세서

[atmega1281] 실험 8. AVR MCU의 타이머 활용 2 – 스피커 구동

by TSpoons 2024. 11. 21.

 

실험 목적

- ATmega1281 타이머의 CTC 모드를 활용하여 원하는 주파수를 가진 구형파를 출력하는 방법을 이해한다.

- 타이머와 외부 인터럽트를 동시에 사용하는 방법을 익힌다.

- 타이머 출력을 이용하여 스피커를 구동해 본다.

 

 

 

 

실험 예비 과제

 

1. 아래의 동작을 하도록 timer_init() 함수와 interrupt_init() 함수를 작성한다.

 

void timer_init(void) 
{ 
	// Timer3을 CTC 모드로 하고 compare match가 발생할 때마다 OC3A 핀을 toggle시키도록 설정한다. 
	// 이 때 타이머의 동작 모드만 설정하고 실제로 타이머가 동작하지는 않도록 해야 한다. 
	// Timer5를 CTC 모드로 하고 0.1 초 간격으로 compare match interrupt가 발생하도록 설정한다. 
} 
void interrupt_init(void) 
{ 
	// 하강 모서리 (falling edge)에서 INT3이 발생하도록 EICRA를 설정한다. 
	// 하강 모서리 (falling edge)에서 INT7이 발생하도록 EICRB를 설정한다. 
	// INT3과 INT7을 enable시킨다.  
	// global interrupt flag을 set시킨다. 
}

 

2. Timer5의 compare match interrupt ISR을 작성한다. ISR이 호출될 때마다 FND에 표시되는 값을 1씩 증가시키려고 한다. FND에 표시되는 숫자의 범위는 0 ~ 999이다.

 

 

3. 1 번에서처럼 타이머가 설정되었다는 가정하에 다음 함수 sound_set_frequency()와 sound_mute() 함수를 작성한다.

 

 

 

4. 16 비트 길이의 변수 freq_hz를 인자로 받아 그 주파수의 구형파를 OC3A 핀으로 출력하는 일을 하는 함수 sound_set_frequency()를 작성하여라. 이 때 Timer3의 prescaler는 8로 한다.

void sound_set_frequency(uint16_t freq_hz) 
{ 
	// OCR3A 값 설정 
	// Timer3의 prescaler 설정 
}

prescaler와 관련된 비트 이외의 값을 바꾸지 않도록 주의해야 한다. -> masking

 

 

5. 4 번에서 작성한 sound_set_frequency() 함수는 구형파를 OC3A 핀으로 출력하는 기능을 한다. 이 함수를 호출한 후 구형파 출력을 중단하려고 할 때 sound_mute() 함수를 호출할 생각이다. 다음 함수를 작성하여라.

 

timer의 source를 설정하는 레지스터에 masking 하여 no clock source로 만든다.

 

 


 

 

실험 과정

 

 

 

2. timer_init() 함수의 내용을 채우고 build한 다음 실행시켜 보아라. FND에 표시되는 숫자는 최초 0이 되도록 한다. timer_init() 함수의 내용을 채우고 build한 다음 실행시켜 보아라. 지시한 대로 코딩을 했다면 FND에 표시되는 숫자가 0.1 초마다 1씩 증가하는 것을 볼 수 있을 것이다.

 

 

 

3. sound.c 파일을 열어 sound_set_frequency()와 sound_mute()의 내용을 채운다. 그리고 while() 루프로 진입하기 직전에 sound_set_frequency() 함수를 호출하여라. 200 ~ 2000 사이의 임의의 주파수 값을 인자로 주었을 때 OC3A 핀으로 그 주파수의 구형파가 출력되는지 오실로스코우프로 확인하여라. 만일 설정한 주파수와 다른 주파수의 구형파가 출력된다면 sound_set_frequency() 함수의 내용에 오류가 있다는 것을 의미한다.

 
4. 3 번에서 sound_set_frequency() 함수의 동작을 확인했다면 OC3A 핀으로 다음과 같은 신호를 무한 반복 출력하도록 while() 루프에 들어갈 내용을 완성하여라. 320 Hz와 480 Hz를 80ms 동안 8 회 반복 출력하고 그 다음 1 초 동안은 출력을 끄며 이 과정 전체를 무한 반복한다.

 

 
5. 아래와 같은 배열이 있다고 가정하자.
uint16_t m_notes_tmp[] = {320, 480, 320, 480, 320, 480, 320, 480, 320, 480, 320, 480, 320, 480, 320, 480, 0};
uint8_t m_duration_tmp[] = {80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 1000};
이 배열을 사용하도록 4 번의 코드를 수정하여라. 다만 이 경우 _delay_ms() 함수를 사용하면 컴파일할 때 오류가 생긴다. _delay_ms() 함수는 컴파일 할 때 지연 시간이 결정되어 있어야 하기 때문에 _delay_ms(60)과 같이 상수 인자만 사용할 수 있기 때문이다. 여기에서는 _delay_ms() 함수 대신 main.c에 포함되어 있는 my_delay_ms() 함수를 사용하여라. 스피커에서 출력되는 소리가 4 번의 결과와 동일한지 확인하여라.

for 문

 
 
* issue: 소리가 밀리는 현상이 이질감이 드는데 해결

6. 5 번의 방법을 사용하여 어떤 음악을 스피커로 출력하려고 한다. 다음은 music.data.h에 포함된 배열이다. uint16_t m_notes[] = {HG, HE, HF, HG, HE, HF, HG, MG, MA, MB, HC, HD, …} uint8_t m_duration[] = {E, S, S, E, S, S, S, S, S, S, S, S, S, S, E, S, …} 위 배열은 어떤 악보의 정보를 저장하고 있다. m_notes[]는 악보의 각 음의 계이름을 저장하고 있다. 여기에 사용된 HG, MG 등의 값은 musical_notes.h에 정의되어 있는데 이 값은 각 계이름의 주파수이다. XX는 0으로 정의되어 있으며 이 값은 스피커를 mute시킨다는 것을 의미한다. m_duration[]는 각 음의 길이를 저장하고 있으며, E, S 등의 값은 musical_notes.h에 정의되어 있다. 이 값은 단순히 각 음의 음표가 무엇인가를 뜻하며 실제 출력의 지속 시간은 이 값에 music_tempo라는 변수를 곱해서 계산해야 한다. music_tempo는 music_data.h에 정의되어 있는 전역 변수이며 이 변수의 값을 바꾸면 출력 음악의 빠르기를 조절할 수 있다. 이 두 배열을 사용하여 음악을 출력하기 위한 코드를 완성하여라. 앞에서 설명했듯이 XX는 음의 주파수가 아니며 스피커를 mute시킨다는 것을 뜻하기 때문에 출력할 음이 XX인 경우와 아닌 경우를 다르게 처리해야 한다. 이 때 배열 m_notes[] 길이를 알기 위해서 배열에 포함되어 있는 값의 수를 일일이 세는 바보 같은 일은 하지 말기 바란다. (sizeof() 연산자를 잘 활용하면 된다.) 제대로 코딩했다면 여러분들에게 익숙한 음악이 출력된 것이다.
 




7. 다음과 같은 기능을 포함하도록 프로그램을 수정하여라.
가) MCU가 리셋된 직후에는 FND의 숫자만 0.1 초 간격으로 증가하고 스피커로 음악이 출력되지 않는다.
나) SW2를 누르면 음악 출력을 시작한다.
다) SW2를 다시 누르면 음악 출력을 중단한다. 또한 FND에 출력되는 값은 0으로 바뀐다.
라) 음악 출력이 중단된 상황에서 SW2를 다시 누르면 조금 전에 음악 출력이 중단된 위치부터 음악이 다시 출력된다. SW2를 누를 때 마다 (나)와 (다)를 반복한다.
마) 음악이 출력되고 있는 동안 SW3을 누르면 즉시 음악 출력을 맨 처음 위치에서 다시 시작한다.
 

가) 초기 mode=0 상태에서 TIMER5 인터럽트만 동작하여 fnd_num을 0.1초마다 증가

나/다) SW2(INT7) 인터럽트에서 mode 값을 토글하여 음악 재생을 제어하고, mode=0일 때 fnd_num을 0으로 초기화

라) note_idx 변수가 volatile로 선언되어 음악 중단 시에도 값이 유지되므로, 다시 mode=1이 되면 이어서 재생

마) SW3(INT3) 인터럽트에서 note_idx=0으로 설정하여 음악을 처음부터 다시 시작

 
 
 
8. (선택: 난이도 중) sound_mute() 함수에 있는 if 문의 역할은 무엇일까? 이 if 문을 없애고 프로그램을 실행해도 똑 값은 음악을 들을 수 있다. 하지만 회로적인 문제가 있을 수 있다. 이 if 문을 포함한 상태에서 코드를 실행하면서 펄스 출력이 중단된 상황에서 PE3 신호의 논리값을 확인해 보아라. 또한 이 if 문을 없앤 상태에서 같은 실험을 반복해 보아라. 차이를 볼 수 있는가? 차이를 확인했다면 if 문이 하는 일이 무엇인지 datasheet를 참조하여 설명하여라.

(스피커 출력 시 해당 핀 신호)


 
 If 문을 제거하면, SOUND_OUT(PE3) 핀이 High일 때만 실행되도록 하는데 스피커 출력을 중단할 때도, 기존의 논리 상태를 유지한다. if문을 사용하지 않으면  PE3 신호가 중단될 때 의도치 않은 전환이나 짧은 펄스 발생이 가능.

 

현재 타이머 상태와 무관하게 즉시 출력 핀의 상태를 변경하는 동작을 트리거하여 핀을 초기화하거나 타이머를 정지하는 경우 등에 사용 안정화




 

https://github.com/Shoons23/AVR_basic/tree/main/TimerBasic2

 

AVR_basic/TimerBasic2 at main · Shoons23/AVR_basic

microprocessor basic in AVR. Contribute to Shoons23/AVR_basic development by creating an account on GitHub.

github.com

 

 


issue1 : 스피커음이 이상하게 들림

output compare resgister에 의도하지 않은 값이 들어가서 수정

 

issue2 : 스피커음이 중간에 끊기는 듯한 결과가 나타남

 

timer3 counter를 초기화