WS2812 адресуемый RGB светодиод с цифровым управлением по 1 проводу. Обычно, светодиоды WS2812 распаивают на ленту или матрицу.
Каждый светодиод может
#include "pico/stdlib.h"
#include "hardware/pio.h"
#include "ws2812.pio.h"
#define LED_PIN 23 // Пин управления светодиодом
#define BRIGHTNESS 64 // 25% яркости (0-255)
// Формирование цвета
static inline uint32_t make_color(uint8_t r, uint8_t g, uint8_t b) {
// Формат: 0xGGRRBB (для WS2812)
return (uint32_t)g << 24 |
(uint32_t)r << 16 |
(uint32_t)b << 8;
}
int main() {
// Инициализация PIO
PIO pio = pio0;
uint sm = pio_claim_unused_sm(pio, true);
uint offset = pio_add_program(pio, &ws2812_program);
// Настройка протокола WS2812 (800 кГц, RGB)
ws2812_program_init(pio, sm, offset, LED_PIN, 800000, false);
while (true) {
// Плавное увеличение красного (0-255)
for (uint32_t r = 0; r < BRIGHTNESS; r++) {
pio_sm_put_blocking(pio, sm, r << 16u);
sleep_ms(40*64/BRIGHTNESS);
}
// Плавное увеличение зеленого (0-255)
for (uint32_t g = 0; g < BRIGHTNESS; g++) {
pio_sm_put_blocking(pio, sm, g << 24u);
sleep_ms(40*64/BRIGHTNESS);
}
// Плавное увеличение синего (0-255)
for (uint32_t b = 0; b < BRIGHTNESS; b++) {
pio_sm_put_blocking(pio, sm, b << 8u);
sleep_ms(40*64/BRIGHTNESS);
}
// Плавное увеличение всех компонентов (255-0)
for (uint8_t i = 0; i < (uint8_t) BRIGHTNESS / 3; i++) {
pio_sm_put_blocking(pio, sm, make_color(i, i, i));
sleep_ms(160*64/BRIGHTNESS);
}
}
}
Лист. 1. Программа main.c
;
; Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
;
; SPDX-License-Identifier: BSD-3-Clause
;
.pio_version 0 // Версия PIO (0 - минимальная, совместимая со всеми)
.program ws2812
.side_set 1 // Использование 1 бита для side-set
; The following constants are selected for broad compatibility with WS2812,
; WS2812B, and SK6812 LEDs. Other constants may support higher bandwidths for
; specific LEDs, such as (7,10,8) for WS2812B LEDs.
; === ТАЙМИНГИ ДЛЯ WS2812/WS2812B/SK6812 ===
; T1: Длительность высокого уровня для "1" (стандарт: 0.8us)
; T2: Длительность высокого уровня для "0" (стандарт: 0.4us)
; T3: Общая длительность бита (стандарт: 1.25us)
.define public T1 3 ; Циклы PIO для "1"
.define public T2 3 ; Циклы PIO для "0"
.define public T3 4 ; Всего циклов на бит
; === ОСНОВНАЯ ПРОГРАММА ===
.wrap_target ; Начало цикла
bitloop:
; Чтение 1 бита из сдвигового регистра (32-битный буфер)
out x, 1 side 0 [T3 - 1] ; [Задержка T3-1 циклов] side-set=0
; Проверка бита: 0 или 1?
jmp !x do_zero side 1 [T1 - 1] ; Если 0 -> do_zero, side-set=1 [Задержка T1-1]
do_one: ; Обработка бита "1"
jmp bitloop side 1 [T2 - 1] ; Продолжаем высокий уровень, side-set=1 [Задержка T2-1]
do_zero: ; Обработка бита "0"
nop side 0 [T2 - 1] ; Переключаем на низкий уровень, side-set=0 [Задержка T2-1]
.wrap ; Конец цикла (возврат к bitloop)
% c-sdk {
#include "hardware/clocks.h"
static inline void ws2812_program_init(PIO pio, uint sm, uint offset, uint pin, float freq, bool rgbw) {
pio_gpio_init(pio, pin);
pio_sm_set_consecutive_pindirs(pio, sm, pin, 1, true);
pio_sm_config c = ws2812_program_get_default_config(offset);
sm_config_set_sideset_pins(&c, pin);
sm_config_set_out_shift(&c, false, true, rgbw ? 32 : 24);
sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_TX);
int cycles_per_bit = ws2812_T1 + ws2812_T2 + ws2812_T3;
float div = clock_get_hz(clk_sys) / (freq * cycles_per_bit);
sm_config_set_clkdiv(&c, div);
pio_sm_init(pio, sm, offset, &c);
pio_sm_set_enabled(pio, sm, true);
}
%}
.lang_opt python sideset_init = pico.PIO.OUT_HIGH
.lang_opt python out_init = pico.PIO.OUT_HIGH
.lang_opt python out_shiftdir = 1
Лист. 2. Программа ws2812.pio
# Минимальная требуемая версия CMake
cmake_minimum_required(VERSION 3.13)
# Настройки стандартов языков
set(CMAKE_C_STANDARD 11) # Стандарт C11
set(CMAKE_CXX_STANDARD 17) # Стандарт C++17
# === УСТАНОВКА ПЛАТФОРМЫ ===
set(PICO_BOARD pico) # Используем базовую плату Raspberry Pi Pico
# Подключение SDK Raspberry Pi Pico
include(pico_sdk_import.cmake)
# Инициализация проекта
project(pio_ws2812 C CXX ASM) # Имя проекта и поддерживаемые языки
# Инициализация Pico SDK
pico_sdk_init()
# === НАСТРОЙКА ИСПОЛНЯЕМОГО ФАЙЛА ===
# Создаем исполняемый файл с именем проекта
add_executable(pio_ws2812)
# Генерируем заголовочный файл из PIO-программы
# OUTPUT_DIR указывает, куда поместить сгенерированный файл ws2812.pio.h
pico_generate_pio_header(pio_ws2812
${CMAKE_CURRENT_LIST_DIR}/ws2812.pio
OUTPUT_DIR ${CMAKE_CURRENT_LIST_DIR}/generated
)
# Добавляем исходный код в проект
target_sources(pio_ws2812 PRIVATE main.c)
# === ПОДКЛЮЧЕНИЕ БИБЛИОТЕК ===
# pico_stdlib: базовая функциональность SDK
# hardware_pio: работа с PIO (программируемые I/O блоки)
target_link_libraries(pio_ws2812 PRIVATE
pico_stdlib
hardware_pio
)
# Генерация дополнительных выходных файлов (UF2, HEX и др.)
pico_add_extra_outputs(pio_ws2812)
# === ОПЦИОНАЛЬНО: ГЕНЕРАЦИЯ ФАЙЛОВ ДЛЯ ДОКУМЕНТАЦИИ ===
# (Можно закомментировать, если не требуется)
# Создаем каталог для сгенерированных файлов
file(MAKE_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}/generated)
# Генерация Python-версии PIO программы (для документации)
add_custom_command(
OUTPUT ${CMAKE_CURRENT_LIST_DIR}/generated/ws2812.py
DEPENDS ${CMAKE_CURRENT_LIST_DIR}/ws2812.pio
COMMAND pioasm -o python ${CMAKE_CURRENT_LIST_DIR}/ws2812.pio ${CMAKE_CURRENT_LIST_DIR}/generated/ws2812.py
VERBATIM
)
# Добавляем зависимость от генерации документации
add_custom_target(pio_ws2812_datasheet DEPENDS ${CMAKE_CURRENT_LIST_DIR}/generated/ws2812.py)
add_dependencies(pio_ws2812 pio_ws2812_datasheet)
Лист. 3. Файл CMakeLists.txt