중급 예제

약간은 익숙하시거나 익숙해지셨나요? 그렇다면 조금더 깊이 다뤄볼까요?

74hc165 (ShiftIn Register)

2015-07-23 17:56:26

<개요>

 

Kocoafab 질문 게시판 및 각종 아두이노 커뮤니티, 포럼 등에서 가장 많이 나오는 질문 중 하나로

'포트확장' 문의가 상당히 많습니다. 아두이노 우노의 규격에 호환시키기 위하여 오렌지보드의 코어인 ATmega328P의 모든 핀을

사용 할 수 없는 하드웨어적 제한 때문에 발생하는 문제점이라고 할 수 있습니다. 아두이노의 디지털 입출력 핀은 0~13까지 총 14개의

핀을 사용하는데, 만약 만들고자 하는 프로젝트에 필요한 GPIO가 20개 30개 이런 식으로 된다면 아무래도 당황스러워 질 수 있습니다.

 

이런 당황스러운 사태를 해결 할 수 있는 것이 바로 입/출력 확장 IC입니다. 이번 튜토리얼에서는 '입력' 포트 확장 IC인 74hc165에 대해

알아보도록 하겠습니다.

 

 

 

 

- 74hc595로 출력 확장하기

 

이번 튜토리얼에서 다루고자 하는 칩과 비슷한 기능이지만 74hc595 는 출력 포트 확장 IC입니다. 동작원리가 다소 다른 면이 있으니 꼭 공부하셔야겠습니다.

 

 

 

(74hc165 란?)

 

 

(74hc165 PinMap)

 

데이터 시트를 보면 [8-bit parallel-load or serial-in shift register] 라고 명시되어 있습니다. 

그대로 직역하자면 병렬형태의 8비트 부하, 그리고 씨리얼 입력 쉬프트입니다. 말 그대로 IC에 8비트의 부하(입력핀)가 하나의 레지스터에 병렬로 연결되어 있습니다.

 

(8bit Register)  ㅁ  ㅁ  ㅁ  ㅁ  ㅁ  ㅁ  ㅁ  ㅁ

(bit no)          D0 D1 D2 D3 D4 D5 D6 D7

 

D0~D7 은 각각 독립적인 디지털 값(0,1)을 받는 입력핀과 같습니다. (parallel load(PL))핀에 LOW입력이 될 경우 D0~D7은 입력 값을 받아 레지스터에 값을 저장한 후

(parallel load(PL))핀에 HIGH 입력이 되면 레지스터에 저장된 마지막 상태 값을 data 핀(Q7핀)으로 보내줍니다. 이때 레지스터의 bit를 한칸씩 쉬프트하면서 읽어나가기 때문에 쉬프트 레지스터라고 합니다. 

 

개략적인 동작원리는 간단하지만  IC에서 오렌지보드 쪽으로 보내는 (레지스터의 상태)값은 통신형태로 전달되는 것이 아니라 단순한 HIGH, LOW형태로, IC의 레지스터를 쉬프트하여 순차적으로 보내주기 때문에

규칙적인, 정확한 데이터를 전달하기 위해서는 클럭을 공급하여 동기화를 해주어야 합니다. <데이터 시트를 참조하시면 IC의 데이터 핀에 대한 타이밍 도가 나와있습니다.> 따라서 클럭을 입력받을 핀과 허용해주는 핀으로

Clock Enable Pin, Clock Pin이 별도로 할당 되어야 합니다.

 

 

 

 

결과적으로 하나의 확장 IC를 사용하기 위해서는 4개의 핀이 할당되어야 하며 이를 통해 얻는 입력은 IC당 8개의 입력이므로 4개의 핀을 8개의 핀으로 확장한 것과 같은 효과가 있습니다.

74hc165의 장점은, IC끼리 병렬 연결이 가능하다는 점입니다. 오렌지보드의 Fanout 범위 내로 IC를 병렬 연결시켜주면 4개의 핀으로 포트확장 IC를 추가 할 수 있습니다. 

