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

2018. 2. 9. 15:57

Arduino/Display

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

이전 글에서는 출력 데이터를 처리하는 부분과 LED 모듈에 출력하는 부분을 구분하였습니다. 우선 LED 모듈에 패턴을 출력하는 함수를 작성하였습니다. 이를 위해서, buffers[][]라는 메모리상 가상 공간을 만들고, 이 버퍼의 내용을 출력하는 buffersOut() 함수를 작성하였습니다. buffersOut() 함수는 프로그램의 다른 부분들이 어떻게 처리되는 지는 상관없이 미리 주어진 일정 시간 마다 버퍼의 내용을 LED 모듈에 전송하기만 합니다.

이번 글에서는 계속해서 출력 데이터를 처리하는 부분을 만들어 보겠습니다.

출력 데이터를 버퍼에 전송하는 함수 만들기

이제, 출력할 데이터를 준비하고 필요한 작업을 처리하는 함수가 필요합니다. 출력할 데이터는 사용자로부터 입력 받거나 예제처럼 미리 주어져 있을 수 있는데, "ABC", "123", "aB3" 같은 문자열 데이터입니다. 이를 LED 모듈에 출력하기 위해서는 해당 문자에 맞는 개별 LED의 on/off 값을 가지고 있는 bit열 데이터가 필요합니다.

이전 글에서 작성한 numbers, chars, symbols 배열이 출력할 패턴을 비트값으로 가지고 있는 배열 변수이며 이를 이용해 출력하는 예제도 작성하였습니다. 출력할 문자열 데이터를 버퍼에 전송하기 전, 원하는 패턴으로 변환하는 함수를 작성하겠습니다.

출력할 데이터를 패턴으로 변환하는 코드 작성

이 기능은 이전 글들에서 이미 다루었던 부분입니다. 아스키(ASCII) 코드값을 이용해 영문 대소문자와 숫자, 기호를 구분하여 해당하는 패턴을 배열에서 찾아 오는 코드입니다.

#include "LedControl.h"
//
LedControl lc = LedControl(12,11,10,3);
//
unsigned long refreshed = 0; // 출력 주기를 위한 변수
unsigned int refreshInterval = 50; // 출력 refresh 간격
//
char outputData[] = "aB3"; // 출력할 텍스트 데이터

우선, 8번 행처럼 출력할 데이터와 변수를 준비하였습니다. 모듈 개수와 동일하게 "aB3" 세 문자를 출력하고 영문 대문자, 소문자, 숫자가 제대로 출력되는 지 확인할 수 있도록 세 가지 모두 담았습니다. outputData 변수는 char type 배열로 선언하여 for문을 통해 한 문자씩 처리(outputData[0], outputData[1], outputData[2], ... )하기 쉽습니다.

void loop() {
  // 출력 함수
  buffersIn();
  //
  // LED 출력
  buffersOut();
}

데이터를 패턴으로 바꿔서 버퍼(buffers[][])에 전송하는 부분도 buffersIn() 라는 함수로 묶었습니다. 점점 길어질 것이기 때문에 따로 함수로 만들어 사용하는 것이 깔끔할 듯 합니다.

// 출력 데이터를 buffers에 전송
void buffersIn() {
  int i, index;
  for (i = 0; i < strlen(outputData); i++) {
    if (outputData[i]가 숫자라면) {
      숫자 패턴을 digits[][8] 배열에서 찾아 버퍼에 전송;
      //
    } else if (outputData[i]가 영대문자라면) {
      영대문자 패턴을 chars[][8] 배열 앞부분에서 찾아 버퍼에 전송;
      //
    } else if (outputData[i]가 영소문자라면) {
      영소문자 패턴을 chars[][8] 배열 뒷부분에서 찾아 버퍼에 전송;
      //
    }
  }
}

기호 패턴은 나중에 추가하기로 하고, 우선 위와 같이 출력 함수를 구성하겠습니다.

