※ 결론보다 문제점 설명이 더 길기에 미리 쓰는 해결방법 : 풀 다운 저항


아두이노의 디지털 핀 확장을 위해 많은 분들께서 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로 유용할 수 있다는 정보를 공유하고 싶었습니다 ㅎㅎ


그럼 오늘의 포스팅은 여기까지!! 휘리릭 뿅~

Arduino tips

Arduino mega 2560

2016. 12. 27. 09:48
  • Arduino(아두이노) ?

Arduino(이하 아두이노)란 오픈소스를 기반으로 한 단일보드 마이크로컨트롤러로 완성된 보드와 관련한 개발도구 및 환경을 말합니다. 이탈리아에서 2005년 고안되었으며, 하드웨어에 익숙치 않은 학생들이 자신의 디자인을 쉽게 제어하기 위해 제안된 제품입니다.


처음엔 AVR 기반으로 설계되어 현재까지도 아트멜 AVR 계열 보드가 보편적이며, ARM계열의 제품 또한 존재합니다.


정품은 이탈리아 제품이지만, 중국제 저가 호환보드가 많이 있으며, 국내에선 '오렌지보드'라는 호환 제품이 있습니다.

성능은 약간씩 편차가 있지만 크게 두드러지진 않으며, 중국제 호환보드는 아무래도 마감이 좀 정교하지 못한 반면, 국산 호환보드인 오렌지 보드는 마감도 훌륭하며 아두이노 우노 R3와 완벽호환되는 특징도 있습니다.


  • 아두이노 보드의 종류

아두이노 개발을 처음 공부할때 아두이노 보드의 선택에서 많은 분들이 고민하십니다. 아두이노 보드는 2016년말 현재 17가지 종류가 있으며 사용 목적에 따라 각 보드의 선택또한 중요합니다.


일반적으로 아두이노 관련 공부를 처음 하실때는 아두이노 우노를 많이 선택하며, 더 많은 제어 핀과 퍼포먼스를 필요로 할 때는 아두이노 메가를 선택하는 경우도 있습니다.


제 경우 아두이노 메가로 처음 아두이노에 입문하였습니다.


기본 아두이노 보드 외에도 이더넷 쉴드, 와이파이 쉴드, 블루투스 쉴드 등 보드에 체결하여 기능을 확장하는 확장보드도 있으니 이 부분은 아두이노 홈페이지의 제품정보(https://www.arduino.cc/en/Main/Products)를 확인하시는게 더 정확하겠습니다.


  • Arduino Mega 2560

제가 처음 아두이노에 입문한 메가2560 보드는 아두이노 우노 보드에 입출력핀과 퍼포먼스를 확장한 보드입니다.

형상은 다음 그림과 같습니다.




아두이노는 포럼 활동이 활발하며, 각종 정보가 많아 초심자도 쉽게 공부할 수 있다는 장점이 있습니다.

제 경우엔 아두이노 관련 서적은 한권도 없이 포럼과 네이버 카페를 참고하여 공부하고, 관련 프로젝트를 진행하고 있습니다.


  • Ethernet shield

아두이노는 소스 코드를 빌드하여 업로드 하기 위해 USB 케이블을 사용합니다. 이 USB 케이블로 전원또한 인가되며, USB 케이블이 아닌 일반 DC 전원 잭도 포함하고 있습니다. USB 케이블로 PC와 연결되어 있으면 이 USB 케이블을 이용하여 PC와 시리얼 통신이 가능합니다. 하지만 제 경우 다수의 아두이노 보드를 하나의 PC로 제어해야 하며, 데이터 송수신의 명확성을 위하여 TCP/IP 통신 패킷으로 각 아두이노 메가 보드를 제어해야 했습니다.


이때 필요한 것이 Ethernet shield(이하 이더넷 쉴드) 입니다.

아두이노 메가(또는 우노)보드 위에 체결하면 일반 랜 케이블을 이용하여 local network을 통한 제어가 가능해집니다.

이더넷 쉴드에 대한 예제 코드 또한 쉽게 구할 수 있으며, 아두이노에서의 이더넷 서버/클라이언트 생성이 매우 쉽습니다.


이더넷 쉴드는 아두이노 메가(또는 우노)의 9~12번 핀(SPI communication pin)을 사용합니다.

제 프로젝트에서 사용하는 IC 중 TLC5940 이라는 LED 제어 드라이버 IC는 아두이노 메가의 9, 11, 12번 핀을 사용하기 때문에 소스코드 작성에 약간의 주의가 필요했습니다.


이더넷 쉴드가 송/수신한 정보를 아두이노 보드에서 처리할때 11, 12번의 SPI 핀을 사용하기 때문에, 이 신호가 TLC5940 IC에는 노이즈 신호로 들어가기 때문입니다. TLC5940을 제어할 때 송/수신이 발생하면 TLC5940이 오작동을 일으키게 됩니다.


제 경우, 송/수신 중 TLC5940 제어를 해야하는 시점에서 Boolean flag를 통해 송/수신을 잠시 중단한 후 IC제어가 끝난 시점에서 다시 송/수신을 재개하는 방식으로 처리하였습니다.







업무상 필요에 의해 갑작스럽게 아두이노를 사용하게 되었습니다.

갑자기 시작했지만, 다행히 넷상에 정보가 워낙 방대하기에 찾아가며 공부하기는 수월하더군요.

지금은 프로젝트에서 사용하기에 계속 하겠지만, 프로젝트가 끝나면 기껏 공부한 내용을 잊어버릴까 싶어 블로그에 아두이노 카테고리를 만들어 그간 공부한 내용 및 제 프로젝트에 대한 로그를 남기고자 합니다.


아두이노 카테고리의 첫글은 간단히 이정도 선에서 마무리 합니다.


1