


이번 대회는 특이하게 대구엑스코에서 열리던데, 위치도 학교에서 가까워서 박스째로 오실로스코프, 인두기, 전원공급기, 함수발생기 등등 가져갈수 있는건 모두 다 챙겨서 가져갔다ㅋㅋ
누구보다 준비물은 많이 챙겨갔을 꺼라고 자부했는데 다른학교 팀에서 컴퓨터 모니터째로 들고온 걸 보고 한 수 배웠다...

근데 가보니깐 의외로 모든게 다 준비되어 있고 멀티미터나 오실로스코프같은 장비도 팀당 1대씩 주셨다.
대회 전에 대회에 대한 정보는 거의 주지 않았고, 만능기판에 납땜을 해서 회로기판을 하나 만드는지, 브래드보드로 만드는지 정보도 없었기에 의문이 많았는데, 둘다 사용한다고는 생각 못했다.


주제는 피아노 visualizer였다.
기존 장난감 피아노는 피아노를 입력하게 되면 그 입력값을 피아노 내부 회로로 받아오게 되고 그 입력에 따른 소리가 나는 형식으로 설계되어있다.
우리가 할 일은 그 피아노의 입력신호를 가로채서 우리의 MCU로 피아노입력 정보를 가져온 후, 그 입력에 따른 DOT MATRIX를 설계하는 것이 첫번째 과제였다.
여기서 가장 중요한점은 중복입력이 가능하야 한다는 점이다.
피아노에 경우 화음을 넣을 수도 있고 정상적인 연주가 가능하려면 신뢰성이 중요하다.
먼저 38개의 건반이 MCU로 입력되는 방식을 파악한 다음에 그 방식을 토대로 동시입력을 가능하게 설계하는 것이 관건이다.
입력된 피아노의 건반은 MCU에서 처리하고 DOT MATRIX를 사용하여 visualize해야하고 이를 위해서 SPI통신을 활용해야 한다.

키보드를 입력한 후 오실로스코프로 파형을 분석해보니 주기성이 있다는 것을 확인할 수 있었고, 멀티미터로 short테스트를 해본 결과 예상한 바와 같이 마프설에서 배운 key matrix방식으로 작동된다는 것을 확인하였다.


