aboutsummaryrefslogtreecommitdiff
path: root/book/ru/src/by-example/app.md
blob: 5beca2393010ae696947085b50dda2f7bfa8cea4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
# Атрибут `app`

Это простейшая из возможных программ на RTIC:

``` rust
{{#include ../../../../examples/smallest.rs}}
```

Все программы на RTIC используют атрибут [`app`] (`#[app(..)]`). Этот атрибут
должен применяться к элементу `mod`. Атрибут `app` имеет обязательный аргумент `device`,
который принимает *путь* как значение. Это должен быть полный путь, указывающий на
*крейт доступа к периферии* (PAC), сгенерированный с помощью [`svd2rust`] версии **v0.14.x**
или новее. Более подробно в разделе [Создание нового проекта](./new.md).

Атрибут `app` будет раскрыт в подходящую точку входа программы, поэтому
атрибут [`cortex_m_rt::entry`] не нужен.

[`app`]: ../../../api/cortex_m_rtic_macros/attr.app.html
[`svd2rust`]: https://crates.io/crates/svd2rust
[`cortex_m_rt::entry`]: ../../../api/cortex_m_rt_macros/attr.entry.html

## `init`

Внутри модуля `app` атрибут ожидает найти функцию инициализации, помеченную
атрибутом `init`. Эта функция должна иметь сигнатуру
`fn(init::Context) (-> init::LateResources, init::Monotonics)`.

Эта функция инициализации будет первой частью программы, выполняемой при запуске.
Функция `init` будет запущена *с отключенными прерываниями* и будет иметь эксклюзивный доступ
к Cortex-M, в котором токен `bare_metal::CriticalSection` доступен как `cs`.
Опционально, устройство-специфичные периферия доступна через поля `core` и `device` структуры
`init::Context`.

`static mut` переменные, определенные в начале `init` будут преобразованы в
`&'static mut` ссылки, безопасные для доступа. Обратите внимание, данная возможность может
быть удалена в следующем релизе, см. `task_local` ресурсы.

[`rtic::Peripherals`]: ../../api/rtic/struct.Peripherals.html

Пример ниже показывает типы полей `core`, `device` и `cs`, и демонстрирует
безопасный доступ к `static mut` переменной. Поле `device` доступно только
когда аргумент `peripherals` установлен в `true` (по умолчанию).
В редких случаях, когда вы захотите создать приложение с минимальным потреблением ресурсов,
можно явно установить `peripherals` в `false`.

``` rust
{{#include ../../../../examples/init.rs}}
```

Запуск примера напечатате `init` в консоли, а затем завершит процесс QEMU.

```  console
$ 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` может опционально добавить в модуль.
Эта функция используется как специальная *задача ожидания* и должна иметь сигнатуру
`fn(idle::Context) - > !`.

Если она присутствует, задача `idle` будет запущена после `init`. В отличие от
`init`, `idle` будет запущена *с включенными прерываниями* и она не может вернуть результат,
а значит должна работать вечно.

Если функция `idle` не определена, среда вполнения устанавливает бит [SLEEPONEXIT], а затем
отправляет микроконтроллер в сон после запуска `init`.

[SLEEPONEXIT]: https://developer.arm.com/docs/100737/0100/power-management/sleep-mode/sleep-on-exit-bit

Как и в  `init`, `static mut` переменные будут трансформированы в `&'static mut` ссылки,
безопасные для доступа. Обратите внимание, данная возможность может
быть удалена в следующем релизе, см. `task_local` ресурсы.

Пример ниже показывает, что `idle` запускается после `init`.

**Примечание:** Цикл `loop {}` в функци ожидания не может быть пустым, так как это сломает
микроконтроллер, из-за того, что LLVM компилирует пустые циклы в инструкцию `UDF` в release mode.
Чтобы избежать неопределенного поведения, цикл должен включать "side-effect"
путем вставки ассемблерной инструкции (например, `WFI`) или ключевого слова `continue`.

``` rust
{{#include ../../../../examples/idle.rs}}
```

``` console
$ cargo run --example idle
{{#include ../../../../ci/expected/idle.run}}
```

## Аппаратные задачи

Чтобы объявить обработчик прерывания, фреймворк предоставляет атрибут `#[task]`,
который можно применять к функциям. Этот атрибут берет аргумент `binds`, чье значение -
это имя прерывания, которому будет назначен обработчик;
функция, декорированная этим атрибутом становится обработчиком прерывания.
В фреймворке такие типы задач именуются *аппаратными*, потому что они начинают
выполняться в ответ на аппаратное событие.

Пример ниже демонстрирует использование атрибута `#[task]`, чтобы объявить
обработчик прерывания. Как и в случае с `#[init]` и `#[idle]` локальные  `static
mut` переменные безопасны для использования с аппаратной задачей.

``` rust
{{#include ../../../../examples/hardware.rs}}
```

``` console
$ cargo run --example hardware
{{#include ../../../../ci/expected/hardware.run}}
```

До сих пор все программы на RTIC, которые мы видели, не отличались от программ,
которые можно написать, используя лишь крейт `cortex-m-rt`. С этого момента мы
начинаем представлять возможности, уникальные для RTIC.

## Приоритеты

Статический приоритет каждого обработчика можно оределить в атрибуте `task`, используя
аргумент `priority`. Задачи могут иметь приоритет в диапазоне `1..=(1 << NVIC_PRIO_BITS)`,
где `NVIC_PRIO_BITS` - это константа, определенная в крейте `устройства`.
Когда аргумент `priority` не указан, предполагается, что приоритет равен `1`.
Задача `idle` имеет ненастраиваемый приоритет `0`, наименьший из возможных.

> Более высокое значение означает более высокий приоритет в RTIC, что противоположно тому,
> что указано в периферии NVIC Cortex-M.
> Точнее, это значит, что число `10` обозначает приоритет **выше**, чем число `9`.

Когда несколько задач готовы к запуску, задача с самым большим статическим
приоритетом будет запущена первой. Приоритезацию задач можно рассматривать по
такому сценарию: сигнал прерывания приходит во время выполнения задачи с низким приоритетом;
сигнал переключает задачу с высоким приоритетом в режим ожидания.
Разница в приоритетах приводи к тому, что задача с высоким приоритетом вытесняет задачу с низким:
выполнение задачи с низким приоритетом замораживается и задача с высоким приоритетом выполняется,
пока не будет завершена. Как только задача с высоким приоритетом будет остановлена,
продолжится выполнение задачи с низким приоритетом.

Следующий пример демонстрирует диспетчеризацию на основе приоритетов задач.

``` rust
{{#include ../../../../examples/preempt.rs}}
```

``` console
$ cargo run --example preempt
{{#include ../../../../ci/expected/preempt.run}}
```

Заметьте, что задача `gpiob` *не* вытесняет задачу `gpioc`, потому что ее приоритет
*такой же*, как и у `gpioc`. Однако, как только `gpioc` возвращает результат,
выполненяется задача `gpiob`, как более приоритетная по сравнению с `gpioa`.
Выполнение `gpioa` возобновляется только после выхода из `gpiob`.

Еще одно замечание по поводу приоритетов: выбор приоритета большего, чем поддерживает устройство
(а именно `1 << NVIC_PRIO_BITS`) приведет к ошибке компиляции.
Из-за ограничений языка, сообщение об ошибке далеко от понимания:
вам скажут что-то похожее на "evaluation of constant value failed", а указатель на ошибку
*не* покажет на проблемное значение прерывания --
мы извиняемся за это!