Interrupt(인터럽트) - 시리얼 통신 입력
1. 이전 소스에 시리얼 통신을 테스트하기 위해 setup()함수에서 Serial.begin()함수를 설정하고 serialEvent()함수를 추가하여 컴파일하고 업로드합니다.
void setup() {
for (int pinIndex = 0; pinIndex < pinCount; pinIndex++) {
pinMode(ledPins[pinIndex], OUTPUT);
}
Serial.begin(9600);
while (!Serial) {
}
Serial.println("Serial Port Connected.");
}
void serialEvent() {
int readByte = Serial.read();
Serial.println(readByte);
}
2. 시리얼 모니터를 실행시킨 후 "1234567890"을 입력하고 전송시킵니다.
응답 결과를 보면 10초마다(실제로는 10초 이상씩 걸립니다.) serialEvent()함수가 한 번씩 호출되는 것을 확인할 수 있습니다.
그 이유는 loop()함수에서 for문의 반복 횟수가 많거나 처리할 내용 복잡하여 연산 시간이 오해 걸리거나 for문안에 delay()함수가 포함되어 있으면 시리얼 통신으로 입력이 들어와도 바로 serialEvent()함수가 호출되지 않고 for문이 종료된 후 한 번만 호출됩니다.
그래서 loop()함수에는 for문대신 전역 변수와 delay()함수만 사용하여 for문처리 처리되게 수정합니다.
1. 전역에 반복 인덱스 변수를 선언합니다.
// 반복 인덱스
byte loopIndex = 0;
2. loop()함수에서 반복 인덱스를 증가시켜 7-세그먼트에서 숫자(0부터 9까지)를 표시하게 하고 1초를 대기하게 합니다.
void loop() {
if (loopIndex > 9) {
loopIndex = 0;
}
segmentDigitWrite(loopIndex);
delay(1000);
loopIndex++;
}
3. 컴파일하고 업로드합니다. 그리고 시리얼 모니터를 실행시킨 후 "1234567890"을 입력하고 전송시킵니다.
응답 결과를 보면 1초마다 serialEvent()함수가 한 번씩 호출되는 것을 확인할 수 있습니다.
for문이 사용하지 않기 때문에 serialEvent()함수가 호출되는 시간이 짧아졌습니다.
향후에 delay()함수를 사용하지 않고 millis()함수로 처리하는 방법에 대해 알아보겠습니다.
이제부터 serialEvent()함수를 사용하여 숫자 카운트를 실행시키거나 중지시키거나 지정된 숫자부터 다시 시작되게 해보겠습니다.
디지털 핀 출력 제어 - 7-Segment LED 숫자 카운트 제어
1. 전역에 반복 여부 변수와 반복 상태 변수를 선언합니다.
// 반복 여부
boolean loopDigit = true;
// 반복 상태
byte loopState = 's';
숫자 2차원 배열에 8개 LED(a, b, c, d, e, f, g, dp)에 디지털 신호를 주는 배열을 추가합니다.
// 숫자 0~9
byte segmentDigit[12][8] = {
{1, 1, 1, 0, 1, 0, 1, 1}, // 0
{0, 1, 1, 0, 0, 0, 0, 0}, // 1
{1, 1, 0, 0, 0, 1, 1, 1}, // 2
{1, 1, 1, 0, 0, 1, 0, 1}, // 3
{0, 1, 1, 0, 1, 1, 0, 0}, // 4
{1, 0, 1, 0, 1, 1, 0, 1}, // 5
{1, 0, 1, 0, 1, 1, 1, 1}, // 6
{1, 1, 1, 0, 0, 0, 0, 0}, // 7
{1, 1, 1, 0, 1, 1, 1, 1}, // 8
{1, 1, 1, 0, 1, 1, 0, 1}, // 9
{0, 0, 0, 1, 0, 0, 0, 0}, // dot
{0, 0, 0, 0, 0, 0, 0, 0} // clear
};
2. loop()함수에서 반복 여부 변수를 이용하여 조건으로 true이면 처리하고 false이면 처리하지 않게 합니다.
void loop() {
if (loopDigit) {
if (loopIndex > 9) {
loopIndex = 0;
}
segmentDigitWrite(loopIndex);
Serial.println(loopIndex);
delay(1000);
loopIndex++;
}
}
3. 7-세그먼트에 숫자를 Blink(블링크 - 깜박거리다) 처리하게 함수를 선언합니다.
1초 동안 0.1초마다 7-세그먼트에 숫자를 표시하고 지워서 깜빡거리게 합니다.
void segmentDigitBlink(byte digit) {
for (byte index = 0; index < 5; index++) {
segmentDigitWrite(digit);
delay(100);
segmentDigitWrite(11);
delay(100);
segmentDigitWrite(digit);
}
}
4. serialEvent()함수가 시리얼 버퍼로부터 읽은 바이트를 switch case문으로 처리합니다.
switch case 문은 if 문과 비슷하지만 여러 조건들을 처리할 수 있습니다. if문에 else if문을 여러 개 사용하는 것과 같습니다.
switch의 변수와 case의 비교 값을 맞으면 처리됩니다. case는 break가 나올 때까지 처리합니다. default는 case에 맞는 조건이 것이 없으면 처리됩니다. default는 생략할 수 있습니다.
switch(변수) {
case 비교1 값:
처리할 코드
break;
...
case 비교n 값:
처리할 코드
break;
default:
처리할 코드
break;
}
아두이노 IDE에서는 시리얼 통신을 통한 디지털 핀 제어를 switch case 문으로 처리하는 방법을 예제로 제공하고 있습니다.
다음은 아두이노 IDE에서 제공하는 switch case문의 예제 소스입니다.
메뉴 : 파일 > 예제 > 05. Control > switchCase2
void setup() {
// initialize serial communication:
Serial.begin(9600);
// initialize the LED pins:
for (int thisPin = 2; thisPin < 7; thisPin++) {
pinMode(thisPin, OUTPUT);
}
}
void loop() {
// read the sensor:
if (Serial.available() > 0) {
int inByte = Serial.read();
// do something different depending on the character received.
// The switch statement expects single number values for each case; in this
// example, though, you're using single quotes to tell the controller to get
// the ASCII value for the character. For example 'a' = 97, 'b' = 98,
// and so forth:
switch (inByte) {
case 'a':
digitalWrite(2, HIGH);
break;
case 'b':
digitalWrite(3, HIGH);
break;
case 'c':
digitalWrite(4, HIGH);
break;
case 'd':
digitalWrite(5, HIGH);
break;
case 'e':
digitalWrite(6, HIGH);
break;
default:
// turn all the LEDs off:
for (int thisPin = 2; thisPin < 7; thisPin++) {
digitalWrite(thisPin, LOW);
}
}
}
}
's'가 입력되면 시작, 'p'가 입력되면 중지, 'r'이 입력되면 재시작, 'e'가 입력되면 종료 처리하게 합니다.
조건문 처리에 대한 내용은 주석문으로 참고하세요.
그리고 숫자 0부터 9까지 입력되면 7-세그먼트에 숫자를 표시하고 Blink(블링크 - 깜박거리다) 처리합니다.
void serialEvent() {
int readByte = Serial.read();
switch (readByte) {
case 's':
if (loopState == 'e') { // 반복 상태가 end일때 처리
loopIndex = 0; // 숫자를 0으로 변경
loopState = 's';
loopDigit = true;
Serial.println("start");
}
break;
case 'p':
if (loopState == 's') { // 반복 상태가 start일때 처리
loopDigit = false;
loopState = 'p';
Serial.println(loopIndex);
Serial.println("pause");
segmentDigitWrite(loopIndex); // pause 될때 숫자
}
break;
case 'r':
if (loopState == 'p') { // 반복 상태가 pause일때 처리
loopDigit = true;
loopState = 's';
Serial.println("restart");
}
break;
case 'e':
if (loopState == 's' || loopState == 'p') { // 반복 상태가 start 또는 pause일때 처리
loopDigit = false;
loopState = 'e';
Serial.println("end");
segmentDigitWrite(11); // 7-세그먼트의 표시를 지움
}
break;
default:
if (loopState == 'p') { // 반복 상태가 pause일때 처리
if (readByte >= '0' && readByte <= '9') { // 숫자 0부터 9까지만 처리
// ASCII코드
// 0: 48, 1: 49, 2: 50, 3: 51, 4: 52, 5: 53, 6: 54, 7: 55, 8: 56, 9: 57
loopIndex = readByte - 48; // 0의 ASCII코드는 값인 48로 빼면 숫자 값이 됨
segmentDigitBlink(loopIndex); // 변경된 숫자를 깜빡 거림
Serial.println(loopIndex);
}
}
break;
}
}
도트 표시는 숫자가 아니라 재시작할 수 없어서 제어 처리에서는 제외하였습니다.
5. 컴파일하고 업로드합니다. 잠시 후 7-세그먼트의 LED에 불이 들어오면서 숫자 0부터 순차적으로 숫자가 표시됩니다. 시리얼 모니터에서 8자일 때 'p'을 전송하여 중지시키고 다시 'r'를 전송하여 재시작시킵니다. 그리고 2자일 때 'p'을 전송하여 중지시키고 '4'를 전송하여 깜빡거리면서 숫자 '4'가 표시됩니다. 다시 '7'를 전송하여 깜빡거리면서 숫자 '7'이 표시됩니다. 마지막으로 'e'를 전송하여 반복 처리와 숫자를 지웁니다.
다음 시리얼 모니터에 전송된 내용입니다.