즉 IC를 3개를 사용하게 된다면 4개의 핀을 (8bit*3)24개의 입력핀으로 확장 시킬 수 있습니다.

 

IC에 대한 구체적인 내용은 해당 데이터 시트를 참조하시고 바로 실습을 해보도록 하겠습니다.

 

 

 

<필요한 부품>

 

 

No PART QTY ETC
1 Orange Board 1  
2 74HC165 1 Mfg. NXP
3 Bread Board 1  
4 Tact Switch 5 Small size(dip)

 

Orange Board 74HC165 Bread Board Tact Switch

 

 

 

<하드웨어 메이킹>

 

-브레드 보드 레이아웃-

 

 

* Fritzing 라이브러리가 없어 595 칩 이미지를 넣었습니다. 해당 튜토리얼에서는 74HC165 IC를 사용합니다. *

 

 

 

<아두이노 코드>

 

#define NUMBER_OF_SHIFT_CHIPS   1

/* Width of data (how many ext lines).
*/
#define DATA_WIDTH   NUMBER_OF_SHIFT_CHIPS * 8

/* Width of pulse to trigger the shift register to read and latch.
*/
#define PULSE_WIDTH_USEC   5

/* Optional delay between shift register reads.
*/
#define POLL_DELAY_MSEC   1

/* You will need to change the "int" to "long" If the
 * NUMBER_OF_SHIFT_CHIPS is higher than 2.
*/
#define BYTES_VAL_T unsigned int

int ploadPin        = 8;  // Connects to Parallel load pin the 165
int clockEnablePin  = 9;  // Connects to Clock Enable pin the 165
int dataPin         = 11; // Connects to the Q7 pin the 165
int clockPin        = 12; // Connects to the Clock pin the 165

BYTES_VAL_T pinValues;
BYTES_VAL_T oldPinValues;

/* This function is essentially a "shift-in" routine reading the
 * serial Data from the shift register chips and representing
 * the state of those pins in an unsigned integer (or long).
*/
BYTES_VAL_T read_shift_regs()
{
    long bitVal;
    BYTES_VAL_T bytesVal = 0;

    /* Trigger a parallel Load to latch the state of the data lines,
    */
    digitalWrite(clockEnablePin, HIGH);
    digitalWrite(ploadPin, LOW);
    delayMicroseconds(PULSE_WIDTH_USEC);
    digitalWrite(ploadPin, HIGH);
    digitalWrite(clockEnablePin, LOW);

    /* Loop to read each bit value from the serial out line
     * of the SN74HC165N.
    */
    for(int i = 0; i < DATA_WIDTH; i++)
    {
        bitVal = digitalRead(dataPin);

        /* Set the corresponding bit in bytesVal.
        */
        bytesVal |= (bitVal << ((DATA_WIDTH-1) - i));

        /* Pulse the Clock (rising edge shifts the next bit).
        */
        digitalWrite(clockPin, HIGH);
        delayMicroseconds(PULSE_WIDTH_USEC);
        digitalWrite(clockPin, LOW);
    }

    return(bytesVal);
}

/* Dump the list of zones along with their current status.
*/
void display_pin_values()
{
    Serial.print("Pin States:\r\n");

    for(int i = 0; i < DATA_WIDTH; i++)
    {
        Serial.print("  Pin-");
        Serial.print(i);
        Serial.print(": ");

        if((pinValues >> i) & 1)
            Serial.print("HIGH");
        else
            Serial.print("LOW");

        Serial.print("\r\n");
    }

    Serial.print("\r\n");
}

