#msp430 #run-time #startup

nightly no-std msp430-rt

MSP430 微控制器的最小运行时/启动程序

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 嵌入式开发

Download history 48/week @ 2024-03-25 112/week @ 2024-04-01 40/week @ 2024-04-08 49/week @ 2024-04-15 52/week @ 2024-04-22 57/week @ 2024-04-29 54/week @ 2024-05-06 46/week @ 2024-05-13 59/week @ 2024-05-20 55/week @ 2024-05-27 37/week @ 2024-06-03 44/week @ 2024-06-10 47/week @ 2024-06-17 43/week @ 2024-06-24 7/week @ 2024-07-01 24/week @ 2024-07-08

每月下载量 132
用于 少于 17 软件包

MIT/Apache

25KB
141

包含 (静态库,2KB) bin/msp430-none-elf.a

crates.io crates.io

msp430-rt

MSP430 微控制器的最小运行时/启动程序

此软件包基于 Jorge Aparicio (@japaric) 的 cortex-m-rt 软件包。

文档

许可协议

根据以下任一协议许可:

任选其一。

贡献

除非您明确声明,否则您提交的任何贡献(根据 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

符号(objdumpnm

您将在 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 的输出中找到它,例如 NMIWDT

高级用法

设置程序入口点

本节描述了如何实现 #[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 将保留位置设置为 0None(《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);

这个链接器脚本将FooBar弱别名。 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