You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

无库Arm Cortex-M0中如何将Systick_Handler()关联至SysTick异常

问题分析与解决方案

你的核心问题在于Cortex-M0的异常触发依赖硬件中断向量表机制,STM32等MCU的示例之所以不用手动调用Systick_Handler,是因为它们的启动文件已经帮你完成了向量表的配置——这不是库的“魔法”,而是Arm Cortex-M架构的硬件规定。下面是具体的解决步骤和代码修正:

1. 核心原理:中断向量表的作用

Cortex-M0硬件在触发异常(比如SysTick计数归零)时,会自动从中断向量表中找到对应异常的处理函数地址,然后跳转执行。向量表必须放在系统内存的起始地址(默认0x00000000),其中第15项(从0开始计数)就是SysTick异常的入口地址:

  • 索引0:栈顶地址
  • 索引1:复位向量
  • ...
  • 索引15:SysTick Handler

没有内置库的情况下,你需要手动定义向量表并配置链接脚本,让硬件能找到Systick_Handler的地址。

2. 代码修正与补充

2.1 修正现有代码的错误

  • 把main里的局部变量time改为全局volatile变量(中断服务函数无法访问局部变量,volatile防止编译器优化)
  • 修复systick_disable中的位操作错误(!是逻辑非,应该用~按位取反)
  • 移除手动挂起SysTick异常的代码(ICSR[0] =1<<26;是手动触发异常,正常场景不需要)

修正后的基础代码:

#include <stdint.h>

// 定义SysTick基地址(根据你的MCU手册确认,通常是0xE000E010)
#define SYSTICK_BASE ((volatile uint32_t*)0xE000E010)

// 全局volatile变量,用于中断计数
volatile int time = 0;

void systick_enable(void){
  SYSTICK_BASE[0] = 0x0003;  // bit0=ENABLE, bit1=TICKINT(开启中断触发)
}

void systick_disable(void){
  SYSTICK_BASE[0] &= ~0x0003;  // 关闭SysTick和中断
}

void systick_clear(void){
  SYSTICK_BASE[1] = 0x0000;  // 清除当前计数器值
}

void systick_init(uint32_t reload_val) {
    systick_clear();
    SYSTICK_BASE[2] = reload_val;  // 设置重载值
    systick_enable();
}

void Systick_Handler(void){
  time++;
}

2.2 添加中断向量表与启动代码

需要手动定义向量表,并实现复位处理函数来初始化系统并调用main:

// 栈顶地址:根据你的MCU RAM大小设置,示例为16KB RAM(0x20000000 ~ 0x20004000)
#define STACK_TOP 0x20004000

// 声明异常处理函数
void Reset_Handler(void);
void NMI_Handler(void) __attribute__((weak));
void HardFault_Handler(void) __attribute__((weak));
void Systick_Handler(void);

// 中断向量表:放到专门的段,保证链接到Flash起始地址
__attribute__((section(".isr_vector")))
void (* const g_pfnVectors[])(void) = {
    (void (*)(void))STACK_TOP,  // 栈顶地址
    Reset_Handler,              // 复位处理函数
    NMI_Handler,                // NMI异常(弱定义,可重写)
    HardFault_Handler,          // 硬 fault(弱定义,可重写)
    0, 0, 0, 0, 0, 0, 0, 0,     // 保留向量
    0, 0,                       // 保留向量
    Systick_Handler,            // SysTick异常(索引15)
    // 其他外设中断可根据你的MCU手册添加
};

// 默认异常的空实现
void NMI_Handler(void) {}
void HardFault_Handler(void) { while(1); }  // 硬fault后死循环

// 复位处理函数:初始化数据段、bss段,调用main
void Reset_Handler(void) {
    // 初始化.data段:从Flash复制到RAM
    extern uint32_t __data_start__;
    extern uint32_t __data_end__;
    extern uint32_t __data_load__;
    uint32_t *pSrc = &__data_load__;
    uint32_t *pDest = &__data_start__;
    while(pDest < &__data_end__) {
        *pDest++ = *pSrc++;
    }

    // 初始化.bss段:清零
    extern uint32_t __bss_start__;
    extern uint32_t __bss_end__;
    pDest = &__bss_start__;
    while(pDest < &__bss_end__) {
        *pDest++ = 0;
    }

    // 调用main函数
    main();

    // main返回后死循环
    while(1);
}

2.3 配置链接脚本

需要编写链接脚本,把向量表段.isr_vector放到Flash的起始地址,示例链接脚本(适用于GCC工具链):

MEMORY
{
    FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 64K  // 根据你的MCU Flash大小调整
    RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 16K   // 根据你的MCU RAM大小调整
}

SECTIONS
{
    .isr_vector :
    {
        . = ALIGN(4);
        KEEP(*(.isr_vector))  // 强制保留向量表,防止被优化
        . = ALIGN(4);
    } > FLASH

    .text :
    {
        . = ALIGN(4);
        *(.text)
        *(.text*)
        . = ALIGN(4);
    } > FLASH

    .data :
    {
        . = ALIGN(4);
        __data_start__ = .;
        *(.data)
        *(.data*)
        __data_end__ = .;
        . = ALIGN(4);
    } > RAM AT > FLASH
    __data_load__ = LOADADDR(.data);

    .bss :
    {
        . = ALIGN(4);
        __bss_start__ = .;
        *(.bss)
        *(.bss*)
        *(COMMON)
        __bss_end__ = .;
        . = ALIGN(4);
    } > RAM
}

3. 编译与运行

用GCC工具链编译时,需要指定链接脚本:

arm-none-eabi-gcc -mcpu=cortex-m0 -mthumb -c your_code.c -o your_code.o
arm-none-eabi-ld -T your_linker_script.ld your_code.o -o your_program.elf
arm-none-eabi-objcopy -O binary your_program.elf your_program.bin

烧录后,SysTick计数归零就会自动触发Systick_Handler,无需在main中调用。

内容的提问来源于stack exchange,提问作者Jingxuan Cai

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.28 06:29:59