Печать

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