아두이노로 LED matrix 제어하기 #8 : 스크롤링(Shift) effect 3

2018. 2. 17. 19:30

Arduino/Display

Arduino LedControl Library : 애니메이션 함수 만들기 #3

이전 글에서부터 문자 패턴 시프트 효과를 구현하고 있습니다. 모듈 개수만큼 출력한 후, 나머지 문자가 있을 경우 그냥 무시하거나, 또는 정해진 시간마다 하나씩 왼쪽으로 시프트하도록 구성했었고, 이번 글에서는 이어서 코딩 연습을 위해 "시프트 인"이라는 효과를 코딩하겠습니다.

"시프트 인"이라는 이름은 편의상 붙인 말이고, 패턴을 처음부터 출력하지 않고 하나씩 시프트하며 출력하는 모습을 구현해보려 합니다. 이전 글에서 그림으로 설명한 부분입니다.

시프트 인(Shift In) 효과 구현하기

시프트 인 효과는 위 그림과 같습니다. 프로그램에서는 SHIFT_IN_YES 모드로 구분할 것이고, SHIFT_IN_NO 모드일 때와 처음 세 개의 패턴을 찍느냐 아니냐의 차이점이 있습니다. 우선 이 모드일 때 시작 시 패턴을 출력하지 않도록 코딩해야 합니다.

이전 예제의 아래 버퍼 전송 함수 코드 부분을 보겠습니다.

void buffersIn(int mode) {
  int i;
  // NO_SHIFT, SHIFT_IN_NO mode는 먼저 패턴 3개 출력
  if ( (displayCount == 0 ) && (mode  == NO_SHIFT || mode == SHIFT_IN_NO) ) {
    for (i = 0; i < 3; i++) {
      memcpy(&buffers[i], &outputPattern[i], sizeof(outputPattern[0]));
    }
    displayCount = i;
    shifted = currentTime;
  }
  // 시프트 처리
}

buffersIn(int mode) 함수의 처음에 먼저 패턴 3개를 출력하는 코드가 나옵니다. 4번 행 if문 조건문을 보면 해당하는 모드일 때만 실행하도록 되어 있습니다. 따라서, SHIFT_IN_YES 모드일 경우엔 실행하지 않기 때문에 이전 예제를 수정하지 않아도 원하는 결과를 얻을 수 있습니다.

#include "LedControl.h"
// 시프트 모드 구분
#define NO_SHIFT      0
#define SHIFT_IN_NO   1
#define SHIFT_IN_YES  2

buffersIn(int mode) 함수는 mode변수에 인수를 받아 수행 모드를 구분하고, SHIFT_IN_YES 모드에 대해서도 이전 예제에서 포함시켰습니다.

// SHIFT_IN_NO, SHIFT_IN_YES mode는 마지막 패턴까지 시프트 수행
  if ( (displayCount < dataLength) && (mode  == SHIFT_IN_NO || mode == SHIFT_IN_YES) ) {
    if (currentTime - shifted >= shiftInterval) {
      memcpy(&buffers[0], &buffers[1], sizeof(outputPattern[0]));
      memcpy(&buffers[1], &buffers[2], sizeof(outputPattern[0]));
      memcpy(&buffers[2], outputPattern[displayCount], sizeof(outputPattern[0]));
      displayCount++;
      shifted = currentTime;
    }
  }

시프트 효과를 구현하는 부분입니다. SHIFT_IN_YES 모드일 때 처음 만나는 부분이고, 출력할 문자 패턴 길이(dataLength)에 도달할 때까지 출력할 위치(displayCount)를 증가시켜 가며 출력합니다. 두 모드는 displayCount가 3부터 시작하는지 0부터 시작하는지만 다를 뿐 하나씩 시프트하는 건 동일하기 때문에 역시 이전 예제의 코드를 그대로 사용하면 됩니다. 주석외엔 수정하지 않았습니다.

결국, 프로그램을 수정하지 않아도, buffersIn(SHIFT_IN_YES)와 같이 해당하는 인수로 함수를 호출하면 위와 같은 결과를 확인할 수 있습니다.

시프트 아웃 효과 만들기

"시프트 인" 효과가 한 문자씩 시프트하며 등장한다면, 이와 비슷한 방식으로 모든 문자를 출력한 후, 한 문자씩 시프트하며 사라지는 효과를 만들어 보겠습니다. "시프트 아웃"이라 이름 붙였습니다.

동작은 위 그림과 같고 구현은 간단합니다. 이전의 방식대로 출력한 후, 공백으로 채우면서 3번 더 시프트하면 됩니다.

#include "LedControl.h"
// 시프트 모드 구분
#define NO_SHIFT          0
#define SHIFT_IN_NO       1
#define SHIFT_IN_YES      2
#define SHIFT_IN_NO_OUT   3
#define SHIFT_IN_YES_OUT  4

우선 프로그램 시작 부분에 시프트 모드를 두 가지 더했습니다. 시프트를 하지 않을 시엔 필요 없는 기능이므로, "시프트 인"할 때와 하지 않을 때 각각 "시프트 아웃"을 처리하는 모드를 추가하였습니다.

// 출력 데이터를 buffers에 전송
void buffersIn(int mode) {
  int i;
  // NO_SHIFT, SHIFT_IN_NO, SHIFT_IN_NO_OUT mode는 먼저 패턴 3개 출력
  if ( (displayCount == 0 ) && (mode  == NO_SHIFT || mode == SHIFT_IN_NO || mode == SHIFT_IN_NO_OUT) ) {
    for (i = 0; i < 3; i++) {
      memcpy(&buffers[i], &outputPattern[i], sizeof(outputPattern[0]));
    }
    displayCount = i;
    shifted = currentTime;
  }
  // SHIFT_IN_NO, SHIFT_IN_YES, SHIFT_IN_NO_OUT, SHIFT_IN_YES_OUT mode는 마지막 패턴까지 시프트 수행
  if ( (displayCount < dataLength) && (mode  == SHIFT_IN_NO || mode == SHIFT_IN_YES || mode == SHIFT_IN_NO_OUT || mode == SHIFT_IN_YES_OUT) ) {
    if (currentTime - shifted >= shiftInterval) {
      memcpy(&buffers[0], &buffers[1], sizeof(outputPattern[0]));
      memcpy(&buffers[1], &buffers[2], sizeof(outputPattern[0]));
      memcpy(&buffers[2], outputPattern[displayCount], sizeof(outputPattern[0]));
      displayCount++;
      shifted = currentTime;
    }
  }
}

