Программа демонстрирует параллельную работу ядра микроконтроллера (printf) и PIO (мигание светодиодом), использование аппаратного ускорения для точного управления временем, автоматизацию сборки с помощью CMake.

PIO (Programmable I/O) - программируемые периферийные блоки RP2040, позволяют создавать собственные протоколы и тайминги.

#include <stdio.h>
#include "pico/stdlib.h"
#include "hardware/pio.h"       // Работа с PIO (Programmable I/O)
#include "blink.pio.h"          // Автогенерируемый заголовок для PIO программы

// Инициализация программы PIO для мигания светодиодом
void blink_program_init(PIO pio, uint sm, uint offset, uint pin) {
   pio_gpio_init(pio, pin);     // Настройка GPIO для работы с PIO
   pio_sm_set_consecutive_pindirs(pio, sm, pin, 1, true); // Направление вывода (OUTPUT)
   
   // Получение конфигурации по умолчанию для программы blink
   pio_sm_config c = blink_program_get_default_config(offset);
   
   // Настройка набора выводов (в данном случае один пин)
   sm_config_set_set_pins(&c, pin, 1);
   
   // Инициализация state machine с заданной конфигурацией
   pio_sm_init(pio, sm, offset, &c);
}

// Основная функция для запуска мигания светодиодом
void blink_pin_forever(PIO pio, uint sm, uint offset, uint pin, uint freq) {
    // Инициализация программы PIO
    blink_program_init(pio, sm, offset, pin);
    
    // Включение state machine
    pio_sm_set_enabled(pio, sm, true);
    
    // Расчет и установка значения задержки:
    // 125000000 - тактовая частота RP2040 (125 МГц)
    // Делим на (2 * freq) так как мигание включает ВКЛ/ВЫКЛ
    // -3 для компенсации задержки выполнения инструкций
    pio->txf[sm] = (125000000 / (2 * freq)) - 3;
}

int main() {
    stdio_init_all();  // Инициализация стандартного ввода/вывода
    
    // Используем PIO блок 0
    PIO pio = pio0;
    
    // Загрузка программы blink в память PIO
    uint offset = pio_add_program(pio, &blink_program);
    
    // Запуск мигания на встроенном светодиоде с частотой 1 Гц
    blink_pin_forever(pio, 0, offset, PICO_DEFAULT_LED_PIN, 1);

    // Основной цикл
    while (true) {
        printf("Hello, world!\n");  // Вывод в консоль
        sleep_ms(1000);             // Пауза 1 секунда
    }
}

Лист. 1. main.c

; Программа мигания светодиодом для PIO (Programmable I/O)

.program blink     ; Объявление программы
    pull block     ; Ожидание данных из FIFO (блокирующий режим)
    out y, 32      ; Копирование 32 бит данных в регистр Y (значение задержки)
    
.wrap_target       ; Начало точки цикла (для бесконечного выполнения)
    mov x, y       ; Копирование значения Y в X (установка счетчика)
    set pins, 1    ; Включение светодиода (установка пина в HIGH)
    
lp1:               ; Метка первого цикла задержки
    jmp x-- lp1    ; Уменьшение X и переход к lp1 пока X > 0
    
    mov x, y       ; Сброс счетчика
    set pins, 0    ; Выключение светодиода (установка пина в LOW)
    
lp2:               ; Метка второго цикла задержки
    jmp x-- lp2    ; Уменьшение X и переход к lp2 пока X > 0
    
.wrap              ; Конец точки цикла (возврат к .wrap_target)

Лист. 2. blink.pio

# Минимальная требуемая версия CMake
cmake_minimum_required(VERSION 3.13)

# Настройка стандартов языка
set(CMAKE_C_STANDARD 11)      # C11
set(CMAKE_CXX_STANDARD 17)    # C++17
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)  # Генерация файла компиляции

# === Секция для совместимости с VS Code ===
if(WIN32)
    set(USERHOME $ENV{USERPROFILE})   # Домашняя директория Windows
else()
    set(USERHOME $ENV{HOME})          # Домашняя директория Linux/Mac
endif()

# Версии компонентов SDK
set(sdkVersion 2.1.1)
set(toolchainVersion 14_2_Rel1)
set(picotoolVersion 2.1.1)

# Подключение специфичных для VS Code настроек
set(picoVscode ${USERHOME}/.pico-sdk/cmake/pico-vscode.cmake)
if (EXISTS ${picoVscode})
    include(${picoVscode})
endif()
# ==========================================

# Установка целевой платы (Raspberry Pi Pico)
set(PICO_BOARD pico CACHE STRING "Board type")

# Подключение SDK Pico
include(pico_sdk_import.cmake)

# Создание проекта
project(v06 C CXX ASM)  # Проект поддерживает C, C++ и ассемблер

# Инициализация SDK Pico
pico_sdk_init()

# Добавление исполняемого файла (основной файл main.c)
add_executable(v06 main.c)

# Настройка имени и версии программы
pico_set_program_name(v06 "v06")       # Имя программы
pico_set_program_version(v06 "0.1")    # Версия программы

# Генерация заголовочного файла из PIO программы
pico_generate_pio_header(v06 ${CMAKE_CURRENT_LIST_DIR}/blink.pio)

# Настройка стандартного ввода/вывода
pico_enable_stdio_uart(v06 0)  # Отключение UART
pico_enable_stdio_usb(v06 1)   # Отключение USB

# Подключение стандартной библиотеки Pico
target_link_libraries(v06 pico_stdlib)

# Настройка путей для include-файлов
target_include_directories(v06 PRIVATE ${CMAKE_CURRENT_LIST_DIR})

# Подключение дополнительных библиотек
target_link_libraries(v06 hardware_pio)  # Библиотека для работы с PIO

# Генерация дополнительных выходных файлов (.uf2, .hex и т.д.)
pico_add_extra_outputs(v06)

Лист. 3. CMakeLists.txt

  • Замечания:
  • Проект создан и компилируется в Visual Studio Code с установленным дополнением Raspberry Pico SDK.
  • State Machine - конечный автомат, выполняющий PIO программу.
  • wrap_target/.wrap - создание бесконечного цикла в PIO программе
  • pico_add_program() - загрузка PIO программы в память микроконтроллера
  • pico_generate_pio_header() - автоматическая генерация C-заголовка из PIO файла
  • pico_add_extra_outputs() - создание файлов прошивки (.uf2, .hex, .bin)
  • project(v06 C CXX ASM) Проект создан в папке PIO_Blink/v06 имя проекта v06 выбрано VS Code автоматически