mirror of
https://github.com/rtic-rs/rtic.git
synced 2024-12-25 19:39:32 +01:00
Merge #139
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:
commit
672886a67a
37 changed files with 818 additions and 48 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -1,6 +1,6 @@
|
||||||
**/*.rs.bk
|
**/*.rs.bk
|
||||||
.#*
|
.#*
|
||||||
.gdb_history
|
.gdb_history
|
||||||
/book/book
|
/book/*/book
|
||||||
/target
|
/target
|
||||||
Cargo.lock
|
Cargo.lock
|
||||||
|
|
|
@ -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
|
|
@ -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}}```
|
|
@ -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}}```
|
|
@ -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}}```
|
|
@ -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}}```
|
|
@ -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}}
|
||||||
```
|
```
|
|
@ -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}}```
|
|
@ -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}}
|
||||||
```
|
```
|
|
@ -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
5
book/ru/book.toml
Normal 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
94
book/ru/src/README_RU.md
Normal 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
16
book/ru/src/SUMMARY.md
Normal 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
16
book/ru/src/by-example.md
Normal 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
|
101
book/ru/src/by-example/app.md
Normal file
101
book/ru/src/by-example/app.md
Normal 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.
|
67
book/ru/src/by-example/new.md
Normal file
67
book/ru/src/by-example/new.md
Normal 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}}```
|
122
book/ru/src/by-example/resources.md
Normal file
122
book/ru/src/by-example/resources.md
Normal 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}}```
|
26
book/ru/src/by-example/singletons.md
Normal file
26
book/ru/src/by-example/singletons.md
Normal 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}}```
|
63
book/ru/src/by-example/tasks.md
Normal file
63
book/ru/src/by-example/tasks.md
Normal 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}}```
|
90
book/ru/src/by-example/timer-queue.md
Normal file
90
book/ru/src/by-example/timer-queue.md
Normal 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}}
|
||||||
|
```
|
63
book/ru/src/by-example/tips.md
Normal file
63
book/ru/src/by-example/tips.md
Normal 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}}```
|
59
book/ru/src/by-example/types-send-sync.md
Normal file
59
book/ru/src/by-example/types-send-sync.md
Normal 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
7
book/ru/src/internals.md
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
# Под капотом
|
||||||
|
|
||||||
|
В этом разделе описывабтся внутренности фркймворка на *высоком уровне*.
|
||||||
|
Низкоуровневые тонкости, такие как парсинг и кодогенерация производимые
|
||||||
|
процедурным макросом (`#[app]`) здесь объясняться не будут. Мы сосредоточимся
|
||||||
|
на анализе пользовательской спецификации и структурах данных, используемых
|
||||||
|
рантаймом.
|
3
book/ru/src/internals/ceilings.md
Normal file
3
book/ru/src/internals/ceilings.md
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
# Ceiling analysis
|
||||||
|
|
||||||
|
**TODO**
|
3
book/ru/src/internals/tasks.md
Normal file
3
book/ru/src/internals/tasks.md
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
# Task dispatcher
|
||||||
|
|
||||||
|
**TODO**
|
3
book/ru/src/internals/timer-queue.md
Normal file
3
book/ru/src/internals/timer-queue.md
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
# Timer queue
|
||||||
|
|
||||||
|
**TODO**
|
12
book/ru/src/preface.md
Normal file
12
book/ru/src/preface.md
Normal 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:}}
|
|
@ -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
|
||||||
|
|
13
ci/script.sh
13
ci/script.sh
|
@ -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
|
||||||
|
|
|
@ -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.
|
||||||
|
|
Loading…
Reference in a new issue