SHIFT_IN_NO_OUT 모드는 SHIFT_IN_NO 모드에 시프트 아웃 효과를 추가한 것이므로, 우선 시작할 때 3개의 패턴을 출력해햐 합니다. 따라서, 위 5번 행처럼 기존 buffersIn(int mode) 함수를 수정해야 합니다.

또, 새로 추가되는 두 가지 모드도 시프트 효과가 기본이므로, 위 13번 행처럼 시프트를 수행할 수 있도록 조건문을 변경하였습니다. 여기까지 수정한 후 수행하면, 시프트 아웃 효과는 없지만 정상적으로 돌아갑니다.

// SHIFT_IN_NO_OUT, SHIFT_IN_YES_OUT mode 수행
  if ( (mode == SHIFT_IN_NO_OUT || mode == SHIFT_IN_YES_OUT) ) {
    if (currentTime - shifted >= shiftInterval) {
      memcpy(&buffers[0], &buffers[1], sizeof(outputPattern[0]));
      memcpy(&buffers[1], &buffers[2], sizeof(outputPattern[0]));
      memcpy(&buffers[2], symbols[0], sizeof(outputPattern[0])); // 공백으로 채움
      displayCount++;
      shifted = currentTime;
    }
  }

이제, 시프트 아웃을 수행하는 코드를 작성하겠습니다. 모든 처리가 끝난 후 SHIFT_IN_NO_OUT, SHIFT_IN_YES_OUT 모드일 때만 추가적으로 실행하도록 하고, 왼쪽으로 하나씩 시프트하는 건 동일하므로 바로 위 시프트 수행 코드를 복사해서 아래쪽에 추가한 후 수정하면 됩니다.

해당하는 모드일 때만 수행하도록 2번 행에 조건을 걸었습니다. 여기에 두 가지 조건이 더 필요한데 우선, 모든 패턴의 출력이 끝난 후에 추가하여 실행해야 하므로 이를 체크해야 합니다. 또, 모듈이 딱 3개이므로 왼쪽으로 세 번만 더 시프트하면 빈 화면만 보이므로 결국 모든 출력이 끝난 후에 3번만 시프트 하도록 코딩해야 합니다.

시프트 아웃은 왼쪽으로 시프트하며 사라지는 효과를 내기 위함이므로, 한번 시프트할 때마다 공백으로 채워야 합니다. 6번 행을 수정해서 이 기능을 하도록 했습니다.

// SHIFT_IN_NO_OUT, SHIFT_IN_YES_OUT mode 수행
  if ( (displayCount >= dataLength) && (displayCount < dataLength + 3) && (mode == SHIFT_IN_NO_OUT || mode == SHIFT_IN_YES_OUT) ) {
    if (currentTime - shifted >= shiftInterval) {
      memcpy(&buffers[0], &buffers[1], sizeof(outputPattern[0]));
      memcpy(&buffers[1], &buffers[2], sizeof(outputPattern[0]));
      memcpy(&buffers[2], symbols[0], sizeof(outputPattern[0])); // 공백으로 채움
      displayCount++;
      shifted = currentTime;
    }
  }

displayCount 변수는 출력된 문자 수를 체크하기 위한 변수이고 다음 출력할 패턴의 인덱스 값을 저장하고 있습니다. 만약, 전체 출력이 완료되었다면 이 변수는 전체 패턴의 길이인 dataLength 변수와 동일한 값이 되므로, 이를 체크해서 시프트 아웃을  시작할 때를 결정할 수 있습니다. 또, 전체 길이보다 딱 세 번 더 출력할 수 있도록 두 번째 조건문을 추가했습니다. 나머지는 이전 시프트 소스와 동일합니다.