이전 글에서 나왔던 부분입니다. 우선 4번 행에 있는 strlen() 함수는 문자열의 길이를 구하는 함수입니다. buffersIn() 함수는 outputData[] 배열에서 한 문자씩 읽어서 처리하기 때문에, 배열의 첫 문자부터 마지막 문자까지 즉, 배열의 문자 수 만큼 for문을 반복해 주면 됩니다. strlen() 함수는 이 배열의 문자 수를 구하기 위해 사용하였습니다. 문자열은 말 그대로 일정 문자가 연속되어 하나의 데이터 단위로 사용되는 데이터 형이고, 메모리상에서도 열을 지어 저장됩니다.

for문 내에서는 if문을 통하여 문자의 종류를 구분합니다. 이전 글의 예제에서는 모든 패턴이 하나의 배열에 저장되어 있었지만, 이번 예제는 종류별로 구분된 패턴 배열을 사용하기 때문에, 코드의 내용이 좀 달라졌습니다. 위 소스에서 하나씩 읽어 온 문자가 숫자인지 영문 대, 소문자인지 구분하는 부분을 아스키(ASCII) 코드 값으로 처리합니다. 아스키 코드값은 정수형 값이기 때문에 크기 비교가 가능합니다.

// 출력 데이터를 buffers에 전송
void buffersIn() {
  int i, index;
  for (i = 0; i < strlen(outputData); i++) {
    if (outputData[i] >= '0' && outputData[i] <= '9') {
      index = outputData[i] - '0';
      memcpy(&buffers[i], &digits[index], sizeof(digits[0]));
    } else if (outputData[i] >= 'A' && outputData[i] <= 'Z') {
      index = outputData[i] - 'A';
      memcpy(&buffers[i], &chars[index], sizeof(digits[0]));
    } else if (outputData[i] >= 'a' && outputData[i] <= 'z') {
      index = outputData[i] - 'a' + 26;
      memcpy(&buffers[i], &chars[index], sizeof(digits[0]));
    }
  }
}

함수를 완성하였습니다. 8번 행을 보면, 영문자 사이에 "크다", "작다" 비교문을 사용하였고 이는 내부적으로 아스키 코드값으로 다뤄지기 때문에 가능한 부분입니다. 만약, 출력 데이터가 A, B, C 라면, 코드값은 65, 66, 67 이렇게 65부터 1씩 증가하고, 패턴 배열 chars[0], chars[1], chars[2]에 역시 차례로 저장되어 있습니다. 65부터 1씩 증가하는 값을 0부터 시작하는 배열 첨자로 변환하려면 간단하게 65를 빼주면 되고, 이는 문자 "A"의 코드값입니다. 소문자는 대문자와 같은 배열의 대문자 이후에 저장되어 있기 때문에 26(알파벳 26개)만 더해주면 쉽게 액세스할 수 있습니다.

memcpy() 함수의 마지막 인수는 모두 동일합니다. 복사할 대상의 크기를 지정하는 인수이고, 모든 패턴 배열의 한 항목의 크기는 같기 때문에 어떤 걸 사용해도 상관 없습니다.

#include "LedControl.h"
//
LedControl lc = LedControl(12,11,10,3);
//
unsigned long refreshed = 0; // 출력 주기를 위한 변수
unsigned int refreshInterval = 50; // 출력 refresh 간격
//
char outputData[] = "aB3"; // 출력할 텍스트 데이터
// 버퍼
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() {
  Serial.begin(9600);
  ledSetup(); // LED모듈 초기화
  // buffers 초기화
  for (int i = 0; i < 3; i++) {
    memcpy(&buffers[i], &symbols[0], sizeof(symbols[0]));
  }
}
void loop() {
  // 출력 함수
  buffersIn();
  //
  // LED 출력
  buffersOut();
}
// 출력 데이터를 buffers에 전송
void buffersIn() {
  int i, index;
  for (i = 0; i < strlen(outputData); i++) {
    if (outputData[i] >= '0' && outputData[i] <= '9') {
      index = outputData[i] - '0';
      memcpy(&buffers[i], &digits[index], sizeof(digits[0]));
    } else if (outputData[i] >= 'A' && outputData[i] <= 'Z') {
      index = outputData[i] - 'A';
      memcpy(&buffers[i], &chars[index], sizeof(digits[0]));
    } else if (outputData[i] >= 'a' && outputData[i] <= 'z') {
      index = outputData[i] - 'a' + 26;
      memcpy(&buffers[i], &chars[index], sizeof(digits[0]));
    }
  }
}
// buffers를 LED 모듈에 출력하는 함수
void buffersOut() {
  if (millis() - refreshed >= refreshInterval ) {
    for (int i = 0; i < 3; i++) {
      for (int j = 0; j < 8; j++) {
        lc.setRow(i, j, buffers[i][j]);
      }
    }
    refreshed = millis();
  }
}
// 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);
}