이 방식은 여러개의 입력을 받을 때 입력핀을 크게 줄일 수 있는 방식이다.
1) active high 방식
관심있는 column에 high를 인가하고 row를 입력받는다. 이때 특정 row값이 1이 입력된다면 누른 장소가 특정된다.
이때 입력받는 입장에서는 pull up, pull down을 달아주지 않으면 아무 입력이 되지 않을때 floating상태가 된다.
따라서 입력이 없을 때 0이 인가되게 하는 mcu 내부에 pull down 저항을 켜준다.
2) active low 방식(채택)
모든 col에 high를 인가하다가 관심있는 col에 low를 인가한다.
input port 입장에서는 idle 상태가 high인데, 스위치가 눌리면 low가 되는 것으로 스위치 여부를 판단하는 것이다.
pull up 저항을 켜주어야 한다.
int KeySet(void) {
volatile int KeyBuff = 99;
volatile int col8;
for (int col = 0; col < 5; col++) // Column loop (PC0 to PC4)
{
col8 = col * 8;
// 활성화된 컬럼 핀만 LOW로 설정
PORTC &= ~(1 << col);
_delay_us(5); // 안정화 시간
// Row 핀 확인 (PD2, PD3, PD4, PB7, PD5, PD6, PD7, PB0)
if (!(PIND & (1 << PD2)))
KeyBuff = col8 + 0;
if (!(PIND & (1 << PD3)))
KeyBuff = col8 + 1;
if (!(PIND & (1 << PD4)))
KeyBuff = col8 + 2;
if (!(PINB & (1 << PB7)))
KeyBuff = col8 + 3;
if (!(PIND & (1 << PD5)))
KeyBuff = col8 + 4;
if (!(PIND & (1 << PD6)))
KeyBuff = col8 + 5;
if (!(PIND & (1 << PD7)))
KeyBuff = col8 + 6;
if (!(PINB & (1 << PB0)))
KeyBuff = col8 + 7;
// 다시 해당 컬럼 핀을 HIGH로 복원
PORTC |= (1 << col);
}
return KeyBuff;
}
이 방식은 직관적이고 코딩하기 쉽다는 장점이 있지만 큰 단점이 하나 존재한다.
바로 한 열에서 동시에 입력이 될 경우 구분할 수 없다는 점이다.
코드상에서 keyset은 입력된 키를 반환하는 것이다. 하지만 동시입력이 되었다고 생각해보자.
해당되는 입력 pin이 low가 되어서 값을 받아올 수 있다고 가정하여도, 최종적으로 return되는 값은 하나의 음에 해당되는 값이기 때문에 이 코드는 구조적으로 병렬처리를 할 수 없다.
이 단점을 획기적으로 개선하였는데, 그것은 8bit를 한꺼번에 return하는 것이다.
개선 후 코드
void KeySet(void) {
volatile int col8;
for (int col = 0; col < 5; col++) // Column loop (PC0 to PC4)
{
col8 = col * 8;
PORTC &= ~(1 << col);
_delay_us(5); // 안정화 시간
// Row 핀 확인 (PD2, PD3, PD4, PB7, PD5, PD6, PD7, PB0)
// 확인해서 눌려있으면 btnPushed를 Set, 안 눌려있으면 Clear
(!(PIND & 1 << PD2)) ? (btnPushed |= (uint64_t)1 << (col8 + 0))
: (btnPushed &= ~((uint64_t)1 << (col8 + 0)));
(!(PIND & 1 << PD3)) ? (btnPushed |= (uint64_t)1 << (col8 + 1))
: (btnPushed &= ~((uint64_t)1 << (col8 + 1)));
(!(PIND & 1 << PD4)) ? (btnPushed |= (uint64_t)1 << (col8 + 2))
: (btnPushed &= ~((uint64_t)1 << (col8 + 2)));
(!(PINB & 1 << PB7)) ? (btnPushed |= (uint64_t)1 << (col8 + 3))
: (btnPushed &= ~((uint64_t)1 << (col8 + 3)));
(!(PIND & 1 << PD5)) ? (btnPushed |= (uint64_t)1 << (col8 + 4))
: (btnPushed &= ~((uint64_t)1 << (col8 + 4)));
(!(PIND & 1 << PD6)) ? (btnPushed |= (uint64_t)1 << (col8 + 5))
: (btnPushed &= ~((uint64_t)1 << (col8 + 5)));
(!(PIND & 1 << PD7)) ? (btnPushed |= (uint64_t)1 << (col8 + 6))
: (btnPushed &= ~((uint64_t)1 << (col8 + 6)));
(!(PINB & 1 << PB0)) ? (btnPushed |= (uint64_t)1 << (col8 + 7))
: (btnPushed &= ~((uint64_t)1 << (col8 + 7)));
// 다시 해당 컬럼 핀을 HIGH로 복원
PORTC |= (1 << col);
}
}
먼저 btnPushed라는 전역변수를 하나 선언한다.
이 값은 전체 피아노에서 입력된 모든 값을 저장하는 역할을 한다.
기존 코드는 여러 값이 입력되더라도 하나의 값이 return된다는 단점을 가지고 있었지만 이 코드는 38개의 버튼눌림 유무를 판단하기 위한 비트를 가지고 있다.
먼저 1번 col에서 동시입력이 되었을 경우 그 버튼에 해당되는 비트가 마스킹에 의해 1이 된다. 이때 다른 버튼 또한 조건문에 걸리게 된다면 그에 해당되는 비트도 1이 된다.
이 과정이 모든 row에 걸쳐 진행이 되면, 피아노에 모든 상태에 대한 정보가 하나의 변수에 담기게 되는 것이다.
따라서 하나의 함수를 호출을 할 때 하나의 값이 아닌 38개의 값을 한번에 불러온다.

'프로젝트 > MCU 경진대회' 카테고리의 다른 글
| 2024 COSS 차세대반도체 MCU 응용 경진대회 본선 2일차 (0) | 2025.04.02 |
|---|---|
| 2024 COSS 차세대반도체 MCU 응용 경진대회 본선 1일차(2/2) (0) | 2025.04.02 |
| 2024 COSS 차세대반도체 MCU 응용 경진대회 예선 (2) | 2025.04.01 |