Teensy 4.1 로 WS2812(네오픽셀) LED 링을 구동하는 기본기를 정리한다. 개발환경 설정부터 R·G·B 패턴 코드까지 훑는다.
1. Teensy 4.1 개발환경 설정
Arduino IDE 대신 VSCode + PlatformIO 조합을 쓴다. 자동완성과 헤더 추적이 제대로 되고, 프로젝트마다 라이브러리 버전이 독립적으로 관리된다.
VSCode 를 설치한 뒤 확장 세 가지를 넣는다.
- PlatformIO IDE — 빌드·업로드·라이브러리 관리를 전담한다
- C/C++ — 코드 탐색과 자동완성
- Arduino — 아두이노 문법 보조
프로젝트 루트의 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() 가 주력이므로 이 둘을
반드시 맞춰 둔다.
2. LED Strip 기본 개념
흔히 네오픽셀이라 부르는 WS2812B 는 LED 안에 제어 IC 가 들어 있는 소자다. 그래서 선이 세 가닥뿐이다 — 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 로 쓸 수 있는 핀에서만 동작하니, 라이브러리 예제의 핀 목록을 확인하고 배선한다.
| 라이브러리 | 특징 |
|---|---|
| WS2812Serial | Teensy 전용. 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 안팎이 적당했다.
다음에는 링을 회전시키는 패턴과, 상위 제어기에서 패턴 번호를 받아 바꾸는 구조를 다룬다.

답글 남기기