// shift out 처리
#include "LedControl.h"
// 시프트 모드 구분
#define NO_SHIFT          0
#define SHIFT_IN_NO       1
#define SHIFT_IN_YES      2
#define SHIFT_IN_NO_OUT   3
#define SHIFT_IN_YES_OUT  4
//
LedControl lc = LedControl(12,11,10,3);
// 시간 관련 변수들
unsigned long currentTime; // 현재 시간 저장
unsigned long refreshed = 0; // 출력 주기를 위한 변수
unsigned long refreshInterval = 50; // 출력 refresh 간격
unsigned long shifted = 0; // 시프트 간격 체크위한 이전 시프트 시간 저장
unsigned long shiftInterval = 500; // 시프트 간격 ?초 마다
//
char outputData[] = "ABCDEFGHIJ"; // 출력할 데이터
char outputPattern[10][8]; // 출력 패턴 저장
byte dataLength = 0; // 출력할 데이터의 길이
byte displayCount = 0; // 출력된 위치 체크
// 버퍼
byte buffers[3][8];
// 숫자 패턴
byte digits[10][8] = {
  {
  B00111000,
  B01000100,
  B01000100,
  B01000100,
  B01000100,
  B01000100,
  B01000100,
  B00111000
},{
  B00010000,
  B00110000,
  B00010000,
  B00010000,
  B00010000,
  B00010000,
  B00010000,
  B00111000
},{
  B00111000,
  B01000100,
  B00000100,
  B00000100,
  B00001000,
  B00010000,
  B00100000,
  B01111100
},{
  B00111000,
  B01000100,
  B00000100,
  B00011000,
  B00000100,
  B00000100,
  B01000100,
  B00111000
},{
  B00000100,
  B00001100,
  B00010100,
  B00100100,
  B01000100,
  B01111100,
  B00000100,
  B00000100
},{
  B01111100,
  B01000000,
  B01000000,
  B01111000,
  B00000100,
  B00000100,
  B01000100,
  B00111000
},{
  B00111000,
  B01000100,
  B01000000,
  B01111000,
  B01000100,
  B01000100,
  B01000100,
  B00111000
},{
  B01111100,
  B00000100,
  B00000100,
  B00001000,
  B00010000,
  B00100000,
  B00100000,
  B00100000
},{
  B00111000,
  B01000100,
  B01000100,
  B00111000,
  B01000100,
  B01000100,
  B01000100,
  B00111000
},{
  B00111000,
  B01000100,
  B01000100,
  B01000100,
  B00111100,
  B00000100,
  B01000100,
  B00111000
}};
// 영문자
byte chars[52][8] = {
{
  B00000000,
  B00111100,
  B01100110,
  B01100110,
  B01111110,
  B01100110,
  B01100110,
  B01100110
},{
  B00000000,
  B01111100,
  B01100110,
  B01100110,
  B01111100,
  B01100110,
  B01100110,
  B01111100
},{
  B00000000,
  B00111100,
  B01100110,
  B01100000,
  B01100000,
  B01100000,
  B01100110,
  B00111100
},{
  B00000000,
  B01111100,
  B01100110,
  B01100110,
  B01100110,
  B01100110,
  B01100110,
  B01111100
},{
  B00000000,
  B01111110,
  B01100000,
  B01100000,
  B01111100,
  B01100000,
  B01100000,
  B01111110
},{
  B00000000,
  B01111110,
  B01100000,
  B01100000,
  B01111100,
  B01100000,
  B01100000,
  B01100000
},{
  B00000000,
  B00111100,
  B01100110,
  B01100000,
  B01100000,
  B01101110,
  B01100110,
  B00111100
},{
  B00000000,
  B01100110,
  B01100110,
  B01100110,
  B01111110,
  B01100110,
  B01100110,
  B01100110
},{
  B00000000,
  B00111100,
  B00011000,
  B00011000,
  B00011000,
  B00011000,
  B00011000,
  B00111100
},{
  B00000000,
  B00011110,
  B00001100,
  B00001100,
  B00001100,
  B01101100,
  B01101100,
  B00111000
},{
  B00000000,
  B01100110,
  B01101100,
  B01111000,
  B01110000,
  B01111000,
  B01101100,
  B01100110
},{
  B00000000,
  B01100000,
  B01100000,
  B01100000,
  B01100000,
  B01100000,
  B01100000,
  B01111110
},{
  B00000000,
  B01100011,
  B01110111,
  B01111111,
  B01101011,
  B01100011,
  B01100011,
  B01100011
},{
  B00000000,
  B01100011,
  B01110011,
  B01111011,
  B01101111,
  B01100111,
  B01100011,
  B01100011
},{
  B00000000,
  B00111100,
  B01100110,
  B01100110,
  B01100110,
  B01100110,
  B01100110,
  B00111100
},{
  B00000000,
  B01111100,
  B01100110,
  B01100110,
  B01100110,
  B01111100,
  B01100000,
  B01100000
},{
  B00000000,
  B00111100,
  B01100110,
  B01100110,
  B01100110,
  B01101110,
  B00111100,
  B00000110
},{
  B00000000,
  B01111100,
  B01100110,
  B01100110,
  B01111100,
  B01111000,
  B01101100,
  B01100110
},{
  B00000000,
  B00111100,
  B01100110,
  B01100000,
  B00111100,
  B00000110,
  B01100110,
  B00111100
},{
  B00000000,
  B01111110,
  B01011010,
  B00011000,
  B00011000,
  B00011000,
  B00011000,
  B00011000
},{
  B00000000,
  B01100110,
  B01100110,
  B01100110,
  B01100110,
  B01100110,
  B01100110,
  B00111110
},{
  B00000000,
  B01100110,
  B01100110,
  B01100110,
  B01100110,
  B01100110,
  B00111100,
  B00011000
},{
  B00000000,
  B01100011,
  B01100011,
  B01100011,
  B01101011,
  B01111111,
  B01110111,
  B01100011
},{
  B00000000,
  B01100011,
  B01100011,
  B00110110,
  B00011100,
  B00110110,
  B01100011,
  B01100011
},{
  B00000000,
  B01100110,
  B01100110,
  B01100110,
  B00111100,
  B00011000,
  B00011000,
  B00011000
},{
  B00000000,
  B01111110,
  B00000110,
  B00001100,
  B00011000,
  B00110000,
  B01100000,
  B01111110
},{
  B00000000,
  B00000000,
  B00000000,
  B00111100,
  B00000110,
  B00111110,
  B01100110,
  B00111110
},{
  B00000000,
  B01100000,
  B01100000,
  B01100000,
  B01111100,
  B01100110,
  B01100110,
  B01111100
},{
  B00000000,
  B00000000,
  B00000000,
  B00111100,
  B01100110,
  B01100000,
  B01100110,
  B00111100
},{
  B00000000,
  B00000110,
  B00000110,
  B00000110,
  B00111110,
  B01100110,
  B01100110,
  B00111110
},{
  B00000000,
  B00000000,
  B00000000,
  B00111100,
  B01100110,
  B01111110,
  B01100000,
  B00111100
},{
  B00000000,
  B00011100,
  B00110110,
  B00110000,
  B00110000,
  B01111100,
  B00110000,
  B00110000
},{
  B00000000,
  B00000000,
  B00111110,
  B01100110,
  B01100110,
  B00111110,
  B00000110,
  B00111100
},{
  B00000000,
  B01100000,
  B01100000,
  B01100000,
  B01111100,
  B01100110,
  B01100110,
  B01100110
},{
  B00000000,
  B00000000,
  B00011000,
  B00000000,
  B00011000,
  B00011000,
  B00011000,
  B00111100
},{
  B00000000,
  B00001100,
  B00000000,
  B00001100,
  B00001100,
  B01101100,
  B01101100,
  B00111000
},{
  B00000000,
  B01100000,
  B01100000,
  B01100110,
  B01101100,
  B01111000,
  B01101100,
  B01100110
},{
  B00000000,
  B00011000,
  B00011000,
  B00011000,
  B00011000,
  B00011000,
  B00011000,
  B00011000
},{
  B00000000,
  B00000000,
  B00000000,
  B01100011,
  B01110111,
  B01111111,
  B01101011,
  B01101011
},{
  B00000000,
  B00000000,
  B00000000,
  B01111100,
  B01111110,
  B01100110,
  B01100110,
  B01100110
},{
  B00000000,
  B00000000,
  B00000000,
  B00111100,
  B01100110,
  B01100110,
  B01100110,
  B00111100
},{
  B00000000,
  B00000000,
  B01111100,
  B01100110,
  B01100110,
  B01111100,
  B01100000,
  B01100000
},{
  B00000000,
  B00000000,
  B00111100,
  B01101100,
  B01101100,
  B00111100,
  B00001101,
  B00001111
},{
  B00000000,
  B00000000,
  B00000000,
  B01111100,
  B01100110,
  B01100110,
  B01100000,
  B01100000
},{
  B00000000,
  B00000000,
  B00000000,
  B00111110,
  B01000000,
  B00111100,
  B00000010,
  B01111100
},{
  B00000000,
  B00000000,
  B00011000,
  B00011000,
  B01111110,
  B00011000,
  B00011000,
  B00011000
},{
  B00000000,
  B00000000,
  B00000000,
  B01100110,
  B01100110,
  B01100110,
  B01100110,
  B00111110
},{
  B00000000,
  B00000000,
  B00000000,
  B00000000,
  B01100110,
  B01100110,
  B00111100,
  B00011000
},{
  B00000000,
  B00000000,
  B00000000,
  B01100011,
  B01101011,
  B01101011,
  B01101011,
  B00111110
},{
  B00000000,
  B00000000,
  B00000000,
  B01100110,
  B00111100,
  B00011000,
  B00111100,
  B01100110
},{
  B00000000,
  B00000000,
  B00000000,
  B01100110,
  B01100110,
  B00111110,
  B00000110,
  B00111100
},{
  B00000000,
  B00000000,
  B00000000,
  B00111100,
  B00001100,
  B00011000,
  B00110000,
  B00111100
}};
// 기호
byte symbols[][8] = {
{
  B00000000,
  B00000000,
  B00000000,
  B00000000,
  B00000000,
  B00000000,
  B00000000,
  B00000000
},
{
  B00000000,
  B00011000,
  B00111100,
  B00111100,
  B00011000,
  B00011000,
  B00000000,
  B00011000
},{
  B00000000,
  B00111100,
  B01100110,
  B00000110,
  B00011100,
  B00011000,
  B00000000,
  B00011000
}};
//
void setup() {
  ledSetup(); // LED모듈 초기화
  // buffers 초기화
  for (int i = 0; i < 3; i++) {
    memcpy(&buffers[i], &symbols[0], sizeof(symbols[0]));
  }
}
void loop() {
  // 현재 시간 저장
  currentTime = millis();
  // 패턴 변환 함수
  if (dataLength == 0) {
    dataToPattern();
  }
  // 출력 함수
  buffersIn(SHIFT_IN_YES_OUT);
  //
  // LED 출력
  buffersOut();
}
// 출력 데이터를 buffers에 전송
void buffersIn(int mode) {
  int i;
  // NO_SHIFT, SHIFT_IN_NO, SHIFT_IN_NO_OUT mode는 먼저 패턴 3개 출력
  if ( (displayCount == 0 ) && (mode  == NO_SHIFT || mode == SHIFT_IN_NO || mode == SHIFT_IN_NO_OUT) ) {
    for (i = 0; i < 3; i++) {
      memcpy(&buffers[i], &outputPattern[i], sizeof(outputPattern[0]));
    }
    displayCount = i;
    shifted = currentTime;
  }
  // SHIFT_IN_NO, SHIFT_IN_YES, SHIFT_IN_NO_OUT, SHIFT_IN_YES_OUT mode는 마지막 패턴까지 시프트 수행
  if ( (displayCount < dataLength) && (mode  == SHIFT_IN_NO || mode == SHIFT_IN_YES || mode == SHIFT_IN_NO_OUT || mode == SHIFT_IN_YES_OUT) ) {
    if (currentTime - shifted >= shiftInterval) {
      memcpy(&buffers[0], &buffers[1], sizeof(outputPattern[0]));
      memcpy(&buffers[1], &buffers[2], sizeof(outputPattern[0]));
      memcpy(&buffers[2], outputPattern[displayCount], sizeof(outputPattern[0]));
      displayCount++;
      shifted = currentTime;
    }
  }
  // SHIFT_IN_NO_OUT, SHIFT_IN_YES_OUT mode 수행
  if ( (displayCount >= dataLength) && (displayCount < dataLength + 3) && (mode == SHIFT_IN_NO_OUT || mode == SHIFT_IN_YES_OUT) ) {
    if (currentTime - shifted >= shiftInterval) {
      memcpy(&buffers[0], &buffers[1], sizeof(outputPattern[0]));
      memcpy(&buffers[1], &buffers[2], sizeof(outputPattern[0]));
      memcpy(&buffers[2], symbols[0], sizeof(outputPattern[0])); // 공백으로 채움
      displayCount++;
      shifted = currentTime;
    }
  }
}
void dataToPattern() {
  //
  dataLength = strlen(outputData);
  if (dataLength > 10) {
    dataLength = 10;
  }
  //
  int i, index;
  for (int i = 0; i < dataLength; i++) {
    if (outputData[i] >= '0' && outputData[i] <= '9') {
      index = outputData[i] - '0';
      memcpy(&outputPattern[i], &digits[index], sizeof(digits[0]));
    } else if (outputData[i] >= 'A' && outputData[i] <= 'Z') {
      index = outputData[i] - 'A';
      memcpy(&outputPattern[i], &chars[index], sizeof(digits[0]));
    } else if (outputData[i] >= 'a' && outputData[i] <= 'z') {
      index = outputData[i] - 'a' + 26;
      memcpy(&outputPattern[i], &chars[index], sizeof(digits[0]));
    } else {
      memcpy(&outputPattern[i], &symbols[0], sizeof(digits[0]));
    }
  }
}
// buffers를 LED 모듈에 출력하는 함수
void buffersOut() {
  if (currentTime - refreshed >= refreshInterval ) {  
    for (int i = 0; i < 3; i++) {
      for (int j = 0; j < 8; j++) {
        lc.setRow(i, j, buffers[i][j]);
      }
    }
    refreshed = currentTime;
  }
}
// LED모듈 초기화 함수
void ledSetup() {
  lc.shutdown(0, false);
  lc.shutdown(1, false);
  lc.shutdown(2, false);
  //
  lc.setIntensity(0,2);
  lc.setIntensity(1,2);
  lc.setIntensity(2,2);
  //
  lc.clearDisplay(0);
  lc.clearDisplay(1);
  lc.clearDisplay(2);
}

