# 프로그램 설명
1. MsTimer2 라이브러리를 사용한다. 스피커 단자를 13번으로 선언하고 상태를 저장하는 변수(주파수 입력이 가능한지) state, 스피커 상태를 저장하는 변수 speak, 인터럽트 횟수를 세는 변수 count를 전역 변수로 선언하고 0으로 초기화한다. 입력받을 주파수 freq와 myTone() 함수에서 음정을 출력하기 위해 사용될 (주기/2) us 초를 저장할 변수 x를 선언한다.
2. setup() 부분에서 주파수를 입력 받기 위해서 시리얼 전송 속도를 설정하고 MsTimer2를 사용해서 1ms에 한 번씩 인터럽트 발생 시에 호출될 isr 함수를 설정하고 인터럽트를 발생시킨다. 스피커 단자를 출력 모드로 설정한다.
3. 인터럽트 발생 시에 호출될 함수 isr을 선언한다. 이 함수는 상태가 1이면 (주파수가 입력되어 출력 가능한 범위이면) 인터럽트가 발생할 때마다(1ms마다) count 값을 올린다. 이 (count*1000) ms 값을 (주기/2) us인 x값으로 나누어 1이 되면 스피커 단자에 입력받은 주파수를 출력하므로 count값을 0으로 초기화하고 스피커 상태 변수를 1로 바꾸어 loop 부분에서 myTone() 함수를 사용하여 스피커 단자에 원하는 주파수의 음정을 출력할 수 있도록 한다. count는 1ms마다 올라가고 (주기/2)는 us이므로 1000을 곱하여 us로 만들어 비교할 수 있도록 한다. 예를 들어 100hz의 주파수가 입력되면 count가 5000us와 같은 5000일 때 스피커가 출력된다.
4. myTone() 함수는 핀 단자 번호와 원하는 주파수를 입력받아서 2초 동안 그 주파수의 음정을 단자에 출력하는 함수이다. dgitialWrite() 함수로 HIGH를 내보낸 뒤 (주기/2)us초(주기/2) us초 동안 Microsecondsdelay() 함수로 지연시킨 후 digitalWrite() 함수로 LOW를 내보낸 뒤 (주기/2) us초 동안 Microsecondsdelay() 함수로 지연시키면 원하는 주파수의 음정을 출력할 수 있다.
5. myNoTone() 함수는 핀 단자 번호를 입력받아 그 단자에 LOW를 출력하여 스피커 단자의 소리를 끄는 함수이다.
6. loop() 부분에서 상태가 1이고 (주파수가 입력되어 출력 가능한 범위) isr() 함수에서 (count*1000)값을 (주기/2)인 x값으로 나누어 1이 되어 스피커 상태 변수가 1이 되면 myTone() 함수를 사용하여 스피커 단자에 원하는 주파수의 음정을 2초 동안 출력한 후 myNoTone() 함수를 사용하여 스피커 단자의 소리를 끈 뒤 상태 변수 state를 주파수를 입력 가능한 상태인 0으로 바꾼다.
7. 스트링 변수를 선언하고 상태가 0이면(주파수 입력이 가능한 상태) 주파수를 입력하여 들어온 값을 문자단위로 읽어서 출력한 후 이를 스트링 변수에 더한다. 스트링 변수를 int형으로 변환하여 변수 freq에 저장하고 이 주파수를 이용하여 myTone() 함수에서 음정을 출력하기 위하여 1000000/(freq*2) 공식을 이용하여 (주기/2) us초인 값을 변수 x에 저장한다. 입력받은 주파수가 500hz 이하 이면, 즉 주기가 2ms(2000us) 이상인 경우((주기/2) us인 x가 1000 이상인 경우) 주파수가 출력 가능한 범위라는 것을 알리기 위해 상태를 1로 바꾼다. 입력받은 주파수가 출력 불가능한 범위이면, 즉 주기가 2ms 보다 작은 경우에는 거절 메시지를 출력한다.
8. 스피커 단자의 신호 단자를 13번 단자에 연결하고 GND 단자를 GND 단자에 연결한다.
# 소스
#include <MsTimer2.h> // MsTimer2 라이브러리 사용
#define spkPin 13 // 스피커 단자
volatile int state = 0; // 상태를 저장하는 변수
volatile int speak = 0; // 스피커 상태를 저장하는 변수
volatile int count = 0; // 인터럽트 횟수를 세는 변수
int freq; // 주파수
int x; // 주기/2
void setup() {
Serial.begin(9600); // 시리얼 전송 속도 설정
MsTimer2::set(1,isr); // 1ms에 한 번씩 인터럽트 발생 시 호출될 isr 함수 설정
MsTimer2::start(); // 인터럽트 발생
pinMode(spkPin, OUTPUT); // 스피커 단자 출력 모드로 설정
}
void loop() {
if(state == 1){ // 상태가 1이면(주파수가 입력되어 출력 가능한 범위)
if(speak == 1){ // 스피커 상태 변수가 1이면
speak=0; // 스피커 상태 변수를 0으로 바꾼 후
myTone(spkPin,freq); // 스피커 단자에 원하는 주파수의 음정 출력
myNoTone(spkPin); // 스피커 단자 소리 끔
state = 0; // 상태 0으로 바꿈(주파수 입력 가능한 상태)
}
}
String inString; // 스트링 변수 선언
if(state == 0){ // 상태가 0이면(주파수 입력 가능한 상태)
while(1){
if(Serial.available()>0){ // 시리얼에 주파수 입력했으면
char inChar = Serial.read(); // 들어온 값 문자단위로 읽음
if(inChar == '\n')
break;
Serial.print(inChar); // 들어온 값 출력
inString += (char)inChar; // 스트링 변수에 더함
freq = inString.toInt(); // int형으로 변환
x = 1000000/(freq*2); // (주기/2)us를 x에 저장
}
}
Serial.println();
state = 1;
if(x < 1000){ // 주파수가 출력 불가능한 범위이면 거절 메세지 출력
state = 0;
Serial.println("생성할 수 없는 주파수입니다.");
}
}
}
void isr(){ // 인터럽트 발생 시 호출되는 함수
if(state == 1) // 상태가 1이면 (주파수가 출력 가능한 범위이면)
count++; // 인터럽트 발생할 때마다 count값 올림
if((count*1000)/x == 1){ // count(ms)*1000를 (주기/2)us인 x값로 나누어 1이 되면
count = 0; // count값 0으로 초기화
speak = 1; // 스피커 상태 변수 1로 바꿈
}
}
void myTone(int pin, int frequency){ // 원하는 주파수 출력하는 함수
for(int i=0; i<frequency*2; i++){ // 2초 동안 출력
digitalWrite(pin, HIGH);
delayMicroseconds(x);
digitalWrite(pin, LOW);
delayMicroseconds(x);
}
delay(1000);
}
void myNoTone(int pin){ // 스피커 단자 소리 끄는 함수
digitalWrite(pin, LOW);
}
# 실행화면(Tera Term)
- 낮은 도부터 높은 도까지의 주파수와 200, 500, 1000 입력
- 높은 도와, 1000는 출력 불가능 범위이므로 음정이 출력되지 않음
- 낮은 도 ~ 시, 200, 500은 출력 가능 범위이므로 해당하는 음정이 출력됨
'전공 공부 > 아두이노프로그래밍' 카테고리의 다른 글
pulseIn()과 같은 기능을 하는 함수를 인터럽트를 이용하여 구현 (0) | 2021.01.22 |
---|---|
20KHz의 클럭 소스를 이용하여 인터럽트를 발생. 이를 기반으로 tone() 함수를 설계하고 한 옥타브의 음정을 연주 (0) | 2021.01.22 |
analogWrite() 함수 이용하여 390Hz의 periodic interrupt를 발생시키는 프로그램 (0) | 2021.01.22 |
장애물까지의 거리를 mm단위로 반환하는 함수 (4) | 2021.01.18 |
스피커를 이용한 연주 (0) | 2021.01.18 |