前言
在ARM Cortex-M 微控制器编写 Rust 代码时,通常使用 cortex-m-rt
和 cortex-m
crate。cortex-m-rt
提供了 Cortex-M 内核的基本寄存器存取接口,启动代码,内核标准向量表、代码段、数据段的定义,cortex-m
提供对所有基于 Cortex-M 的微控制器标准的内核中的外设(例如 NVIC和 SysTick)以及一些外设操作的标准接口代码。cortex-m-rt
还确保复位、中断和异常向量表的定位,确保应用程序定义的中断入口函数填充到指定的位置。 按照 cortex-m-rt
中使用定义,向量表由以下部分组成:
链接段 | 符号名 | 功能 |
---|---|---|
_stack_start | – | 堆栈指针的初始值 |
vector_table.reset_vector | __RESET_VECTOR | 芯片复位地址 |
vector_table.exceptions | __EXCEPTIONS | Cortex-M 常见异常的向量表 |
vector_table.interrupts | __INTERRUPTS | 各厂商自定义的中断的向量表 |
如果为cortex-m-rt
crate启用device
功能,则它不会填充设备特定中断的向量表。相反,这是留给外设访问箱(PAC),用于特定的设备。.text
和 _stack_start
部分也可以通过配置移动或修改,但异常向量表则必须与芯片硬件定义的规则一致。 异常向量表由 ARM 制定,因此在任何基于 Cortex-M 的微控制器上都应该是相同的。当然也有一些芯片厂商没有完全按照 ARM 的规则设计芯片,例如,某些微控制器将启动配置、标记或校验和存储在异常向量表的“保留”字段中,或者需要在向量表或应用程序之间添加额外的标头,在这种情况下,我们需要修改表以便我们的代码能够自动运行,而不至于跑飞。
建立一个基本的项目
让我们设置一个典型的、最小的 Cortex-M0 项目作为示例。我们将省略 PAC 部分的代码,因为本示例不需要它。
$ cargo new cmexample --bin
Created binary (application) `cmexample` package
$ cd cmexample/
$ cargo add cortex-m-rt
Adding cortex-m-rt v0.7.2 to dependencies.
Features:
- device
- set-sp
- set-vtor
$ cargo add -F critical-section-single-core cortex-m
Adding cortex-m v0.7.7 to dependencies.
Features:
+ critical-section-single-core
- cm7
- cm7-r0p1
- critical-section
- inline-asm
- linker-plugin-lto
- serde
- std
$ cargo add panic-halt
Adding panic-halt v0.2.0 to dependencies.
新建一个简单的 main 文件
#![no_std]
#![no_main]
use panic_halt as _;
use cortex_m_rt::entry;
#[entry]
fn main() -> ! {
loop {}
}
添加链接文件memory.x
MEMORY
{
FLASH : ORIGIN = 0x00000000, LENGTH = 64K
RAM : ORIGIN = 0x20000000, LENGTH = 20K
}
配置 Cargo 使用thumbv6目标并添加以下内容到.cargo/config.toml
来获取cortex-m-rt
提供的链接器脚本:
[target.'cfg(all(target_arch = "arm", target_os = "none"))']
rustflags = [
"-C", "link-arg=-Tlink.x",
]
[build]
target = "thumbv6m-none-eabi"
查看可执行文件的中断向量表
如果一切顺利,构建后,将有一个针对 ARM 目标的 ELF 可执行文件,其中包含向量表和代码段、数据段等部分:
$ file target/thumbv6m-none-eabi/release/cmexample
target/thumbv6m-none-eabi/release/cmexample: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), statically linked, with debug_info, not stripped
$ arm-none-eabi-size -Ax target/thumbv6m-none-eabi/release/cmexample
target/thumbv6m-none-eabi/release/cmexample :
section size addr
.vector_table 0xc0 0x0
.text 0x64 0xc0
.rodata 0x0 0x124
.data 0x0 0x20000000
.gnu.sgstubs 0x0 0x140
.bss 0x0 0x20000000
.uninit 0x0 0x20000000
.ARM.attributes 0x32 0x0
.debug_frame 0x6528 0x0
.debug_abbrev 0x1139 0x0
.debug_info 0x26271 0x0
.debug_aranges 0x1e00 0x0
.debug_ranges 0x153f0 0x0
.debug_str 0x3e653 0x0
.debug_pubnames 0x16152 0x0
.debug_pubtypes 0x252 0x0
.debug_line 0x25015 0x0
.debug_loc 0x74 0x0
.comment 0x6d 0x0
Total 0xbe705
使用 objdump 我们可以检查向量表的内容:
$ arm-none-eabi-objdump -s --section .vector_table target/thumbv6m-none-eabi/release/cmexample
target/thumbv6m-none-eabi/release/cmexample: file format elf32-littlearm
Contents of section .vector_table:
0000 00500020 c1000000 0b010000 0f010000 .P. ............
0010 00000000 00000000 00000000 00000000 ................
0020 00000000 00000000 00000000 0b010000 ................
0030 00000000 00000000 0b010000 0b010000 ................
0040 0b010000 0b010000 0b010000 0b010000 ................
0050 0b010000 0b010000 0b010000 0b010000 ................
0060 0b010000 0b010000 0b010000 0b010000 ................
0070 0b010000 0b010000 0b010000 0b010000 ................
0080 0b010000 0b010000 0b010000 0b010000 ................
0090 0b010000 0b010000 0b010000 0b010000 ................
00a0 0b010000 0b010000 0b010000 0b010000 ................
00b0 0b010000 0b010000 0b010000 0b010000 ................
所有向量都指向默认处理程序。让我们为 SysTick 中断定义一个新的处理程序,例如:
#[exception]
fn SysTick() {
todo!();
}
现在我们可以看到 0x3c 处的 SysTick 向量已更新:
$ arm-none-eabi-objdump -s --section .vector_table target/thumbv6m-none-eabi/release/cmexample
target/thumbv6m-none-eabi/release/cmexample: file format elf32-littlearm
Contents of section .vector_table:
0000 00500020 c1000000 23010000 91010000 .P. ....#.......
0010 00000000 00000000 00000000 00000000 ................
0020 00000000 00000000 00000000 23010000 ............#...
0030 00000000 00000000 23010000 09010000 ........#.......
0040 23010000 23010000 23010000 23010000 #...#...#...#...
0050 23010000 23010000 23010000 23010000 #...#...#...#...
0060 23010000 23010000 23010000 23010000 #...#...#...#...
0070 23010000 23010000 23010000 23010000 #...#...#...#...
0080 23010000 23010000 23010000 23010000 #...#...#...#...
0090 23010000 23010000 23010000 23010000 #...#...#...#...
00a0 23010000 23010000 23010000 23010000 #...#...#...#...
00b0 23010000 23010000 23010000 23010000 #...#...#...#...
定义厂商芯片的向量表
注意:通常不需要我们这样做, 当在应用程序中定义异常处理程序时,cortex-m-rt
和 PAC
将自动填充该表。仅对于具有 cortex-m-rt
无法满足的特殊要求的控制器时才需要执行此操作。以某厂商的CortexM33
芯片为例。
#[cfg(feature = "rt")]
extern "C" {
fn UART0();
fn UART1();
fn TIMR0();
fn TIMR1();
fn DMA();
fn SPI0();
fn PWM_CH0();
fn WDT();
fn UART2();
fn PWM_CH1();
fn SARADC0();
fn BTIMER0();
fn HALL0();
fn PWM_CH2();
fn PWM_HALT();
fn I2C0();
fn CAN0();
fn SPI1();
fn RTC_BASE();
fn PWM_CH3();
fn TIMER2();
fn UART3();
fn TIMER3();
fn SARADC1();
fn BOD();
fn CORDIC();
fn BTIMER1();
fn PWM_CH4();
fn HALL1();
}
#[doc(hidden)]
pub union Vector {
_handler: unsafe extern "C" fn(),
_reserved: u32,
}
#[cfg(feature = "rt")]
#[doc(hidden)]
#[link_section = ".vector_table.interrupts"]
#[no_mangle]
pub static __INTERRUPTS: [Vector; 29] = [
Vector { _handler: UART0 },
Vector { _handler: UART1 },
Vector { _handler: TIMR0 },
Vector { _handler: TIMR1 },
Vector { _handler: DMA },
Vector { _handler: SPI0 },
Vector { _handler: PWM_CH0 },
Vector { _handler: WDT },
Vector { _handler: UART2 },
Vector { _handler: PWM_CH1 },
Vector { _handler: SARADC0 },
Vector { _handler: BTIMER0 },
Vector { _handler: HALL0 },
Vector { _handler: PWM_CH2 },
Vector { _handler: PWM_HALT },
Vector { _handler: I2C0 },
Vector { _handler: CAN0 },
Vector { _handler: SPI1 },
Vector { _handler: RTC_BASE },
Vector { _handler: PWM_CH3 },
Vector { _handler: TIMER2 },
Vector { _handler: UART3 },
Vector { _handler: TIMER3 },
Vector { _handler: SARADC1 },
Vector { _handler: BOD },
Vector { _handler: CORDIC },
Vector { _handler: BTIMER1 },
Vector { _handler: PWM_CH4 },
Vector { _handler: HALL1 },
];
#[doc = r"Enumeration of all the interrupts."]
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[repr(u16)]
pub enum Interrupt {
#[doc = "0 - UART0 global interrupt"]
UART0 = 0,
#[doc = "1 - UART1 global interrupt"]
UART1 = 1,
#[doc = "2 - TIMR0 global interrupt"]
TIMR0 = 2,
#[doc = "3 - TIMR1 global interrupt"]
TIMR1 = 3,
#[doc = "4 - DMA global interrupt"]
DMA = 4,
#[doc = "5 - SPI0 global interrupt"]
SPI0 = 5,
#[doc = "6 - PWM_CH0 global interrupt"]
PWM_CH0 = 6,
#[doc = "7 - WDT global interrupt"]
WDT = 7,
#[doc = "8 - UART2 global interrupt"]
UART2 = 8,
#[doc = "9 - PWM_CH1 global interrupt"]
PWM_CH1 = 9,
#[doc = "10 - SARADC0 global interrupt"]
SARADC0 = 10,
#[doc = "11 - BTIMER0 global interrupt"]
BTIMER0 = 11,
#[doc = "12 - HALL0 global interrupt"]
HALL0 = 12,
#[doc = "13 - PWM_CH2 global interrupt"]
PWM_CH2 = 13,
#[doc = "14 - PWM_HALT global interrupt"]
PWM_HALT = 14,
#[doc = "15 - I2C0 global interrupt"]
I2C0 = 15,
#[doc = "16 - CAN0 global interrupt"]
CAN0 = 16,
#[doc = "17 - SPI1 global interrupt"]
SPI1 = 17,
#[doc = "18 - RTC_BASE global interrupt"]
RTC_BASE = 18,
#[doc = "19 - PWM_CH3 global interrupt"]
PWM_CH3 = 19,
#[doc = "20 - TIMER2 global interrupt"]
TIMER2 = 20,
#[doc = "21 - UART3 global interrupt"]
UART3 = 21,
#[doc = "22 - TIMER3 global interrupt"]
TIMER3 = 22,
#[doc = "23 - SARADC1 global interrupt"]
SARADC1 = 23,
#[doc = "24 - BOD global interrupt"]
BOD = 24,
#[doc = "25 - CORDIC global interrupt"]
CORDIC = 25,
#[doc = "26 - BTIMER1 global interrupt"]
BTIMER1 = 26,
#[doc = "27 - PWM_CH4 global interrupt"]
PWM_CH4 = 27,
#[doc = "28 - HALL1 global interrupt"]
HALL1 = 28,
}
unsafe impl cortex_m::interrupt::InterruptNumber for Interrupt {
#[inline(always)]
fn number(self) -> u16 {
self as u16
}
}
原创文章,作者:guozi,如若转载,请注明出处:https://www.sudun.com/ask/88944.html