13 个不稳定版本 (3 个破坏性更新)
0.4.0 | 2022年9月11日 |
---|---|
0.3.1 | 2022年2月20日 |
0.3.0 | 2022年1月26日 |
0.2.5 | 2021年10月27日 |
0.1.0 | 2017年7月22日 |
#925 in 嵌入式开发
每月下载量 132
用于 少于 17 软件包
25KB
141 行
包含 (静态库,2KB) bin/msp430-none-elf.a
msp430-rt
MSP430 微控制器的最小运行时/启动程序
此软件包基于 Jorge Aparicio (@japaric) 的 cortex-m-rt 软件包。
文档
许可协议
根据以下任一协议许可:
-
Apache 许可证2.0版本 (LICENSE-APACHE 或 https://apache.ac.cn/licenses/LICENSE-2.0)
-
MIT 许可证 (LICENSE-MIT 或 http://opensource.org/licenses/MIT)
任选其一。
贡献
除非您明确声明,否则您提交的任何贡献(根据 Apache-2.0 许可证定义),均应按上述方式双重许可,而无需任何附加条款或条件。
lib.rs
:
为 MSP430 微控制器提供的启动代码和最小运行时
此软件包基于 Jorge Aparicio (@japaric) 的 cortex-m-rt 软件包。
此软件包包含构建针对 MSP430 微控制器的 no_std
应用程序(二进制软件包)所需的所有部分。
功能
此软件包负责以下内容:
-
程序的内存布局。特别是,它填充向量表,以便设备可以正确启动,并正确分配中断。
-
在程序入口点之前初始化
static
变量。
此软件包还提供了以下属性:
#[entry]
用于声明程序的入口点#[pre_init]
在static
变量初始化之前运行代码
此包还实现了一个名为 #[interrupt]
的相关属性,允许您定义中断处理程序。然而,由于可用的中断取决于所使用的微控制器,此属性应从 PAC 包重新导出并使用。
这些属性的文档可以在 属性宏 部分找到。
要求
内存.x
此包期望用户或某些其他包通过名为 memory.x
的链接脚本提供目标设备的内存布局。本节涵盖了 memory.x
的内容
MEMORY
链接脚本必须指定设备中可用的内存,至少有三个 MEMORY
区域:一个名为 ROM
,一个名为 RAM
,以及一个名为 VECTORS
的区域。程序的 .text
和 .rodata
区域将放置在 ROM
区域,而 .bss
和 .data
区域以及堆,将放置在 RAM
区域。包括中断矢量和重置地址的 .vector_table
区域将放置在闪存末尾的 VECTORS
区域。应将 ROM
区域的末尾设置为 VECTORS
区域的开始地址。
需要 VECTORS
区域,因为在 (以及在) msp430 设备系列之间
- 设备没有固定大小的单个向量表。
- 设备没有固定大小的向量表起始地址。请查阅您的系列用户指南(例如,MSP430x5xx 系列用户指南,slau208),特别是内存映射部分,以及您的设备数据表(例如,msp430g2553)有关向量表布局和大小的信息。 如果您的设备数据表明确标记了一组连续的向量未使用,您可能能够获得更多的程序空间!
/* Linker script for the MSP430G2553 */
MEMORY
{
RAM : ORIGIN = 0x0200, LENGTH = 0x0200
ROM : ORIGIN = 0xC000, LENGTH = 0x3FE0
VECTORS : ORIGIN = 0xFFE0, LENGTH = 0x20
}
示例
本节介绍了一个基于 msp430-rt
的最小应用程序。
// IMPORTANT the standard `main` interface is not used because it requires nightly
#![no_main]
#![no_std]
extern crate msp430_rt;
// Simple panic handler that infinitely loops.
extern crate panic_msp430;
use msp430_rt::entry;
// use `main` as the entry point of this application
// `main` is not allowed to return
#[entry]
fn main() -> ! {
// initialization
loop {
// application logic
}
}
要实际构建此程序,您需要将 memory.x
链接脚本放在链接器可以找到的地方,例如当前目录;然后使用 msp430-rt
的链接脚本链接程序: link.x
。以下显示了所需的步骤
$ cat > memory.x <<EOF
/* Memory layout of the MSP430G2553 */
MEMORY
{
RAM : ORIGIN = 0x0200, LENGTH = 0x0200
ROM : ORIGIN = 0xC000, LENGTH = 0x3FE0
VECTORS : ORIGIN = 0xFFE0, LENGTH = 0x20
}
EOF
$ cargo rustc --target msp430-none-elf -Zbuild-std=core -- \
-C link-arg=-nostartfiles -C link-arg=-Tlink.x
$ file target/msp430-none-elf/debug/app
app: ELF 32-bit LSB executable, TI msp430, version 1 (embedded), statically linked, not stripped
可选功能
设备
如果禁用此功能,则此包将填充整个向量表。向量表中的所有中断(即使目标设备未使用),也将绑定到默认的中断处理程序。这使得最终应用程序与设备无关:您将能够在任何 MSP430 设备上运行它 -- 只要您正确地在 memory.x
中指定了其内存布局,而不会出现未定义的行为。
如果启用此功能,则向量表的中断部分将保持未填充,并且某些其他包或用户将必须填充它。此模式旨在与使用 svd2rust
生成的 PAC 包一起使用。当这些 PAC 包 的 "rt"
功能启用时,它们将填充向量表的缺失部分。
检查
本节涵盖了如何检查基于 msp430-rt
构建的二进制文件。
部分(size
)
msp430-rt
使用标准的段,如 .text
、.rodata
、.bss
和 .data
,正如人们所期望的那样。 msp430-rt
将向量表放在自己的段中,名为 .vector_table
。这使得您可以区分向量表在Flash中占用的空间与实际指令(.text
)和常量(.rodata
)所占用的空间。
$ size -Ax target/msp430-none-elf/examples/app
section size addr
.vector_table 0x20 0xffe0
.text 0x44 0xc000
.rodata 0x0 0xc044
.bss 0x0 0x200
.data 0x0 0x200
.MSP430.attributes 0x17 0x0
Total 0x7b
没有 --A
参数 size
报告 "text" 下 .text
、.rodata
和 .vector_table
大小的总和。
$ size target/msp430-none-elf/examples/app
text data bss dec hex filename
100 0 0 100 64 target/msp430-none-elf/release/app
符号(objdump
、nm
)
您将在 msp430-rt
应用程序中始终找到以下(未混淆的)符号
-
Reset
。此函数将初始化堆栈指针,调用PreInit
,初始化静态变量(.data
和.bss
)然后使用main
符号调用用户程序入口点(参见#[entry]
)。在此crate的先前版本(0.2.4及以下版本)中,启动代码是用Rust实现的,有时
main
将内联到Reset
中(使用ResetTrampoline
进行堆栈指针初始化)。然而,从版本0.2.5开始,您应该始终在程序中找到main
符号,因为当前包含Reset
的启动代码是用汇编实现的。 -
DefaultHandler
。这是默认的中断处理程序。如果不使用#[interrupt] fn DefaultHandler(..
重写,这将是一个无限循环。 -
__RESET_VECTOR
。这是复位向量,指向ResetTrampoline
。此向量位于.vector_table
段的末尾。 -
__INTERRUPTS
。这是向量表中设备特定的中断部分。此数组位于__RESET_VECTOR
之前,在.vector_table
段中。 -
PreInit
。这是一个在初始化RAM之前要运行的函数。默认情况下是一个空函数。可以通过使用#[pre_init]
属性来更改要调用的函数。在此crate的先前版本中,标记有#[pre_init]
的空函数会被优化掉。从版本0.2.5开始,将始终包含PreInit
函数。
如果您覆盖了任何中断处理程序,您将在 objdump
的输出中找到它,例如 NMI
或 WDT
。
高级用法
设置程序入口点
本节描述了如何实现 #[entry]
。这些信息对希望提供比 #[entry]
提供更多保证的替代方案的开发商非常有用。
Reset
处理器将在初始化 .bss
和 .data
之后调用一个名为 main
(未混淆)的符号。在 #[entry]
的展开中提供此符号。
#[entry]
fn main() -> ! {
/* user code */
}
// expands into
#[no_mangle]
extern "C" fn main() -> ! {
/* user code */
}
未混淆的 main
符号必须具有签名 extern "C" fn() -> !
,否则从 Reset
的调用将导致未定义行为。
集成设备特定中断
本节介绍了外部包如何将设备特定中断处理程序插入向量表中。大多数用户不需要关注这些细节,但如果您对使用 svd2rust
生成的设备包如何与 msp430-rt
集成感兴趣,请继续阅读。
本节中的信息适用于已启用 "device"
功能的情况。
__INTERRUPTS
外部包必须通过一个名为 __INTERRUPTS
(未混淆)的 static
变量来提供向量表的中断部分,该变量必须放置在其对象文件的 .vector_table.interrupts
部分中。
此 static
变量将放置在 ORIGIN(VECTORS)
。此地址对应于 IRQ0(中断号 0)所在的位置。
为了符合 MSP430 ABI,__INTERRUPTS
必须是一个函数指针数组;如果该数组中某些位置在数据表/参考手册中被标记为 保留,则可能需要将其设置为 0。我们建议使用 union
将保留位置设置为 0
;None
(《Option<fn()>)也可能起作用,但无法保证 None
变体将始终表示为值 0
。
让我们用一个假想的例子来说明,假设设备只有两个中断:具有中断号 = 2 的 Foo
和具有中断号 = 4 的 Bar
。
union Vector {
handler: extern "msp430-interrupt" fn(),
reserved: usize,
}
extern "msp430-interrupt" {
fn Foo();
fn Bar();
}
#[link_section = ".vector_table.interrupts"]
#[no_mangle]
static __INTERRUPTS: [Vector; 15] = [
// 0-1: Reserved
Vector { reserved: 0 },
Vector { reserved: 0 },
// 2: Foo
Vector { handler: Foo },
// 3: Reserved
Vector { reserved: 0 },
// 4: Bar
Vector { handler: Bar },
// 5-14: Reserved
Vector { reserved: 0 },
Vector { reserved: 0 },
Vector { reserved: 0 },
Vector { reserved: 0 },
Vector { reserved: 0 },
Vector { reserved: 0 },
Vector { reserved: 0 },
Vector { reserved: 0 },
Vector { reserved: 0 },
Vector { reserved: 0 },
];
设备.x
链接 __INTERRUPTS
会创建大量未定义引用。如果用户没有为所有设备特定中断设置处理程序,则链接将失败,并出现 "undefined reference"
错误。
我们希望在为所有中断提供默认处理程序的同时,仍然允许用户单独覆盖每个中断处理程序。在C项目中,这通常通过在外部汇编文件中声明弱别名来实现。在Rust中,我们可以使用global_asm!
实现类似的功能,但这是一个不稳定的功能。
一个不需要global_asm!
或外部汇编文件的方法是,在链接器脚本中使用PROVIDE
命令来创建弱别名。这是msp430-rt
采用的方案;当启用"device"
特性时,msp430-rt
的链接器脚本(link.x
)依赖于一个名为device.x
的链接器脚本。提供__INTERRUPTS
的crate也必须提供这个文件。
对于我们的运行示例,device.x
链接器脚本如下
/* device.x */
PROVIDE(Foo = DefaultHandler);
PROVIDE(Bar = DefaultHandler);
这个链接器脚本将Foo
和Bar
弱别名。 DefaultHandler
是默认的中断处理程序。
因为该链接器脚本由最终应用程序的依赖项提供,所以依赖项必须包含一个构建脚本,该脚本将device.x
放在链接器可以找到的地方。以下是一个此类构建脚本的示例
use std::{env, fs::File, io::Write, path::PathBuf};
fn main() {
// Put the linker script somewhere the linker can find it
let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap());
File::create(out.join("device.x"))
.unwrap()
.write_all(include_bytes!("device.x"))
.unwrap();
println!("cargo:rustc-link-search={}", out.display());
}
依赖项
~2MB
~41K SLoC