Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Использование прерываний с Embassy

В предыдущей главе мы рассмотрели, что такое прерывания и как в эту картину вписывается NVIC. Теперь посмотрим, как прерывания на самом деле используются в Embassy.

В Embassy обычно не нужно писать обработчики прерываний самостоятельно. Асинхронные драйверы используют прерывания внутри себя, чтобы ждать аппаратных событий и пробуждать задачи, когда эти события происходят. Ваш код просто ожидает операцию (await) и продолжается, когда она готова.

Для некоторой периферии Embassy нужна небольшая настройка, чтобы знать, какое аппаратное прерывание относится к какому драйверу. Здесь на помощь приходит bind_interrupts!.

Зачем нужен bind_interrupts!

Асинхронная периферия, такая как I2C и SPI, не завершает свою работу за один шаг. Пока операция выполняется, задача засыпает, а аппаратура генерирует прерывания по мере продвижения работы.

Обработчики прерываний для такой периферии Embassy уже предоставляет. От вас требуется только связь между аппаратным прерыванием и обработчиком, который должен его обслуживать. Именно эту связь и создаёт макрос bind_interrupts!.

Здесь вы не пишете обработчик прерывания. Вы просто подключаете всё так, чтобы асинхронный драйвер мог работать.

Привязка прерывания для I2C

Вот простой пример для I2C:

#![allow(unused)]
fn main() {
use embassy_rp::{bind_interrupts, i2c};
use embassy_rp::peripherals::I2C0;

bind_interrupts!(struct Irqs {
    I2C0_IRQ => i2c::InterruptHandler<I2C0>;
});
}

Это говорит Embassy, что прерывание I2C0_IRQ должен обрабатывать драйвер I2C для I2C0. Когда это настроено, асинхронные операции I2C могут корректно засыпать и просыпаться.

Использование асинхронного I2C

После привязки прерывания использование асинхронного I2C выглядит привычно:

#![allow(unused)]
fn main() {
use embassy_rp::i2c::{I2c, Config as I2cConfig};
use embassy_rp::peripherals::I2C0;

let sda = p.PIN_16;
let scl = p.PIN_17;

let mut i2c = I2c::new_async(
    p.I2C0,
    scl,
    sda,
    Irqs,
    I2cConfig::default(),
);
}

Когда позже вы вызываете асинхронную операцию, например:

#![allow(unused)]
fn main() {
i2c.write(0x3C, &[0x00]).await;
}

ваша задача приостанавливается и даёт выполниться другому коду. Тем временем аппаратура I2C делает свою работу. Когда аппаратура заканчивает, срабатывает прерывание, и Embassy пробуждает вашу задачу. Прерывание происходит за кулисами: вы видите только, что код продолжается после .await.