diff --git a/README_ru.md b/README_ru.md index 918d03e3d3..49e66d1d71 100644 --- a/README_ru.md +++ b/README_ru.md @@ -50,10 +50,16 @@ - Приложения должны быть написаны в редакции 2018. +## [Руководство пользователя](https://rtic.rs) - [(Версия в разработке)](https://rtic.rs/dev) + ## [Документация пользователя](https://rtic.rs) ## [Справочник по API](https://rtic.rs/stable/api/) +## [Сборник примеров, предоставляемы сообществом][examples] + +[examples]: https://github.com/rtic-rs/rtic-examples + ## Чат Присоединяйтесь к нам, чтобы говорить о RTIC [в Matrix-комнате][matrix-room]. diff --git a/book/ru/src/by-example/app.md b/book/ru/src/by-example/app.md index 628819adcd..5beca23930 100644 --- a/book/ru/src/by-example/app.md +++ b/book/ru/src/by-example/app.md @@ -23,7 +23,7 @@ Внутри модуля `app` атрибут ожидает найти функцию инициализации, помеченную атрибутом `init`. Эта функция должна иметь сигнатуру -`fn(init::Context) [-> init::LateResources]` (возвращаемый тип нужен не всегда). +`fn(init::Context) (-> init::LateResources, init::Monotonics)`. Эта функция инициализации будет первой частью программы, выполняемой при запуске. Функция `init` будет запущена *с отключенными прерываниями* и будет иметь эксклюзивный доступ @@ -54,6 +54,12 @@ $ cargo run --example init {{#include ../../../../ci/expected/init.run}} ``` +> **ПРИМЕЧАНИЕ**: Не забывайте указывать выбранное вами целевое устройство, передавая параметр target +> в cargo (например `cargo run --example init --target thumbv7m-none-eabi`) или +> настроив устройство, используемое по умолчанию для сборки примеров в `.cargo/config.toml`. +> В нашем случае используется Cortex M3, эмулируемый с помощью QEMU, поэтому пишем `thumbv7m-none-eabi`. +> Смотрите [`Создание нового проекта`](./new.md) для большей информации. + ## `idle` Функцию, помеченную атрибутом `idle` может опционально добавить в модуль. diff --git a/book/ru/src/by-example/resources.md b/book/ru/src/by-example/resources.md index 70f798d25e..ed8904ba93 100644 --- a/book/ru/src/by-example/resources.md +++ b/book/ru/src/by-example/resources.md @@ -7,21 +7,26 @@ Фреймворк дает пользователю полный контроль за тем, какой контекст может получить доступ к какому ресурсу. -Все ресурсы определены в одной структуре внутри модуля `#[app]`. -Каждое поле структуры соответствует отдельному ресурсу. -`struct`-ура должна быть аннотирована следующим атрибутом: `#[resources]`. - -Ресурсам могут быть опционально даны начальные значения с помощью атрибута `#[init]`. -Ресурсы, которым не передано начально значение, называются -*поздними* ресурсами, более детально они описаны в одном из разделов на этой странице. +Все ресурсы определены в *двух* структурах внутри модуля `#[app]`. +Каждое поле этих структур соответствует отдельному ресурсу. +Одна `struct`-ура должна быть аннотирована атрибутом `#[local]`. +Другая `struct`-ура должна быть аннотирована атрибутом `#[shared]`. +Разница между этими двумя множествами ресурсов будет описана познее. Каждый контекс (задача-обработчик, `init` или `idle`) должен указать ресурсы, к которым он намерен обращаться, в соответсятвующем ему атрибуте с метаданными, используя -аргумент `resources`. Этот аргумент принимает список имен ресурсов в качестве значения. -Перечисленные ресурсы становятся доступны в контексте через поле `resources` структуры `Context`. +либо аргумент `local`, либо `shared`. Этот аргумент принимает список имен ресурсов в качестве значения. +Перечисленные ресурсы становятся доступны в контексте через поля `local` и `shared` структуры `Context`. -Пример программы, показанной ниже содержит два обработчика прерывания, которые разделяют -доступ к ресурсу под названием `shared`. +Во время выполнения при выходе из функции `#[init]` все ресурсы инициализированы. +Функция `#[init]` должна возвращать начальные значения для всех ресурсов; +отсюда следует, что тип возвращаемого ею значения включает типы +структур `#[shared]` и `#[local]`. +Поскольку ресурсы инициализированы в ходе функции `#[init]`, к ним нельзя +получить доступ внетри функции `#[init]`. + +Пример программы, показанной ниже содержит два обработчика прерывания. +Каждый обработчик имеет доступ к его собственному `#[local]` ресурсу. ``` rust {{#include ../../../../examples/resource.rs}} @@ -32,15 +37,17 @@ $ cargo run --example resource {{#include ../../../../ci/expected/resource.run}} ``` -Заметьте, что к ресурсу `shared` нельзя получить доступ из `idle`. Попытка сделать это -приведет к ошибке компиляции. +К ресурсу `#[local]` нельзя получить доступ извне задачи к которой он +привязан атрибутом `#[task]`. +Попытка обращения к одному и тому же ресурсу `#[local]` из более чем одной +задачи - ошибка компиляции. ## `lock` -Критические секции необходимы для разделения изменяемых данных таким образом, +Критические секции необходимы для доступа к ресурсам `#[shared]` таким образом, чтобы избежать гонок данных. -Поле `resources`, передаваемого `Context` реализует трейт [`Mutex`] для каждого разделяемого +Поле `shared`, передаваемого `Context` реализует трейт [`Mutex`] для каждого разделяемого ресурса, доступного задаче. Единственный метод этого трейта, [`lock`], запускает свой аргумент-замыкание в критической секции. @@ -81,33 +88,7 @@ $ cargo run --example lock {{#include ../../../../examples/multilock.rs}} ``` -## Поздние ресурсы - -Поздние ресурсы - такие ресурсы, которым не передано начальное значение во время компиляции -с помощью атрибута `#[init]`, но которые вместо этого инициализируются во время выполнения -с помощью значений из структуры `init::LateResources`, возвращаемой функцией `init`. - -Поздние ресурсы полезны, например, для *move* (передача владения) периферии, -инициализированной в `init`, в задачи. - -Пример ниже использует поздние ресурсы, чтобы установить неблокируемый односторонний канал -между обработчиком прерывания `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}} -``` - -## Только разделяемый доступ +## Только разделяемый (`&-`) доступ По-умолчанию фреймворк предполагает, что все задачи требуют эксклюзивный доступ (`&mut-`) к ресурсам, но возможно указать, что задаче достаточен разделяемый доступ (`&-`) к ресурсы с помощью синтакисиса @@ -139,11 +120,21 @@ $ cargo run --example only-shared-access ## Неблокируемый доступ к изменяемым ресурсам -Есть две других возможности доступа к ресурсам +Критическая секция *не* требуется для доступа к ресурсу `#[shared]`, +к которому обращаются только из задач с *одинаковым* приоритетом. +В этом случае вы можете избежать `lock` API, добавив атрибут поля `#[lock_free]` при объявдении ресурса (смотреть пример ниже). +Заметьте, что это лишь для удобства: даже если вы используете `lock` API, +во время выполнения фреймворк *не* создаст критическую секцию. +Еще одно ценное замечание: использование `#[lock_free]` на ресурсах, +разделяемых задачами, запускаемыми с разными приоритетами +приведет к ошибке *компиляции* -- не импользование `lock` API может +привести к гонке данных в этом случае. -* `#[lock_free]`: могут быть несколько задач с одинаковым приоритетом, - получающие доступ к ресурсу без критических секций. Так как задачи с - одинаковым приоритетом никогда не могут вытеснить друг друга, это безопасно. -* `#[task_local]`: в этом случае должна быть только одна задача, использующая - этот ресурс, так же как локальный `static mut` ресурс задачи, но (опционально) устанавливаемая с в init. +``` rust +{{#include ../../../../examples/lock-free.rs}} +``` +``` console +$ cargo run --example lock-free +{{#include ../../../../ci/expected/lock-free.run}} +``` \ No newline at end of file diff --git a/book/ru/src/by-example/tips.md b/book/ru/src/by-example/tips.md index cf66c4b741..f19cfee9d8 100644 --- a/book/ru/src/by-example/tips.md +++ b/book/ru/src/by-example/tips.md @@ -1,5 +1,9 @@ # Советы и хитрости +Полные примеры для RTIC смотрите в репозитарии [rtic-examples][rtic-examples]. + +[rtic-examples]: https://github.com/rtic-rs/rtic-examples + ## Обобщенное программирование (Generics) Все объекты, предоставляющие ресурысы реализуют трейт `rtic::Mutex`. diff --git a/book/ru/src/internals/late-resources.md b/book/ru/src/internals/late-resources.md index 0fad0aecf8..146c438d70 100644 --- a/book/ru/src/internals/late-resources.md +++ b/book/ru/src/internals/late-resources.md @@ -103,8 +103,7 @@ mod app { } ``` -Важная деталь здесь то, что `interrupt::enable` ведет себя как like a *compiler -fence*, которое не дает компилятору пореставить запись в `X` *после* +Важная деталь здесь то, что `interrupt::enable` ведет себя как *барьер компиляции*, который не дает компилятору переставить запись в `X` *после* `interrupt::enable`. Если бы компилятор мог делать такие перестановки появились бы гонки данных между этой записью и любой операцией `foo`, взаимодействующей с `X`. diff --git a/book/ru/src/internals/tasks.md b/book/ru/src/internals/tasks.md index 665032515c..01380ba907 100644 --- a/book/ru/src/internals/tasks.md +++ b/book/ru/src/internals/tasks.md @@ -79,8 +79,8 @@ mod app { } // очередь готовности диспетчера задач - // `U4` - целое число, представляющее собой емкость этой очереди - static mut RQ1: Queue, U4> = Queue::new(); + // `5-1=4` - представляет собой емкость этой очереди + static mut RQ1: Queue, 5> = Queue::new(); // обработчик прерывания, выбранный для диспетчеризации задач с приоритетом `1` #[no_mangle] @@ -151,9 +151,9 @@ mod app { const RQ1_CEILING: u8 = 2; // используется, чтобы отследить сколько еще сообщений для `bar` можно поставить в очередь - // `U2` - емкость задачи `bar`; максимум 2 экземпляра можно добавить в очередь + // `3-1=2` - емкость задачи `bar`; максимум 2 экземпляра можно добавить в очередь // эта очередь заполняется фреймворком до того, как запустится `init` - static mut bar_FQ: Queue<(), U2> = Queue::new(); + static mut bar_FQ: Queue<(), 3> = Queue::new(); // Поиск максимального приоритета для конечного потребителя `bar_FQ` const bar_FQ_CEILING: u8 = 2; @@ -227,7 +227,7 @@ mod app { // список свободной памяти: используется для отслеживания свободных ячеек в массиве `baz_INPUTS` // эта очередь инициализируется значениями `0` и `1` перед запуском `init` - static mut baz_FQ: Queue = Queue::new(); + static mut baz_FQ: Queue = Queue::new(); // Поиск максимального приоритета для конечного потребителя `baz_FQ` const baz_FQ_CEILING: u8 = 2; diff --git a/book/ru/src/migration/migration_v5.md b/book/ru/src/migration/migration_v5.md index 04aedc5f81..870c20fd62 100644 --- a/book/ru/src/migration/migration_v5.md +++ b/book/ru/src/migration/migration_v5.md @@ -30,7 +30,46 @@ mod app { Так как теперь используется обычный модуль Rust, это значит, что можно использовать обычный пользовательский код в этом модуле. -Также жто значит, что `use`-выражения для ресурсов (и т.п.) могут понадобиться. +Также это значит, что `use`-выражения для ресурсов, используемые +в пользовательском коде должны быть перемещены внутрь `mod app`, +либо на них можно сослаться с помощью `super`. Например, измените: + +```rust +use some_crate::some_func; + +#[rtic::app(/* .. */)] +const APP: () = { + fn func() { + some_crate::some_func(); + } +}; +``` + +на + +```rust +#[rtic::app(/* .. */)] +mod app { + use some_crate::some_func; + + fn func() { + some_crate::some_func(); + } +} +``` + +или + +```rust +use some_crate::some_func; + +#[rtic::app(/* .. */)] +mod app { + fn func() { + super::some_crate::some_func(); + } +} +``` ## Перенос диспетчеров из `extern "C"` в аргументы app. @@ -63,6 +102,182 @@ mod app { Это работает и для ОЗУ-функций, см. examples/ramfunc.rs +## Структуры ресурсов - `#[shared]`, `#[local]` + +Ранее ресурсы RTIC должны были размещаться в структуре с именем "Resources": + +``` rust +struct Resources { + // Ресурсы определяются здесь +} +``` + +Начиная с RTIC v0.6.0 структуры ресурсов аннотируются подобно +`#[task]`, `#[init]`, `#[idle]`: аттрибутами `#[shared]` и `#[local]` + +``` rust +#[shared] +struct MySharedResources { + // Разделяемые задачами ресурсы определены здесь +} + +#[local] +struct MyLocalResources { + // Ресурсы, определенные здесь нельзя передавать между задачами; каждый из них локальный для единственной задачи +} +``` + +Эти структуры разработчик может называть по своему желанию. + +## `shared` и `local` аргументы в `#[task]`'ах + +В v0.6.0 ресурсы разделены на `shared` ресурсы и `local` ресурсы. +`#[task]`, `#[init]` и `#[idle]` больше не имеют аргумента `resources`; +они должны использовать аргументы `shared` и `local`. + +В v0.5.x: + +``` rust +struct Resources { + local_to_b: i64, + shared_by_a_and_b: i64, +} + +#[task(resources = [shared_by_a_and_b])] +fn a(_: a::Context) {} + +#[task(resources = [shared_by_a_and_b, local_to_b])] +fn b(_: b::Context) {} +``` + +В v0.6.0: + +``` rust +#[shared] +struct Shared { + shared_by_a_and_b: i64, +} + +#[local] +struct Local { + local_to_b: i64, +} + +#[task(shared = [shared_by_a_and_b])] +fn a(_: a::Context) {} + +#[task(shared = [shared_by_a_and_b], local = [local_to_b])] +fn b(_: b::Context) {} +``` + +## Симметричные блокировки + +Теперь RTIC использует симметричные блокировки, это значит, что метод `lock` нужно использовать для +всех доступов к `shared` ресурсам. Поскольку высокоприоритетные задачи имеют эксклюзивный доступ к ресурсу, +в старом коде можно было следующее: + +``` rust +#[task(priority = 2, resources = [r])] +fn foo(cx: foo::Context) { + cx.resources.r = /* ... */; +} + +#[task(resources = [r])] +fn bar(cx: bar::Context) { + cx.resources.r.lock(|r| r = /* ... */); +} +``` + +С симметричными блокировками нужно вызывать `lock` для обоих задач: + +``` rust +#[task(priority = 2, shared = [r])] +fn foo(cx: foo::Context) { + cx.shared.r.lock(|r| r = /* ... */); +} + +#[task(shared = [r])] +fn bar(cx: bar::Context) { + cx.shared.r.lock(|r| r = /* ... */); +} +``` + +Заметьте, что скорость работы не изменяется благодаря оптимизациям LLVM, которые убирают ненужные блокировки. + +## Неблокирующий доступ к ресурсам + +В RTIC 0.5 к ресурсам разделяемым задачами, запускаемыми с одинаковым +приоритетом, можно получить доступ *без* `lock` API. +Это все еще возможно в 0.6: ресурс `#[shared]` должен быть аннотирован +аттрибутом поля `#[lock_free]`. + +v0.5 код: + +``` rust +struct Resources { + counter: u64, +} + +#[task(resources = [counter])] +fn a(cx: a::Context) { + *cx.resources.counter += 1; +} + +#[task(resources = [counter])] +fn b(cx: b::Context) { + *cx.resources.counter += 1; +} +``` + +v0.6 код: + +``` rust +#[shared] +struct Shared { + #[lock_free] + counter: u64, +} + +#[task(shared = [counter])] +fn a(cx: a::Context) { + *cx.shared.counter += 1; +} + +#[task(shared = [counter])] +fn b(cx: b::Context) { + *cx.shared.counter += 1; +} +``` + +## нет преобразования `static mut` + +`static mut` переменные больше не преобразуются в безопасные `&'static mut` ссылки. +Вместо этого синтаксиса используйте аргумент `local` в `#[init]`. + +v0.5.x code: + +``` rust +#[init] +fn init(_: init::Context) { + static mut BUFFER: [u8; 1024] = [0; 1024]; + let buffer: &'static mut [u8; 1024] = BUFFER; +} +``` + +v0.6.0 code: + +``` rust +#[init(local = [ + buffer: [u8; 1024] = [0; 1024] +// type ^^^^^^^^^^^^ ^^^^^^^^^ initial value +])] +fn init(cx: init::Context) -> (Shared, Local, init::Monotonics) { + let buffer: &'static mut [u8; 1024] = cx.local.buffer; + + (Shared {}, Local {}, init::Monotonics()) +} +``` + ## Init всегда возвращает поздние ресурсы С целью сделать API более симметричным задача #[init] всегда возвращает поздние ресурсы. @@ -83,51 +298,27 @@ mod app { на это: + ``` rust #[rtic::app(device = lm3s6965)] mod app { + #[shared] + struct MySharedResources {} + + #[local] + struct MyLocalResources {} + #[init] - fn init(_: init::Context) -> init::LateResources { + fn init(_: init::Context) -> (MySharedResources, MyLocalResources, init::Monotonics) { rtic::pend(Interrupt::UART0); - init::LateResources {} + (MySharedResources, MyLocalResources, init::Monotonics()) } - // [еще код] + // [more code] } ``` -## Структура Resources - `#[resources]` - -Ранее ресурсы RTIC должны были располагаться в структуре с именем "Resources": - -``` rust -struct Resources { - // Ресурсы определены здесь -} -``` - -В RTIC v0.6.0 структура ресурсов аннотируется также, как и -`#[task]`, `#[init]`, `#[idle]`: атрибутом `#[resources]` - -``` rust -#[resources] -struct Resources { - // Ресурсы определены здесь -} -``` - -На самом деле, имя структуры предоставлено на усмотрение разработчика: - -``` rust -#[resources] -struct Whateveryouwant { - // Ресурсы определены здесь -} -``` - -будет работать так же хороршо. - ## Вызов/планирование откуда угодно С этой новой возвожностью, старый код, такой как: @@ -161,40 +352,6 @@ fn bar(_c: bar::Context) { Заметьте, что атрибуты `spawn` и `schedule` больше не нужны. -## Симметричные блокировки - -Теперь RTIC использует симметричные блокировки, это значит, что метод `lock` нужно использовать для -всех доступов к ресурсам. Поскольку высокоприоритетные задачи имеют эксклюзивный доступ к ресурсу, -в старом коде можно было следующее: - -``` rust -#[task(priority = 2, resources = [r])] -fn foo(cx: foo::Context) { - cx.resources.r = /* ... */; -} - -#[task(resources = [r])] -fn bar(cx: bar::Context) { - cx.resources.r.lock(|r| r = /* ... */); -} -``` - -С симметричными блокировками нужно вызывать `lock` для обоих задач: - -``` rust -#[task(priority = 2, resources = [r])] -fn foo(cx: foo::Context) { - cx.resources.r.lock(|r| r = /* ... */); -} - -#[task(resources = [r])] -fn bar(cx: bar::Context) { - cx.resources.r.lock(|r| r = /* ... */); -} -``` - -Заметьте, что скорость работы не изменяется благодаря оптимизациям LLVM, которые убирают ненужные блокировки. - --- ## Дополнительно