여기 까지 전체 소스와 결과 화면입니다. 출력할 데이터를 그에 맞는 패턴으로 변환하여 버퍼에 보내는 기능이 제대로 처리되었음을 확인할 수 있습니다.

출력 데이터를 패턴으로 변환하는 함수 작성

위 예제에서 buffersIn() 함수를 작업 성격에 따라 또 분리하겠습니다. 위 소스는 한 문자씩 변환하고 버퍼에 전송한 후 다음 문자를 처리하도록 코딩되어 있습니다. 이 부분을 변경하여, 출력 데이터를 한 번에 패턴으로 변환하여 따로 저장한 후, 이 저장된 패턴 변수에서 버퍼로 한 번에 출력하도록 변경하겠습니다. 이렇게 하면, 패턴을 준비하는 부분과 처리하여 버퍼에 전송하는 부분을 나눠서 생각할 수 있기 때문입니다.

위 그림을 보면, 데이터를 패턴으로 변환하는 부분과 버퍼로 전송하는 부분이 둘로 나뉘어 있는데, 각각 dataToPattern() 함수와 buffersIn() 함수로 처리됩니다.

outputData 배열은 입력받은 문자열 데이터를 저장하고 있고, outputPattern 배열은 출력할 패턴을 저장하는 변수입니다. dataToPattern() 함수는 outputData의 문자를 하나씩 읽어 해당하는 패턴을 패턴 배열들에서 찾은 후, outputPattern 배열에 저장합니다. buffersIn() 함수는 outputPattern 배열에 있는 패턴을 적당히 처리하여 버퍼에 전송합니다. 출력은 buffersOut() 함수가 알아서 할 테구요!

//
char outputData[] = "aB3"; // 출력할 텍스트 데이터
byte outputPattern[10][8]; // 출력 패턴 저장
byte dataLength = 0; // outputPattern에 저장된 길이 체크

출력 데이터를 dataToPattern() 함수로 일괄 변환한 후에 버퍼에 출력해야 하므로, 변환된 패턴 데이터를 저장할 변수가 필요 합니다. 변수 이름은 outputPattern으로 하고 다른 패턴 저장 변수와 마찬가지로 byte 타입 2차원 배열로 선언하였습니다. 첫 번째 첨자가 10 이므로 문자는 열 개만 저장 가능합니다. 따라서 출력할 수 있는 데이터는 10 문자까지입니다. 출력할 문자열의 최대 길이에 맞게 변경하면 되고, 예제를 작성하는 동안은 10개의 문자만 출력하겠습니다.

바로 위 예제처럼 출력할 문자가 "aB3", 세 개라면 outputPattern 배열을 다 채우지 않게 됩니다. 따라서, 저장된 문자의 수를 기억할 변수가 필요하고, 10까지의 숫자만 기억하면 되기 때문에 byte type의 dataLength 라는 이름으로 선언하였습니다.

void loop() {
  // 패턴 변환 함수
  dataToPattern();
  // 출력 함수
  buffersIn();
  //
  // LED 출력
  buffersOut();
}

함수명은 dataToPattern() 이며, 데이터의 패턴 변환은 수행 중에 한 번만 실행해야 하기 때문에, 함수 내에서나 밖에서 이 부분을 체크해야 합니다.

void dataToPattern() {
  //
  dataLength = strlen(outputData);
  if (dataLength > 10) {
    dataLength = 10;
  }
  //
}

dataToPattern() 함수 내부입니다. strlen() 함수를 이용해 출력할 데이터(outputData)의 길이를 구하여 dataLength 변수에 저장하고, 만약, 10보다 크다면 10으로 줄여서 저장합니다. outputPattern 변수가 10개까지만 저장할 수 있기 때문입니다. 그리고, loop() 함수에서 dataToPattern() 함수를 호출하는 부분도 아래와 같이 변경하였습니다.

