WS2812 адресуемый RGB светодиод с цифровым управлением по 1 проводу. Обычно, светодиоды WS2812 распаивают на ленту или матрицу.
Каждый светодиод может
#include "pico/stdlib.h"
#include "generated/ws2812.pio.h"
#include "hardware/clocks.h"
// Инициализация PIO для WS2812
static inline void ws2812_program_init(PIO pio, uint sm, uint offset, uint pin, float freq, bool rgbw) {
pio_gpio_init(pio, pin); // Настройка пина в режим PIO
pio_sm_set_consecutive_pindirs(pio, sm, pin, 1, true); // Направление - вывод
// Базовая конфигурация State Machine
pio_sm_config c = ws2812_program_get_default_config(offset);
sm_config_set_sideset_pins(&c, pin); // Sideset использует указанный пин
sm_config_set_out_shift(&c, false, true, rgbw ? 32 : 24); // Сдвиг вправо, автозагрузка
sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_TX); // Объединение FIFO для больших передач
// Расчет делителя частоты
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); // Инициализация State Machine
pio_sm_set_enabled(pio, sm, true); // Запуск State Machine
}
#define LED_PIN 23 // GPIO23 для управления светодиодом
#define BRIGHTNESS 128 // Половина максимальной яркости (0-255)
// Формирование 32-битного цвета в формате GRB
static inline uint32_t make_color(uint8_t r, uint8_t g, uint8_t b) {
return ((uint32_t)g << 24) | // Зеленый: биты 24-31
((uint32_t)r << 16) | // Красный: биты 16-23
((uint32_t)b << 8); // Синий: биты 8-15
}
int main() {
// Инициализация PIO
PIO pio = pio0; // Используем первый PIO блок
uint sm = pio_claim_unused_sm(pio, true); // Занимаем свободную State Machine
uint offset = pio_add_program(pio, &ws2812_program); // Загружаем PIO-программу
// Настройка протокола WS2812 (800 кГц, RGB формат)
ws2812_program_init(pio, sm, offset, LED_PIN, 800000, false);
// Основной цикл анимации
while (true) {
// Плавное увеличение красного компонента
for (uint32_t r = 0; r < BRIGHTNESS; r++) {
pio_sm_put_blocking(pio, sm, r << 16); // Красный: сдвиг на 16 бит
sleep_ms(40*64/BRIGHTNESS); // Динамическая задержка
}
// Плавное увеличение зеленого компонента
for (uint32_t g = 0; g < BRIGHTNESS; g++) {
pio_sm_put_blocking(pio, sm, g << 24); // Зеленый: сдвиг на 24 бита
sleep_ms(40*64/BRIGHTNESS);
}
// Плавное увеличение синего компонента
for (uint32_t b = 0; b < BRIGHTNESS; b++) {
pio_sm_put_blocking(pio, sm, b << 8); // Синий: сдвиг на 8 бит
sleep_ms(40*64/BRIGHTNESS);
}
// Плавное увеличение всех компонентов (оттенки серого)
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
.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)
Лист. 2. Программа ws2812.pio
# == DO NOT EDIT THE FOLLOWING LINES for the Raspberry Pi Pico VS Code Extension to work ==
if(WIN32)
set(USERHOME $ENV{USERPROFILE})
else()
set(USERHOME $ENV{HOME})
endif()
set(sdkVersion 2.1.1)
set(toolchainVersion 14_2_Rel1)
set(picotoolVersion 2.1.1)
set(picoVscode ${USERHOME}/.pico-sdk/cmake/pico-vscode.cmake)
if (EXISTS ${picoVscode})
include(${picoVscode})
endif()
# ====================================================================================
# Минимальная требуемая версия 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)
include_directories(${CMAKE_CURRENT_LIST_DIR}/generated)
# Генерируем заголовочный файл из 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
hardware_clocks
)
# Генерация дополнительных выходных файлов (UF2, HEX и др.)
pico_add_extra_outputs(pio_ws2812)
Лист. 3. Файл CMakeLists.txt