void setup()
{
    Serial.begin(9600);

    /* Initialize our digital pins...
    */
    pinMode(ploadPin, OUTPUT);
    pinMode(clockEnablePin, OUTPUT);
    pinMode(clockPin, OUTPUT);
    pinMode(dataPin, INPUT);

    digitalWrite(clockPin, LOW);
    digitalWrite(ploadPin, HIGH);

    /* Read in and display the pin states at startup.
    */
    pinValues = read_shift_regs();
    display_pin_values();
    oldPinValues = pinValues;
}

void loop()
{
    /* Read the state of all zones.
    */
    pinValues = read_shift_regs();

    /* If there was a chage in state, display which ones changed.
    */
    if(pinValues != oldPinValues)
    {
        Serial.print("*Pin value change detected*\r\n");
        display_pin_values();
        oldPinValues = pinValues;
    }

    delay(POLL_DELAY_MSEC);
}

 

<출처 : Arduino Playground>

 

튜토리얼 소스코드치고는 다소 긴 코드이지만 74HC165에서 값을 받아오기 위한 함수는 BYTES_VAL_T read_shift_regs() 부분입니다. 

 

BYTES_VAL_T read_shift_regs()
{
    long bitVal;
    BYTES_VAL_T bytesVal = 0;

    /* Trigger a parallel Load to latch the state of the data lines,
       PL 핀과 CE 핀에 1회 펄스를 트리거합니다. 의미적으로는 IC로부터 데이터를 받아오기 위한 준비 신호입니다.
    */
    digitalWrite(clockEnablePin, HIGH);
    digitalWrite(ploadPin, LOW);
    delayMicroseconds(PULSE_WIDTH_USEC);
    digitalWrite(ploadPin, HIGH);
    digitalWrite(clockEnablePin, LOW);

    /* Loop to read each bit value from the serial out line
     * of the SN74HC165N.
       1회 펄스 트리거 이후 IC로 부터 들어오는 값을 읽어 변수에 저장합니다.
       digitalRead 횟수는 IC의 갯수에 따라 반복 횟수를 정해줍니다.
    */
    for(int i = 0; i < DATA_WIDTH; i++)
    {
        bitVal = digitalRead(dataPin);

        /* Set the corresponding bit in bytesVal.
        */
        bytesVal |= (bitVal << ((DATA_WIDTH-1) - i));

        /* Pulse the Clock (rising edge shifts the next bit).
           클럭 핀에 상승엣지 신호를 걸어주어 다음 비트의 값으로 쉬프트하여 읽어오도록 합니다.
        */
        digitalWrite(clockPin, HIGH);
        delayMicroseconds(PULSE_WIDTH_USEC);
        digitalWrite(clockPin, LOW);
    }

    return(bytesVal); // bytesVal 값을 반환합니다.
}

 

 

이번 예제소스에서는 IC로 부터 읽어온 8bit의 값을 특정 배열에 저장하지 않고, 32bit 자료형인 long bytesval에 저장합니다. IC의 1번핀 (최초로 받는 값)을 bytesval의 7번 비트에 저장하고

다음 값은 1번 비트 쉬프트하여 6번비트에 저장... 이런 식으로 총 8개의 값을 저장해 나갑니다.

 

이후 반환된 bytesval 값을 Serial 모니터로 출력해주는 방식입니다. (void display_pin_values())

 

Loop 문에서는 읽어온 값이 다를 때, 즉 각 비트의 값이 (High->Low) 또는 (Low-> High)일 때 출력하도록 되어 있습니다.

 

1번 버튼을 누르면 다음과 같이 Serial Monitor가 출력됩니다.

 

 

브레드 보드 레이아웃을 다시 참고하면 5,6,7번 핀은 GND로 묶여있기 때문에 Low신호가 입력되고 있으며

0,1,2,3,4 핀은 풀업상태이므로 Default 값이 High입니다. 버튼을 누를 경우 GND로 떨어져 입력시 Low 신호가 됩니다.

 

kocoafabeditor

항상 진취적이고, 새로운 것을 추구하는 코코아팹 에디터입니다!

쉬프트레지스터, 아두이노, 오렌지보드