void loop() {
  // 패턴 변환 함수
  if (dataLength == 0) {
    dataToPattern();
  }
  // 출력 함수
  buffersIn();
  //
  // LED 출력
  buffersOut();
}

dataLength 변수가 0 일때만 패턴 변환 함수를 실행합니다. 함수 실행 후에는 dataLength 변수는 문자 개수가 저장되기 때문에 0보다 크게 되며, 프로그램 수행중에 한 번만 수행하도록 제어할 수 있습니다.

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 배열에 저장하던 것을 outputPattern에 저장하는 것으로 변경하였고, 숫자나 영문자가 아닐 경우 빈 화면 즉 공백을 출력하도록 추가했습니다.

// 출력 데이터를 buffers에 전송
void buffersIn() {
  for (int i = 0; i < 3; i++) {
    memcpy(&buffers[i], &outputPattern[i], sizeof(outputPattern[0]));
  }
}

덕분에 buffersIn() 함수는 잠시나마 간단해졌습니다. 출력할 패턴을 미리 준비된 outputPattern 배열에서 가져와서 출력합니다.

#include "LedControl.h"
//
LedControl lc = LedControl(12,11,10,3);
//
unsigned long refreshed = 0; // 출력 주기를 위한 변수
unsigned int refreshInterval = 50; // 출력 refresh 간격
//
char outputData[] = "aB3"; // 출력할 텍스트 데이터
byte outputPattern[10][8]; // 출력 패턴 저장
byte dataLength = 0; // outputPattern에 저장된 길이 체크
// 버퍼
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() {
  // 패턴 변환 함수
  if (dataLength == 0) {
    dataToPattern();
  }
  // 출력 함수
  buffersIn();
  //
  // LED 출력
  buffersOut();
}
// 출력 데이터를 buffers에 전송
void buffersIn() {
  for (int i = 0; i < 3; i++) {
    memcpy(&buffers[i], &outputPattern[i], sizeof(outputPattern[0]));
  }
}
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 (millis() - refreshed >= refreshInterval ) {
    for (int i = 0; i < 3; i++) {
      for (int j = 0; j < 8; j++) {
        lc.setRow(i, j, buffers[i][j]);
      }
    }
    refreshed = millis();
  }
}
// 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);
}

여기 까지의 전체 소스이고, 결과는 처음 예제와 동일하기 때문에 생략하였습니다.

시프트 기능 구현하기

원래의 기능에 시프트가 가능하도록 추가합니다. 따라서, buffersIn() 함수를 호출할 때도, 시프트 기능을 이용할 지를 알려 주는 부분이 필요합니다.

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

이제 패턴을 출력하는 방법을 세 가지로 구분합니다. 데이터를 시프트하지 않을 때는 NO_SHIFT 모드이고, 나머지는 시프트하여 출력하는 모드입니다. 시프트도 두 가지로 구분합니다. 우선 출력하고 나머지 문자들을 시프트하여 보여주는 것과 처음부터 한 문자씩 시프트하여 보여 주는 모드로 나누어 처리하도록 하겠습니다.

#define 구문은 사용자가 원하는 이름을 만들어 사용할 수 있도록 합니다. 출력 모드는 숫자 0, 1, 2 값으로 구분하고 알아 보기 쉽도록 각각 이름을 부여하여 사용하는 것입니다.

우선, 시프트 기능을 확인하기 위해서 LED 모듈 개수바도 많은 열 개의 데이터를 준비하였습니다. "NO SHIFT" 모드일 때는 말 그대로 시프트 하지 않습니다. 문자 수가 많더라도 LED 모듈 개수만큼 출력하고 나머지 데이터는 표시하지 않습니다. 이와 대비되는 모드가 "SHIFT_IN_NO" 입니다. 처음 세 문자를 출력하는 것은 동일하지만, 나머지 문자를 하나씩 시프트하여 보여주는 부분이 다릅니다.

