diff options
Diffstat (limited to 'book/ru/src/by-example')
| -rw-r--r-- | book/ru/src/by-example/app.md | 159 | ||||
| -rw-r--r-- | book/ru/src/by-example/new.md | 45 | ||||
| -rw-r--r-- | book/ru/src/by-example/resources.md | 177 | ||||
| -rw-r--r-- | book/ru/src/by-example/singletons.md | 26 | ||||
| -rw-r--r-- | book/ru/src/by-example/tasks.md | 113 | ||||
| -rw-r--r-- | book/ru/src/by-example/timer-queue.md | 122 | ||||
| -rw-r--r-- | book/ru/src/by-example/tips.md | 142 | ||||
| -rw-r--r-- | book/ru/src/by-example/types-send-sync.md | 50 |
8 files changed, 531 insertions, 303 deletions
diff --git a/book/ru/src/by-example/app.md b/book/ru/src/by-example/app.md index 04dd5b2..628819a 100644 --- a/book/ru/src/by-example/app.md +++ b/book/ru/src/by-example/app.md @@ -1,77 +1,84 @@ -# The `app` attribute +# Атрибут `app` -Это наименьшая возможная программа на RTIC: +Это простейшая из возможных программ на RTIC: ``` rust {{#include ../../../../examples/smallest.rs}} ``` Все программы на RTIC используют атрибут [`app`] (`#[app(..)]`). Этот атрибут -нужно применять к `const`-элементам, содержащим элементы. Атрибут `app` имеет -обязательный аргумент `device`, в качестве значения которому передается *путь*. -Этот путь должен указывать на библиотеку *устройства*, сгенерированную с помощью -[`svd2rust`] **v0.14.x**. Атрибут `app` развернется в удобную точку входа, -поэтому нет необходимости использовать атрибут [`cortex_m_rt::entry`]. +должен применяться к элементу `mod`. Атрибут `app` имеет обязательный аргумент `device`, +который принимает *путь* как значение. Это должен быть полный путь, указывающий на +*крейт доступа к периферии* (PAC), сгенерированный с помощью [`svd2rust`] версии **v0.14.x** +или новее. Более подробно в разделе [Создание нового проекта](./new.md). + +Атрибут `app` будет раскрыт в подходящую точку входа программы, поэтому +атрибут [`cortex_m_rt::entry`] не нужен. [`app`]: ../../../api/cortex_m_rtic_macros/attr.app.html [`svd2rust`]: https://crates.io/crates/svd2rust [`cortex_m_rt::entry`]: ../../../api/cortex_m_rt_macros/attr.entry.html -> **ОТСТУПЛЕНИЕ**: Некоторые из вас удивятся, почему мы используем ключевое слово `const` как -> модуль, а не правильное `mod`. Причина в том, что использование атрибутов на -> модулях требует feature gate, который требует ночную сборку. Чтобы заставить -> RTIC работать на стабильной сборке, мы используем вместо него слово `const`. -> Когда большая часть макросов 1.2 стабилизируются, мы прейдем от `const` к `mod` и в конце концов в атрибуту уровне приложения (`#![app]`). - ## `init` -Внутри псевдо-модуля атрибут `app` ожидает найти функцию инициализации, обозначенную -атрибутом `init`. Эта функция должна иметь сигнатуру `[unsafe] fn()`. +Внутри модуля `app` атрибут ожидает найти функцию инициализации, помеченную +атрибутом `init`. Эта функция должна иметь сигнатуру +`fn(init::Context) [-> init::LateResources]` (возвращаемый тип нужен не всегда). -Эта функция инициализации будет первой частью запускаемого приложения. -Функция `init` запустится *с отключенными прерываниями* и будет иметь эксклюзивный -доступ к периферии Cortex-M и специфичной для устройства периферии через переменные -`core` and `device`, которые внедряются в область видимости `init` атрибутом `app`. -Не вся периферия Cortex-M доступна в `core`, потому что рантайм RTIC принимает владение -частью из неё -- более подробно см. структуру [`rtic::Peripherals`]. +Эта функция инициализации будет первой частью программы, выполняемой при запуске. +Функция `init` будет запущена *с отключенными прерываниями* и будет иметь эксклюзивный доступ +к Cortex-M, в котором токен `bare_metal::CriticalSection` доступен как `cs`. +Опционально, устройство-специфичные периферия доступна через поля `core` и `device` структуры +`init::Context`. -Переменные `static mut`, определённые в начале `init` будут преобразованы -в ссылки `&'static mut` с безопасным доступом. +`static mut` переменные, определенные в начале `init` будут преобразованы в +`&'static mut` ссылки, безопасные для доступа. Обратите внимание, данная возможность может +быть удалена в следующем релизе, см. `task_local` ресурсы. [`rtic::Peripherals`]: ../../api/rtic/struct.Peripherals.html -Пример ниже показывает типы переменных `core` и `device` и -демонстрирует безопасный доступ к переменной `static mut`. +Пример ниже показывает типы полей `core`, `device` и `cs`, и демонстрирует +безопасный доступ к `static mut` переменной. Поле `device` доступно только +когда аргумент `peripherals` установлен в `true` (по умолчанию). +В редких случаях, когда вы захотите создать приложение с минимальным потреблением ресурсов, +можно явно установить `peripherals` в `false`. ``` rust {{#include ../../../../examples/init.rs}} ``` -Запуск примера напечатает `init` в консоли и завершит процесс QEMU. +Запуск примера напечатате `init` в консоли, а затем завершит процесс QEMU. ``` console $ cargo run --example init -{{#include ../../../../ci/expected/init.run}}``` +{{#include ../../../../ci/expected/init.run}} +``` ## `idle` -Функция, помеченная атрибутом `idle` может присутствовать в псевдо-модуле -опционально. Эта функция используется как специальная *задача ожидания* и должна иметь -сигнатуру `[unsafe] fn() - > !`. +Функцию, помеченную атрибутом `idle` может опционально добавить в модуль. +Эта функция используется как специальная *задача ожидания* и должна иметь сигнатуру +`fn(idle::Context) - > !`. -Когда она присутствует, рантайм запустит задачу `idle` после `init`. В отличие от -`init`, `idle` запустится *с включенными прерываниями* и не может завершиться, -поэтому будет работать бесконечно. +Если она присутствует, задача `idle` будет запущена после `init`. В отличие от +`init`, `idle` будет запущена *с включенными прерываниями* и она не может вернуть результат, +а значит должна работать вечно. -Когда функция `idle` не определена, рантайм устанавливает бит [SLEEPONEXIT], после чего -отправляет микроконтроллер в состояние сна после выполнения `init`. +Если функция `idle` не определена, среда вполнения устанавливает бит [SLEEPONEXIT], а затем +отправляет микроконтроллер в сон после запуска `init`. [SLEEPONEXIT]: https://developer.arm.com/docs/100737/0100/power-management/sleep-mode/sleep-on-exit-bit -Как и в `init`, переменные `static mut`будут преобразованы в ссылки `&'static mut` -с безопасным доступом. +Как и в `init`, `static mut` переменные будут трансформированы в `&'static mut` ссылки, +безопасные для доступа. Обратите внимание, данная возможность может +быть удалена в следующем релизе, см. `task_local` ресурсы. -В примере ниже показан запуск `idle` после `init`. +Пример ниже показывает, что `idle` запускается после `init`. + +**Примечание:** Цикл `loop {}` в функци ожидания не может быть пустым, так как это сломает +микроконтроллер, из-за того, что LLVM компилирует пустые циклы в инструкцию `UDF` в release mode. +Чтобы избежать неопределенного поведения, цикл должен включать "side-effect" +путем вставки ассемблерной инструкции (например, `WFI`) или ключевого слова `continue`. ``` rust {{#include ../../../../examples/idle.rs}} @@ -79,23 +86,75 @@ $ cargo run --example init ``` console $ cargo run --example idle -{{#include ../../../../ci/expected/idle.run}}``` +{{#include ../../../../ci/expected/idle.run}} +``` -## `interrupt` / `exception` +## Аппаратные задачи -Как Вы бы сделали с помощью библиотеки `cortex-m-rt`, Вы можете использовать атрибуты -`interrupt` и `exception` внутри псевдо-модуля `app`, чтобы определить обработчики -прерываний и исключений. В RTIC, мы называем обработчики прерываний и исключений -*аппаратными* задачами. +Чтобы объявить обработчик прерывания, фреймворк предоставляет атрибут `#[task]`, +который можно применять к функциям. Этот атрибут берет аргумент `binds`, чье значение - +это имя прерывания, которому будет назначен обработчик; +функция, декорированная этим атрибутом становится обработчиком прерывания. +В фреймворке такие типы задач именуются *аппаратными*, потому что они начинают +выполняться в ответ на аппаратное событие. + +Пример ниже демонстрирует использование атрибута `#[task]`, чтобы объявить +обработчик прерывания. Как и в случае с `#[init]` и `#[idle]` локальные `static +mut` переменные безопасны для использования с аппаратной задачей. ``` rust -{{#include ../../../../examples/interrupt.rs}} +{{#include ../../../../examples/hardware.rs}} ``` ``` console -$ cargo run --example interrupt -{{#include ../../../../ci/expected/interrupt.run}}``` +$ cargo run --example hardware +{{#include ../../../../ci/expected/hardware.run}} +``` + +До сих пор все программы на RTIC, которые мы видели, не отличались от программ, +которые можно написать, используя лишь крейт `cortex-m-rt`. С этого момента мы +начинаем представлять возможности, уникальные для RTIC. + +## Приоритеты + +Статический приоритет каждого обработчика можно оределить в атрибуте `task`, используя +аргумент `priority`. Задачи могут иметь приоритет в диапазоне `1..=(1 << NVIC_PRIO_BITS)`, +где `NVIC_PRIO_BITS` - это константа, определенная в крейте `устройства`. +Когда аргумент `priority` не указан, предполагается, что приоритет равен `1`. +Задача `idle` имеет ненастраиваемый приоритет `0`, наименьший из возможных. + +> Более высокое значение означает более высокий приоритет в RTIC, что противоположно тому, +> что указано в периферии NVIC Cortex-M. +> Точнее, это значит, что число `10` обозначает приоритет **выше**, чем число `9`. + +Когда несколько задач готовы к запуску, задача с самым большим статическим +приоритетом будет запущена первой. Приоритезацию задач можно рассматривать по +такому сценарию: сигнал прерывания приходит во время выполнения задачи с низким приоритетом; +сигнал переключает задачу с высоким приоритетом в режим ожидания. +Разница в приоритетах приводи к тому, что задача с высоким приоритетом вытесняет задачу с низким: +выполнение задачи с низким приоритетом замораживается и задача с высоким приоритетом выполняется, +пока не будет завершена. Как только задача с высоким приоритетом будет остановлена, +продолжится выполнение задачи с низким приоритетом. + +Следующий пример демонстрирует диспетчеризацию на основе приоритетов задач. + +``` rust +{{#include ../../../../examples/preempt.rs}} +``` + +``` console +$ cargo run --example preempt +{{#include ../../../../ci/expected/preempt.run}} +``` -До сих пор программы RTIC, которые мы видели не отличались от программ, которые -можно написать, используя только библиотеку `cortex-m-rt`. В следующем разделе -мы начнем знакомиться с функционалом, присущим только RTIC. +Заметьте, что задача `gpiob` *не* вытесняет задачу `gpioc`, потому что ее приоритет +*такой же*, как и у `gpioc`. Однако, как только `gpioc` возвращает результат, +выполненяется задача `gpiob`, как более приоритетная по сравнению с `gpioa`. +Выполнение `gpioa` возобновляется только после выхода из `gpiob`. + +Еще одно замечание по поводу приоритетов: выбор приоритета большего, чем поддерживает устройство +(а именно `1 << NVIC_PRIO_BITS`) приведет к ошибке компиляции. +Из-за ограничений языка, сообщение об ошибке далеко от понимания: +вам скажут что-то похожее на "evaluation of constant value failed", а указатель на ошибку +*не* покажет на проблемное значение прерывания -- +мы извиняемся за это! diff --git a/book/ru/src/by-example/new.md b/book/ru/src/by-example/new.md index cba84c1..fcf5237 100644 --- a/book/ru/src/by-example/new.md +++ b/book/ru/src/by-example/new.md @@ -16,19 +16,19 @@ $ cargo generate \ $ # следуйте остальным инструкциям ``` -2. Добавьте крейт устройства, сгенерированный с помощью [`svd2rust`] **v0.14.x**, -или библиотеку отладочной платы, у которой в зависимостях одно из устройств. +2. Добавьте крейт доступа к периферии (PAC), сгенерированный с помощью[`svd2rust`] + **v0.14.x**, или крейт отладочной платы, у которой в зависимостях один из таких PAC'ов. Убедитесь, что опция `rt` крейта включена. [`svd2rust`]: https://crates.io/crates/svd2rust -В этом примере я покажу использование крейта устройства [`lm3s6965`]. +В этом примере я буду использовать крейт устройства [`lm3s6965`]. Эта библиотека не имеет Cargo-опции `rt`; эта опция всегда включена. [`lm3s6965`]: https://crates.io/crates/lm3s6965 Этот крейт устройства предоставляет линковочный скрипт с макетом памяти -целевого устройства, поэтому `memory.x` и `build.rs` не нужно удалять. +целевого устройства, поэтому `memory.x` и `build.rs` нужно удалить. ``` console $ cargo add lm3s6965 --vers 0.1.3 @@ -36,24 +36,40 @@ $ cargo add lm3s6965 --vers 0.1.3 $ rm memory.x build.rs ``` -3. Добавьте библиотеку `cortex-m-rtic` как зависимость, и если необходимо, -включите опцию `timer-queue`. +3. Добавьте крейт `cortex-m-rtic` как зависимость. ``` console -$ cargo add cortex-m-rtic --allow-prerelease --upgrade=none +$ cargo add cortex-m-rtic --allow-prerelease ``` -4. Напишите программу RTIC. +4. Напишите свою RTIC программу. -Здесь я буду использовать пример `init` из библиотеки `cortex-m-rtic`. +Здесь я буду использовать пример `init` из крейта `cortex-m-rtic`. + +Примеры находтся в папке `examples`, а содержание `init.rs` показано здесь: ``` console -$ curl \ - -L https://github.com/japaric/cortex-m-rtic/raw/v0.4.0-beta.1/examples/init.rs \ - > src/main.rs +{{#include ../../../../examples/init.rs}} +``` + +Пример `init` использует устройство `lm3s6965`. Не забудьте настроить аргумент `device` +в атрибуте макроса app так, чтобы он соответствовал пути к PAC-крейту, если он отличается, +а также добавить перифериб и другие аргументы если необходимо. +Несмотря на то, что в программе могут использоваться псевдонимы типов, +здесь необходимо указать полный путь (из корня крейта). Для многих устройств, +есть общий подход в крейтах реализации HAL (с псевдонимом `hal`) и крейтах поддержки +отладочных плат реекспортиорвать PAC как `pac`, что приводит нас к образцу, аналогичному +приведенному ниже: + +```rust +use abcd123_hal as hal; +//... + +#[rtic::app(device = crate::hal::pac, peripherals = true, monotonic = rtic::cyccnt::CYCCNT)] +mod app { /*...*/ } ``` -Этот пример зависит от библиотеки `panic-semihosting`: +Пример `init` также зависит от крейта `panic-semihosting`: ``` console $ cargo add panic-semihosting @@ -64,4 +80,5 @@ $ cargo add panic-semihosting ``` console $ # ПРИМЕЧАНИЕ: Я раскомментировал опцию `runner` в `.cargo/config` $ cargo run -{{#include ../../../../ci/expected/init.run}}``` +{{#include ../../../../ci/expected/init.run}} +``` diff --git a/book/ru/src/by-example/resources.md b/book/ru/src/by-example/resources.md index b53ef40..70f798d 100644 --- a/book/ru/src/by-example/resources.md +++ b/book/ru/src/by-example/resources.md @@ -1,22 +1,27 @@ -## Ресурсы - -Одно из ограничений атрибутов, предоставляемых библиотекой `cortex-m-rt` является -то, что совместное использование данных (или периферии) между прерываниями, -или прерыванием и функцией `init`, требуют `cortex_m::interrupt::Mutex`, который -*всегда* требует отключения *всех* прерываний для доступа к данным. Отключение всех -прерываний не всегда необходимо для безопасности памяти, но компилятор не имеет -достаточно информации, чтобы оптимизировать доступ к разделяемым данным. - -Атрибут `app` имеет полную картину приложения, поэтому может оптимизировать доступ к -`static`-переменным. В RTIC мы обращаемся к `static`-переменным, объявленным внутри -псевдо-модуля `app` как к *ресурсам*. Чтобы получить доступ к ресурсу, контекст -(`init`, `idle`, `interrupt` или `exception`) должен сначала определить -аргумент `resources` в соответствующем атрибуте. - -В примере ниже два обработчика прерываний имеют доступ к одному и тому же ресурсу. -Никакого `Mutex` в этом случае не требуется, потому что оба обработчика запускаются -с одним приоритетом и никакого вытеснения быть не может. -К ресурсу `SHARED` можно получить доступ только из этих двух прерываний. +# Ресурсы + +Фреймворк предоставляет абстракцию для разделения данных между любыми контекстами, +с которыми мы встречались в предыдущей главе (задачами-обработчиками, `init` и `idle`): ресурсы. + +Ресурсы - это данные, видимые только функциями, определенными внутри модуля `#[app]`. +Фреймворк дает пользователю полный контроль за тем, какой контекст может +получить доступ к какому ресурсу. + +Все ресурсы определены в одной структуре внутри модуля `#[app]`. +Каждое поле структуры соответствует отдельному ресурсу. +`struct`-ура должна быть аннотирована следующим атрибутом: `#[resources]`. + +Ресурсам могут быть опционально даны начальные значения с помощью атрибута `#[init]`. +Ресурсы, которым не передано начально значение, называются +*поздними* ресурсами, более детально они описаны в одном из разделов на этой странице. + +Каждый контекс (задача-обработчик, `init` или `idle`) должен указать ресурсы, к которым +он намерен обращаться, в соответсятвующем ему атрибуте с метаданными, используя +аргумент `resources`. Этот аргумент принимает список имен ресурсов в качестве значения. +Перечисленные ресурсы становятся доступны в контексте через поле `resources` структуры `Context`. + +Пример программы, показанной ниже содержит два обработчика прерывания, которые разделяют +доступ к ресурсу под названием `shared`. ``` rust {{#include ../../../../examples/resource.rs}} @@ -27,41 +32,36 @@ $ cargo run --example resource {{#include ../../../../ci/expected/resource.run}} ``` -## Приоритеты +Заметьте, что к ресурсу `shared` нельзя получить доступ из `idle`. Попытка сделать это +приведет к ошибке компиляции. -Приоритет каждого прерывания можно определить в атрибутах `interrupt` и `exception`. -Невозможно установить приоритет любым другим способом, потому что рантайм -забирает владение прерыванием `NVIC`; также невозможно изменить приоритет -обработчика / задачи в рантайме. Благодаря этому ограничению у фреймворка -есть знание о *статических* приоритетах всех обработчиков прерываний и исключений. +## `lock` -Прерывания и исключения могут иметь приоритеты в интервале `1..=(1 << NVIC_PRIO_BITS)`, -где `NVIC_PRIO_BITS` - константа, определённая в библиотеке `device`. -Задача `idle` имеет приоритет `0`, наименьший. +Критические секции необходимы для разделения изменяемых данных таким образом, +чтобы избежать гонок данных. -Ресурсы, совместно используемые обработчиками, работающими на разных приоритетах, -требуют критических секций для безопасности памяти. Фреймворк проверяет, что -критические секции используются, но *только где необходимы*: например, -критические секции не нужны для обработчика с наивысшим приоритетом, имеющим -доступ к ресурсу. +Поле `resources`, передаваемого `Context` реализует трейт [`Mutex`] для каждого разделяемого +ресурса, доступного задаче. -API критической секции, предоставляемое фреймворком RTIC (см. [`Mutex`]), -основано на динамических приоритетах вместо отключения прерываний. Из этого следует, -что критические секции не будут допускать *запуск некоторых* обработчиков, -включая все соперничающие за ресурс, но будут позволять запуск обработчиков с -большим приоритетом не соперничащих за ресурс. +Единственный метод этого трейта, [`lock`], запускает свой аргумент-замыкание в критической секции. [`Mutex`]: ../../../api/rtic/trait.Mutex.html +[`lock`]: ../../../api/rtic/trait.Mutex.html#method.lock -В примере ниже у нас есть 3 обработчика прерываний с приоритетами от одного -до трех. Два обработчика с низким приоритетом соперничают за ресурс `SHARED`. -Обработчик с низшим приоритетом должен заблокировать ([`lock`]) ресурс -`SHARED`, чтобы получить доступ к его данным, в то время как обработчик со -средним приоритетом может напрямую получать доступ к его данным. Обработчик -с наивысшим приоритетом может свободно вытеснять критическую секцию, -созданную обработчиком с низшим приоритетом. +Критическая секция, создаваемая интерфейсом `lock` основана на динамических приоритетах: +она временно повышает динамический приоритет контекста до *максимального* приоритета, +что не дает другим задачам возможности вытеснить критическую секцию. +Этот протокол синхронизации известен как [Протокол немедленного максимального приоритета +(ICPP)][icpp], и компилируется диспетчером RTIC с [Политикой ресурсов стека(SRP)][srp]. -[`lock`]: ../../../api/rtic/trait.Mutex.html#method.lock +[icpp]: https://en.wikipedia.org/wiki/Priority_ceiling_protocol +[srp]: https://en.wikipedia.org/wiki/Stack_Resource_Policy + +В примере ниже у нас есть три обработчика прерываний с приоритетами от одного до трех. +Два из обработчиков с более низким приоритетом соревнуются за ресурс `shared`, +поэтому должны блокировать доступа к данным ресурса. +Обработчик с наивысшим приоритетом, который не имеет доступа к ресурсу `shared`, +может свободно вытеснять критическую секцию, созданную обработчиком с низким приоритетом. ``` rust {{#include ../../../../examples/lock.rs}} @@ -69,26 +69,32 @@ API критической секции, предоставляемое фрей ``` console $ cargo run --example lock -{{#include ../../../../ci/expected/lock.run}}``` +{{#include ../../../../ci/expected/lock.run}} +``` + +## Множественное блокировка + +Это расширение к `lock`, чтобы уменьшить количесво отступов, блокируемые ресурсы можно объединять в кортежи. +Следующий пример это демонстрирует: + +``` rust +{{#include ../../../../examples/multilock.rs}} +``` ## Поздние ресурсы -В отличие от обычных `static`-переменных, к которым должно быть присвоено -начальное значение, ресурсы можно инициализировать в рантайме. -Мы называем ресурсы, инициализируемые в рантайме *поздними*. Поздние ресурсы -полезны для *переноса* (как при передаче владения) периферии из `init` в -обработчики прерываний и исключений. +Поздние ресурсы - такие ресурсы, которым не передано начальное значение во время компиляции +с помощью атрибута `#[init]`, но которые вместо этого инициализируются во время выполнения +с помощью значений из структуры `init::LateResources`, возвращаемой функцией `init`. -Поздние ресурсы определяются как обычные ресурсы, но им присваивается начальное -значение `()` (the unit value). `init` должен вернуть начальные значения для -всех поздних ресурсов, упакованные в структуру типа `init::LateResources`. +Поздние ресурсы полезны, например, для *move* (передача владения) периферии, +инициализированной в `init`, в задачи. -В примере ниже использованы поздние ресурсы, чтобы установить неблокированный, -односторонний канал между обработчиком прерывания `UART0` и функцией `idle`. -Очередь типа один производитель-один потребитель [`Queue`] использована как канал. -Очередь разделена на элементы потребителя и поизводителя в `init` и каждый элемент -расположен в отдельном ресурсе; `UART0` владеет ресурсом произодителя, а `idle` -владеет ресурсом потребителя. +Пример ниже использует поздние ресурсы, чтобы установить неблокируемый односторонний канал +между обработчиком прерывания `UART0` и задачей `idle`. Для канала использована очередь типа +один производитель-один потребитель [`Queue`]. Структура очереди разделяется на потребителя +и производителя в `init`, а затем каждая из частей располагается в отдельном ресурсу; +`UART0` владеет ресурсом производителя, а `idle` владеет ресурсом потребителя. [`Queue`]: ../../../api/heapless/spsc/struct.Queue.html @@ -98,25 +104,46 @@ $ cargo run --example lock ``` console $ cargo run --example late -{{#include ../../../../ci/expected/late.run}}``` +{{#include ../../../../ci/expected/late.run}} +``` + +## Только разделяемый доступ + +По-умолчанию фреймворк предполагает, что все задачи требуют эксклюзивный доступ (`&mut-`) к ресурсам, +но возможно указать, что задаче достаточен разделяемый доступ (`&-`) к ресурсы с помощью синтакисиса +`&resource_name` в списке `resources`. -## `static`-ресурсы +Преимущество указания разделяемого досупа (`&-`) к ресурсу в том, что для доступа к ресурсу +не нужна блокировка, даже если за ресурс соревнуются несколько задач, запускаемые с +разными приоритетами. Недостаток в том, что задача получает только разделяемую ссылку (`&-`) +на ресурс, и ограничена операциями, возможными с ней, но там, где разделяемой ссылки достаточно, +такой подход уменьшает количесво требуемых блокировок. +В дополнение к простым неизменяемым данным, такой разделяемый доступ может быть полезен для +ресурсов, безопасно реализующих внутреннюю мутабельность с самоблокировкой или атомарными операциями. -Переменные типа `static` также можно использовать в качестве ресурсов. Задачи -могут получать только (разделяемые) `&` ссылки на ресурсы, но блокировки не -нужны для доступа к данным. Вы можете думать о `static`-ресурсах как о простых -`static`-переменных, которые можно инициализировать в рантайме и иметь лучшие -правила видимости: Вы можете контролировать, какие задачи получают доступ к -переменной, чтобы переменная не была видна всем фунциям в область видимости, -где она была объявлена. +Заметьте, что в этом релизе RTIC невозможно запросить и эксклюзивный доступ (`&mut-`) +и разделяемый (`&-`) для *одного и того же* ресурса из различных задач. +Попытка это сделать приведет к ошибке компиляции. -В примере ниже ключ загружен (или создан) в рантайме, а затем использован в двух -задачах, запущенных на разных приоритетах. +В примере ниже ключ (например криптографический ключ) загружается (или создается) во время выполнения, +а затем используется двумя задачами, запускаемымы с различным приоритетом без каких-либо блокировок. ``` rust -{{#include ../../../../examples/static.rs}} +{{#include ../../../../examples/only-shared-access.rs}} ``` ``` console -$ cargo run --example static -{{#include ../../../../ci/expected/static.run}}``` +$ cargo run --example only-shared-access +{{#include ../../../../ci/expected/only-shared-access.run}} +``` + +## Неблокируемый доступ к изменяемым ресурсам + +Есть две других возможности доступа к ресурсам + +* `#[lock_free]`: могут быть несколько задач с одинаковым приоритетом, + получающие доступ к ресурсу без критических секций. Так как задачи с + одинаковым приоритетом никогда не могут вытеснить друг друга, это безопасно. +* `#[task_local]`: в этом случае должна быть только одна задача, использующая + этот ресурс, так же как локальный `static mut` ресурс задачи, но (опционально) устанавливаемая с в init. + diff --git a/book/ru/src/by-example/singletons.md b/book/ru/src/by-example/singletons.md deleted file mode 100644 index d6d60ef..0000000 --- a/book/ru/src/by-example/singletons.md +++ /dev/null @@ -1,26 +0,0 @@ -# Одиночки - -Атрибут `app` знает о библиотеке [`owned-singleton`] и её атрибуте [`Singleton`]. -Когда этот атрибут применяется к одному из ресурсов, рантайм производит для Вас -`unsafe` инициализацию одиночки, проверяя, что только один экземпляр одиночки -когда-либо создан. - -[`owned-singleton`]: ../../api/owned_singleton/index.html -[`Singleton`]: ../../api/owned_singleton_macros/attr.Singleton.html - -Заметьте, что когда Вы используете атрибут `Singleton`, Вым нужно иметь -`owned_singleton` в зависимостях. - -В примере ниже атрибутом `Singleton` аннотирован массив памяти, -а экземпляр одиночки использован как фиксированный по размеру пул памяти -с помощью одной из абстракций [`alloc-singleton`]. - -[`alloc-singleton`]: https://crates.io/crates/alloc-singleton - -``` rust -{{#include ../../../../examples/singleton.rs}} -``` - -``` console -$ cargo run --example singleton -{{#include ../../../../ci/expected/singleton.run}}``` diff --git a/book/ru/src/by-example/tasks.md b/book/ru/src/by-example/tasks.md index 3782804..3c99d00 100644 --- a/book/ru/src/by-example/tasks.md +++ b/book/ru/src/by-example/tasks.md @@ -1,22 +1,20 @@ # Программные задачи -RTIC обрабатывает прерывания и исключения как *аппаратные* задачи. Аппаратные -задачи могут вызываться устройством в ответ на события, такие как нажатие кнопки. -RTIC также поддерживает *программные* задачи, порождаемые программой из любого -контекста выполнения. +В дополнение к аппаратным задачам, вызываемым в ответ на аппаратные события, +RTIC также поддерживает *программные* задачи, которые могут порождаться +приложением из любого контекста выполнения. -Программным задачам также можно назначать приоритет и диспетчеризовать из -обработчиков прерываний. RTIC требует определения свободных прерываний в блоке -`extern`, когда используются программные задачи; эти свободные прерывания будут использованы, чтобы диспетчеризовать программные задачи. Преимущество программных -задач перед аппаратными в том, что на один обработчик прерывания можно назначить -множество задач. +Программным задачам можно также назначать приоритет и, под капотом, они +диспетчеризуются обработчиками прерываний. RTIC требует, чтобы свободные +прерывания, были указаны в аргументе `dispatchers` модуля `app`, если используются +программные задачи; часть из этих свободных прерываний будут использованы для +управления программными задачами. Преимущество программных задач над аппаратными +в том, что множество задач можно назначить на один обработчик прерывания. -Программные задачи определяются заданием функциям атрибута `task`. Чтобы было -возможно вызывать программные задачи, имя задачи нужно передать в аргументе -`spawn` контекста атрибута (`init`, `idle`, `interrupt`, etc.). +Программные задачи также определяются атрибутом `task`, но аргумент `binds` опускается. -В примере ниже продемонстрированы три программных задачи, запускаемые на 2-х -разных приоритетах. Трем задачам назначены 2 обработчика прерываний. +Пример ниже демонстрирует три программные задачи, запускаемых 2-х разных приоритетах. +Три программные задачи привязаны к 2-м обработчикам прерываний. ``` rust {{#include ../../../../examples/task.rs}} @@ -24,15 +22,16 @@ RTIC также поддерживает *программные* задачи, ``` console $ cargo run --example task -{{#include ../../../../ci/expected/task.run}}``` +{{#include ../../../../ci/expected/task.run}} +``` ## Передача сообщений -Другое преимущество программных задач - возможность передавать сообщения задачам -во время их вызова. Тип полезной нагрузки сообщения должен быть определен в -сигнатуре обработчика задачи. +Другое преимущество программной задачи в том, что задачам можно передать сообщения +в момент их запуска. Тип передаваемого сообщения должен быть определен в сигнатуре +задачи-обработчика. -Пример ниже демонстрирует три задачи, две из которых ожидают сообщения. +Пример ниже демонстрирует три задачи, две из которых ожидают сообщение. ``` rust {{#include ../../../../examples/message.rs}} @@ -40,19 +39,23 @@ $ cargo run --example task ``` console $ cargo run --example message -{{#include ../../../../ci/expected/message.run}}``` +{{#include ../../../../ci/expected/message.run}} +``` -## Ёмкость +## Вместимость -Диспетчеры задач *не* используют динамическое выделение памяти. Память -необходимая для размещения сообщений, резервируется статически. Фреймворк -зарезервирует достаточно памяти для каждого контекста, чтобы можно было вызвать -каждую задачу как минимум единожды. Это разумно по умолчанию, но -"внутреннюю" ёмкость каждой задачи можно контролировать используя аргумент -`capacity` атрибута `task`. +RTIC *не* производит никакого рода аллокаций памяти в куче. +Память, необходимая для размещения сообщения резервируется статически. +По-умолчанию фреймворк минимизирует выделение памяти программой таким образом, +что каждая задача имеет "вместимость" для сообщения равную 1: +это значит, что не более одного сообщения можно передать задаче перед тем, как +у нее появится возможность к запуску. Это значение по-умолчанию можно +изменить для каждой задачи, используя аргумент `capacity`. +Этот аргумент принимает положительное целое, которое определяет как много +сообщений буфер сообщений задачи может хранить. -В примере ниже установлена ёмкость программной задачи `foo` на 4. Если ёмкость -не определена, тогда второй вызов `spawn.foo` в `UART0` вызовет ошибку. +Пример ниже устанавливает вместимость программной задачи `foo` равной 4. +Если вместимость не установить, второй вызов `spawn.foo` в `UART0` приведет к ошибке (панике). ``` rust {{#include ../../../../examples/capacity.rs}} @@ -60,4 +63,54 @@ $ cargo run --example message ``` console $ cargo run --example capacity -{{#include ../../../../ci/expected/capacity.run}}``` +{{#include ../../../../ci/expected/capacity.run}} +``` + +## Обработка ошибок + +Интерфейс `spawn` возвращает вариант `Err`, если для размещения сообщения нет места. +В большинстве сценариев возникающие ошибки обрабатываются одним из двух способов: + +- Паника, с помощью `unwrap`, `expect`, и т.п. Этот метод используется, чтобы обнаружить + ошибку программиста (например bug) выбора вместительности, которая оказалась недостаточна. + Когда эта паника встречается во время тестирования, выбирается большая вместительность, + и перекомпиляция программы может решить проблему, но иногда достаточно окунуться глубже + и провести анализ времени выполнения программы, чтобы выяснить, может ли платформа + обрабатывать пиковые нагрузки, или процессор необходимо заменить на более быстрый. + +- Игнорирование результата. В программах реального времени, как и в обычных, может быть + нормальным иногда терять данные, или не получать ответ на некоторые события в пиковых ситуациях. + В таких сценариях может быть допустимо игнорирование ошибки вызова `spawn`. + +Следует отметить, что повторная попытка вызова `spawn` обычно неверный подход, поскольку +такая операция на практике вероятно никогда не завершится успешно. +Так как у нас есть только переключения контекста на задачи с *более высоким* приоритетом, +повторение вызова `spawn` на задаче с низким приоритом никогда не позволит планировщику +вызвать задачу, что значит, что буфер никогда не будет очищен. Такая ситуация отражена в +следующем наброске: + +``` rust +#[rtic::app(..)] +mod app { + #[init(spawn = [foo, bar])] + fn init(cx: init::Context) { + cx.spawn.foo().unwrap(); + cx.spawn.bar().unwrap(); + } + + #[task(priority = 2, spawn = [bar])] + fn foo(cx: foo::Context) { + // .. + + // программа зависнет здесь + while cx.spawn.bar(payload).is_err() { + // повтор попытки вызова spawn, если произошла ошибка + } + } + + #[task(priority = 1)] + fn bar(cx: bar::Context, payload: i32) { + // .. + } +} +``` diff --git a/book/ru/src/by-example/timer-queue.md b/book/ru/src/by-example/timer-queue.md index 3c35e29..c8818d7 100644 --- a/book/ru/src/by-example/timer-queue.md +++ b/book/ru/src/by-example/timer-queue.md @@ -1,57 +1,76 @@ # Очередь таймера -Когда включена опция `timer-queue`, фреймворк RTIC включает -*глобальную очередь таймера*, которую приложения могут использовать, чтобы -*планировать* программные задачи на запуск через некоторое время в будущем. - -Чтобы была возможность планировать программную задачу, имя задачи должно -присутствовать в аргументе `schedule` контекста атрибута. Когда задача -планируется, момент ([`Instant`]), в который задачу нужно запустить, нужно передать -как первый аргумент вызова `schedule`. - -[`Instant`]: ../../../api/rtic/struct.Instant.html - -Рантайм RTIC включает монотонный, растущий только вверх, 32-битный таймер, -значение которого можно запросить конструктором `Instant::now`. Время ([`Duration`]) -можно передать в `Instant::now()`, чтобы получить `Instant` в будущем. Монотонный -таймер отключен пока запущен `init`, поэтому `Instant::now()` всегда возвращает -значение `Instant(0 /* циклов тактовой частоты */)`; таймер включается сразу перед -включением прерываний и запуском `idle`. - -[`Duration`]: ../../../api/rtic/struct.Duration.html - -В примере ниже две задачи планируются из `init`: `foo` и `bar`. `foo` - -запланирована на запуск через 8 миллионов тактов в будущем. Кроме того, `bar` -запланирован на запуск через 4 миллиона тактов в будущем. `bar` запустится раньше -`foo`, т.к. он запланирован на запуск первым. - -> **ВАЖНО**: Примеры, использующие API `schedule` или абстракцию `Instant` -> **не** будут правильно работать на QEMU, потому что функциональность счетчика -> тактов Cortex-M не реализована в `qemu-system-arm`. +В отличие от интерфейса `spawn`, который немедленно передает программную задачу +планировщику для немедленного запуска, интерфейс `schedule` можно использовать +для планирования задачи к запуске через какое-то время в будущем. + +Чтобы использовать интерфейс `schedule`, предварительно должен быть определен +монотонный таймер с помощью аргумента `monotonic` атрибута `#[app]`. +Этот аргумент принимает путь к типу, реализующему трейт [`Monotonic`]. +Ассоциированный тип, `Instant`, этого трейта представляет метку времени в соответствущих +единицах измерения и широко используется в интерфейсе `schedule` -- предлагается смоделировать +этот тип позднее [один из таких есть в стандартной библиотеке][std-instant]. + +Хотя это не отражено в определении трейта (из-за ограничений системы типов / трейтов), +разница двух `Instant`ов должна возвращать какой-то тип `Duration` (см. [`core::time::Duration`]) +и этот `Duration` должен реализовывать трейт `TryInto<u32>`. +Реализация этого трейта должна конвертировать значение `Duration`, которое +использует какую-то определенную единицу измерения времени, в единицы измерения "тактов системного таймера +(SYST)". Результат преобразований должен быть 32-битным целым. +Если результат не соответствует 32-битному целому, тогда операция должна возвращать ошибку любого типа. + +[`Monotonic`]: ../../../api/rtic/trait.Monotonic.html +[std-instant]: https://doc.rust-lang.org/std/time/struct.Instant.html +[`core::time::Duration`]: https://doc.rust-lang.org/core/time/struct.Duration.html + +Для целевых платформ ARMv7+ крейт `rtic` предоставляет реализацию `Monotonic`, основанную на +встроенном CYCle CouNTer (CYCCNT). Заметьте, что это 32-битный таймер, работающий на +частоте центрального процессора, и поэтому не подходит для отслеживания интервалов времени в секундах. + +Когда планируется задача, (определенный пользователем) `Instant`, в который задача должна быть +выполнена, должен передаваться в качестве первого аргумента вызова `schedule`. + +К тому же, выбранный `monotonic` таймер, необходимо сконфигурировать и инициализировать в +фазе работы `#[init]`. Заметьте, что *также* касается случая использования `CYCCNT`, +предоставляемого крейтом `cortex-m-rtic`. + +Пример ниже планирует к выполнению две задачи из `init`: `foo` и `bar`. `foo` запланирована +к запуску через 8 миллионов циклов в будущем. Далее, `bar` запланировано запустить через +4 миллиона циклов в будущем. Таким образом, `bar` запустится до `foo`, так как и запланировано. + +> **DF:YJ**: Примеры, использующие интерфейс `schedule` или абстракцию `Instant` +> **не будут** правильно работать на эмуляторе QEMU, поскольку счетчик циклов Cortex-M +> функционально не был реализован в `qemu-system-arm`. ``` rust {{#include ../../../../examples/schedule.rs}} ``` -Запуск программы на реальном оборудовании производит следующий вывод в консоли: +Запусе программы на реальном оборудовании создает следующий вывод в консоли: ``` text {{#include ../../../../ci/expected/schedule.run}} ``` +Когда интерфейс `schedule` используется, среда исполнения использует внутри +обработчик прерываний `SysTick` и периферию системного таймера (`SYST`), поэтому ни +тот ни другой нельзя использовать в программе. Это гарантируется изменением типа +`init::Context.core` с `cortex_m::Peripherals` на `rtic::Peripherals`. +Последняя структура содержит все поля из предыдущей кроме `SYST`. + ## Периодические задачи -Программные задачи имеют доступ к `Instant` в момент, когда были запланированы -на запуск через переменную `scheduled`. Эта информация и API `schedule` могут -быть использованы для реализации периодических задач, как показано в примере ниже. +Программные задачи имеют доступ к моменту времени `Instant`, в который они были запланированы +на выполнение переменной `scheduled`. Эта информация и интерфейс `schedule` можно использовать, +чтобы реализовать периодические задачи, как показано ниже. ``` rust {{#include ../../../../examples/periodic.rs}} ``` -Это вывод, произведенный примером. Заметьте, что есть смещение / колебание нуля -даже если `schedule.foo` была вызвана в *конце* `foo`. Использование -`Instant::now` вместо `scheduled` имело бы влияние на смещение / колебание. +Это вывод, создаваемый примером. Заметьте, что здесь пристствует небольшой дрейф / колебания +даже несмотря на то, что `schedule.foo` была вызвана в *конце* `foo`. Использование +`Instant::now` вместо `scheduled` вызвало бы дрейф / колебания. ``` text {{#include ../../../../ci/expected/periodic.run}} @@ -59,31 +78,30 @@ ## Базовое время -Для задач, планируемых из `init` мы имеем точную информацию о их планируемом -(`scheduled`) времени. Для аппаратных задач нет `scheduled` времени, потому -что эти задачи асинхронны по природе. Для аппаратных задач рантайм предоставляет -время старта (`start`), которе отражает время, в которое обработчик прерывания -был запущен. +Для задач, вызываемых из `init` мы имеем точную информацию о их `scheduled` времени. +Для аппаратных задач такого времени нет, поскольку они асинхронны по природе. +Для аппаратных задач среда исполнения предоставляет время запуска (`start`), которое отражает +время, в которое обработчик прерывания будет запущен. -Заметьте, что `start` **не** равен времени возникновения события, вызвавшего -задачу. В зависимости от приоритета задачи и загрузки системы время -`start` может быть сильно отдалено от времени возникновения события. +Заметьте, что `start` **не** равно времени прихода события, которое вызывает задачу. +В зависимости от приоритета задачи и загрузки системы, время `start` может сильно отдалиться от +времени прихода события. -Какое по Вашему мнению будет значение `scheduled` для программных задач которые -*вызываются*, вместо того чтобы планироваться? Ответ в том, что вызываемые -задачи наследуют *базовое* время контекста, в котором вызваны. Бызовым для -аппаратных задач является `start`, базовым для программных задач - `scheduled` -и базовым для `init` - `start = Instant(0)`. `idle` на сомом деле не имеет -базового времени но задачи, вызванные из него будут использовать `Instant::now()` -как их базовое время. +Какое по вашему мнению будет значение `scheduled` для программных задач, которые вызываются через +`spawn` вместо планирования? Ответ в том, что вызываемые задачи наследуют +*базовое* время того контекста, который их вызывает. Базовое время аппаратных задач - +это их время `start`, базовое время программных задач - их время `scheduled`, а +базовое время `init` - время старта системы, или нулевое +(`Instant::zero()`). `idle` на самом деле не имеет базового времени, но задачи вызываемые из нее, +используют `Instant::now()` в качестве базового. -Пример ниже демонстрирует разное значение *базового времени*. +Пример ниже демонстрирует разные смыслы *базового времени*. ``` rust {{#include ../../../../examples/baseline.rs}} ``` -Запуск программы на реальном оборудовании произведет следующий вывод в консоли: +Запуск программы на реальном оборудовании приведет к следующему выводу в консоли: ``` text {{#include ../../../../ci/expected/baseline.run}} diff --git a/book/ru/src/by-example/tips.md b/book/ru/src/by-example/tips.md index 249e8f4..cf66c4b 100644 --- a/book/ru/src/by-example/tips.md +++ b/book/ru/src/by-example/tips.md @@ -2,10 +2,15 @@ ## Обобщенное программирование (Generics) -Ресурсы, совместно используемые двумя или более задачами, реализуют трейт `Mutex` -во *всех* контекстах, даже в тех, где для доступа к данным не требуются -критические секции. Это позволяет легко писать обобщенный код оперирующий -ресурсами, который можно вызывать из различных задач. Вот такой пример: +Все объекты, предоставляющие ресурысы реализуют трейт `rtic::Mutex`. +Если ресурс не реализует его, можно обернуть его в новый тип [`rtic::Exclusive`], +который реализует трейт `Mutex`. С помощью этого нового типа +можно написать обобщенную функцию, которая работает с обобщенным ресурсом и +вызывать его из различных задач, чтобы производить однотипные операции над +похожим множеством ресурсов. +Вот один такой пример: + +[`rtic::Exclusive`]: ../../../api/rtic/struct.Exclusive.html ``` rust {{#include ../../../../examples/generics.rs}} @@ -13,12 +18,29 @@ ``` console $ cargo run --example generics -{{#include ../../../../ci/expected/generics.run}}``` +{{#include ../../../../ci/expected/generics.run}} +``` + +## Условная компиляция + +Вы можете использовать условную компиляцию (`#[cfg]`) на ресурсах (полях структуры +`#[resources] struct Resources`) и задачах (элементах `fn`). +Эффект использования атрибутов `#[cfg]` в том, что ресурс/ задача +будут *не* доступны в соответствующих структурах `Context` если условие не выполняется. + +В примере ниже выводится сообщение каждый раз, когда вызывается задача `foo`, но только +если программы скомпилирова с профилем `dev`. + +``` rust +{{#include ../../../../examples/cfg.rs}} +``` + +``` console +$ cargo run --example cfg --release -Это также позволяет Вам изменять статические приоритеты задач без -переписывания кода. Если Вы единообразно используете `lock`-и для доступа -к данным в разделяемых ресурсах, тогда Ваш код продолжит компилироваться, -когда Вы измените приоритет задач. +$ cargo run --example cfg +{{#include ../../../../ci/expected/cfg.run}} +``` ## Запуск задач из ОЗУ @@ -31,10 +53,10 @@ RTIC v0.4.x была возможность взаимодействия с др > очень мощные, но их легко использовать неправильно. Неверное использование > любого из этих атрибутов может вызвать неопределенное поведение; > Вам следует всегда предпочитать использование безопасных, высокоуровневых -> атрибутов вокруг них, таких как атрибуты `interrupt` и `exception` +> атрибутов вместо них, таких как атрибуты `interrupt` и `exception` > из `cortex-m-rt`. > -> В особых случаях функций RAM нет безопасной абстракции в `cortex-m-rt` +> В особых функций, размещаемых в ОЗУ нет безопасной абстракции в `cortex-m-rt` > v0.6.5 но создано [RFC] для добавления атрибута `ramfunc` в будущем релизе. [RFC]: https://github.com/rust-embedded/cortex-m-rt/pull/100 @@ -45,37 +67,105 @@ RTIC v0.4.x была возможность взаимодействия с др {{#include ../../../../examples/ramfunc.rs}} ``` -Запуск этой программы произведет ожидаемый вывод. +Запуск этой программы создаст ожидаемый вывод. ``` console $ cargo run --example ramfunc -{{#include ../../../../ci/expected/ramfunc.run}}``` +{{#include ../../../../ci/expected/ramfunc.run}} +``` Можно посмотреть на вывод `cargo-nm`, чтобы убедиться, что `bar` расположен в ОЗУ (`0x2000_0000`), тогда как `foo` расположен во Flash (`0x0000_0000`). ``` console $ cargo nm --example ramfunc --release | grep ' foo::' -{{#include ../../../../ci/expected/ramfunc.grep.foo}}``` +{{#include ../../../../ci/expected/ramfunc.grep.foo}} +``` ``` console $ cargo nm --example ramfunc --release | grep ' bar::' -{{#include ../../../../ci/expected/ramfunc.grep.bar}}``` +{{#include ../../../../ci/expected/ramfunc.grep.bar}} +``` + +## Обходной путь для быстрой передачи сообщений -## `binds` +Передача сообщений всегда вызывает копирование от отправителя в +статическую переменную, а затем из статической переменной получателю. +Таким образом, при передаче большого буфера, например `[u8; 128]`, передача сообщения +вызывает два дорогих вызова `memcpy`. Чтобы минимизировать накладные расходы на передачу +сообщения, можно использовать обходной путь: вместо передачи буфера по значению, +можно передавать владеющий указатель на буфер. -**ПРИМЕЧАНИЕ**: Требуется RTIC не ниже 0.4.2 +Можно использовать глобальный аллокатор, чтобы реализовать данный трюк (`alloc::Box`, +`alloc::Rc`, и т.п.), либо использовать статически аллоцируемый пул памяти, например [`heapless::Pool`]. -Вы можете давать аппаратным задачам имена похожие на имена обычных задач. -Для этого нужно использовать аргумент `binds`: Вы называете функцию -по своему желанию и назначаете ей прерывание / исключение -через аргумент `binds`. `Spawn` и другие служебные типы будут размещены в модуле, -названном в соответствии с названием функции, а не прерывания / исключения. -Давайте посмотрим пример: +[`heapless::Pool`]: https://docs.rs/heapless/0.5.0/heapless/pool/index.html + +Здесь приведен пример использования `heapless::Pool` для "упаковки" буфера из 128 байт. ``` rust -{{#include ../../../../examples/binds.rs}} +{{#include ../../../../examples/pool.rs}} ``` + ``` console -$ cargo run --example binds -{{#include ../../../../ci/expected/binds.run}}```
\ No newline at end of file +$ cargo run --example pool +{{#include ../../../../ci/expected/pool.run}} +``` + +## Инспектирование раскрываемого кода + +`#[rtic::app]` - это процедурный макрос, который создает код. +Если по какой-то причине вам нужно увидеть код, сгенерированный этим макросом, +у вас есть два пути: + +Вы можете изучить файл `rtic-expansion.rs` внутри папки `target`. Этот файл +содержит элемент `#[rtic::app]` в раскрытом виде (не всю вашу программу!) +из *последней сборки* (с помощью `cargo build` или `cargo check`) RTIC программы. +Раскрытый код не отформатирован по-умолчанию, но вы можете запустить `rustfmt` +на нем перед тем, как читать. + +``` console +$ cargo build --example foo + +$ rustfmt target/rtic-expansion.rs + +$ tail target/rtic-expansion.rs +``` + +``` rust +#[doc = r" Implementation details"] +mod app { + #[doc = r" Always include the device crate which contains the vector table"] + use lm3s6965 as _; + #[no_mangle] + unsafe extern "C" fn main() -> ! { + rtic::export::interrupt::disable(); + let mut core: rtic::export::Peripherals = core::mem::transmute(()); + core.SCB.scr.modify(|r| r | 1 << 1); + rtic::export::interrupt::enable(); + loop { + rtic::export::wfi() + } + } +} +``` + +Или, вы можете использовать подкоманду [`cargo-expand`]. Она раскроет +*все* макросы, включая атрибут `#[rtic::app]`, и модули в вашем крейте и +напечатает вывод в консоль. + +[`cargo-expand`]: https://crates.io/crates/cargo-expand + +``` console +$ # создаст такой же вывод, как выше +$ cargo expand --example smallest | tail +``` + +## Деструктуризация ресурса + +Если задача требует нескольких ресурсов, разбиение структуры ресурсов +может улучшить читабельность. Вот два примера того, как это можно сделать: + +``` rust +{{#include ../../../../examples/destructure.rs}} +``` diff --git a/book/ru/src/by-example/types-send-sync.md b/book/ru/src/by-example/types-send-sync.md index 8511889..755a379 100644 --- a/book/ru/src/by-example/types-send-sync.md +++ b/book/ru/src/by-example/types-send-sync.md @@ -1,16 +1,15 @@ # Типы, Send и Sync -Атрибут `app` вводит контекст, коллекцию переменных в каждую из функций. -Все эти переменные имеют предсказуемые, неанонимные типы, поэтому Вы можете -писать простые функции, получающие их как аргументы. +Каждая функция в модуле `app` принимает структуру `Context` в качесте первого параметра. +Все поля этих структур имеют предсказуемые, неанонимные типы, +поэтому вы можете написать обычные функции, принимающие их как аргументы. -Описание API определяет как эти типы эти типы генерируются из входных данных. -Вы можете также сгенерировать документацию для Вашей бинарной библиотеки -(`cargo doc --bin <name>`); в документации Вы найдете структуры `Context` -(например `init::Context` и `idle::Context`), чьи поля представляют переменные -включенные в каждую функцию. +Справочник по API определяет как эти типы генерируются на основе входных данных. +Вы можете также сгенерировать документацию к вашему крейту программы (`cargo doc --bin <name>`); +в документации вы найдете структуры `Context` (например `init::Context` и +`idle::Context`). -В примере ниже сгенерированы разные типы с помощью атрибута `app`. +Пример ниже показывает различные типы, сгенерированные атрибутом `app`. ``` rust {{#include ../../../../examples/types.rs}} @@ -18,39 +17,30 @@ ## `Send` -[`Send`] - маркерный типаж (trait) для "типов, которые можно передавать через границы -потоков", как это определено в `core`. В контексте RTIC типаж `Send` необходим +[`Send`] - это маркерный трейт для "типов, которые можно передавать через границы +потоков", как это определено в `core`. В контексте RTIC трейт `Send` необходим только там, где возможна передача значения между задачами, запускаемыми на *разных* приоритетах. Это возникает в нескольких случаях: при передаче сообщений, -в совместно используемых `static mut` ресурсах и инициализации поздних ресурсов. +в разделяемых `static mut` ресурсах и при инициализации поздних ресурсов. [`Send`]: https://doc.rust-lang.org/core/marker/trait.Send.html -Атрибут `app` проверит, что `Send` реализован, где необходимо, поэтому Вам не -стоит волноваться об этом. Более важно знать, где Вам *не* нужен типаж `Send`: -в типах, передаваемых между задачами с *одинаковым* приоритетом. Это возникает -в двух случаях: при передаче сообщений и в совместно используемых `static mut` -ресурсах. - -В примере ниже показано, где можно использовать типы, не реализующие `Send`. - -``` rust -{{#include ../../../../examples/not-send.rs}} -``` +Атрибут `app` проверит, что `Send` реализован, где необходимо, поэтому вам не +стоит волноваться об этом. В настоящий момент все передаваемые типы в RTIC должны быть `Send`, но +это ограничение возможно будет ослаблено в будущем. ## `Sync` -Похожая ситуация, [`Sync`] - маркерный типаж для "типов, на которых можно -ссылаться в разных потоках", как это определено в `core`. В контексте RTIC -типаж `Sync` необходим только там, где возможны две или более задачи, -запускаемые на разных приоритетах, чтобы захватить разделяемую ссылку на -ресурс. Это возникает только совместно используемых `static`-ресурсах. +Аналогично, [`Sync`] - маркерный трейт для "типов, на которые можно безопасно разделять между потоками", +как это определено в `core`. В контексте RTIC типаж `Sync` необходим только там, +где возможно для двух или более задач, запускаемых на разных приоритетах получить разделяемую ссылку (`&-`) на +ресурс. Это возникает только (`&-`) ресурсах с разделяемым доступом. [`Sync`]: https://doc.rust-lang.org/core/marker/trait.Sync.html Атрибут `app` проверит, что `Sync` реализован, где необходимо, но важно знать, -где ограничение `Sync` не требуется: в `static`-ресурсах, разделяемых между -задачами с *одинаковым* приоритетом. +где ограничение `Sync` не требуется: в (`&-`) ресурсах с разделяемым доступом, за которые +соперничают задачи с *одинаковым* приоритетом. В примере ниже показано, где можно использовать типы, не реализующие `Sync`. |
