실험 목적
- ATmega1281 타이머로 PWM 출력을 만드는 방법을 익힌다.
- PWM 출력을 사용하여 DC 모터의 속도를 조절하는 방법을 익힌다.
실험 예비 과제
1. Timer2와 Timer3은 PWM 모드로 동작시키고 Timer5는 실험 8에서처럼 CTC 모드로 동작시키려고 한다. 아래의 timer_init() 함수를 완성하고 그 내용을 설명하여라.
void timer_init(void) {
// Timer2를 Fast PWM 모드로 동작시키고 PWM 신호는 OC2A 핀으로 출력하려고 한다.
// datasheet의 Table 20-8을 참조하여 Mode 3의 Fast PWM으로 동작하도록 설정한다.
// 이 때 prescaler는 1로 설정하고 COM2A1 비트와 COM2A0 비트는 각각 1과 0으로 설정한다.
// 출력 PWM의 duty cycle이 50 %가 되도록 OCR2A 레지스터 값을 설정한다.
// Timer3을 Phase Correct PWM 모드로 동작시키고 PWM 신호는 OC3A 핀으로 출력하려고 한다.
// datasheet의 Table 17-2를 참조하여 Mode 10의 Phase Correct PWM으로 동작하도록 설정한다.
// prescaler를 8로 설정하고 COM3A1 비트와 COM3A0 비트는 각각 1과 0으로 설정한다.
// PWM 주파수가 1 KHz가 되도록 하려면 ICR3 레지스터에 값을 저장한다.
// 출력 PWM의 duty cycle이 50 %가 되도록 OCR3A 레지스터 값을 설정한다.
// Timer5를 CTC 모드로 하고 256×4 Hz의 주파수로 compare match interrupt가 발생하도록 설정한다.
// 이 때 prescaler는 8로 설정한다.
}
- Timer2의 경우 timer1,3,4,5와 다르게 WGM이 조금 다른 것을 주의하고, TIMER/Counter Control Register 설정을 prescaler, mode를 고려하여 작성 후, timer2는 fast PWM mode이므로, single-slope로 작동하므로 127(1+0xff(TOP)/2 -1)이고,
- Timer 3는 PWM, Phase Correct mode이며 dual-slope로 동작하고 TOP값은 ICR3이다. 1kHz의 PWM 주파수로 만들기 위해 적절한 TOP값은 F_CPU/2*8*freq 인 1000 이므로, ICR3 = 1000을 넣어주면 duty cycle 50%를 맞추기 위해 OCR3A =500 을 넣어준다.
Timer 5에는 어쩔 수 없이 소수점으로 나오므로 int형으로 선언되었으므로 소수점은 버려지고, -1을 한 값 1952를 Output Compare Register (OCR5A)에 대입했다.
2. Timer5의 compare match interrupt ISR을 작성한다. signal_table[]이라는 이름의 uint8_t형 배열이 전역 변수로 미리 선언되어 있다고 가정하자. 다시 말해서 ISR에서 이 배열을 선언할 필요는 없다. 이 배열은 256 개의 값을 가지고 있다. 이 ISR이 호출될 때마다 이 배열의 값을 하나씩 읽어서 OCR2A 레지스터에 저장한다. 이 배열은 256개의 값을 가지고 있으므로 이 ISR이 256 회 호출되면 모든 값을 한 번씩 OCR2A 레지스터로 저장한 상태가 된다. 그 이후에는 다시 배열의 첫 번째 값부터 출력을 반복하도록 ISR을 완성하여라. 이 때 변수의 길이와 그 때 변수가 표현할 수 있는 숫자의 범위를 잘 생각해 보면 if 문이 필요하지 않다는 것을 알 수 있다.
signal_table도 uint8_t로 똑같이 8bit(0~255)의 크기를 가지고 있으므로 오버플로우가 나도 i와 같이 0으로 초기화 되어 if문을 굳이 쓸 이유가 없다.
3. 위 ISR에서 배열 signal_table[]의 모든 값이 1 번 출력되면 OC2A 핀으로 어떤 신호가 한 주기 출력된다고 가정하자. 그렇다면 이 신호의 주기는 얼마인가?
Timer5 compare match가 256번 일어났음을 의미하므로 Timer 5 compare match 주파수가 256*4[Hz]이고, 주기가 1/256*4 이므로 배열 signal_table이 모두 출력되면 1/4[s] = 0.25이고, 즉 이 신호의 주기가 0.25초이다.
4. 위 timer_init() 함수를 실행했을 때 OC2A 핀으로 출력되는 PWM 신호의 주파수는 얼마인가?
Timer 2는 Fast PWM mode 이므로 PWM 신호의 주파수는 TOP값의 주파수와 같다. 다시말해, TOV2가 set 되는 주파수와도 같다. 즉, 16M/256 = 62.5kHz 이다.
5. Timer3의 출력인 OC3A 핀으로 출력되는 PWM의 duty cycle을 조절하기 위한 다음 함수를 작성하여라.
void set_timer3_dutycycle(uint8_t dutycycle) {
OCR3A = ??? x dutycycle;
}
여기에서 dutycycle은 0에서 100 사이의 값이며 0과 100을 포함할 수 있다. ???는 어떤 숫자이다. 이 숫자는 얼마인가?
실험 준비물
- 5.1 K 또는 비슷한 값의 저항
- 100 nF capacitor
실험 과정
2. 새로운 project를 만들고 필요한 파일과 코드를 모두 추가하여라. 미리 배포한 signal_table.h 파일을 project에 포함시키고 Timer5의 compare match interrupt ISR이 있는 파일에서 include한다. 실험 8에서 사용한 sound.h 및 sound.c 파일은 프로젝트에 포함시킬 필요가 없다. FND에 표시되는 숫자는 최초 0이 되도록 한다. 필요한 파일과 코드를 다 추가했다면 실험 5에서처럼 250 ms 간격으로 LED의 상태가 아래처럼 바뀌도록 하기 위한 코드를 while() 루프에 추가하여라. 검은 동그라미가 불이 켜진 LED이다.
3. 일단 Timer5의 compare match interrupt ISR이 발생하지 않도록 timer_init() 함수를 수정한다. 프로그램을 실행시켜 OC2A 핀으로 출력되는 파형을 오실로스코우프로 관측하여라. duty cycle이 50 %인 구형파가 출력되어야 한다. duty cycle이 50 %인지 PWM의 주파수가 예비 과제 4 번에서 계산한 값과 일치하는지 확인하여라. duty cycle도 오실로스코우프의 Quick Measurement 기능이나 커서 기능을 사용하면 쉽게 확인할 수 있다.
Timer2는 8-bit timer여서 duty_cycle을 50 %로 설정하기 위해 Output Compare Register 값을 127((1+TOP)/2 -1 )로 설정하여 오실로스코프로 확인했다.
4. OC3A 핀으로 출력되는 PWM 신호의 주파수가 1 KHz인지 그리고 duty cycle이 50 %인지 확인하여라.
5. 이제 timer_init() 함수를 원래대로 복원시켜 Timer5의 compare match interrupt가 발생하도록 만든 다음 프로그램을 실행시키고 OC2A 핀으로 출력되는 파형을 관측하여라. 이제 Timer5의 ISR이 주기적으로 실행되면서 OCR2A 레지스터의 값을 바꾸기 때문에 이제 OC2A 핀으로 출력되는 펄스의 duty cycle이 계속 변하는 것을 볼 수 있을 것이다. 만일 duty cycle이 변하지 않는다면 ISR이 실행되고 있지 않거나 또는 ISR에서 OCR2A 레지스터의 값을 제대로 바꾸지 못하고 있는 것이다. 원인을 파악하고 코드를 수정하여라.
6. 5 번에서 OC2A 핀으로 PWM 신호가 정상적으로 출력된다면 확장 보드의 LED의 색이 계속 변하는 것을 볼 수 있을 것이다. 그리고 색이 변하는 주기는 예비 과제 3 번에서 계산한 주기와 같을 것이다.
연두색 빛과 주황색 빛이 번갈아 나타난다.
7. 6 번에서 LED 색의 변화를 확인했다면 아래 회로를 연결한다.
이렇게 연결한 다음 위 그림에서 화살표로 표시한 노드를 오실로스코프로 관측해 보아라. 여러분들에게 매우 익숙한 파형이 깨끗하게 출력되는 것을 볼 수 있을 것이다. 만일 파형이 깨끗하지 않고 어딘가에 불연속적인 부분이 있다면 코딩에 문제가 있다는 뜻이다. 만일 파형은 깨끗하지만 이 파형의 주기가 예비 과제 3 번에서 계산한 값과 다르다면 ISR이 호출되는 주파수가 1024 Hz가 아닐 가능성이 매우 높다.
4Hz 주파수가 나오게 되는데 이는 위에서 RC 필터로 LPF를 만들었기 때문에 1024Hz는 아주 작게 감쇠되고, 4Hz의 구형파 신호가 사인파의 형태로 들어오게 된 것이다.
8. 이제 set_timer3_dutycycle() 함수에 임의의 값을 대입한 후 프로그램을 실행시켜 OC3A 핀으로 출력되는 신호의 duty cycle이 의도한 대로 바뀌는지 여부를 확인하여라. 이를 확인했다면 MCU가 reset된 다음 OC3A로 출력되는 PWM 신호의 duty cycle은 0이 되도록 timer_init() 함수를 수정하여라.
9. 외부 인터럽트를 사용하여 SW2 스위치를 한 번씩 누를 때 마다 OC3A 핀으로 출력되는 펄스의 duty cycle이 10 %씩 증가하도록 하여라. duty cycle의 최대 값은 100 %이므로 100 %가 되었다면 스위치 SW2를 눌러도 duty cycle은 계속 100 %를 유지해야 한다.
10. 외부 인터럽트를 사용하여 SW3 스위치를 한 번씩 누를 때 마다 OC3A 핀으로 출력되는 펄스의 duty cycle이 10 %씩 감소하도록 하여라. duty cycle이 0 %가 되었다면 스위치 SW3를 눌러도 duty cycle은 계속 0 %를 유지해야 한다.
11. 이제 J16에 DC 모터를 연결하고 duty cycle이 10 %씩 변할 때 마다 모터의 회전 속도가 변하는지 확인해 보아라. 이런 방법으로 PWM을 사용하여 DC 모터의 속도를 조절할 수 있다.
https://github.com/Shoons23/AVR_basic/tree/main/TimerBasic3
'전자공학 > 마이크로프로세서' 카테고리의 다른 글
[atmega1281] 실험 11. AVR ADC의 활용 (0) | 2024.12.11 |
---|---|
[atmega1281] 실험 10. AVR의 UART를 사용한 비동기 직렬 통신 (0) | 2024.12.02 |
[atmega1281] 실험 8. AVR MCU의 타이머 활용 2 – 스피커 구동 (1) | 2024.11.21 |
[atmega1281] 실험 7. AVR MCU의 타이머 활용 1 – 기초 (1) | 2024.11.20 |
[atmega1281] 사진 첨부 해야 함 - 실험 6. AVR MCU의 PCINT 활용 및 스위치 바운싱 (Bouncing) 관측 (0) | 2024.11.06 |