"SHIFT_IN_NO", "SHIFT_IN_YES" 모드는 위와 같습니다. 출력 가능한 문자만큼 모두 출력한 후 나머지 문자를 하나씩 시프트하여 보여주는 것과 처음부터 하나씩 시프트하여 표시되는 부분이 다릅니다.

SHIFT_IN_NO 기능 추가하기

먼저, 기존 예제에 SHIFT_IN_NO 모드를 추가하겠습니다.

char outputData[] = "ABCDEFGHIJ"; // 출력할 데이터

시프트 기능을 테스트하기 위해 출력할 데이터를 열 개로 늘렸습니다.

void loop() {
  // 패턴 변환 함수
  if (dataLength == 0) {
    dataToPattern();
  }
  // 출력 함수
  buffersIn(SHIFT_IN_NO);
  //
  // LED 출력
  buffersOut();
}

이제 출력 모드를 구분하여 호출해야 합니다. 7번 행처럼 호출시에 인수를 추가했습니다.

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

buffersIn() 함수 정의입니다. 함수 인수로 mode라는 int type 변수를 추가하였습니다.

NO_SHIFT, SHIFT_IN_NO 모드 둘 다, 처음 세 개의 문자를 출력하는 부분은 동일합니다. 따라서, 위와 같이 코드를 공유할 수 있습니다. SHIFT_IN_NO 모드일 경우에만 나머지 부분을 시프트하여 출력하도록 아래쪽에 코드를 추가합니다.

NO_SHIFT 모드일 경우, 처음 한 번만 출력한 후 다시 출력할 필요는 없습니다. 출력 데이터의 변화가 없기 때문인데, SHIFT_IN_NO 모드일 때는 절대로 다시 출력하면 안되겠죠! 그 이후의 문자를 출력해야 하니까요! 그래서, 처음 세 개의 문자를 출력하는 위 코드를 한 번만 실행되도록 수정하겠습니다.

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

5번 행처럼 displayCount 라는 변수를 추가했습니다. 몇 번째 문자까지 출력했는 지 체크하기 위한 용도입니다.

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;
  }
}

4번 행 if문의 비교 구문을 변경했습니다. displayCount 변수가 0값이라면 출력된 문자가 하나도 없는 처음 실행시에 해당하므로 이후 코드를 실행합니다. 실행 후에, 8번 행처럼 for문의 반복 값을 가지고 있던 변수 i 의 값을 displayCount 변수에 할당합니다. 보통 숫자 3이 할당될 테고, 다시 loop()를 돌아서 이 부분에 도달한다고 해도, if문의 displayCount == 0 구문에 의해 실행하지 않게 됩니다.

실행해보면 결과는 동일하게 보이지만, 이전 코드는 loop()를 돌 때마다 계속해서 출력한 반면, 수정된 코드는 딱 한 번만 출력하고 다음부터 건너 뜁니다.

시프트 코드 구현하기

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;
  }
  // 시프트 처리
  if ( (displayCount < dataLength) && (mode  == SHIFT_IN_NO || mode == SHIFT_IN_YES) ) {
    // 이전 문자를 하나씩 시프트하고 다음 문자 출력
    displayCount++;
  }
}

표시된 코드가 시프트하여 출력하는 부분이 들어갈 곳입니다. SHIFT_IN_NO, SHIFT_IN_YES 모드만 해당하고, 또 모든 데이터를 출력한 후엔 시프트하지 말아야 하므로, displayCount 변수를 체크합니다. 출력한 후에는 displayCount 변수를 1 증가시켜 출력된 수를 체크할 수 있도록 합니다.

시프트는 일정 시간 마다 한 문자씩 옆으로 이동하는 기능입니다. 따라서, 몇 초마다 시프트할 지 시간 간격을 제공해야 합니다. 또한, 시프트 한 후에, 다음 시프트할 시간이 되는 지 체크하고, 그 사이에는 출력하지 않도록 해야 합니다. 이 부분은 이전 글에서 buffersOut() 함수를 작성할 때 다뤘던 부분입니다.