여기까지의 전체 소스와 결과 화면입니다. 동영상을 보시면 SHIFT_IN_NO_OUT, SHIFT_IN_YES_OUT 모드를 차례로 확인할 수 있습니다.

로테이션(Rotation) 모드 구현하기

로테이션은 말 그대로 모든 패턴을 출력한 후 처음부터 다시 출력하는 기능입니다. 이제까지의 함수는 한 번만 출력하고 더이상 실행하지 않는데, 이번에 추가할 기능은 계속해서 출력을 반복하는 것이며, 처음 패턴이 공백없이 바로 이어서 나와야 하기 때문에, 시프트 아웃과는 동시에 수행하지 않습니다. 

시프트 아웃 코드를 추가할 때와 비슷하게 작업합니다.

#include "LedControl.h"
// 시프트 모드 구분
#define NO_SHIFT          0
#define SHIFT_IN_NO       1
#define SHIFT_IN_YES      2
#define SHIFT_IN_NO_OUT   3
#define SHIFT_IN_YES_OUT  4
#define SHIFT_IN_NO_ROT   5
#define SHIFT_IN_YES_ROT  6

"시프트 아웃"때와 마찬가지로 두 가지 모드값을 추가합니다. 역시, "시프트 인" 모드일 때와 아닐 때 각각 로테이션할 수 있습니다.

