실험 목적
- AVR MCU의 외부 인터럽트 중 PCINT의 활용 방법을 익힌다.
- 기계식 스위치에서 발생하는 바운싱 (bouncing)을 이해한다.
- 오실로스코우프를 사용하여 원하는 타이밍에 신호를 포착하는 방법을 익힌다.
실험 예비 과제
1. 기계식 스위치에서 발생하는 바운싱 (bouncing) 또는 채터링 (chattering)이 무엇인지 설명하라.
2. 다음은 바운싱을 방지하는 (debouncing) 기능을 포함한 스위치 회로이다.
가) 오랜 시간 동안 스위치가 눌려지지 않고 있다. 이 때의 상태를 설명하는 회로를 그리고 출력 전압 v를 구하여라.
나) 위 상태에서 사용자가 스위치를 누른 후 계속 그 상태를 유지하였다. 스위치를 누른 직후부터 출력 전압 v의 변화를 그래프로 그려라. 출력 전압 v가 최종적으로 수렴하는 값과 그 값까지 수렴하는데 걸리는 시간을 구하고 이를 그래프에 표시하여라.
다) 사용자가 스위치를 누르고 있다가 뗐다. 스위치를 뗀 직후부터 출력 전압 v의 변화를 그래프로 그려라. 출력 전압 v가 최종적으로 수렴하는 값과 수렴하는데 걸리는 시간을 구하고 이를 그래프에 표시하여라.
라) 사용자가 스위치를 누를 때와 뗄 때 출력 전압 v의 변화 속도는 어떻게 다른지 그 이유가 무엇인지 설명하여라. (HINT: 시정수)
마) 위 회로에서 capacitor가 없는 상태에서 스위치를 눌렀다 떼었다. 이 때 스위치에서 바운싱 현상이 발생하여 출력 전압 v는 다음 그림과 같은 형태가 되었다고 가정하자.

