#include #include "pico/stdlib.h" #include "FreeRTOS.h" #include "task.h" #include "pico/multicore.h" // Required for interacting with the second core / Обязательно для работы со вторым ядром #include "hardware/sync.h" // For safe data increment between cores / Для безопасного инкремента данных между ядрами /** * @brief Global counter incremented by Core 1 via hardware timer. * volatile ensures the compiler doesn't cache the value in registers. * @brief Глобальный счетчик, который увеличивается Ядром 1 через аппаратный таймер. * volatile гарантирует, что компилятор не будет кэшировать значение в регистрах. */ volatile uint32_t core1_counter = 0; /** * @brief Pointer to an individual interrupt pool for Core 1 * @brief Указатель на индивидуальный пул прерываний для Ядра 1 */ alarm_pool_t *core1_alarm_pool = nullptr; /** * @brief Maximum number of simultaneous timers in Core 1 pool * @brief Максимальное количество одновременных таймеров в пуле Ядра 1 */ const uint CORE1_MAX_TIMERS = 4; /** * @brief Structure for storing Core 1 hardware timer information * @brief Структура для хранения информации об аппаратном таймере Ядра 1 */ struct repeating_timer timer_core1; /** * @brief Pin of the built-in LED on Raspberry Pi Pico 2 * @brief Пин встроенного светодиода на Raspberry Pi Pico 2 */ const uint LED_PIN = 25; // ====================================================================================== // User-defined class for LED control // Пользовательский класс для управления светодиодом // ====================================================================================== class Pico2LED { private: uint m_pin; // GPIO pin number / Номер GPIO пина public: /** * @brief Constructor initializes the GPIO pin * @brief Конструктор инициализирует GPIO пин * @param pin The GPIO pin to use / Используемый GPIO пин */ Pico2LED(uint pin) : m_pin(pin) { gpio_init(m_pin); gpio_set_dir(m_pin, GPIO_OUT); } /** * @brief Turns the LED on * @brief Включает светодиод */ void turnOn() { gpio_put(m_pin, 1); } /** * @brief Turns the LED off * @brief Выключает светодиод */ void turnOff() { gpio_put(m_pin, 0); } }; /** * @brief Функция обратного вызова для аппаратного таймера Ядра 1 * Вызывается автоматически каждые 1000 мс прерыванием таймера НА ЯДРЕ 1. */ bool core1_timer_callback(struct repeating_timer *t) { // Безопасно увеличиваем счетчик в многоядерной среде core1_counter++; return true; // Возвращаем true, чтобы таймер продолжил циклически работать } /** * @brief Главная функция, выполняемая Ядром 1 */ void core1_entry() { // 1. Создаем выделенный пул аппаратных прерываний (таймеров) специально для Ядра 1. // Функция привязывает этот пул к аппаратному аппаратному контроллеру прерываний Core 1. core1_alarm_pool = alarm_pool_create_with_unused_hardware_alarm(CORE1_MAX_TIMERS); if (core1_alarm_pool != nullptr) { // 2. Добавляем циклический таймер именно в НАШ созданный пул Ядра 1. // Теперь прерывание гарантированно будет обрабатываться только на Core 1. alarm_pool_add_repeating_timer_ms( core1_alarm_pool, // Наш кастомный пул 1000, // Период (1000 мс) core1_timer_callback,// Функция обратного вызова NULL, // Пользовательские параметры &timer_core1 // Структура таймера ); } else { // Ошибка создания пула (например, если закончились аппаратные таймеры) printf("Ошибка: Не удалось создать alarm pool на Ядре 1!\n"); } // Вечный цикл (пул) Ядра 1 while (true) { __wfi(); // Спим до тех пор, пока не сработает прерывание таймера на Core 1 } } // ====================================================================================== // STANDARD FREERTOS TASKS (Simple functions) // ОБЫЧНЫЕ ЗАДАЧИ FREERTOS (Простые функции) // ====================================================================================== /** * @brief Task 1: Blinks the LED using the Pico2LED class locally * @brief Задача 1: Мигает светодиодом, используя класс Pico2LED локально * @param pvParameters Task parameters (unused) / Параметры задачи (не используются) */ void led_blink_task(void *pvParameters) { // Create an object of the class directly inside the task // Создаем объект класса прямо внутри задачи Pico2LED my_led(25); // 25 - built-in LED on Pico 2 / 25 - встроенный LED на Pico 2 while (true) { my_led.turnOn(); vTaskDelay(pdMS_TO_TICKS(200)); // Delay for 200ms / Задержка 200мс my_led.turnOff(); vTaskDelay(pdMS_TO_TICKS(200)); // Delay for 200ms / Задержка 200мс } } /** * @brief Task 2: Prints a counter to the USB console * @brief Задача 2: Выводит счетчик в USB консоль * @param pvParameters Task parameters (unused) / Параметры задачи (не используются) */ void usb_print_task(void *pvParameters) { int core0_counter = 0; while (true) { // Выводим в консоль счетчик FreeRTOS (Core 0) и аппаратный счетчик из Core 1 printf("Core 0 (FreeRTOS) tics: %d | Core 1 (Timer bare-metal) counter: %lu\n", core0_counter++, core1_counter); vTaskDelay(pdMS_TO_TICKS(1000)); } } // ====================================================================================== // MAIN FUNCTION // Главная функция // ====================================================================================== int main() { // Initialize all standard I/O (USB/UART) // Инициализация всех стандартных средств ввода-вывода (USB/UART) stdio_init_all(); // Launch Core 1 BEFORE starting the FreeRTOS scheduler. // Pass the core1_entry function as the entry point. // Запускаем Ядро 1 ДО старта планировщика FreeRTOS. // Передаем ему функцию входа core1_entry. multicore_launch_core1(core1_entry); /** * Create standard tasks by passing function pointers * Создаем обычные задачи, передавая указатели на функции * * xTaskCreate parameters: * 1. Task function / Функция задачи * 2. Task name / Имя задачи * 3. Stack size / Размер стека * 4. Parameters / Параметры * 5. Priority / Приоритет * 6. Task handle / Хэндл задачи */ xTaskCreate(led_blink_task, "LED_Task", 256, NULL, 1, NULL); xTaskCreate(usb_print_task, "Print_Task", 256, NULL, 1, NULL); // Start the FreeRTOS scheduler // Запуск планировщика FreeRTOS vTaskStartScheduler(); // Should never reach here // Сюда выполнение не должно дойти while (true) {} }