Это директива компилятора (атрибут), который говорит компилятору Rust: "В этой программе нет стандартной функции main, используемой как точка входа".
Ключевые особенности:
1. Запрещает стандартную точку входа в программу (standard main() entry point. Обычно программы Rust начинают свое выполнение в fn main(). Директива #![no_main] говорит компилятору, что не нужно ожидать в коде или генерировать обычный код настройки функции main.
2. Обычно используется в специальном рабочем окружении:
• Встраиваемые системы (например приложения микроконтроллера ESP32) - там где вам нужен специальный пользовательский код запуска (custom startup code). • Ядра операционных систем - пользовательские процессы загрузки (custom boot processes). • "Bare-metal" программирование - приложение без операционной системы. • Пользовательская среда выполнения (custom runtime environment) - где какой-то другой код обрабатывает начальную инициализацию. Например, это может быть код отдельного модуля/субмодуля, см. Q003 []).
3. Обычно используется совместно другими атрибутами:
#![no_std]// используется модель приложения без стандартной библиотеки #![no_main]// не используется стандартная функция main
4. Требуется пользовательская точка входа (custom entry point): нужно определить вашу собственную функцию, обычно с такими атрибутами:
#[entry]// Часто применяются в embedded Rust (например RTIC, embassy) #[no_mangle]// Предотвращает изменение имен (name mangling) pubextern"C"fnmain()->!{ // Ваша точка входа в программу: loop{}
}
Когда используется или не используется #![no_main]:
Сценарий
Normal Rust
#![no_main] Rust
Desktop-приложение
fn main()
❌ Не используется
Web-сервер
fn main()
❌ Не используется
Микроконтроллер
❌
✅ Custom entry
OS kernel
❌
✅ Custom boot
Bootloader
❌
✅ Custom init
Важные замечания:
• Не для обычных приложений - используется только в специальных рабочих окружениях • Требует пользовательского кода (custom runtime) - вы отвечаете за инициализацию приложения • Обычно используется вместе с атрибутом #![no_std] - в большинстве случаев приложения микроконтроллеров не используют стандартную библиотеку • Зависит от специфики платформы - реальное имя точки входа или соглашения о её оформлении зависит от целевой системы (target)
В заключение: #![no_main] говорит "Сам разберусь с запуском программы, спасибо" - для случая, когда вам нужно полностью управлять началом выполнения вашей программы.
#![no_std]
Это фундаментальный атрибут, который говорит компилятору Rust: "Этот крейт не использует стандартную библиотеку Rust".
Ключевые последствия:
1. Недоступна стандартная библиотека.
• Нет std:: модулей (наподобие std::vec::Vec, std::string::String) • Нет кучи (если вы специально не вставите allocator) • Нет файловой системы, поддержки сети, или специфичного для OS функционала • Нет стандартных thread, mutex или других примитивов синхронизации
// 3. Опционально: элементы языка (редко используется) #[lang = "eh_personality"] extern"C"fneh_personality(){}
// 4. Точка входа (Entry point) #[no_mangle] pubextern"C"fn_start()->!{ // Код вашего приложения loop{}
}
В заключение: #![no_std] предназначен для случаев, когда вы работаете в средах, где предположение о наличии стандартной библиотеки (о наличии ОС, выделении памяти и т. д.) не соответствуют действительности. Это дает вам полный контроль над кодом, но требует, чтобы вы предоставили все, что обычно дает стандартная библиотека.
#![deny(clippy::mem_forget)]
Атрибут #![deny(clippy::mem_forget)] это линт-директива, которая инструктирует Clippy linter обрабатывать любое использование std::mem::forget как ошибку.
Примечание: linter это процесс обработки кода программы, который анализирует код на наличие потенциальных ошибок. Clippy linter [1] это набор линт-директив для перехвата общих промахов в программировании, что позволяет улучшить ваш код Rust.
#![deny(...)]: это стандартный атрибут Rust, который устанавливает уровень lint-ов, заключенных в скобки на "deny", т. е. запрет. Это означает, что если указанные линты сработают, то компиляция завершится с сообщением об ошибке.
clippy::mem_forget: это относится к проверке на наличие вызова std::mem::forget. Если такой вызов будет обнаружен, то будет выведено сообщение об ошибке reason.
Функция std::mem::forget не дает выбрасывать значение, т. е. реализация Drop (если она есть) для значения не будет вызвана. Хотя std::mem::forget может быть полезна в очень специфических сценариях (например для оптимизации производительности в условиях жестких ограничений или для взаимодействия с интерфейсом внешних функций), эта техника считается небезопасной, потому что потенциально может привести к утечке ресурсов при неаккуратном использовании.
Таким образом, #![deny(clippy::mem_forget)] дает следующие преимущества:
1. Предотвращаются утечки памяти. Библиотеки, где используется Drop, всегда будут правильно выполнять очистку переменных и буферов. 2. Повышается безопасность кода. Снижается риск связанных с памятью проблем для случаев, когда неправильно организовано управление ресурсами. 3. Поощряется явное управление ресурсами: способствует использованию более безопасных и более идиоматических моделей кода Rust для управления ресурсами.
Пример использования:
#![deny( clippy::mem_forget, reason = "Использование mem::forget обычно небезопасно с типами esp_hal, \ особенно с теми, что удерживают буферы в течение передачи данных." )]
use core::{i32, u16};
Эта строчка делает следующее:
• Импортирует примитивные типы i32 и u16 из библиотеки core в текущую область видимости кода • Делает эти типы доступными для использования без необходимости добавлять к ним префикс core::
Типы:
i32: 32-разрядное число со знаком (диапазон значений: -2147483648 .. 2147483647) u16: 16-разрядное число без знака (диапазон значений: 0 .. 65535)
Почему это используют:
1. Переменные такого типа входят в подмножество типов стандартной библиотеки, для которых не требуется специальная процедура выделения памяти или зависимость от OS. 2. Полезно для применения в проектах с моделью no_std (встраиваемые системы, разработка OS и т. п.), где требуется работа с ресурсами на низком уровне. 3. В обычном использовании Rust (с моделью std) эти типы доступны автоматически без префикса, так что такой импорт не требуется.
usecore::{i32,u16};
// После этой директивы импорта вы можете использовать: letx:i32=42; lety:u16=1000;
// .. вместо следующих конструкций: letx:core::i32=42; lety:core::u16=1000;
use defmt::info;
Эта директива импортирует макрос вывода в лог из крейта defmt, специально разработанного для встраиваемых приложений (микроконтроллеров). Директива делает следующее:
• Импортирует макрос info! из крейта defmt в текущую область видимости кода • Позволяет использовать info!(...) вместо defmt::info!(...)
Ключевые характеристики defmt:
• Очень эффективный, структурированный фреймворк лога для встраиваемых устройств • Намного меньше расходует память чем традиционный println! крейта log • Поддерживает двоичный формат, который требует декодирования на стороне хоста (использование defmt-print) • Устраняет во время компиляции не используемые строки формата
Пример использования:
usedefmt::info;
fnmain(){ lettemperature=25.5; letstatus="ok";
info!("Температура: {}°C, Статус: {}",temperature,status); // На хосте: defmt-print target/device.log // Вывод: Температура: 25.5°C, Статус: ok
}
Сравнение с другими вариантами вывода в лог::
• println!: стандартный тяжелый вывод, требует полной поддержки модели std • log::info!: более структурированное API, но все еще очень требовательное к ресурсам • defmt::info!: оптимизированный лог для встраиваемых систем, с минимальными тратами runtime-ресурсов
Типовой процесс использования:
1. Устройство выводит сообщения вызовами defmt::info! 2. Логи сохраняются в двоичном формате 3. Компьютер хоста декодирует формат лога с помощью утилиты defmt-print
Импорт defmt::info особенно важен для эффективной отладки и мониторинга систем с ограниченными ресурсами, где традиционный вывод в лог текста оказывается слишком дорогим решением.
use embassy_executor::{task, Spawner};
Импортирует ключевые компоненты из Embassy executor для асинхронного (на основе потоков) программирования. Импортируется следующее:
1. Макрос атрибута task
#[task] asyncfnmy_task(){ // Код асинхронной задачи
}
• Декларируется асинхронная функция как Embassy task • Задачи (tasks) в Embassy представляют фундаментальные единицы выполнения
2. Тип Spawner
letspawner:Spawner=/* извлекается из executor */;
• Используется для запуска задач (tasks) и управления ими runtime • Позволяет динамически порождать асинхронные задачи
Пример, как это работает:
useembassy_executor::{task,Spawner};
// Декларация функции задачи #[task] asyncfnblink_led(){ // Код, мигающий светодиодом
}
// Другая задача, которая использует spawner #[task] asyncfnmain_task(spawner:Spawner){ // Порождение задачи blink_led spawner.spawn(blink_led()).unwrap();
// Другая асинхронная работа ...
}
Ключевые концепции Embassy:
• Executor: облегченная среда асинхронного runtime для встраиваемых систем • Tasks: асинхронные функции, которые работают одновременно и конкурентно, разделяя общее процессорное время • Spawner: механизм для динамического запуска задач • Без выделения памяти в куче: разработано для bare-metal рабочей среды
Типовая схема применения:
1. Определяются задачи как функции с атрибутом #[task] 2. Получение Spawner из настройки executor 3. Использование spawner.spawn() для запуска задач 4. Embassy обслуживает планировку и выполнение кода задач (scheduling)
Импорт embassy_executor::{task, Spawner} это фундаментальный метод построения асинхронных встраиваемых приложений в среде Embassy, предоставляющий эффективное конкурентное выполнение функций без OS threads, и чрезмерного расхода ресурсов runtime.
use embassy_time::{Delay, Duration, Ticker, Timer};
Импортирует утилиты для обработки времени из Embassy embedded async framework.
Что делает каждый из импортируемых компонентов:
1. Duration
letdelay_time=Duration::from_millis(500);
• Представляет интервалы времени (миллисекунды, секунды, и т. д.) • Используется для указания интервалов времени
• Периодичный таймер, срабатывающий с регулярными интервалами • Полезен для периодического выполнения задач (опроса сенсоров, мигание светодиодами и т. д.)
• Async-aware: все методы имеют функцию .await • Нет busy-waiting: эффективно уступаются ресурсы процессора для других задач • Hardware-backed: использует аппаратные таймеры при их наличии • Zero-cost: минимальные накладные расходы ресурсов (runtime overhead)
Импорт embassy_time::{Delay, Duration, Ticker, Timer} особенно важен для организации интервалов времени, задержек и периодических операций для программирования асинхронных приложений с использованием Embassy.
use esp_hal::analog::adc::{Adc, AdcCalBasic, AdcCalScheme, AdcChannel, AdcConfig, Attenuation};
Импортирует компоненты ADC (Analog-to-Digital Converter) из библиотеки ESP-HAL (Hardware Abstraction Layer) микроконтроллеров ESP.
Что делает каждый компонент:
1. Adc - представляет основное периферийное устройство АЦП
letmutadc=Adc::new(peripherals.ADC1,config);
• Обеспечивает первичный интерфейс к контроллеру ADC • Обслуживает операции и преобразования ADC
2. AdcConfig - конфигурация ADC
letconfig=AdcConfig::new();
• Настраивает параметры ADC (разрешающая способность, тактирование и т. п.) • Используется для конфигурирования периферийного устройства ADC
3. AdcChannel - входной канал ADC
letmutpin=gpio_pin.into_analog();
• Представляет определенный вход ADC (вывод корпуса/канал) • Используемые выводы должны быть сконфигурированы как аналоговые входы
4. Attenuation - масштабирование чувствительности по входу
• Учитывает специфику микроконтроллеров Espressif: оптимизация для ESP32, ESP32-C3, ESP32-S3 и т. п. • Предоставляет абстракцию от аппаратуры: обеспечивает универсальный интерфейс программирования для различных вариантов микроконтроллеров ESP • Поддержка калибровки: встроенная компенсация нелинейностей ADC • Async-ready: разработано с учетом работы в среде асинхронного выполнения наподобие Embassy
Импорт esp_hal::analog::adc::{Adc, AdcCalBasic, AdcCalScheme, AdcChannel, AdcConfig, Attenuation}; важен для чтения аналоговых датчиков (потенциометров, термопар, фотодатчиков, и т. д.) на микроконтроллерах ESP.
use esp_hal::gpio::{AnalogPin, Output, OutputConfig, Level};
Импортирует компоненты GPIO (General Purpose Input/Output) из ESP-HAL для микроконтроллеров ESP.
Что делает каждый компонент:
1. Output - тип вывода, настроенного на выход
letmutled_pin=gpio_pin.into_output();
• Конфигурирует ножку порта GPIO как цифровой выход • Может программно управлять выходным цифровым сигналом (high или low, т. е. лог. 1 или лог. 0)
2. AnalogPin - тип вывода, настроенного как аналоговый вход
letmutadc_pin=gpio_pin.into_analog();
• Конфигурирует ножку порта GPIO как аналоговый вход • Применяется для входа ADC (Analog-to-Digital Converter)
• Нагрузочная способность (возможности по выходному току) • Открытый сток (open-drain) или двухтактный ключ (push-pull) • Начальный выходной логический уровень
• Учитывается специфика ESP: оптимизировано для микроконтроллеров серии ESP32 • Type-safe: предотвращает неправильную конфигурацию (аналоговый вывод нельзя использовать как цифровой выход) • Гибкое конфигурирование: различные нагрузочные способности и режимы • Низкоуровневое управление: прямой доступ к аппаратуре без гарантий безопасности
Импорт esp_hal::gpio важен для управления цифровыми выходами и входами (светодиоды, моторы, реле, кнопки) и конфигурирования аналоговых входов (сенсоров) на микроконтроллерах ESP.
use esp_hal::{peripherals, Async};
Импортирует два фундаментальных компонента из ESP-HAL, позволяющих работать с микроконтроллерами ESP в асинхронных контекстах.
1. peripherals - доступ к аппаратным периферийным устройствам
• Предоставляет безопасный доступ ко всей аппаратной периферии (GPIO, ADC, SPI, I2C, UART и т. д.) • Использует систему владения (Rust ownership system) для предотвращения конфликтов конкурентного доступа • Возвратит структуру Peripherals, содержащую все доступные аппаратные интерфейсы
• Тип нулевого размера, который помечает периферийное устройство как используемого для асинхронных операций. • Преобразует функции периферии с блокировкой в их версии, совместимые с асинхронным контекстом • Разрешает функционал .await на операциях с аппаратурой
// Преобразование вывода в async-режим letmutled=pins.gpio2.into_output().into_async();
// Теперь мы можем использовать асинхронно отслеживаемые // интервалы задержек (с освобождением runtime-контекста // для других задач): loop{ led.set_high(); Timer::after_millis(500).await; led.set_low(); Timer::after_millis(500).await; }
}
Типовой шаблон настройки:
useesp_hal::{peripherals,Async};
#[main] asyncfnmain(){ // Взятие во владение (ownership) всех периферийных устройств letperipherals=peripherals::Peripherals::take().unwrap();
• Обеспечивает безопасный доступ к периферийным устройствам: предотвращает аппаратные конфликты во время компиляции • Интеграция асинхронного функционала: позволяет выполнять операции с аппаратурой без блокировки выполнения • Учитывает специфику ESP: оптимизировано для семейства микроконтроллеров ESP • Абстракция с нулевыми затратами: минимальные накладные расходы (runtime overhead)
Импорт esp_hal::{peripherals, Async} важен для любых асинхронных встраиваемых приложений на чипах ESP, что предоставляет защищенный доступ как к аппаратуре, так и к async-функционалу.
use esp_hal::{clock::CpuClock, gpio::Input, gpio::InputConfig};
Импортирует компоненты для конфигурации тактирования и обработки входов GPIO из библиотеки ESP-HAL.
Что делает каждый компонент:
1. clock::CpuClock - конфигурация частоты тактирования CPU
letclocks=ClockControl::configure( system.clock_control, CpuClock::Clock240MHz,// установит CPU на частоту 240 МГц // другие настройки тактирования...
).freeze();
• Управляет частотой тактов CPU микроконтроллеров ESP. Доступны опции:
// Конфигурирует CPU на максимальную скорость (240 МГц) letclocks=ClockControl::configure( system.clock_control, CpuClock::Clock240MHz, // Другие домены тактирования...
).freeze();
Пример опроса кнопки с замыканием на землю и использованием pull-up:
useesp_hal::gpio::{Input,InputConfig};
#[task] asyncfnread_button(mutbutton_pin:InputPin){ letconfig=InputConfig::default() .pull_up(true);// разрешение внутреннего резистора pull-up
// Конфигурация входа с внутренним резистором pull-down и фильтрацией letconfig=InputConfig::default() .pull_up(false) .pull_down(true)// разрешение pull-down резистора .glitch_filter(true);// разрешение фильтрации шума
• Управление производительностью: CpuClock позволяет выбрать баланс между скоростью и потреблением тока • Гибкая конфигурация входа: управление встроенными резисторами подтяжки (pull-up, pull-down), фильтрация помех, поддержка прерываний • Оптимизация поддержки аппаратуры: реализация, учитывающая специфику микроконтроллеров ESP • Type-safe: предотвращает неправильную конфигурацию во время компиляции
Импорт esp_hal::{clock::CpuClock, gpio::Input, gpio::InputConfig} важен для настройки частоты тактирования микроконтроллера ESP и чтения цифровых сигналов наподобие кнопок, переключателей или цифровых датчиков.
use esp_hal::timer::systimer::SystemTimer;
Импортирует периферийное устройство System Timer (SYSTIMER) из библиотеки ESP-HAL. SystemTimer это периферийное устройство выделенного системного таймера, имеющегося на кристалле микроконтроллеров ESP. Этот таймер предоставляет следующие ключевые возможности:
• 64-битный счетчик с высокой разрешающей способностью • Не зависит от тактовых сигналов основного CPU • Возможность работы низкого энергопотребления • Несколько alarm-компараторов для генерации прерываний
// Часто используется внутри кода async executors наподобие Embassy // для планировщика задач (task scheduling) и управления временем
Преимущества в сравнении с другими таймерами:
• Выше точность, чем у обычных (general-purpose) таймеров • Продолжает работать в режимах пониженного энергопотребления (sleep modes) - полезная фича для приложений, критичных для экономии потребляемого тока • Выделенная аппаратура, которая не используется для других функций
// Использование для точного отсчета времени lettimestamp=systimer.now();
Импорт esp_hal::timer::systimer::SystemTimer необходим для отсчета времени высокой точности, анализа производительности выполняемого кода, низкоуровневого управления временем на микроконтроллерах ESP, особенно когда приложение критично к точности отсчета времени.
use {esp_backtrace as _, esp_println as _};
На языке Rust это переименование импорта с шаблоном подстановки, который служит определенным целям в разработке встраиваемых приложений.
1. esp_backtrace as _
• Импортирует и инициализирует функционал обратной трассировки (backtrace) для чипов ESP • as _ означает: "импортировать это, но не вводить в область видимости" • Автоматически установит обработчик паники (panic handler) для отображения стека вызовов (backtraces) в случае паники
2. esp_println as _
• Импортирует и инициализирует функционал println с учетом специфики ESP • as _ означает: "импортировать это, но не вводить в область видимости" • Настраивает инфраструктуру лога для устройств ESP
Вместо использования:
useesp_backtrace; useesp_println;
.. вы используете:
use{esp_backtraceas_,esp_printlnas_};
Это дает следующие выгоды:
1. Инициализация без загрязнения кода: крейты настраивают необходимую инфраструктуру без замусоривания вашего namespace 2. Side-effect import: импорт сам запустит код инициализации 3. Чистая область видимости: нет неиспользуемых имен в вашем коде
Включается обратная трассировка стека вызовов при панике:
use{esp_backtraceas_};
fnmain(){ letx=None.unwrap();// Это вызовет panic // Но вы получите чистый backtrace вместо молчаливого падения
}
Можно использовать макрос println!:
use{esp_printlnas_};
fnmain(){ println!("Hello ESP!");// работает через последовательный вывод println!("Temperature: {:.1}°C",25.5);
}
Шаблон use {esp_backtrace as _, esp_println as _} важен для отладки (backtraces) и вывода в лог сообщений (println) проектов ESP, предоставляя важнейшие инструменты для поддержки чистоты и ясности кода.
use heapless::Vec;
Импортирует выделяемый в стеке вектор фиксированного размера из крейта heapless, что важно для модели no_std и разработки встраиваемых приложений.
heapless::Vec это версия Vec со следующими особенностями:
• Не использует выделение памяти из кучи (выделяемая память полностью находится в стеке) • Вектор занимает фиксированный объем памяти (fixed capacity), определяемый во время компиляции • Нет возможности динамического изменения объема выделения памяти - емкость вектора фиксирована • Совместимость с моделью no_std - работает во встраиваемых системах
Ключевые отличия от std::Vec:
Фича
std::Vec
heapless::Vec
Allocation (где выделяется память)
Heap (куча)
Stack (стек)
Capacity (выделяемый размер)
Dynamic
Fixed
Resizable (возможность перераспределения памяти)
✅ да
❌ нет
Модель no_std
❌ нет
✅ да
Паника при переполнении
❌ нет
конфигурируется
Синтаксис и использование:
useheapless::Vec;
// Создание массива Vec фиксированной емкости из 8 элементов letmutvec:Vec<i32,8>=Vec::new();
// Проталкивание в массив элементов (до своей емкости)
vec.push(1).unwrap();// возвратит Result< (), ()>
vec.push(2).unwrap();
vec.push(3).unwrap();
// Доступ к элементам массива println!("First: {}",vec[0]);
// Итерация по элементам массива foritemin&vec{ println!("Item: {}",item);
}
Общепринятые шаблоны использования:
1. Буфер фиксированного размера
useheapless::Vec;
// Буфер для 16 результатов чтения датчика letmutreadings:Vec<f32,16>=Vec::new();
for_in0..16{ letreading=read_sensor(); readings.push(reading).unwrap();// Произойдет паника, если буфер заполнится
}
// Предварительно выделенная память staticmutBUFFER:MaybeUninit<Vec<u8,1024>>=MaybeUninit::uninit();
Почему используют heapless::Vec:
• Не требуется специальные функции выделения памяти (no allocator) - хорошо работает в bare-metal окружениях • Предсказуемое использование памяти - нет фрагментации кучи • Безопасность, проверяемая в момент компиляции - емкость известна в момент компиляции • Минимальные затраты процессорного времени - нет задержек на выделение памяти
Недостатки (?): необходимо заранее указывать емкость выделяемой памяти. Например:
// ❌ неправильно - не указана емкость letmutv=Vec::new();// ошибка: пропущена аннотация типа
Импорт heapless::Vec особенно важен для разработки встраиваемых приложений, когда выделение памяти в куче недоступно или нежелательно, однако все еще необходимо поведение, похожее на динамическое выделение памяти.
use embassy_sync::channel::Channel;
Импортирует тип Channel из библиотеки embassy-sync, что предоставляет примитивы синхронизации для async/await программирования встраиваемых систем.
• embassy_sync: субмодуль фреймворка Embassy для embedded async Rust • channel::Channel: thread-safe сообщение, передаваемое по каналу. Разработано для кода синхронизации задач, работающих по принципу async/await.
// Обычно используется примерно так: useembassy_sync::channel::Channel;
// Создается канал, который может содержать до 4 сообщений типа i32 staticCHANNEL:Channel<embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex, \ i32,4>=Channel::new();
Назначение embassy_sync::channel::Channel:
• Коммуникация между задачами: пересылка данных между асинхронно выполняемыми задачами (async tasks) • Thread-safe: безопасное использование между различными контекстами выполнения кода • Async-aware: разработано для интеграции в функционал async/await • Не используется выделение памяти в куче: подходит для встраиваемых систем
Общий пример использования:
// Задача, передающая данные asyncfnproducer(ch:&Channel<implMutex,i32,4>){ ch.send(42).await;
}
// Задача, принимающая данные asyncfnconsumer(ch:&Channel<implMutex,i32,4>){ letvalue=ch.receive().await; println!("Принято: {}",value);
}
Фреймворк Embassy популярен для разработки встраиваемых систем, где нужны эффективные примитивы async/await, не нуждающиеся в стандартной библиотеке или выделении памяти из кучи.
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
Импортирует примитив синхронизации из фреймворка Embassy, разработанного для встраиваемых систем.
• embassy_sync: модуль синхронизации Embassy • blocking_mutex: мьютекс, который выполняет блокировку место использования async/await • raw: низкоуровневый интерфейс мьютекса • CriticalSectionRawMutex: реализация мьютекса, которая использует для синхронизации критические секции кода
// Это "raw" мьютекс, который для защиты использует критические секции pubstructCriticalSectionRawMutex;
Как это работает:
• Critical Sections: секция кода, в которой запрещены прерывания для обеспечения атомарности операций • Blocking: задачи будут блокироваться (ждать) вместо того, чтобы уступать контекст в состоянии locked • Zero-cost: нет runtime затрат, когда потерян контекст выполнения • SMP-safe: безопасное использование на многоядерных системах (если это поддерживается)
• Код, критичный ко времени выполнения: там, где необходима управляемая латентность прерываний • Короткие критические секции: для защиты очень быстрых операций • Многоядерные системы: когда вам нужен безопасный функционал SMP (symmetric multiprocessing) • Предпочтительна блокировка: когда вам нужны задачи, которые выполняют блокирование (block) вместо уступки процессорного времени (yield)
Альтернативы в Embassy:
• NoopRawMutex - без синхронизации, одно ядро (single-core), одна задача (single-task) • ThreadModeRawMutex - для single-core, multi-task (non-SMP) • Другие raw мьютексы для различных аппаратных возможностей
Импорт embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex представляет часть гибкого функционала синхронизации фреймворка Embassy, который позволяет вам выбрать правильную стратегию блокировки в соответствии с требованиями встраиваемого приложения.
esp_bootloader_esp_idf::esp_app_desc!();
Это процедурный макрос, используемый в приложениях ESP-IDF (Espressif's IoT Development Framework). Он генерирует и инициализирует структуру описания приложения микроконтроллеров ESP (ESP32/ESP8266 и т. п). Это специальная структура данных, содержащая метаданные вашего firmware приложения:
• Версия приложения (major.minor.patch) • Имя проекта • Дата и время сборки (date, time) • Версия IDF (ESP-IDF framework version) • App ELF SHA256 (криптографический хэш вашего приложения)
Типовое использование:
esp_app_desc!();
Назначение:
• Идентификация загрузчика (bootloader) - помогает загрузчику проверять и обслуживать ваше приложение • Обновления OTA - предоставляет информацию версии для обновления по радио (over-the-air, OTA: обычно это Wi-Fi) • Отладка - помогает идентифицировать версию работающего firmware • Безопасность - содержит хэш, позволяющий проверить целостность приложения
Этот макрос Rust эквивалентен макросу языка C:
ESP_APP_DESC_DEFAULT()
Генерируемая структура размещается в специальной секции .rodata_desc бинарного кода, размещение которой известно загрузчику ESP32.
Этот макрос важен для правильной работы приложения ESP32 и он обычно размещается в главном файле вашего приложения Rust ESP-IDF (обычно это файл main.rs).