139: russian translation r=japaric a=japaric



Co-authored-by: Jorge Aparicio <jorge@japaric.io>
Co-authored-by: Andrey Zgarbul <zgarbul.andrey@gmail.com>
This commit is contained in:
bors[bot] 2019-02-11 20:41:49 +00:00
commit 672886a67a
37 changed files with 818 additions and 48 deletions

2
.gitignore vendored
View file

@ -1,6 +1,6 @@
**/*.rs.bk **/*.rs.bk
.#* .#*
.gdb_history .gdb_history
/book/book /book/*/book
/target /target
Cargo.lock Cargo.lock

View file

@ -3,7 +3,7 @@
This is the smallest possible RTFM application: This is the smallest possible RTFM application:
``` rust ``` rust
{{#include ../../../examples/smallest.rs}} {{#include ../../../../examples/smallest.rs}}
``` ```
All RTFM applications use the [`app`] attribute (`#[app(..)]`). This attribute All RTFM applications use the [`app`] attribute (`#[app(..)]`). This attribute
@ -47,7 +47,7 @@ The example below shows the types of the `core` and `device` variables and
showcases safe access to a `static mut` variable. showcases safe access to a `static mut` variable.
``` rust ``` rust
{{#include ../../../examples/init.rs}} {{#include ../../../../examples/init.rs}}
``` ```
Running the example will print `init` to the console and then exit the QEMU Running the example will print `init` to the console and then exit the QEMU
@ -55,7 +55,7 @@ process.
``` console ``` console
$ cargo run --example init $ cargo run --example init
{{#include ../../../ci/expected/init.run}}``` {{#include ../../../../ci/expected/init.run}}```
## `idle` ## `idle`
@ -78,12 +78,12 @@ references that are safe to access.
The example below shows that `idle` runs after `init`. The example below shows that `idle` runs after `init`.
``` rust ``` rust
{{#include ../../../examples/idle.rs}} {{#include ../../../../examples/idle.rs}}
``` ```
``` console ``` console
$ cargo run --example idle $ cargo run --example idle
{{#include ../../../ci/expected/idle.run}}``` {{#include ../../../../ci/expected/idle.run}}```
## `interrupt` / `exception` ## `interrupt` / `exception`
@ -93,12 +93,12 @@ and exception handlers. In RTFM, we refer to interrupt and exception handlers as
*hardware* tasks. *hardware* tasks.
``` rust ``` rust
{{#include ../../../examples/interrupt.rs}} {{#include ../../../../examples/interrupt.rs}}
``` ```
``` console ``` console
$ cargo run --example interrupt $ cargo run --example interrupt
{{#include ../../../ci/expected/interrupt.run}}``` {{#include ../../../../ci/expected/interrupt.run}}```
So far all the RTFM applications we have seen look no different that the So far all the RTFM applications we have seen look no different that the
applications one can write using only the `cortex-m-rt` crate. In the next applications one can write using only the `cortex-m-rt` crate. In the next

View file

@ -64,4 +64,4 @@ $ cargo add panic-semihosting
``` console ``` console
$ # NOTE: I have uncommented the `runner` option in `.cargo/config` $ # NOTE: I have uncommented the `runner` option in `.cargo/config`
$ cargo run $ cargo run
{{#include ../../../ci/expected/init.run}}``` {{#include ../../../../ci/expected/init.run}}```

View file

@ -19,12 +19,12 @@ no preemption is possible. The `SHARED` resource can only be accessed by these
two handlers. two handlers.
``` rust ``` rust
{{#include ../../../examples/resource.rs}} {{#include ../../../../examples/resource.rs}}
``` ```
``` console ``` console
$ cargo run --example resource $ cargo run --example resource
{{#include ../../../ci/expected/resource.run}}``` {{#include ../../../../ci/expected/resource.run}}```
## Priorities ## Priorities
@ -62,12 +62,12 @@ the critical section created by the lowest priority handler.
[`lock`]: ../../api/rtfm/trait.Mutex.html#method.lock [`lock`]: ../../api/rtfm/trait.Mutex.html#method.lock
``` rust ``` rust
{{#include ../../../examples/lock.rs}} {{#include ../../../../examples/lock.rs}}
``` ```
``` console ``` console
$ cargo run --example lock $ cargo run --example lock
{{#include ../../../ci/expected/lock.run}}``` {{#include ../../../../ci/expected/lock.run}}```
## Late resources ## Late resources
@ -91,12 +91,12 @@ the consumer resource.
[`Queue`]: ../../api/heapless/spsc/struct.Queue.html [`Queue`]: ../../api/heapless/spsc/struct.Queue.html
``` rust ``` rust
{{#include ../../../examples/late.rs}} {{#include ../../../../examples/late.rs}}
``` ```
``` console ``` console
$ cargo run --example late $ cargo run --example late
{{#include ../../../ci/expected/late.run}}``` {{#include ../../../../ci/expected/late.run}}```
## `static` resources ## `static` resources
@ -111,9 +111,9 @@ In the example below a key is loaded (or created) at runtime and then used from
two tasks that run at different priorities. two tasks that run at different priorities.
``` rust ``` rust
{{#include ../../../examples/static.rs}} {{#include ../../../../examples/static.rs}}
``` ```
``` console ``` console
$ cargo run --example static $ cargo run --example static
{{#include ../../../ci/expected/static.run}}``` {{#include ../../../../ci/expected/static.run}}```

View file

@ -18,9 +18,9 @@ the [`alloc-singleton`] abstractions.
[`alloc-singleton`]: https://crates.io/crates/alloc-singleton [`alloc-singleton`]: https://crates.io/crates/alloc-singleton
``` rust ``` rust
{{#include ../../../examples/singleton.rs}} {{#include ../../../../examples/singleton.rs}}
``` ```
``` console ``` console
$ cargo run --example singleton $ cargo run --example singleton
{{#include ../../../ci/expected/singleton.run}}``` {{#include ../../../../ci/expected/singleton.run}}```

View file

@ -19,12 +19,12 @@ The example below showcases three software tasks that run at 2 different
priorities. The three tasks map to 2 interrupts handlers. priorities. The three tasks map to 2 interrupts handlers.
``` rust ``` rust
{{#include ../../../examples/task.rs}} {{#include ../../../../examples/task.rs}}
``` ```
``` console ``` console
$ cargo run --example task $ cargo run --example task
{{#include ../../../ci/expected/task.run}}``` {{#include ../../../../ci/expected/task.run}}```
## Message passing ## Message passing
@ -35,12 +35,12 @@ the signature of the task handler.
The example below showcases three tasks, two of them expect a message. The example below showcases three tasks, two of them expect a message.
``` rust ``` rust
{{#include ../../../examples/message.rs}} {{#include ../../../../examples/message.rs}}
``` ```
``` console ``` console
$ cargo run --example message $ cargo run --example message
{{#include ../../../ci/expected/message.run}}``` {{#include ../../../../ci/expected/message.run}}```
## Capacity ## Capacity
@ -55,9 +55,9 @@ capacity is not specified then the second `spawn.foo` call in `UART0` would
fail. fail.
``` rust ``` rust
{{#include ../../../examples/capacity.rs}} {{#include ../../../../examples/capacity.rs}}
``` ```
``` console ``` console
$ cargo run --example capacity $ cargo run --example capacity
{{#include ../../../ci/expected/capacity.run}}``` {{#include ../../../../ci/expected/capacity.run}}```

View file

@ -38,13 +38,13 @@ was scheduled to run first.
> counter functionality has not been implemented in `qemu-system-arm`. > counter functionality has not been implemented in `qemu-system-arm`.
``` rust ``` rust
{{#include ../../../examples/schedule.rs}} {{#include ../../../../examples/schedule.rs}}
``` ```
Running the program on real hardware produces the following output in the console: Running the program on real hardware produces the following output in the console:
``` text ``` text
{{#include ../../../ci/expected/schedule.run}} {{#include ../../../../ci/expected/schedule.run}}
``` ```
## Periodic tasks ## Periodic tasks
@ -54,7 +54,7 @@ through the `scheduled` variable. This information and the `schedule` API can be
used to implement periodic tasks as shown in the example below. used to implement periodic tasks as shown in the example below.
``` rust ``` rust
{{#include ../../../examples/periodic.rs}} {{#include ../../../../examples/periodic.rs}}
``` ```
This is the output produced by the example. Note that there is zero drift / This is the output produced by the example. Note that there is zero drift /
@ -62,7 +62,7 @@ jitter even though `schedule.foo` was invoked at the *end* of `foo`. Using
`Instant::now` instead of `scheduled` would have resulted in drift / jitter. `Instant::now` instead of `scheduled` would have resulted in drift / jitter.
``` text ``` text
{{#include ../../../ci/expected/periodic.run}} {{#include ../../../../ci/expected/periodic.run}}
``` ```
## Baseline ## Baseline
@ -87,11 +87,11 @@ spawned from it will use `Instant::now()` as their baseline time.
The example below showcases the different meanings of the *baseline*. The example below showcases the different meanings of the *baseline*.
``` rust ``` rust
{{#include ../../../examples/baseline.rs}} {{#include ../../../../examples/baseline.rs}}
``` ```
Running the program on real hardware produces the following output in the console: Running the program on real hardware produces the following output in the console:
``` text ``` text
{{#include ../../../ci/expected/baseline.run}} {{#include ../../../../ci/expected/baseline.run}}
``` ```

View file

@ -8,12 +8,12 @@ data. This lets you easily write generic code that operates on resources and can
be called from different tasks. Here's one such example: be called from different tasks. Here's one such example:
``` rust ``` rust
{{#include ../../../examples/generics.rs}} {{#include ../../../../examples/generics.rs}}
``` ```
``` console ``` console
$ cargo run --example generics $ cargo run --example generics
{{#include ../../../ci/expected/generics.run}}``` {{#include ../../../../ci/expected/generics.run}}```
This also lets you change the static priorities of tasks without having to This also lets you change the static priorities of tasks without having to
rewrite code. If you consistently use `lock`s to access the data behind shared rewrite code. If you consistently use `lock`s to access the data behind shared
@ -31,7 +31,7 @@ The example below logs a message whenever the `foo` task is spawned, but only if
the program has been compiled using the `dev` profile. the program has been compiled using the `dev` profile.
``` rust ``` rust
{{#include ../../../examples/cfg.rs}} {{#include ../../../../examples/cfg.rs}}
``` ```
## Running tasks from RAM ## Running tasks from RAM
@ -56,22 +56,22 @@ improve performance in some cases.
The example below shows how to place the higher priority task, `bar`, in RAM. The example below shows how to place the higher priority task, `bar`, in RAM.
``` rust ``` rust
{{#include ../../../examples/ramfunc.rs}} {{#include ../../../../examples/ramfunc.rs}}
``` ```
Running this program produces the expected output. Running this program produces the expected output.
``` console ``` console
$ cargo run --example ramfunc $ cargo run --example ramfunc
{{#include ../../../ci/expected/ramfunc.run}}``` {{#include ../../../../ci/expected/ramfunc.run}}```
One can look at the output of `cargo-nm` to confirm that `bar` ended in RAM One can look at the output of `cargo-nm` to confirm that `bar` ended in RAM
(`0x2000_0000`), whereas `foo` ended in Flash (`0x0000_0000`). (`0x2000_0000`), whereas `foo` ended in Flash (`0x0000_0000`).
``` console ``` console
$ cargo nm --example ramfunc --release | grep ' foo::' $ cargo nm --example ramfunc --release | grep ' foo::'
{{#include ../../../ci/expected/ramfunc.grep.foo}}``` {{#include ../../../../ci/expected/ramfunc.grep.foo}}```
``` console ``` console
$ cargo nm --example ramfunc --release | grep ' bar::' $ cargo nm --example ramfunc --release | grep ' bar::'
{{#include ../../../ci/expected/ramfunc.grep.bar}}``` {{#include ../../../../ci/expected/ramfunc.grep.bar}}```

View file

@ -13,7 +13,7 @@ function.
The example below shows the different types generates by the `app` attribute. The example below shows the different types generates by the `app` attribute.
``` rust ``` rust
{{#include ../../../examples/types.rs}} {{#include ../../../../examples/types.rs}}
``` ```
## `Send` ## `Send`
@ -36,7 +36,7 @@ shared `static mut` resources.
The example below shows where a type that doesn't implement `Send` can be used. The example below shows where a type that doesn't implement `Send` can be used.
``` rust ``` rust
{{#include ../../../examples/not-send.rs}} {{#include ../../../../examples/not-send.rs}}
``` ```
## `Sync` ## `Sync`
@ -56,5 +56,5 @@ resources shared between tasks that run at the *same* priority.
The example below shows where a type that doesn't implement `Sync` can be used. The example below shows where a type that doesn't implement `Sync` can be used.
``` rust ``` rust
{{#include ../../../examples/not-sync.rs}} {{#include ../../../../examples/not-sync.rs}}
``` ```

View file

@ -7,6 +7,10 @@
This book contains user level documentation for the Real Time For the Masses This book contains user level documentation for the Real Time For the Masses
(RTFM) framework. The API reference can be found [here](../api/rtfm/index.html). (RTFM) framework. The API reference can be found [here](../api/rtfm/index.html).
{{#include ../../README.md:5:46}} There is a translation of this book in [Russian].
{{#include ../../README.md:52:}} [Russian]: ../ru/index.html
{{#include ../../../README.md:5:46}}
{{#include ../../../README.md:52:}}

5
book/ru/book.toml Normal file
View file

@ -0,0 +1,5 @@
[book]
authors = ["Jorge Aparicio"]
multilingual = false
src = "src"
title = "Real Time For the Masses"

94
book/ru/src/README_RU.md Normal file
View file

@ -0,0 +1,94 @@
# Real Time For the Masses
Конкурентный фреймворк для создания систем реального времени.
## Возможности
- **Задачи** - единица конкуренции [^1]. Задачи могут *запускаться по событию*
(в ответ на асинхронный стимул) или вызываться программно по желанию.
- **Передача сообщений** между задачами. А именно, сообщения можно передавать
программным задачам в момент вызова.
- **Очередь таймера** [^2]. Программные задачи можно планировать на запуск в
определенный момент в будущем. Это свойство можно использовать, чтобы
реализовывать периодические задачи.
- Поддержка приоритетов задач, и таким образом, **вытесняющей многозадачности**.
- **Эффективное, свободное от гонок данных разделение памяти** через хорошо
разграниченные критические секции на *основе приоритетов* [^1].
- **Выполнение без взаимной блокировки задач**, гарантированное на этапе
компиляции. Это более сильная гарантия, чем предоставляемая
[стандартной абстракцией `Mutex`][std-mutex].
[std-mutex]: https://doc.rust-lang.org/std/sync/struct.Mutex.html
- **Минимальные затраты на диспетчеризацию**. Диспетчер задач имеет
минимальный след; основная часть работы по диспетчеризации делается аппаратно.
- **Высокоэффективное использование памяти**: Все задачи используют общий стек
вызовов и нет сильной зависимости от динамического распределителя памяти.
- **Все устройства Cortex-M полностью поддерживаются**.
- Эта модель задач поддается известному анализу методом WCET (наихудшего
времени исполнения) и техникам анализа диспетчеризации. (Хотя мы еще не
разработали для дружественных инструментов для этого).
## Требования
- Rust 1.31.0+
- Программы нужно писать используя 2018 edition.
## [User documentation](https://japaric.github.io/cortex-m-rtfm/book)
## [API reference](https://japaric.github.io/cortex-m-rtfm/api/rtfm/index.html)
## Благодарности
Эта библиотека основана на [языке RTFM][rtfm-lang], созданном Embedded
Systems group в [Техническом Университете Luleå][ltu], под рук.
[Prof. Per Lindgren][per].
[rtfm-lang]: http://www.rtfm-lang.org/
[ltu]: https://www.ltu.se/?l=en
[per]: https://www.ltu.se/staff/p/pln-1.11258?l=en
## Ссылки
[^1]: Eriksson, J., Häggström, F., Aittamaa, S., Kruglyak, A., & Lindgren, P.
(2013, June). Real-time for the masses, step 1: Programming API and static
priority SRP kernel primitives. In Industrial Embedded Systems (SIES), 2013
8th IEEE International Symposium on (pp. 110-113). IEEE.
[^2]: Lindgren, P., Fresk, E., Lindner, M., Lindner, A., Pereira, D., & Pinho,
L. M. (2016). Abstract timers and their implementation onto the arm cortex-m
family of mcus. ACM SIGBED Review, 13(1), 48-53.
## Лицензия
Все исходные тексты (включая примеры кода) лицензированы либо под:
- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) или
[https://www.apache.org/licenses/LICENSE-2.0][L1])
- MIT license ([LICENSE-MIT](LICENSE-MIT) or
[https://opensource.org/licenses/MIT][L2])
[L1]: https://www.apache.org/licenses/LICENSE-2.0
[L2]: https://opensource.org/licenses/MIT
на Ваше усмотрение.
Текст книги лицензирован по условиям лицензий
Creative Commons CC-BY-SA v4.0 ([LICENSE-CC-BY-SA](LICENSE-CC-BY-SA) или
[https://creativecommons.org/licenses/by-sa/4.0/legalcode][L3]).
[L3]: https://creativecommons.org/licenses/by-sa/4.0/legalcode
### Contribution
Если вы явно не заявляете иначе, любой взнос, преднамеренно представленный
для включения в эту работу, как определено в лицензии Apache-2.0, лицензируется, как указано выше, без каких-либо дополнительных условий.

16
book/ru/src/SUMMARY.md Normal file
View file

@ -0,0 +1,16 @@
# Summary
[Введение](./preface.md)
- [RTFM в примерах](./by-example.md)
- [Атрибут `app`](./by-example/app.md)
- [Ресурсы](./by-example/resources.md)
- [Задачи](./by-example/tasks.md)
- [Очередь таймера](./by-example/timer-queue.md)
- [Одиночки](./by-example/singletons.md)
- [Типы, Send и Sync](./by-example/types-send-sync.md)
- [Создание нового проекта](./by-example/new.md)
- [Советы и хитрости](./by-example/tips.md)
- [Под капотом](./internals.md)
- [Ceiling analysis](./internals/ceilings.md)
- [Диспетчер задач](./internals/tasks.md)
- [Очередь таймера](./internals/timer-queue.md)

16
book/ru/src/by-example.md Normal file
View file

@ -0,0 +1,16 @@
# RTFM в примерах
Эта часть книги представляет фреймворк Real Time For the Masses (RTFM)
новым пользователям через примеры с растущей сложностью.
Все примеры в этой книге можно найти в [репозитории] проекта на GitHub,
и большинство примеров можно запустить на эмуляторе QEMU, поэтому никакого
специального оборудования не требуется их выполнять.
[репозитории]: https://github.com/japaric/cortex-m-rtfm
Чтобы запустить примеры на Вашем ноутбуке / ПК, Вам нужна программа
`qemu-system-arm`. Инструкции по настройке окружения для разработки
встраиваемых устройств, в том числе QEMU, Вы можете найти в [the embedded Rust book].
[the embedded Rust book]: https://rust-embedded.github.io/book/intro/install.html

View file

@ -0,0 +1,101 @@
# The `app` attribute
Это наименьшая возможная программа на RTFM:
``` rust
{{#include ../../../../examples/smallest.rs}}
```
Все программы на RTFM используют атрибут [`app`] (`#[app(..)]`). Этот атрибут
нужно применять к `const`-элементам, содержащим элементы. Атрибут `app` имеет
обязательный аргумент `device`, в качестве значения которому передается *путь*.
Этот путь должен указывать на библиотеку *устройства*, сгенерированную с помощью
[`svd2rust`] **v0.14.x**. Атрибут `app` развернется в удобную точку входа,
поэтому нет необходимости использовать атрибут [`cortex_m_rt::entry`].
[`app`]: ../../api/cortex_m_rtfm_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, который требует ночную сборку. Чтобы заставить
> RTFM работать на стабильной сборке, мы используем вместо него слово `const`.
> Когда большая часть макросов 1.2 стабилизируются, мы прейдем от `const` к `mod` и в конце концов в атрибуту уровне приложения (`#![app]`).
## `init`
Внутри псевдо-модуля атрибут `app` ожидает найти функцию инициализации, обозначенную
атрибутом `init`. Эта функция должна иметь сигнатуру `[unsafe] fn()`.
Эта функция инициализации будет первой частью запускаемого приложения.
Функция `init` запустится *с отключенными прерываниями* и будет иметь эксклюзивный
доступ к периферии Cortex-M и специфичной для устройства периферии через переменные
`core` and `device`, которые внедряются в область видимости `init` атрибутом `app`.
Не вся периферия Cortex-M доступна в `core`, потому что рантайм RTFM принимает владение
частью из неё -- более подробно см. структуру [`rtfm::Peripherals`].
Переменные `static mut`, определённые в начале `init` будут преобразованы
в ссылки `&'static mut` с безопасным доступом.
[`rtfm::Peripherals`]: ../../api/rtfm/struct.Peripherals.html
Пример ниже показывает типы переменных `core` и `device` и
демонстрирует безопасный доступ к переменной `static mut`.
``` rust
{{#include ../../../../examples/init.rs}}
```
Запуск примера напечатает `init` в консоли и завершит процесс QEMU.
``` console
$ cargo run --example init
{{#include ../../../../ci/expected/init.run}}```
## `idle`
Функция, помеченная атрибутом `idle` может присутствовать в псевдо-модуле
опционально. Эта функция используется как специальная *задача ожидания* и должна иметь
сигнатуру `[unsafe] fn() - > !`.
Когда она присутствует, рантайм запустит задачу `idle` после `init`. В отличие от
`init`, `idle` запустится *с включенными прерываниями* и не может завершиться,
поэтому будет работать бесконечно.
Когда функция `idle` определена, рантайм устанавливает бит [SLEEPONEXIT], после чего
отправляет микроконтроллер в состояние сна после выполнения `init`.
[SLEEPONEXIT]: https://developer.arm.com/products/architecture/cpu-architecture/m-profile/docs/100737/0100/power-management/sleep-mode/sleep-on-exit-bit
Как и в `init`, переменные `static mut`будут преобразованы в ссылки `&'static mut`
с безопасным доступом.
В примере ниже показан запуск `idle` после `init`.
``` rust
{{#include ../../../../examples/idle.rs}}
```
``` console
$ cargo run --example idle
{{#include ../../../../ci/expected/idle.run}}```
## `interrupt` / `exception`
Как Вы бы сделали с помощью библиотеки `cortex-m-rt`, Вы можете использовать атрибуты
`interrupt` и `exception` внутри псевдо-модуля `app`, чтобы определить обработчики
прерываний и исключений. В RTFM, мы называем обработчики прерываний и исключений
*аппаратными* задачами.
``` rust
{{#include ../../../../examples/interrupt.rs}}
```
``` console
$ cargo run --example interrupt
{{#include ../../../../ci/expected/interrupt.run}}```
До сих пор программы RTFM, которые мы видели не отличались от программ, которые
можно написать, используя только библиотеку `cortex-m-rt`. В следующем разделе
мы начнем знакомиться с функционалом, присущим только RTFM.

View file

@ -0,0 +1,67 @@
# Создание нового проекта
Теперь, когда Вы изучили основные возможности фреймворка RTFM, Вы можете
попробовать его использовать на Вашем оборудовании следуя этим инструкциям.
1. Создайте экземпляр из шаблона [`cortex-m-quickstart`].
[`cortex-m-quickstart`]: https://github.com/rust-embedded/cortex-m-quickstart#cortex-m-quickstart
``` console
$ # например используя `cargo-generate`
$ cargo generate \
--git https://github.com/rust-embedded/cortex-m-quickstart \
--name app
$ # следуйте остальным инструкциям
```
2. Добавьте крейт устройства, сгенерированный с помощью [`svd2rust`] **v0.14.x**,
или библиотеку отладочной платы, у которой в зависимостях одно из устройств.
Убедитесь, что опция `rt` крейта включена.
[`svd2rust`]: https://crates.io/crates/svd2rust
В этом примере я покажу использование крейта устройства [`lm3s6965`].
Эта библиотека не имеет Cargo-опции `rt`; эта опция всегда включена.
[`lm3s6965`]: https://crates.io/crates/lm3s6965
Этот крейт устройства предоставляет линковочный скрипт с макетом памяти
целевого устройства, поэтому `memory.x` и `build.rs` не нужно удалять.
``` console
$ cargo add lm3s6965 --vers 0.1.3
$ rm memory.x build.rs
```
3. Добавьте библиотеку `cortex-m-rtfm` как зависимость, и если необходимо,
включите опцию `timer-queue`.
``` console
$ cargo add cortex-m-rtfm --allow-prerelease --upgrade=none
```
4. Напишите программу RTFM.
Здесь я буду использовать пример `init` из библиотеки `cortex-m-rtfm`.
``` console
$ curl \
-L https://github.com/japaric/cortex-m-rtfm/raw/v0.4.0-beta.1/examples/init.rs \
> src/main.rs
```
Этот пример зависит от библиотеки `panic-semihosting`:
``` console
$ cargo add panic-semihosting
```
5. Соберите его, загрузите в микроконтроллер и запустите.
``` console
$ # ПРИМЕЧАНИЕ: Я раскомментировал опцию `runner` в `.cargo/config`
$ cargo run
{{#include ../../../../ci/expected/init.run}}```

View file

@ -0,0 +1,122 @@
## Ресурсы
Одно из ограничений атрибутов, предоставляемых библиотекой `cortex-m-rt` является
то, что совместное использование данных (или периферии) между прерываниями,
или прерыванием и функцией `init`, требуют `cortex_m::interrupt::Mutex`, который
*всегда* требует отключения *всех* прерываний для доступа к данным. Отключение всех
прерываний не всегда необходимо для безопасности памяти, но компилятор не имеет
достаточно информации, чтобы оптимизировать доступ к разделяемым данным.
Атрибут `app` имеет полную картину приложения, поэтому может оптимизировать доступ к
`static`-переменным. В RTFM мы обращаемся к `static`-переменным, объявленным внутри
псевдо-модуля `app` как к *ресурсам*. Чтобы получить доступ к ресурсу, контекст
(`init`, `idle`, `interrupt` или `exception`) должен сначала определить
аргумент `resources` в соответствующем атрибуте.
В примере ниже два обработчика прерываний имеют доступ к одному и тому же ресурсу.
Никакого `Mutex` в этом случае не требуется, потому что оба обработчика запускаются
с одним приоритетом и никакого вытеснения быть не может.
К ресурсу `SHARED` можно получить доступ только из этих двух прерываний.
``` rust
{{#include ../../../../examples/resource.rs}}
```
``` console
$ cargo run --example resource
{{#include ../../../../ci/expected/resource.run}}
```
## Приоритеты
Приоритет каждого прерывания можно определить в атрибутах `interrupt` и `exception`.
Невозможно установить приоритет любым другим способом, потому что рантайм
забирает владение прерыванием `NVIC`; также невозможно изменить приоритет
обработчика / задачи в рантайме. Благодаря этому ограничению у фреймворка
есть знание о *статических* приоритетах всех обработчиков прерываний и исключений.
Прерывания и исключения могут иметь приоритеты в интервале `1..=(1 << NVIC_PRIO_BITS)`,
где `NVIC_PRIO_BITS` - константа, определённая в библиотеке `device`.
Задача `idle` имеет приоритет `0`, наименьший.
Ресурсы, совместно используемые обработчиками, работающими на разных приоритетах,
требуют критических секций для безопасности памяти. Фреймворк проверяет, что
критические секции используются, но *только где необходимы*: например,
критические секции не нужны для обработчика с наивысшим приоритетом, имеющим
доступ к ресурсу.
API критической секции, предоставляемое фреймворком RTFM (см. [`Mutex`]),
основано на динамических приоритетах вместо отключения прерываний. Из этого следует,
что критические секции не будут допускать *запуск некоторых* обработчиков,
включая все соперничающие за ресурс, но будут позволять запуск обработчиков с
большим приоритетом не соперничащих за ресурс.
[`Mutex`]: ../../api/rtfm/trait.Mutex.html
В примере ниже у нас есть 3 обработчика прерываний с приоритетами от одного
до трех. Два обработчика с низким приоритетом соперничают за ресурс `SHARED`.
Обработчик с низшим приоритетом должен заблокировать ([`lock`]) ресурс
`SHARED`, чтобы получить доступ к его данным, в то время как обработчик со
средним приоритетом может напрямую получать доступ к его данным. Обработчик
с наивысшим приоритетом может свободно вытеснять критическую секцию,
созданную обработчиком с низшим приоритетом.
[`lock`]: ../../api/rtfm/trait.Mutex.html#method.lock
``` rust
{{#include ../../../../examples/lock.rs}}
```
``` console
$ cargo run --example lock
{{#include ../../../../ci/expected/lock.run}}```
## Поздние ресурсы
В отличие от обычных `static`-переменных, к которым должно быть присвоено
начальное значение, ресурсы можно инициализировать в рантайме.
Мы называем ресурсы, инициализируемые в рантайме *поздними*. Поздние ресурсы
полезны для *переноса* (как при передаче владения) периферии из `init` в
обработчики прерываний и исключений.
Поздние ресурсы определяются как обычные ресурсы, но им присваивается начальное
значение `()` (the unit value). Поздние ресурсы необходимо инициализировать в конце
функции `init` используя обычное присвоение (например `FOO = 1`).
В примере ниже использованы поздние ресурсы, чтобы установить неблокированный,
односторонний канал между обработчиком прерывания `UART0` и функцией `idle`.
Очередь типа один производитель-один потребитель [`Queue`] использована как канал.
Очередь разделена на элементы потребителя и поизводителя в `init` и каждый элемент
расположен в отдельном ресурсе; `UART0` владеет ресурсом произодителя, а `idle`
владеет ресурсом потребителя.
[`Queue`]: ../../api/heapless/spsc/struct.Queue.html
``` rust
{{#include ../../../../examples/late.rs}}
```
``` console
$ cargo run --example late
{{#include ../../../../ci/expected/late.run}}```
## `static`-ресурсы
Переменные типа `static` также можно использовать в качестве ресурсов. Задачи
могут получать только (разделяемые) `&` ссылки на ресурсы, но блокировки не
нужны для доступа к данным. Вы можете думать о `static`-ресурсах как о простых
`static`-переменных, которые можно инициализировать в рантайме и иметь лучшие
правила видимости: Вы можете контролировать, какие задачи получают доступ к
переменной, чтобы переменная не была видна всем фунциям в область видимости,
где она была объявлена.
В примере ниже ключ загружен (или создан) в рантайме, а затем использован в двух
задачах, запущенных на разных приоритетах.
``` rust
{{#include ../../../../examples/static.rs}}
```
``` console
$ cargo run --example static
{{#include ../../../../ci/expected/static.run}}```

View file

@ -0,0 +1,26 @@
# Одиночки
Атрибут `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}}```

View file

@ -0,0 +1,63 @@
# Программные задачи
RTFM обрабатывает прерывания и исключения как *аппаратные* задачи. Аппаратные
задачи могут вызываться устройством в ответ на события, такие как нажатие кнопки.
RTFM также поддерживает *программные* задачи, порождаемые программой из любого
контекста выполнения.
Программным задачам также можно назначать приоритет и диспетчеризовать из
обработчиков прерываний. RTFM требует определения свободных прерываний в блоке
`extern`, когда используются программные задачи; эти свободные прерывания будут использованы, чтобы диспетчеризовать программные задачи. Преимущество программных
задач перед аппаратными в том, что на один обработчик прерывания можно назначить
множество задач.
Программные задачи определяются заданием функциям атрибута `task`. Чтобы было
возможно вызывать программные задачи, имя задачи нужно передать в аргументе
`spawn` контекста атрибута (`init`, `idle`, `interrupt`, etc.).
В примере ниже продемонстрированы три программных задачи, запускаемые на 2-х
разных приоритетах. Трем задачам назначены 2 обработчика прерываний.
``` rust
{{#include ../../../../examples/task.rs}}
```
``` console
$ cargo run --example task
{{#include ../../../../ci/expected/task.run}}```
## Передача сообщений
Другое преимущество программных задач - возможность передавать сообщения задачам
во время их вызова. Тип полезной нагрузки сообщения должен быть определен в
сигнатуре обработчика задачи.
Пример ниже демонстрирует три задачи, две из которых ожидают сообщения.
``` rust
{{#include ../../../../examples/message.rs}}
```
``` console
$ cargo run --example message
{{#include ../../../../ci/expected/message.run}}```
## Ёмкость
Диспетчеры задач *не* используют динамическое выделение памяти. Память
необходимая для размещения сообщений, резервируется статически. Фреймворк
зарезервирует достаточно памяти для каждого контекста, чтобы можно было вызвать
каждую задачу как минимум единожды. Это разумно по умолчанию, но
"внутреннюю" ёмкость каждой задачи можно контролировать используя аргумент
`capacity` атрибута `task`.
В примере ниже установлена ёмкость программной задачи `foo` на 4. Если ёмкость
не определена, тогда второй вызов `spawn.foo` в `UART0` вызовет ошибку.
``` rust
{{#include ../../../../examples/capacity.rs}}
```
``` console
$ cargo run --example capacity
{{#include ../../../../ci/expected/capacity.run}}```

View file

@ -0,0 +1,90 @@
# Очередь таймера
Когда включена опция `timer-queue`, фреймворк RTFM включает
*глобальную очередь таймера*, которую приложения могут использовать, чтобы
*планировать* программные задачи на запуск через некоторое время в будущем.
Чтобы была возможность планировать программную задачу, имя задачи должно
присутствовать в аргументе `schedule` контекста атрибута. Когда задача
планируется, момент ([`Instant`]), в который задачу нужно запустить, нужно передать
как первый аргумент вызова `schedule`.
[`Instant`]: ../../api/rtfm/struct.Instant.html
Рантайм RTFM включает монотонный, растущий только вверх, 32-битный таймер,
значение которого можно запросить конструктором `Instant::now`. Время ([`Duration`])
можно передать в `Instant::now()`, чтобы получить `Instant` в будущем. Монотонный
таймер отключен пока запущен `init`, поэтому `Instant::now()` всегда возвращает
значение `Instant(0 /* циклов тактовой частоты */)`; таймер включается сразу перед
включением прерываний и запуском `idle`.
[`Duration`]: ../../api/rtfm/struct.Duration.html
В примере ниже две задачи планируются из `init`: `foo` и `bar`. `foo` -
запланирована на запуск через 8 миллионов тактов в будущем. Кроме того, `bar`
запланирован на запуск через 4 миллиона тактов в будущем. `bar` запустится раньше
`foo`, т.к. он запланирован на запуск первым.
> **ВАЖНО**: Примеры, использующие API `schedule` или абстракцию `Instant`
> **не** будут правильно работать на QEMU, потому что функциональность счетчика
> тактов Cortex-M не реализована в `qemu-system-arm`.
``` rust
{{#include ../../../../examples/schedule.rs}}
```
Запуск программы на реальном оборудовании производит следующий вывод в консоли:
``` text
{{#include ../../../../ci/expected/schedule.run}}
```
## Периодические задачи
Программные задачи имеют доступ к `Instant` в момент, когда были запланированы
на запуск через переменную `scheduled`. Эта информация и API `schedule` могут
быть использованы для реализации периодических задач, как показано в примере ниже.
``` rust
{{#include ../../../../examples/periodic.rs}}
```
Это вывод, произведенный примером. Заметьте, что есть смещение / колебание нуля
даже если `schedule.foo` была вызвана в *конце* `foo`. Использование
`Instant::now` вместо `scheduled` имело бы влияние на смещение / колебание.
``` text
{{#include ../../../../ci/expected/periodic.run}}
```
## Базовое время
Для задач, планируемых из `init` мы имеем точную информацию о их планируемом
(`scheduled`) времени. Для аппаратных задач нет `scheduled` времени, потому
что эти задачи асинхронны по природе. Для аппаратных задач рантайм предоставляет
время старта (`start`), которе отражает время, в которое обработчик прерывания
был запущен.
Заметьте, что `start` **не** равен времени возникновения события, вызвавшего
задачу. В зависимости от приоритета задачи и загрузки системы время
`start` может быть сильно отдалено от времени возникновения события.
Какое по Вашему мнению будет значение `scheduled` для программных задач которые
*вызываются*, вместо того чтобы планироваться? Ответ в том, что вызываемые
задачи наследуют *базовое* время контекста, в котором вызваны. Бызовым для
аппаратных задач является `start`, базовым для программных задач - `scheduled`
и базовым для `init` - `start = Instant(0)`. `idle` на сомом деле не имеет
базового времени но задачи, вызванные из него будут использовать `Instant::now()`
как их базовое время.
Пример ниже демонстрирует разное значение *базового времени*.
``` rust
{{#include ../../../../examples/baseline.rs}}
```
Запуск программы на реальном оборудовании произведет следующий вывод в консоли:
``` text
{{#include ../../../../ci/expected/baseline.run}}
```

View file

@ -0,0 +1,63 @@
# Советы и хитрости
## Обобщенное программирование (Generics)
Ресурсы, совместно используемые двумя или более задачами, реализуют трейт `Mutex`
во *всех* контекстах, даже в тех, где для доступа к данным не требуются
критические секции. Это позволяет легко писать обобщенный код оперирующий
ресурсами, который можно вызывать из различных задач. Вот такой пример:
``` rust
{{#include ../../../../examples/generics.rs}}
```
``` console
$ cargo run --example generics
{{#include ../../../../ci/expected/generics.run}}```
Это также позволяет Вам изменять статические приоритеты задач без
переписывания кода. Если Вы единообразно используете `lock`-и для доступа
к данным в разделяемых ресурсах, тогда Ваш код продолжит компилироваться,
когда Вы измените приоритет задач.
## Запуск задач из ОЗУ
Главной целью переноса описания программы на RTFM в атрибуты в
RTFM v0.4.x была возможность взаимодействия с другими атрибутами.
Напримерe, атрибут `link_section` можно применять к задачам, чтобы разместить
их в ОЗУ; это может улучшить производительность в некоторых случаях.
> **ВАЖНО**: Обычно атрибуты `link_section`, `export_name` и `no_mangle`
> очень мощные, но их легко использовать неправильно. Неверное использование
> любого из этих атрибутов может вызвать неопределенное поведение;
> Вам следует всегда предпочитать использование безопасных, высокоуровневых
> атрибутов вокруг них, таких как атрибуты `interrupt` и `exception`
> из `cortex-m-rt`.
>
> В особых случаях функций RAM нет безопасной абстракции в `cortex-m-rt`
> v0.6.5 но создано [RFC] для добавления атрибута `ramfunc` в будущем релизе.
[RFC]: https://github.com/rust-embedded/cortex-m-rt/pull/100
В примере ниже показано как разместить высокоприоритетную задачу `bar` в ОЗУ.
``` rust
{{#include ../../../../examples/ramfunc.rs}}
```
Запуск этой программы произведет ожидаемый вывод.
``` console
$ cargo run --example ramfunc
{{#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}}```
``` console
$ cargo nm --example ramfunc --release | grep ' bar::'
{{#include ../../../../ci/expected/ramfunc.grep.bar}}```

View file

@ -0,0 +1,59 @@
# Типы, Send и Sync
Атрибут `app` вводит контекст, коллекцию переменных в каждую из функций.
Все эти переменные имеют предсказуемые, неанонимные типы, поэтому Вы можете
писать простые функции, получающие их как аргументы.
Описание API определяет как эти типы эти типы генерируются из входных данных.
Вы можете также сгенерировать документацию для Вашей бинарной библиотеки
(`cargo doc --bin <name>`); в документации Вы найдете структуры `Context`
(например `init::Context` и `idle::Context`), чьи поля представляют переменные
включенные в каждую функцию.
В примере ниже сгенерированы разные типы с помощью атрибута `app`.
``` rust
{{#include ../../../../examples/types.rs}}
```
## `Send`
[`Send`] - маркерный типаж (trait) для "типов, которые можно передавать через границы
потоков", как это определено в `core`. В контексте RTFM типаж `Send` необходим
только там, где возможна передача значения между задачами, запускаемыми на
*разных* приоритетах. Это возникает в нескольких случаях: при передаче сообщений,
в совместно используемых `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}}
```
## `Sync`
Похожая ситуация, [`Sync`] - маркерный типаж для "типов, на которых можно
ссылаться в разных потоках", как это определено в `core`. В контексте RTFM
типаж `Sync` необходим только там, где возможны две или более задачи,
запускаемые на разных приоритетах, чтобы захватить разделяемую ссылку на
ресурс. Это возникает только совместно используемых `static`-ресурсах.
[`Sync`]: https://doc.rust-lang.org/core/marker/trait.Sync.html
Атрибут `app` проверит, что `Sync` реализован, где необходимо, но важно знать,
где ограничение `Sync` не требуется: в `static`-ресурсах, разделяемых между
задачами с *одинаковым* приоритетом.
В примере ниже показано, где можно использовать типы, не реализующие `Sync`.
``` rust
{{#include ../../../../examples/not-sync.rs}}
```

7
book/ru/src/internals.md Normal file
View file

@ -0,0 +1,7 @@
# Под капотом
В этом разделе описывабтся внутренности фркймворка на *высоком уровне*.
Низкоуровневые тонкости, такие как парсинг и кодогенерация производимые
процедурным макросом (`#[app]`) здесь объясняться не будут. Мы сосредоточимся
на анализе пользовательской спецификации и структурах данных, используемых
рантаймом.

View file

@ -0,0 +1,3 @@
# Ceiling analysis
**TODO**

View file

@ -0,0 +1,3 @@
# Task dispatcher
**TODO**

View file

@ -0,0 +1,3 @@
# Timer queue
**TODO**

12
book/ru/src/preface.md Normal file
View file

@ -0,0 +1,12 @@
<h1 align="center">Real Time For the Masses</h1>
<p align="center">Конкурентный фреймворк для создания систем реального времени</p>
# Введение
Эта книга содержит документацию уровня пользователя фреймворком Real Time For the Masses
(RTFM). Описание API можно найти [здесь](../api/rtfm/index.html).
{{#include ../..ADME_RU.md:5:54}}
{{#include ../..ADME_RU.md:60:}}

View file

@ -4,10 +4,12 @@ main() {
rm -f .cargo/config rm -f .cargo/config
cargo doc --features timer-queue cargo doc --features timer-queue
( cd book && mdbook build ) ( cd book && mdbook build )
( cd ru && mdbook build )
local td=$(mktemp -d) local td=$(mktemp -d)
cp -r target/doc $td/api cp -r target/doc $td/api
cp -r book/book $td/ cp -r book/book $td/
cp -r ru/book $td/book/ru
cp LICENSE-* $td/book/ cp LICENSE-* $td/book/
mkdir ghp-import mkdir ghp-import
@ -22,6 +24,15 @@ main() {
rm -rf $td rm -rf $td
} }
# fake Travis variables to be able to run this on a local machine
if [ -z ${TRAVIS_BRANCH-} ]; then
TRAVIS_BRANCH=master
fi
if [ -z ${TRAVIS_PULL_REQUEST-} ]; then
TRAVIS_PULL_REQUEST=false
fi
if [ $TRAVIS_BRANCH = master ] && [ $TRAVIS_PULL_REQUEST = false ]; then if [ $TRAVIS_BRANCH = master ] && [ $TRAVIS_PULL_REQUEST = false ]; then
main main
fi fi

View file

@ -23,14 +23,19 @@ main() {
else else
cargo doc cargo doc
fi fi
( cd book && mdbook build ) ( cd book/en && mdbook build )
( cd book/ru && mdbook build )
local td=$(mktemp -d) local td=$(mktemp -d)
cp -r target/doc $td/api cp -r target/doc $td/api
cp -r book/book $td/ mkdir $td/book
cp LICENSE-* $td/book/ cp -r book/en/book $td/book/en
cp -r book/ru/book $td/book/ru
cp LICENSE-* $td/book/en
cp LICENSE-* $td/book/ru
linkchecker $td/book/ linkchecker $td/book/en/
linkchecker $td/book/ru/
linkchecker $td/api/rtfm/ linkchecker $td/api/rtfm/
linkchecker $td/api/cortex_m_rtfm_macros/ linkchecker $td/api/cortex_m_rtfm_macros/
fi fi

View file

@ -7,7 +7,7 @@
//! //!
//! The user level documentation can be found [here]. //! The user level documentation can be found [here].
//! //!
//! [here]: https://japaric.github.io/cortex-m-rtfm/book/ //! [here]: https://japaric.github.io/cortex-m-rtfm/book/en/
//! //!
//! Don't forget to check the documentation of the [`#[app]`] attribute, which is the main component //! Don't forget to check the documentation of the [`#[app]`] attribute, which is the main component
//! of the framework. //! of the framework.