위 그림에 표시된 펄스의 폭은 모두 0.5 ms이다. capacitor가 연결되어 있는 상태에서 이런 바운싱 현상이 발생했다면 출력 전압 v의 형태가 어떻게 달라지게 되는 그래프를 그리고 설명하여라. (질문 (라)의 답을 이해한다면 이 질문에 쉽게 답할 수 있을 것이다.
3. 실험 5에서는 외부 인터럽트 INT3과 INT7을 사용했지만 이번 실험에서는 이 인터럽트를 사용하지 않고 PCINT만을 사용할 것이다. interrupt_init() 함수에서 PCINT을 사용하기 위한 설정을 하려고 한다. ATmega1281의 PB5와 PB6은 PCINT5와 PCINT6 입력으로 사용할 수 있다. 이를 위하여 아래 함수를 완성하고 그 내용을 설명하여라.
void interrupt_init(void)
{
// 실험 5에서 설정한 내용은 삭제한다.
// PCINT5와 PCINT6을 PCINT 입력으로 사용하도록 PCMSK0 레지스터를 설정한다.
// PCINT5와 PCINT6을 사용한 PCINT를 enable시키도록 PCICR 레지스터를 설정한다.
sei();
}
실험 과정
1. 이번 실험에서는 PB5와 PB6 pin을 PCINT5와 PCINT6 입력으로 사용하려고 한다. 그런데 실습 보드에서는 이 두 pin에 아무 것도 연결되어 있지 않다. wire를 사용하여 PD3과 PB5를 그리고 PD4와 PB6을 각각 연결하여라. 이렇게 하면 확장 보드의 스위치 SW3과 SW4가 각각 PB5와 PB6에 연결된다.

2. 실험 5에서 사용한 ioport_init() 함수는 DDRB 레지스터의 값을 변경시키지 않으므로 PORTB의 모든 pin은 기본적으로 입력으로 설정되어 있다. (LED의 색 조절을 위해 fnd_init() 함수에서 이미 PB4 pin은 출력으로 설정하고 있다.) 그리고 SW3과 SW4가 각각 연결되어 있는 PD3과 PD4 핀의 pull-up 저항을 이미 활성화시켜 둔 상태이다 (다시 한번 본인의 코드를 확인하기 바람). 따라서 PB5와 PB6을 위한 추가 설정은 별도로 필요 없으며 실험 5에서 사용했던 ioport_init() 함수를 그대로 사용하면 된다. 위 1 번에서 PB5와 PD3을 서로 연결시켰기 때문에 만일 PB5의 pull-up 저항을 추가로 enable시키면 이 저항과 PD3의 pull-up 저항이 병렬 연결되어 pull-up 저항의 크기가 절반으로 줄어드는 결과를 가져온다. 마찬가지 이유로 PB6의 pull-up 저항도 enable시켜서는 안 된다.

3.
SW4의 풀업 저항을 활성화 시키는 코드를 추가하고, 예비6 과제에서 interrupt_init 함수를 가져온다.

4. 4. 1 번의 연결이 제대로 되었는지 오실로스코우프를 사용하여 검증하자. SW3과 SW4 스위치를 눌렀다 뗄 때마다 각각 PB5와 PB6 pin의 전압이 제대로 변하는지 확인하여라. 이런 용도라면 trigger mode를 Auto로 두는 것이 가장 편하다. ioport_init() 함수를 실행시켜야만 스위치의 pull-up 저항이 켜지므로 일단 코드를 실행시킨 상태에서 오실로스코우프로 관측해야 한다.
PB5에서 스위치를 누를 때(왼쪽그림), 뗼 떄(오른쪽 그림) 다음과 같이 전압이 변함을 알 수 있다.


5. 실험 5에서와 마찬가지로 fnd_num이라는 이름의 uint16_t형 전역 변수를 선언하고 0으로 초기화하고 리셋된 다음 FND에는 0이 표시되도록 하여라. 또 실험 5에서와 마찬가지로 while() 루프에서는 확장 보드의 LED 상태를 250 ms 간격으로 바꾸도록 하여라


6. interrupt_init() 함수에서 PCINT0를 활성화시켰으므로 이제 스위치 SW3이나 SW4 둘 중 하나라도 상태가 변하면 PCINT0가 발생한다. 그러므로 PCINT0를 위한 ISR이 필요하다. 우선 SW3을 누를 때마다 FND에 표시된 값을 1씩 증가시키는 일을 하도록 ISR을 작성하여라. FND에 표시된 값이 999라면 그 다음은 0이 되어야 한다. 이 일을 하기 위해서는 어떤 원인에 의해서 PCINT0가 발생했는지 ISR에서 찾아야 한다. PCINT의 ISR에서 PINB 레지스터를 읽어서 그 값으로부터 PCINT0를 발생시킨 원인을 찾을 수 있다. SW3 스위치가 떼어지는 순간에는 FND에 표시된 값에 아무런 변화도 없어야 한다.



7. 6 번에서 작성한 프로그램을 실행시켜 SW3을 누를 때 마다 FND에 표시된 값이 1씩 증가하는지 확인하여라. SW3이 제대로 동작한다면 이제 스위치 SW4에 새로운 기능을 추가하자. 실험 5에서 했던 것처럼 SW4를 누르면 그 때마다 FND에 출력된 값이 1씩 감소하도록 5 번의 ISR에 내용을 추가하고 그 내용을 설명하여라. FND에 표시된 값이 0 일 때 SW4를 한 번 누르면 그 다음 값은 999가 되어야 한다. ISR에서 이전 스위치 상태를 저장한 다음 새롭게 읽은 상태와 이를 비교하는 과정은 필요하지 않다. 현재의 스위치 값을 확인하는 것만으로 이 일을 할 수 있다.




8. 7 번까지 기능 확인을 마쳤다면 이제 SW3과 SW4를 여러 차례 누르면서 스위치를 한 번 누를 때마다 FND에 표시된 값이 정확하게 1씩 변하는지 확인해 보아라. SW3을 눌렀다 뗐을 때에는 FND에 표시된 값이 정확하게 1씩 증가하지만 SW4를 눌렀다 뗐을 때에는 FND에 표시된 값이 2 이상 감소하는 것을 종종 볼 수 있을 것이다. 이 때 상황을 좀 더 자세하게 관찰해 보면 SW3과 달리 SW4는 스위치를 누르는 순간 외에도 스위치를 떼는 순간에도 FND 값이 감소하는 것을 확인할 수 있을 것이다. 오실로스코우프를 사용해 스위치를 떼는 순간 PB5와 PB6 pin의 전압 변화의 차이를 확인하고 왜 이런 현상이 생기는지 그 이유를 회로도를 참고하여 설명하여라. 스위치를 누르는 순간이나 떼는 순간을 포착하기 위해서는 trigger mode를 Normal로 설정하고 trigger level을 0 V와 5 V 사이의 적당한 값으로 두어야 한다. Time/Div는 1 ms 정도로 두면 된다.
바운싱 현상 떄문에 PB5, PB6 pin의 전압 변화가 차이가 있다. SW3을 눌렀다 뗐을 때는 Debouncing을 고려한 RC회로여서 스위치를 누를 때 빠르게 전압이 하락하고, 뗄 때는 천천히 올라 바운싱이 일어나도 한 번만 스위치를 누르게 되는 원래 동작을 한다. SW4를 눌렀다 뗐을 때는 바운싱이 일어난 만큼 동작을 하여 2씩 감소처럼 예상치 못한 동작을 하게 된다.



9. 7 번의 코드에서는 SW3을 누를 때 FND의 값이 1씩 증가하고 SW4를 누를 때 FND의 값이 1씩 감소하도록 되어 있다. 두 스위치 모두 누를 때가 아니라 떼는 순간 이 동작을 하도록 ISR을 수정하고 그 내용을 설명하여라. 이 때 8 번에서 확인한 SW4의 bouncing 문제는 무시한다. 단순히 7번 코드에 있는 if 문의 조건을 논리 반전시키는 것으로는 문제를 해결할 수 없을 것이다.
PinChangeInterrupt에서는 Control Register의 옵션이 하나이므로 상승 엣지 때 인터럽트 발생하려면 앞서 swich_hit 함수 같이 이전상태를 저장하여 현재 상태와 비교하는 다음과 같은 코드를 작성해야 한다. 따라서 SW3가 눌려짐을 판별하고, 현재상태가 이전 상태와 다른지를 판별하여 상승엣지를 나타낸다.

https://github.com/Shoons23/AVR_basic/tree/main/PinChangeInterrupt
AVR_basic/PinChangeInterrupt at main · Shoons23/AVR_basic
microprocessor basic in AVR. Contribute to Shoons23/AVR_basic development by creating an account on GitHub.
github.com
'전자공학 > 마이크로프로세서' 카테고리의 다른 글
| [atmega1281] 실험 11. AVR ADC의 활용 (0) | 2024.12.11 |
|---|---|
| [atmega1281] 실험 10. AVR의 UART를 사용한 비동기 직렬 통신 (0) | 2024.12.02 |
| [atmega1281] 실험 9. AVR MCU의 타이머 활용 3 – PWM (1) | 2024.11.27 |
| [atmega1281] 실험 8. AVR MCU의 타이머 활용 2 – 스피커 구동 (1) | 2024.11.21 |
| [atmega1281] 실험 7. AVR MCU의 타이머 활용 1 – 기초 (1) | 2024.11.20 |