aboutsummaryrefslogtreecommitdiff
path: root/book/ru/src/by-example
diff options
context:
space:
mode:
Diffstat (limited to 'book/ru/src/by-example')
-rw-r--r--book/ru/src/by-example/app.md159
-rw-r--r--book/ru/src/by-example/new.md45
-rw-r--r--book/ru/src/by-example/resources.md177
-rw-r--r--book/ru/src/by-example/singletons.md26
-rw-r--r--book/ru/src/by-example/tasks.md113
-rw-r--r--book/ru/src/by-example/timer-queue.md122
-rw-r--r--book/ru/src/by-example/tips.md142
-rw-r--r--book/ru/src/by-example/types-send-sync.md50
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`.