2 个不稳定版本
0.2.0 | 2022 年 10 月 27 日 |
---|---|
0.1.0 | 2022 年 10 月 27 日 |
#526 在 嵌入式开发
19KB
94 行
基于特质的功能性中断处理器注册。
这个 crate 提供了一种创建职位的方法,并试图减少创建注册时遇到的问题。
用法
作为这个 crate 的最终用户,您将想要使用 take_nvic_interrupt
和 take_exception
宏。这些宏分别生成了 NvicInterruptRegistration
和 ExceptionRegistration
特质的实现者,这些实现者可以传递给使用中断的函数。
有关如何使用 InterruptRegistration
、NvicInterruptRegistration
和 ExceptionRegistration
的信息,请参阅这些特质的文档。
这个 crate 试图解决的问题。
为了说明这个 crate 试图解决的问题,我们将使用一个示例用例。
定义
为了帮助解释这个 crate 的用例,我们使用以下定义
- 注册:放置在中断向量中的函数指针,中断处理器的声明。
- 职位:在处理中断时应该运行的代码,中断处理器的主体。
用例
我们的目标是用例是
- 配置
SysTick
中断,使其每1337
个周期触发一次。 - 每次
SysTick
中断发生时递增一个计数器。
在使用 cortex_m_rt
crate 时,我们会这样做
# fn systick_reload(_: u32) {}
# fn setup_systick_exception(_: u32) {}
use cortex_m_rt::{exception, entry};
#[exception]
fn SysTick() { // This is the "registration" (can only
// be provided once in entirety of the
// program, incl. libraries)
static mut COUNTER: u32 = 0; // This is part of the "occupation"
*COUNTER += 1; // This is the "occupation", the
systick_reload(1337); // actual code to be executed
// when handling the SysTick
// interrupt.
}
#[entry]
fn main() {
setup_systick_exception(1337);
loop {}
}
并在提供 setup_systick_exception
和 systick_reload
的 crate 中
pub fn setup_systick_exception(reload_value: u32) {
/* Setup systick so that it triggers the SysTick interrupt
after `reload_value` cycles
*/
}
pub fn systick_reload(reload_value: u32) {
// Reload the systick value
}
在这个例子中
setup_systick_exception
和注册/职位之间除了命名和可能的文档外,没有任何语义联系。- 将正确的职业添加到正确的注册表中完全取决于编写程序的这个人。
- 提供
setup_systick_exception
和systick_reload
的 crate 维护者无法控制职业或注册。 - 无需使用跳板来设置中断处理程序。
解决方案?
为了表示注册,提供了 InterruptRegistration
、NvicInterruptRegistration
和 ExceptionRegistration
特性。它们可以用来更动态地插入职业。
这些特性允许以下操作
- 希望提供职业但又不直接创建注册的代码可以通过要求用户代码提供注册来编写。
- 希望提供职业的代码可以验证提供的注册是否适合即将创建的职业。
为了减轻创建注册的困难,提供了 take_nvic_interrupt
和 take_exception
proc-macros。它们执行设置注册中不那么容易解释的部分,并分别为 NvicInterruptRegistration
和 ExceptionRegistration
提供实现。
修改后的示例
有了这些新工具,我们可以重写我们的代码,如下所示
# fn setup_systick_exception<T: cortex_m_interrupt::InterruptRegistration>(_: u32, _: T, _: fn()) {}
use cortex_m_rt::entry;
use cortex_m::peripheral::scb::Exception::SysTick;
static mut COUNTER: u32 = 0;
fn increase_counter() {
unsafe { COUNTER += 1 };
}
#[entry]
fn main() -> ! {
// We create the registration
let systick_registration = cortex_m_interrupt::take_exception!(SysTick);
// And pass it to some function that will do some configuration and
// provide a occupation for that registration. It also allows us to
// inject our own expansion to the occupation.
setup_systick_exception(1337, systick_registration, increase_counter);
loop {}
}
在提供 setup_systick_exception
的 crate 中
# use cortex_m_interrupt::ExceptionRegistration;
pub fn setup_systick_exception<Registration: ExceptionRegistration>(
reload_value: u32,
registration: Registration,
f: fn(),
) {
// Assert that we've been given a registration of the correct
// exception/interrupt.
assert_eq!(
cortex_m::peripheral::scb::Exception::SysTick,
registration.exception()
);
/* Setup systick so that it triggers the SysTick interrupt
after `reload_value` cycles
*/
use core::mem::MaybeUninit;
static mut USER_HANDLE: MaybeUninit<fn()> = MaybeUninit::uninit();
static mut RELOAD_VALUE: MaybeUninit<u32> = MaybeUninit::uninit();
unsafe { USER_HANDLE.write(f) };
unsafe { RELOAD_VALUE.write(reload_value) };
core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::Release);
registration.occupy(|| {
systick_reload(unsafe { RELOAD_VALUE.assume_init() });
// Call extra user code
unsafe { (USER_HANDLE.assume_init())(); }
});
}
fn systick_reload(reload_value: u32) {
// Reload the systick value
}
在修改后的示例中
- 注册和职业之间存在更明确的语义连接。
setup_systick_exception
的实现者完全控制职业,并可选择允许用户代码执行一些额外的操作。setup_systick_exception
的实现者可以验证传递给它的注册是否正确。- 现在在中断处理程序中需要跳板,当发生中断时,增加约 ~5 个周期的额外处理时间。
主要区别
cortex-m-rt
方法与 cortex-m-interrupt
提供的内容之间的主要区别如下
cortex-m-interrupt
提供了一种创建更清晰责任分离的方式,尤其是在注册中断处理程序时。cortex-m-interrupt
提供的特性支持创建注册和创建职业之间更紧密的语义连接。cortex-m-rt
提供的方法有一些开销较少,因为它不需要跳板。
依赖项
~2MB
~42K SLoC