// 출력 데이터를 buffers에 전송
void buffersIn(int mode) {
  int i;
  // NO_SHIFT, SHIFT_IN_NO, SHIFT_IN_NO_OUT, SHIFT_IN_NO_ROT mode는 먼저 패턴 3개 출력
  if ( (displayCount == 0 ) && (mode  == NO_SHIFT || mode == SHIFT_IN_NO || mode == SHIFT_IN_NO_OUT || mode == SHIFT_IN_NO_ROT) ) {
    for (i = 0; i < 3; i++) {
      memcpy(&buffers[i], &outputPattern[i], sizeof(outputPattern[0]));
    }
    displayCount = i;
    shifted = currentTime;
  }
}

SHIFT_IN_NO_ROT 모드는 "시프트 인"하지 않고 처음 세 패턴을 출력하고 시작해야 합니다. 그래서, 5번 행처럼 조건문에 추가해주었습니다.

// SHIFT_IN_NO, SHIFT_IN_YES, SHIFT_IN_NO_OUT, SHIFT_IN_YES_OUT, SHIFT_IN_NO_ROT, SHIFT_IN_YES_ROT mode는 마지막 패턴까지 시프트 수행
  if ( (displayCount < dataLength) && (mode  == SHIFT_IN_NO || mode == SHIFT_IN_YES || mode == SHIFT_IN_NO_OUT || mode == SHIFT_IN_YES_OUT || mode == SHIFT_IN_NO_ROT || mode == SHIFT_IN_YES_ROT) ) {
    if (currentTime - shifted >= shiftInterval) {
      memcpy(&buffers[0], &buffers[1], sizeof(outputPattern[0]));
      memcpy(&buffers[1], &buffers[2], sizeof(outputPattern[0]));
      memcpy(&buffers[2], outputPattern[displayCount], sizeof(outputPattern[0]));
      displayCount++;
      shifted = currentTime;
    }
  }

역시 기본적으로 시프트 하는 동작이기 때문에 시프트 루틴에 위와 같이(2번행) 조건을 추가하였습니다.

// SHIFT_IN_NO_OUT, SHIFT_IN_YES_OUT mode 수행
  if ( (displayCount >= dataLength) && (displayCount < dataLength + 3) && (mode == SHIFT_IN_NO_OUT || mode == SHIFT_IN_YES_OUT) ) {
    if (currentTime - shifted >= shiftInterval) {
      memcpy(&buffers[0], &buffers[1], sizeof(outputPattern[0]));
      memcpy(&buffers[1], &buffers[2], sizeof(outputPattern[0]));
      memcpy(&buffers[2], symbols[0], sizeof(outputPattern[0])); // 공백으로 채움
      displayCount++;
      shifted = currentTime;
    }
  }
  // SHIFT_IN_NO_ROT, SHIFT_IN_YES_ROT mode 수행
  if ( (displayCount >= dataLength) && (mode == SHIFT_IN_NO_ROT || mode == SHIFT_IN_YES_ROT) ) {
    displayCount = 0;
    rotation = true;
  }

"로테이션"을 처리하기 위해 위와 같이 코드를 추가하였습니다. 모든 패턴을 출력한 후에 displayCount 변수를 "0"으로 초기화하여 다시 처음부터 출력하도록 조정합니다. 이 때, 한가지 문제가 발생합니다. displayCount 변수의 값이 "0"이 되버리면 "시프트 인"하지 않는 즉, 시작할 때 우선 3개의 패턴을 출력하는 루틴이 실행하게 됩니다. 이 문제를 해결하기 위해, rotation 이라는 변수를 이용해 체크(14번행)하도록 했습니다.

