Teensy 4.1 로 WS2812 LED Strip 구동하기

12구 WS2812 LED 링

작성자

카테고리:

Teensy 4.1 로 WS2812(네오픽셀) LED 링을 구동하는 기본기를 정리한다. 개발환경 설정부터 R·G·B 패턴 코드까지 훑는다.

1. Teensy 4.1 개발환경 설정

Arduino IDE 대신 VSCode + PlatformIO 조합을 쓴다. 자동완성과 헤더 추적이 제대로 되고, 프로젝트마다 라이브러리 버전이 독립적으로 관리된다.

VSCode 를 설치한 뒤 확장 세 가지를 넣는다.

  • PlatformIO IDE — 빌드·업로드·라이브러리 관리를 전담한다
  • C/C++ — 코드 탐색과 자동완성
  • Arduino — 아두이노 문법 보조
VSCode 확장 마켓플레이스에서 PlatformIO IDE 설치
<그림 1> 확장 탭에서 PlatformIO IDE 를 검색해 설치한다

프로젝트 루트의 platformio.ini 가 설정의 전부다.

[env:teensy41]
platform      = teensy
board         = teensy41
framework     = arduino
monitor_speed = 115200

화면 아래 상태 표시줄의 체크 표시가 빌드, 화살표가 업로드다. Teensy 는 업로드 시 Teensy Loader 가 자동으로 뜬다.

★주) 시리얼 모니터에 글자가 깨져 나오면 platformio.ini 의 monitor_speed 와 코드의 Serial.begin() 속도가 어긋난 것이다. J-Tag 없이 디버깅할 때는 결국 Serial.print() 가 주력이므로 이 둘을 반드시 맞춰 둔다.

Teensy 4.1 보드
<그림 2> Teensy 4.1. NXP i.MX RT1062(600 MHz)에 SD 슬롯을 갖췄다

2. LED Strip 기본 개념

흔히 네오픽셀이라 부르는 WS2812B 는 LED 안에 제어 IC 가 들어 있는 소자다. 그래서 선이 세 가닥뿐이다 — 5 V, GND, DATA.

12개 WS2812 LED 링
<그림 3> 12구 링타입 LED. 빨강 5 V, 검정 GND, 노랑 DATA 세 가닥이 전부다

데이터는 한 줄로 흘러간다. 첫 픽셀이 앞의 24 비트(GRB 각 8 비트)를 먹고, 나머지를 다음 픽셀로 흘려보낸다. 그래서 픽셀 수만큼 연달아 보내면 각자 제 몫을 가져간다. 주의할 점은 순서가 RGB 가 아니라 GRB 라는 것이다.

<표 1> 배선 시 확인할 것

항목내용
전류픽셀당 최대 약 60 mA(백색 최대 밝기). 12구면 0.7 A 남짓이므로 USB 로 가능하지만, 수십 개를 넘으면 별도 5 V 전원을 쓴다
신호 레벨Teensy 출력은 3.3 V, WS2812 는 5 V 기준 약 3.5 V 이상을 요구한다. 대개는 동작하지만 경계선이므로 레벨 시프터를 넣는 편이 안전하다
보호 소자DATA 선에 300~500 Ω 직렬 저항, 전원단에 1000 µF 커패시터
접지별도 전원을 쓸 때 GND 를 반드시 공통으로 묶는다

3. Teensy 4.1 네오픽셀 라이브러리

WS2812 는 타이밍이 빡빡해서 보통 라이브러리는 전송 중 인터럽트를 막는다. 모터 제어나 통신을 함께 하면 그 순간 놓치는 것이 생긴다.

Teensy 에는 이를 피하는 WS2812Serial 이 있다. 하드웨어 시리얼과 DMA 로 파형을 내보내므로 CPU 를 붙잡지 않는다. 대신 시리얼 TX 로 쓸 수 있는 핀에서만 동작하니, 라이브러리 예제의 핀 목록을 확인하고 배선한다.

라이브러리특징
WS2812SerialTeensy 전용. DMA 비차단. 핀 제약 있음 — 권장
Adafruit_NeoPixel어느 보드에서나 동작. 전송 중 인터럽트 차단
FastLED색 보정·패턴 함수가 풍부. Teensy 에서 WS2812Serial 을 백엔드로 쓸 수 있다

4. R·G·B 패턴 코드

12구 링을 빨강 → 초록 → 파랑 순으로 한 칸씩 채우는 예제다.

#include <WS2812Serial.h>

const int numled = 12;
const int pin    = 1;          // 시리얼 TX 로 쓸 수 있는 핀

byte drawingMemory[numled * 3];         // 작업용
DMAMEM byte displayMemory[numled * 12]; // DMA 전송용

WS2812Serial leds(numled, displayMemory, drawingMemory, pin, WS2812_GRB);

#define RED   0xFF0000
#define GREEN 0x00FF00
#define BLUE  0x0000FF
#define OFF   0x000000

void setup() {
  leds.begin();
  leds.setBrightness(60);      // 0~255. 눈이 부시고 전류도 크므로 낮춰 둔다
}

// 한 칸씩 차오르게 칠한다
void colorWipe(int color, int wait_ms) {
  for (int i = 0; i < leds.numPixels(); i++) {
    leds.setPixel(i, color);
    leds.show();               // 여기서 DMA 전송이 시작된다
    delay(wait_ms);
  }
}

void loop() {
  colorWipe(RED,   60);
  colorWipe(GREEN, 60);
  colorWipe(BLUE,  60);
  colorWipe(OFF,   60);
}

setPixel() 은 버퍼에 쓰기만 하고 실제 출력은 show() 에서 일어난다. 여러 픽셀을 바꿀 때는 전부 써 넣은 뒤 show() 를 한 번만 부르는 것이 맞다. 위 예제는 차오르는 모습을 보이려 일부러 매번 호출했다.

밝기는 setBrightness() 로 낮춰 쓴다. 12구를 백색 최대로 켜면 0.7 A 가 흐르고 눈으로도 지나치게 밝다. 실제 장비에서는 60 안팎이 적당했다.

다음에는 링을 회전시키는 패턴과, 상위 제어기에서 패턴 번호를 받아 바꾸는 구조를 다룬다.

코멘트

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다

어제 방문자 : 3 명오늘 방문자 : 1 명전체 방문자 : 584 명