Печать

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