char outputData[] = "ABCDEFGHIJ"; // 출력할 데이터
char outputPattern[10][8]; // 출력 패턴 저장
byte dataLength = 0; // 출력할 데이터의 길이
byte displayCount = 0; // 출력된 위치 체크
bool rotation = false; // ROTATION 모드 체크

rotation 변수를 사용하기 전에, 위와 같이 변수를 선언하여 줍니다. 초기값은 아직 로테이션 모드가 아니므로 false입니다.

int i;
  // NO_SHIFT, SHIFT_IN_NO, SHIFT_IN_NO_OUT, SHIFT_IN_NO_ROT mode는 먼저 패턴 3개 출력
  if ( (rotation != true) && (displayCount == 0 ) && (mode  == NO_SHIFT || mode == SHIFT_IN_NO || mode == SHIFT_IN_NO_OUT || mode == SHIFT_IN_NO_ROT) ) {
    for (i = 0; i < 3; i++) {
      memcpy(&buffers[i], &outputPattern[i], sizeof(outputPattern[0]));
    }
    displayCount = i;
    shifted = currentTime;
  }

위 코드처럼 rotation 변수가 true가 아닐 때 즉, false일 때만 해당 루틴을 실행하도록 수정하였습니다.

// shift out 처리
#include "LedControl.h"
// 시프트 모드 구분
#define NO_SHIFT          0
#define SHIFT_IN_NO       1
#define SHIFT_IN_YES      2
#define SHIFT_IN_NO_OUT   3
#define SHIFT_IN_YES_OUT  4
#define SHIFT_IN_NO_ROT   5
#define SHIFT_IN_YES_ROT  6
//
LedControl lc = LedControl(12,11,10,3);
// 시간 관련 변수들
unsigned long currentTime; // 현재 시간 저장
unsigned long refreshed = 0; // 출력 주기를 위한 변수
unsigned long refreshInterval = 50; // 출력 refresh 간격
unsigned long shifted = 0; // 시프트 간격 체크위한 이전 시프트 시간 저장
unsigned long shiftInterval = 500; // 시프트 간격 ?초 마다
//
char outputData[] = "ABCDEFGHIJ"; // 출력할 데이터
char outputPattern[10][8]; // 출력 패턴 저장
byte dataLength = 0; // 출력할 데이터의 길이
byte displayCount = 0; // 출력된 위치 체크
bool rotation = false; // ROTATION 모드 체크
// 버퍼
byte buffers[3][8];
// 숫자 패턴
byte digits[10][8] = {
  {
  B00111000,
  B01000100,
  B01000100,
  B01000100,
  B01000100,
  B01000100,
  B01000100,
  B00111000
},{
  B00010000,
  B00110000,
  B00010000,
  B00010000,
  B00010000,
  B00010000,
  B00010000,
  B00111000
},{
  B00111000,
  B01000100,
  B00000100,
  B00000100,
  B00001000,
  B00010000,
  B00100000,
  B01111100
},{
  B00111000,
  B01000100,
  B00000100,
  B00011000,
  B00000100,
  B00000100,
  B01000100,
  B00111000
},{
  B00000100,
  B00001100,
  B00010100,
  B00100100,
  B01000100,
  B01111100,
  B00000100,
  B00000100
},{
  B01111100,
  B01000000,
  B01000000,
  B01111000,
  B00000100,
  B00000100,
  B01000100,
  B00111000
},{
  B00111000,
  B01000100,
  B01000000,
  B01111000,
  B01000100,
  B01000100,
  B01000100,
  B00111000
},{
  B01111100,
  B00000100,
  B00000100,
  B00001000,
  B00010000,
  B00100000,
  B00100000,
  B00100000
},{
  B00111000,
  B01000100,
  B01000100,
  B00111000,
  B01000100,
  B01000100,
  B01000100,
  B00111000
},{
  B00111000,
  B01000100,
  B01000100,
  B01000100,
  B00111100,
  B00000100,
  B01000100,
  B00111000
}};
// 영문자
byte chars[52][8] = {
{
  B00000000,
  B00111100,
  B01100110,
  B01100110,
  B01111110,
  B01100110,
  B01100110,
  B01100110
},{
  B00000000,
  B01111100,
  B01100110,
  B01100110,
  B01111100,
  B01100110,
  B01100110,
  B01111100
},{
  B00000000,
  B00111100,
  B01100110,
  B01100000,
  B01100000,
  B01100000,
  B01100110,
  B00111100
},{
  B00000000,
  B01111100,
  B01100110,
  B01100110,
  B01100110,
  B01100110,
  B01100110,
  B01111100
},{
  B00000000,
  B01111110,
  B01100000,
  B01100000,
  B01111100,
  B01100000,
  B01100000,
  B01111110
},{
  B00000000,
  B01111110,
  B01100000,
  B01100000,
  B01111100,
  B01100000,
  B01100000,
  B01100000
},{
  B00000000,
  B00111100,
  B01100110,
  B01100000,
  B01100000,
  B01101110,
  B01100110,
  B00111100
},{
  B00000000,
  B01100110,
  B01100110,
  B01100110,
  B01111110,
  B01100110,
  B01100110,
  B01100110
},{
  B00000000,
  B00111100,
  B00011000,
  B00011000,
  B00011000,
  B00011000,
  B00011000,
  B00111100
},{
  B00000000,
  B00011110,
  B00001100,
  B00001100,
  B00001100,
  B01101100,
  B01101100,
  B00111000
},{
  B00000000,
  B01100110,
  B01101100,
  B01111000,
  B01110000,
  B01111000,
  B01101100,
  B01100110
},{
  B00000000,
  B01100000,
  B01100000,
  B01100000,
  B01100000,
  B01100000,
  B01100000,
  B01111110
},{
  B00000000,
  B01100011,
  B01110111,
  B01111111,
  B01101011,
  B01100011,
  B01100011,
  B01100011
},{
  B00000000,
  B01100011,
  B01110011,
  B01111011,
  B01101111,
  B01100111,
  B01100011,
  B01100011
},{
  B00000000,
  B00111100,
  B01100110,
  B01100110,
  B01100110,
  B01100110,
  B01100110,
  B00111100
},{
  B00000000,
  B01111100,
  B01100110,
  B01100110,
  B01100110,
  B01111100,
  B01100000,
  B01100000
},{
  B00000000,
  B00111100,
  B01100110,
  B01100110,
  B01100110,
  B01101110,
  B00111100,
  B00000110
},{
  B00000000,
  B01111100,
  B01100110,
  B01100110,
  B01111100,
  B01111000,
  B01101100,
  B01100110
},{
  B00000000,
  B00111100,
  B01100110,
  B01100000,
  B00111100,
  B00000110,
  B01100110,
  B00111100
},{
  B00000000,
  B01111110,
  B01011010,
  B00011000,
  B00011000,
  B00011000,
  B00011000,
  B00011000
},{
  B00000000,
  B01100110,
  B01100110,
  B01100110,
  B01100110,
  B01100110,
  B01100110,
  B00111110
},{
  B00000000,
  B01100110,
  B01100110,
  B01100110,
  B01100110,
  B01100110,
  B00111100,
  B00011000
},{
  B00000000,
  B01100011,
  B01100011,
  B01100011,
  B01101011,
  B01111111,
  B01110111,
  B01100011
},{
  B00000000,
  B01100011,
  B01100011,
  B00110110,
  B00011100,
  B00110110,
  B01100011,
  B01100011
},{
  B00000000,
  B01100110,
  B01100110,
  B01100110,
  B00111100,
  B00011000,
  B00011000,
  B00011000
},{
  B00000000,
  B01111110,
  B00000110,
  B00001100,
  B00011000,
  B00110000,
  B01100000,
  B01111110
},{
  B00000000,
  B00000000,
  B00000000,
  B00111100,
  B00000110,
  B00111110,
  B01100110,
  B00111110
},{
  B00000000,
  B01100000,
  B01100000,
  B01100000,
  B01111100,
  B01100110,
  B01100110,
  B01111100
},{
  B00000000,
  B00000000,
  B00000000,
  B00111100,
  B01100110,
  B01100000,
  B01100110,
  B00111100
},{
  B00000000,
  B00000110,
  B00000110,
  B00000110,
  B00111110,
  B01100110,
  B01100110,
  B00111110
},{
  B00000000,
  B00000000,
  B00000000,
  B00111100,
  B01100110,
  B01111110,
  B01100000,
  B00111100
},{
  B00000000,
  B00011100,
  B00110110,
  B00110000,
  B00110000,
  B01111100,
  B00110000,
  B00110000
},{
  B00000000,
  B00000000,
  B00111110,
  B01100110,
  B01100110,
  B00111110,
  B00000110,
  B00111100
},{
  B00000000,
  B01100000,
  B01100000,
  B01100000,
  B01111100,
  B01100110,
  B01100110,
  B01100110
},{
  B00000000,
  B00000000,
  B00011000,
  B00000000,
  B00011000,
  B00011000,
  B00011000,
  B00111100
},{
  B00000000,
  B00001100,
  B00000000,
  B00001100,
  B00001100,
  B01101100,
  B01101100,
  B00111000
},{
  B00000000,
  B01100000,
  B01100000,
  B01100110,
  B01101100,
  B01111000,
  B01101100,
  B01100110
},{
  B00000000,
  B00011000,
  B00011000,
  B00011000,
  B00011000,
  B00011000,
  B00011000,
  B00011000
},{
  B00000000,
  B00000000,
  B00000000,
  B01100011,
  B01110111,
  B01111111,
  B01101011,
  B01101011
},{
  B00000000,
  B00000000,
  B00000000,
  B01111100,
  B01111110,
  B01100110,
  B01100110,
  B01100110
},{
  B00000000,
  B00000000,
  B00000000,
  B00111100,
  B01100110,
  B01100110,
  B01100110,
  B00111100
},{
  B00000000,
  B00000000,
  B01111100,
  B01100110,
  B01100110,
  B01111100,
  B01100000,
  B01100000
},{
  B00000000,
  B00000000,
  B00111100,
  B01101100,
  B01101100,
  B00111100,
  B00001101,
  B00001111
},{
  B00000000,
  B00000000,
  B00000000,
  B01111100,
  B01100110,
  B01100110,
  B01100000,
  B01100000
},{
  B00000000,
  B00000000,
  B00000000,
  B00111110,
  B01000000,
  B00111100,
  B00000010,
  B01111100
},{
  B00000000,
  B00000000,
  B00011000,
  B00011000,
  B01111110,
  B00011000,
  B00011000,
  B00011000
},{
  B00000000,
  B00000000,
  B00000000,
  B01100110,
  B01100110,
  B01100110,
  B01100110,
  B00111110
},{
  B00000000,
  B00000000,
  B00000000,
  B00000000,
  B01100110,
  B01100110,
  B00111100,
  B00011000
},{
  B00000000,
  B00000000,
  B00000000,
  B01100011,
  B01101011,
  B01101011,
  B01101011,
  B00111110
},{
  B00000000,
  B00000000,
  B00000000,
  B01100110,
  B00111100,
  B00011000,
  B00111100,
  B01100110
},{
  B00000000,
  B00000000,
  B00000000,
  B01100110,
  B01100110,
  B00111110,
  B00000110,
  B00111100
},{
  B00000000,
  B00000000,
  B00000000,
  B00111100,
  B00001100,
  B00011000,
  B00110000,
  B00111100
}};
// 기호
byte symbols[][8] = {
{
  B00000000,
  B00000000,
  B00000000,
  B00000000,
  B00000000,
  B00000000,
  B00000000,
  B00000000
},
{
  B00000000,
  B00011000,
  B00111100,
  B00111100,
  B00011000,
  B00011000,
  B00000000,
  B00011000
},{
  B00000000,
  B00111100,
  B01100110,
  B00000110,
  B00011100,
  B00011000,
  B00000000,
  B00011000
}};
//
void setup() {
  ledSetup(); // LED모듈 초기화
  // buffers 초기화
  for (int i = 0; i < 3; i++) {
    memcpy(&buffers[i], &symbols[0], sizeof(symbols[0]));
  }
}
void loop() {
  // 현재 시간 저장
  currentTime = millis();
  // 패턴 변환 함수
  if (dataLength == 0) {
    dataToPattern();
  }
  // 출력 함수
  buffersIn(SHIFT_IN_YES_ROT);
  //
  // LED 출력
  buffersOut();
}
// 출력 데이터를 buffers에 전송
void buffersIn(int mode) {
  int i;
  // NO_SHIFT, SHIFT_IN_NO, SHIFT_IN_NO_OUT, SHIFT_IN_NO_ROT mode는 먼저 패턴 3개 출력
  if ( (rotation != true) && (displayCount == 0 ) && (mode  == NO_SHIFT || mode == SHIFT_IN_NO || mode == SHIFT_IN_NO_OUT || mode == SHIFT_IN_NO_ROT) ) {
    for (i = 0; i < 3; i++) {
      memcpy(&buffers[i], &outputPattern[i], sizeof(outputPattern[0]));
    }
    displayCount = i;
    shifted = currentTime;
  }
  // SHIFT_IN_NO, SHIFT_IN_YES, SHIFT_IN_NO_OUT, SHIFT_IN_YES_OUT, SHIFT_IN_NO_ROT, SHIFT_IN_YES_ROT mode는 마지막 패턴까지 시프트 수행
  if ( (displayCount < dataLength) && (mode  == SHIFT_IN_NO || mode == SHIFT_IN_YES || mode == SHIFT_IN_NO_OUT || mode == SHIFT_IN_YES_OUT || mode == SHIFT_IN_NO_ROT || mode == SHIFT_IN_YES_ROT) ) {
    if (currentTime - shifted >= shiftInterval) {
      memcpy(&buffers[0], &buffers[1], sizeof(outputPattern[0]));
      memcpy(&buffers[1], &buffers[2], sizeof(outputPattern[0]));
      memcpy(&buffers[2], outputPattern[displayCount], sizeof(outputPattern[0]));
      displayCount++;
      shifted = currentTime;
    }
  }
  // SHIFT_IN_NO_OUT, SHIFT_IN_YES_OUT mode 수행
  if ( (displayCount >= dataLength) && (displayCount < dataLength + 3) && (mode == SHIFT_IN_NO_OUT || mode == SHIFT_IN_YES_OUT) ) {
    if (currentTime - shifted >= shiftInterval) {
      memcpy(&buffers[0], &buffers[1], sizeof(outputPattern[0]));
      memcpy(&buffers[1], &buffers[2], sizeof(outputPattern[0]));
      memcpy(&buffers[2], symbols[0], sizeof(outputPattern[0])); // 공백으로 채움
      displayCount++;
      shifted = currentTime;
    }
  }
  // SHIFT_IN_NO_ROT, SHIFT_IN_YES_ROT mode 수행
  if ( (displayCount >= dataLength) && (mode == SHIFT_IN_NO_ROT || mode == SHIFT_IN_YES_ROT) ) {
    displayCount = 0;
    rotation = true;
  }
}
void dataToPattern() {
  //
  dataLength = strlen(outputData);
  if (dataLength > 10) {
    dataLength = 10;
  }
  //
  int i, index;
  for (int i = 0; i < dataLength; i++) {
    if (outputData[i] >= '0' && outputData[i] <= '9') {
      index = outputData[i] - '0';
      memcpy(&outputPattern[i], &digits[index], sizeof(digits[0]));
    } else if (outputData[i] >= 'A' && outputData[i] <= 'Z') {
      index = outputData[i] - 'A';
      memcpy(&outputPattern[i], &chars[index], sizeof(digits[0]));
    } else if (outputData[i] >= 'a' && outputData[i] <= 'z') {
      index = outputData[i] - 'a' + 26;
      memcpy(&outputPattern[i], &chars[index], sizeof(digits[0]));
    } else {
      memcpy(&outputPattern[i], &symbols[0], sizeof(digits[0]));
    }
  }
}
// buffers를 LED 모듈에 출력하는 함수
void buffersOut() {
  if (currentTime - refreshed >= refreshInterval ) {  
    for (int i = 0; i < 3; i++) {
      for (int j = 0; j < 8; j++) {
        lc.setRow(i, j, buffers[i][j]);
      }
    }
    refreshed = currentTime;
  }
}
// LED모듈 초기화 함수
void ledSetup() {
  lc.shutdown(0, false);
  lc.shutdown(1, false);
  lc.shutdown(2, false);
  //
  lc.setIntensity(0,2);
  lc.setIntensity(1,2);
  lc.setIntensity(2,2);
  //
  lc.clearDisplay(0);
  lc.clearDisplay(1);
  lc.clearDisplay(2);
}

여기 까지의 전체 소스입니다. 결과는 아래 동영상으로 확인하실 수 있습니다. SHIFT_IN_NO_ROT 모드로 호출했을 때, SHIFT_IN_YES_ROT 모드로 호출할 때의 결과가 차례대로 나옵니다.

여기까지 LedControl 라이브러리를 이용해 기본적인 문자 패턴 출력함수를 작성해 보았습니다. 이번 글을 마무리하고, 앞으로의 연재를 통하여는 좀 더 부드러운 전환을 위한 도트(dot) 단위의 시프트 출력을 포함한 다른 기능들을 추가해 보도록 하겠습니다.

Comments