// 시간 관련 변수들
unsigned long currentTime; // 현재 시간 저장
unsigned long refreshed = 0; // 출력 주기를 위한 변수
unsigned long refreshInterval = 50; // 출력 refresh 간격
unsigned long shifted = 0; // 시프트 간격 체크위한 이전 시프트 시간 저장
unsigned long shiftInterval = 500; // 시프트 간격 ?초 마다

이제 두 가지 시간을 체크해야 합니다. 버퍼에 있는 내용을 LED 모듈에 출력하는 refresh 관련 시간과, 출력 데이터를 버퍼에 시프트할 때 필요한 시간입니다. 두 시간대의 오차를 줄이기 위해, currentTime 변수를 만들어 현재 시간을 저장한 후, 시간 비교시에 사용하겠습니다. shifted 변수는 이전 시프트 시간을 저장하고, shiftInterval 변수는 시프트 간격을 설정합니다.

void loop() {
  // 현재 시간 저장
  currentTime = millis();

loop() 함수가 시작되면 currentTime 변수에 millis() 함수를 이용하여 현재 시간을 저장합니다.

// 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;
  }
}

위 코드의 3, 9번 행처럼 이전 예제에서 millis() 함수를 통하여 직접 입력 받던 부분을 currentTime 변수를 이용하도록 변경했습니다.

// 시프트 처리
  if ( (displayCount < dataLength) && (mode  == SHIFT_IN_NO || mode == SHIFT_IN_YES) ) {
    if (currentTime - shifted >= shiftInterval) {
      // 시프트 처리
      displayCount++;
      shifted = currentTime;
    }
  }

중첩된 if문중 안쪽 if문이 시프트 간격을 체크하기 위한 코드입니다. currentTime은 현재 시간이고, shifted는 이전 시프트 시간입니다. 위 코드 처럼 currentTime에서 shifted를 빼면, 이전에 시프트한 이후로 얼마의 시간이 지났는 지 알 수 있고, 지정된 shiftInterval 이상이면 시프트 처리합니다. buffersOut() 함수의 refreshed, refreshInterval 변수에 대해 설명했듯이, delay() 함수를 사용하면 잠시 지연되는 동안 모든 작업이 멈추게 됩니다.

시프트 처리후엔 shifted에 currentTime 변수값을 할당하여 시프트한 시간을 기억시킵니다.

// 시프트 처리
  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;
    }
  }

실제 시프트처리입니다. 출력된 내용을 앞으로 하나씩 이동시키고 마지막 칸에 다음 문자를 출력합니다. displayCount 변수는 다음에 찍어야 할 문자의 위치를 기억하고 있습니다. SHIFT_IN_NO 모드일땐 3의 값으로 시작하고, SHIFT_IN_YES 모드일때는 0의 값으로 시작합니다.

그런데, 이 코드는 SHIFT_IN_NO 모드일 때 한 가지 문제가 있습니다. 먼저 3개의 문자를 출력한 후 위 코드를 만나면 바로 시프트 처리를 합니다. 즉, "ABC"를 출력한 후에 시간 간격 없이 "BCD"로 변경되어 버립니다. 시프트는 shiftInterval 변수 값만큼 시간이 지난 후에 일어나야 하므로, 처음 실행시에는 위 코드를 지나쳐 가도록 해야 합니다.

// 출력 데이터를 buffers에 전송
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;
  }
  // 시프트 처리
  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;
    }
  }
}

위 코드의 10번 행처럼 3개의 패턴을 출력한 후 이전 시프트 시간(shifted)을 현재 시간으로 설정하면 바로 이어지는 코드는 건너 뛰고, 다음 시프트 시간에만 출력하게 됩니다.

#include "LedControl.h"
// 시프트 모드 구분
#define NO_SHIFT      0
#define SHIFT_IN_NO   1
#define SHIFT_IN_YES  2
//
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_NO);
  //
  // LED 출력
  buffersOut();
}
// 출력 데이터를 buffers에 전송
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;
  }
  // 시프트 처리
  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;
    }
  }
}
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);
}

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

여기 까지 이번 글을 마무리하고, 다음 글에선 Shift In, rotation 기능 등을 추가해 보겠습니다. 시프트 기능에 대한 기본 구성은  마무리했기 때문에 쉽게 구현할 수 있습니다.

Comments