※ 결론보다 문제점 설명이 더 길기에 미리 쓰는 해결방법 : 풀 다운 저항
아두이노의 디지털 핀 확장을 위해 많은 분들께서 74HC595나 74HC165 IC 등을 주로 사용하곤 합니다.
제 경우에는 업무상 필요에 의해 디지털 입력확장 보드를 제작할 필요가 있었는데, 회사에 4051 아날로그 MUX가 많더군요.
이 아날로그 MUX를 디지털 MUX로 사용할 수 있는지 알아봤는데 딱히 이렇게 쓰면 된다 하는 글을 못찾겠더군요.
제 검색 실력이 형편없었을 수도 있고, 그냥 74HC595/165를 쓰는게 효율적이기에 제가 찾는 글이 없었을 수도 있지요.
허나, 업무상 한정적인 자원으로 급하게 진행해야 하는 제 경우엔 또 새로운 IC를 적용해서 테스트를 하고 PCB를 설계할
시간적 여유가 없었기에 결국 4051 아날로그 MUX를 디지털 MUX로 사용하게 되었습니다.
4051 IC는 기본적으로 아날로그 멀티플렉서이며, 9~11번 핀의 A, B, C 컨트롤을 High/Low로 변경하며, 이를 이진법으로 0번에서 7번까지의 채널I/O핀을 제어해 COM I/O핀으로 데이터를 읽던가 보내던가 할 수 있습니다.
다음 그림은 4051 IC를 사용하는 간단한 예제입니다.
# 아두이노 핀 맵핑
- 22 : Ctrl A ( IC 11번핀 )
- 24 : Ctrl B ( IC 10번핀 )
- 26 : Ctrl C ( IC 9번핀 )
- A0 : 0ch I/O ( IC 13번핀 )
- A1 : 1ch I/O ( IC 14번핀 )
- A2 : 2ch I/O ( IC 15번핀 )
- A3 : 3ch I/O ( IC 12번핀 )
- A4 : 4ch I/O ( IC 1번핀 )
- A5 : 5ch I/O ( IC 5번핀 )
- A6 : 6ch I/O ( IC 2번핀 )
- A7 : 7ch I/O ( IC 4번핀 )
- A15 : COM I/O ( IC 3번핀)
위와 같이 회로를 구성하면, 0~7Ch I/O(아두이노 A0~A7핀)중 하나에 analogWrite() 함수로 값을 쓰고, Ctrl A~C(아두이노 22, 24, 26핀)을 사용하여 특정 채널에 써진 값을 COM_I/O(아두이노 A15핀)로 읽어올 수 있고, 반대로 COM_I/O핀에 analogWrite() 함수로 값을 쓰고 Ctrl A~C를 제어하여 특정 채널 I/O에 값을 보낼 수도 있습니다.
예를 들어 A3에 200의 값을 쓰고, Ctrl A(22)에 Low, Ctrl B(24)에 High, Ctrl C(26)에 High를 주면 COM_I/O핀에 200의 값이 전달됩니다. int value = analogRead(A3); 한 후 시리얼 모니터에 찍어보면 200이란 값이 나오겠지요.
여기서 analogRead 대신 digitalRead를 사용하면 High가 나올까요 Low가 나올까요.
결과는 '장담 못한다.' 입니다. 저는 루즈하게 analogRead/Write의 값 범위가 0~1023 이니까 적당히 512를 기준으로 낮으면 Low, 높으면 High가 나오겠지하고 생각했으나, 그렇지 않았습니다.
아날로그 값과 디지털 값을 다 읽어보았으나 어쩔땐 700~800이 나와도 Low일 때가 있고, 어쩔땐 50~100이 나와도 High일 때가 있더군요. 너무 안일했습니다.
제가 제작한 회로의 경우, 4051의 각 채널I/O 핀에 스위치를 통해 5v 전압이 걸리면 High로 인식하여 COM_IO로 HIGH를 가져와야 했으나, 5v 전압을 걸어도 High가 제대로 걸리지 않고, 인접한 다른핀의 5v 인가에도 영향을 받아 High-Low가 지멋대로 바뀌더군요.
이런 저런 시도끝에 4051의 0~7ch 핀에 각 10K옴 풀다운 저항을 걸어주니 값을 정확히 High/Low로 받을 수 있었습니다.
제 회로는 (하나의 4051 IC의 각 채널 I/O를 8개의 또다른 4051 IC의 COM_I/O로 연결) x 2 하여 최종적으로는 A0, A1 두개의 아두이노 핀과 컨트롤신호핀 6개, 총 8개의 아두이노 핀을 사용하여 128개의 디지털 인풋을 입력받을 수 있도록 확장했습니다.
이 경우 스위치와 연결되어 5v를 직접 받는 16개의 4051에는 풀 다운 저항을 달아줄 필요가 없으며, 8개의 4051 IC가 모이는 허브 역할을 하는 4051 IC의 각 채널 I/O에만 풀 다운 저항을 사용하면 인풋을 High/Low 디지털로 읽을 수 있습니다.
이하는 제가 사용한 PCB 설계도 입니다.
무료 설계툴인 Fritzing에서는 2레이어 PCB까지밖에 설계할 수가 없기에 점프 와이어를 꽤 많이 날려야 하지만, 일단 동작은 확실하게 검증하였습니다.
Ctrl A,B,C는 허브 역할을 하는 4051의 채널을 제어하며, Ctrl 1,2,3은 5v가 인가되는 말단 4051을 제어합니다.
Slot 0,1을 아두이노 A0, A1에 연결하여 디지털 High/Low를 가져오도록 되어있습니다.
이하는 위 PCB로 디지털 입력을 확장한 소스코드입니다.
| #define DigitIn_enable 0x01 #define DigitIn_disable 0x00
// 공용 함수 및 변수 bool DebugMode = true; int nDelay = 1; int nSerialBaudrate = 9600; bool breakWhile; bool onLock ;
// Digital Input 관련 함수 및 변수 void DigitalInputProcessing(); // Digital Input 상태 변화를 감지 bool CheckChanged_DigitInput(); // Digital Input 상태가 변화한 경우 데이터 배열에 적용 void Init_DigitalPin(); void Init_ControlPin(); void Setup_DataPin(); void Setup_ControlPin();
int DigitIn_DATA_PIN[2]; // Digital Input 상태 가져올 핀 int DigitIn_CONTROL_PIN[6]; // Digital Input Board 제어할 컨트롤 핀 byte DigitIn_DATA[2][8][8]; // Digital Input 상태 저장할 배열 byte PREV_DigitIn_DATA[8][8][8]; // Digital Input 상태 변화 비교용 버퍼 int DigitIn_nCtrlSignal1 = 0; // DigitIn_nCtrlSignal1 = 말단 pin 나오는 MUX 8개 중 하나 선택 int DigitIn_nCtrlSignal2 = 0; // DigitIn_nCtrlSignal2 = 선택된 MUX중 In/Out Pin 8개 중 하나 선택 int DigitIn_nSlot = 0; // DigitIn_nSlot = IO보드 넘버(0~7).
// DigitIn_DATA_PIN[0][DigitIn_nSlot]에 각 Slot에서 들어온 값 넣음. int CtrlA, CtrlB, CtrlC; // 허브 Mux 컨트롤 신호. 8개의 Mux 중 하나 선택. int Ctrl1, Ctrl2, Ctrl3; // 말단 MUX 컨트롤 신호. Mux의 8개 데이터 핀 중 하나 선택.
void setup() { if(DebugMode) { Serial.begin(nSerialBaudrate); while (!Serial) {;} // 시리얼 포트 오픈되기까지 대기 }
// 공용 설정 breakWhile = false; onLock = false;
// Digital Input 관련 설정 Init_DigitalPin(); Init_ControlPin(); init_AnalogInputPin(); Setup_ControlPin(); Setup_DataPin(); if(DebugMode) Serial.println("Setup done."); }
int cnt = 0; unsigned long currentMillis = 0; void loop() { // Digital Input Process DigitalInputProcessing(); // Digital Input 상태변화 감지 bool bIsChanged_DigitInput = CheckChanged_DigitInput(); // 상태변화가 있으면 현재상태를 저장 if(bIsChanged_DigitInput && !onLock) { onLock = true; // 변화상태 전송 // Send_MSG_0x4008(); // 상태변화 TCP 전송 bIsChanged_DigitInput = false; onLock = false; } delay(1); }
void Init_DigitalPin() { // 데이터핀 하나에서 8*8=64개 인풋 받아옴. DigitIn_DATA_PIN[0] = A0; DigitIn_DATA_PIN[1] = A1; }
void Init_ControlPin() { // [0]~[2] : Control신호1. (MUX 1~8번 선택) // [3]~[5] : Control신호2. (Data Pin 선택) DigitIn_CONTROL_PIN[0] = 22; DigitIn_CONTROL_PIN[1] = 23; DigitIn_CONTROL_PIN[2] = 24; DigitIn_CONTROL_PIN[3] = 25; DigitIn_CONTROL_PIN[4] = 26; DigitIn_CONTROL_PIN[5] = 27; }
void Setup_ControlPin() { for (int nControl = 0; nControl < 6; nControl++) pinMode(DigitIn_CONTROL_PIN[nControl], OUTPUT); }
void Setup_DataPin() { for (int nControl = 0; nControl < 2; nControl++) pinMode(DigitIn_DATA_PIN[nControl], INPUT); }
bool CheckChanged_DigitInput() { bool rtn = false; for(int i=0;i<8;i++) // DigitIn_nCtrlSignal1 { for(int j=0;j<8;j++) // DigitIn_nCtrlSignal2 { for(int k=0;k<2;k++) // DigitIn_nSlot { if(DigitIn_DATA[k][i][j] != PREV_DigitIn_DATA[k][i][j]) { rtn = true; } PREV_DigitIn_DATA[k][i][j] = DigitIn_DATA[k][i][j]; } } } return rtn; }
void DigitalInputProcessing() { for (DigitIn_nCtrlSignal1 = 0; DigitIn_nCtrlSignal1 < 8; DigitIn_nCtrlSignal1++) { // Control signal 1을 변경하며 MUX 선택 CtrlA = bitRead(DigitIn_nCtrlSignal1, 0); CtrlB = bitRead(DigitIn_nCtrlSignal1, 1); CtrlC = bitRead(DigitIn_nCtrlSignal1, 2); digitalWrite(DigitIn_CONTROL_PIN[0], CtrlA); digitalWrite(DigitIn_CONTROL_PIN[1], CtrlB); digitalWrite(DigitIn_CONTROL_PIN[2], CtrlC); for (DigitIn_nCtrlSignal2 = 0; DigitIn_nCtrlSignal2 < 8; DigitIn_nCtrlSignal2++) { // 선택한 MUX에서 Control signal 2를 변경하며 말단 I/O 핀 선택 Ctrl1 = bitRead(DigitIn_nCtrlSignal2, 0); Ctrl2 = bitRead(DigitIn_nCtrlSignal2, 1); Ctrl3 = bitRead(DigitIn_nCtrlSignal2, 2); digitalWrite(DigitIn_CONTROL_PIN[3], Ctrl1); digitalWrite(DigitIn_CONTROL_PIN[4], Ctrl2); digitalWrite(DigitIn_CONTROL_PIN[5], Ctrl3);
// 8개 I/O보드의 1~64번 I/O 핀 상태를 동시에 가져와 데이터 배열에 저장. for (DigitIn_nSlot = 0; DigitIn_nSlot < 2; DigitIn_nSlot++) { if(digitalRead(DigitIn_DATA_PIN[DigitIn_nSlot])) { DigitIn_DATA[DigitIn_nSlot][DigitIn_nCtrlSignal1][DigitIn_nCtrlSignal2] = DigitIn_enable; //Serial.println("input[" + String(DigitIn_nCtrlSignal1) + "][" + String(DigitIn_nCtrlSignal2) + "] is " + String(digitalRead(DigitIn_DATA_PIN[DigitIn_nSlot]))); } else { DigitIn_DATA[DigitIn_nSlot][DigitIn_nCtrlSignal1][DigitIn_nCtrlSignal2] = DigitIn_disable; //Serial.println("input[" + String(DigitIn_nCtrlSignal1) + "][" + String(DigitIn_nCtrlSignal2) + "] is " + String(digitalRead(DigitIn_DATA_PIN[DigitIn_nSlot]))); } } } } }
|
원래 소스코드에는 192개 LED 개별 밝기조절/점등 부분과 TCP/IP 서버 생성하여 클라이언트에 변화감지 시 메세지 날리는 부분도 함께 있었으나, 포스팅의 깔끔함을 위해 편집을 가하였습니다. 편집 과정에서 불필요한 부분이 남아있을 수 있다는 점 미리 양해를 구합니다.
아마도 저처럼 사용하실 분이 계시긴 할런지 의문이긴 한데, 혹시나 4051 아날로그 MUX를 보유중인데, 디지털 MUX가 따로 더 필요하다 하시는 분이 계시다면 풀 다운 저항을 이용하여 디지털 MUX로 유용할 수 있다는 정보를 공유하고 싶었습니다 ㅎㅎ
그럼 오늘의 포스팅은 여기까지!! 휘리릭 뿅~