5.3 Cortex-M0 使用说明

本章说明 N32G033 的 Cortex-M0 内核相关用法,以及 ProcessStack、SysTick 2 个示例。内核不提供独立的标准外设驱动,相关操作由 CMSIS 层提供;系统主频、时钟树与内核异常请配合用户手册的系统章节阅读,芯片已知问题以勘误手册为准。

5.3.1 相关文件

相对路径 作用
firmware/CMSIS/core/core_cm0.h 定义 Cortex-M0 内核的寄存器与 CMSIS 内联函数:__set_PSP、__set_CONTROL、__ISB、SysTick_Config 等,示例直接调用这些接口
firmware/CMSIS/device/system_n32g033.c 芯片系统初始化与 SystemCoreClockUpdate、SystemCoreClock 的实现,示例用它取得系统主频
projects/n32g033_EVAL/examples/Cortex-M0/ Cortex-M0 示例根目录,含 ProcessStack、SysTick 2 个示例
projects/n32g033_EVAL/examples/Cortex-M0/SysTick/ 该示例的工程目录组成:MDK-ARM/、EWARM/、inc/、src/、readme.txt
projects/n32g033_EVAL/bsp/ 示例共用的板级支持文件:log.h 提供串口日志接口

5.3.2 API 参考

Cortex-M0 内核不提供独立的标准外设驱动库,本模块因此没有 :::devkit-api 条目与函数卡片。内核相关的操作都由 CMSIS 提供:firmware/CMSIS/core/core_cm0.h 定义控制寄存器读写与 SysTick_Config 等内联函数,firmware/CMSIS/device/system_n32g033.c 提供系统初始化与主频更新。两个示例直接调用这些接口完成线程模式堆栈切换与系统节拍配置,移植时把相应的内联函数与主频变量一起带走即可。

5.3.3 Demo 示例说明

本节说明 Cortex-M0 的 2 个示例:ProcessStack 演示线程模式堆栈从主栈切换到进程栈并在异常后自动切回,SysTick 演示如何配置 10 ms 系统节拍中断。

5.3.3.1 ProcessStack 线程模式堆栈切换

工程目录: projects/n32g033_EVAL/examples/Cortex-M0/ProcessStack/

声明栈大小与堆栈取值宏→清零进程堆栈区→设置进程栈指针并切换到进程栈→执行同步指令刷新流水线→触发软件异常验证中断上下文使用主栈→读回控制寄存器确认切换成功

项目 源码解读
功能 演示线程模式堆栈从主栈切换到进程栈并在异常进入后自动切回:切换后读控制寄存器确认使用进程栈,触发异常后中断使用主栈,返回后仍是进程栈。
源码组成 src/main.c 中的 main 完成堆栈切换与验证,并定义软件异常调用的内联汇编;
src/n32g033_it.c 中的 SVC_Handler 记录中断上下文使用的堆栈;
inc/main.h、inc/n32g033_it.h 给出声明;
readme.txt 给出测试步骤;
工程文件在 MDK-ARM/ 与 EWARM/。
关键配置 进程栈区大小 0x200;
控制寄存器取 0x02 选择进程栈,取 0x00 表示主栈;
系统时钟 64 MHz,串口 115200,TX 为 PA9、RX 为 PA10。
程序与数据路径 main → log_init → 清零进程堆栈区 → __set_PSP 设置进程栈顶 → __set_CONTROL 切换到进程栈 → __ISB 刷新流水线 → 读 CONTROL 校验 → 触发软件异常 → SVC_Handler 读回中断上下文堆栈 → 返回后再读 CONTROL 确认为进程栈。
主要 API __set_PSP、__set_CONTROL、__ISB、__get_CONTROL、__get_PSP
验证方法 硬件基于评估板 N32G033K8Q7-1_STB V1.0,串口波特率 115200;
编译下载复位运行后,串口打印 Cortex-M0 ProcessStack Test Success 表示切换与自动切回都正确。
使用提示 系统时钟 64 MHz;
也可用编译器的 Cortex 寄存器窗口观察线程模式下的堆栈与代码访问级别。
核心代码解读与移植

示例把线程模式堆栈从主栈切到进程栈,再用一次软件异常验证异常上下文使用主栈、返回后自动切回进程栈。

数据路径: 宏声明栈大小与堆栈取值 → 清零进程堆栈区 → __set_PSP 设置进程栈顶 → __set_CONTROL 切换到进程栈 → __ISB 刷新流水线 → 读回 CONTROL 与 PSP 校验 → 软件异常后 SVC_Handler 读回中断上下文堆栈

1. 栈大小与堆栈取值先用宏定好。 进程栈区大小与两种堆栈的取值都写成宏,切换与校验两侧共用同一组常量。

src/main.csrc/main.c
#define SP_PROCESS_SIZE          0x200 /* Process stack size */
#define SP_PROCESS               0x02  /* Process stack */
#define SP_MAIN                  0x00  /* Main stack */

SP_PROCESS_SIZE 取 0x200 表示进程栈区 512 字节;两个取值直接对应控制寄存器的堆栈选择位。

2. 设置进程栈指针、切换堆栈并读回校验。 先把进程栈指针指向栈顶,再改控制寄存器选择进程栈,随后用同步指令刷新流水线并读回确认。

src/main.csrc/main.c · main
    /* Set Process stack value */
    __set_PSP((uint32_t)PSPMemAlloc + SP_PROCESS_SIZE);

    /* Select Process Stack as Thread mode Stack */
    __set_CONTROL(SP_PROCESS);

    /* Execute ISB instruction to flush pipeline as recommended by Arm */
    __ISB();

    /* Get the Thread mode stack used */
    CurrentStack = (__get_CONTROL() & 0x02);

    /* Get process stack pointer value */
    PSPValue = __get_PSP();

切换堆栈后必须执行同步指令,否则后续指令可能仍按旧堆栈取值执行。

3. 用软件异常验证异常上下文使用主栈。 触发一次软件异常后,中断服务读回控制寄存器,确认异常上下文不再使用进程栈。

src/n32g033_it.csrc/n32g033_it.c · SVC_Handler
void SVC_Handler(void)
{
    IsrStack = (__get_CONTROL() & 0x02);
}

异常进入时硬件自动改用主栈,返回线程模式后再恢复原来的堆栈选择,移植时可直接沿用这段校验逻辑。

对应源码: src/main.c、src/n32g033_it.c、firmware/CMSIS/core/core_cm0.h

5.3.3.2 SysTick 系统节拍中断

工程目录: projects/n32g033_EVAL/examples/Cortex-M0/SysTick/

更新系统主频变量→按 10 ms 计算并设置 SysTick 重装值→使能 SysTick 中断→节拍中断触发后累加计数→计数为偶数时串口打印计数值

项目 源码解读
功能 配置 SysTick 每 10 ms 产生一次中断,中断服务里累加计数,计数为偶数时通过串口打印。
源码组成 src/main.c 中的 main 更新主频并配置 SysTick;
src/n32g033_it.c 中的 SysTick_Handler 累加计数并按需打印;
inc/main.h、inc/n32g033_it.h 给出声明;
readme.txt 给出测试步骤;
工程文件在 MDK-ARM/ 与 EWARM/。
关键配置 系统时钟 64 MHz;
分频系数取 100,使节拍周期为 10 ms;
串口 115200,TX 为 PA9、RX 为 PA10。
程序与数据路径 main → log_init → SystemCoreClockUpdate 更新主频 → SysTick_Config 按 10 ms 配置并启动节拍 → 主循环空转 → 每 10 ms 进入 SysTick_Handler 累加 Tick_num → 计数为偶数时打印计数值。
主要 API SystemCoreClockUpdate、SystemCoreClock、SysTick_Config
验证方法 硬件基于评估板 N32G033K8Q7-1_STB V1.0,串口波特率 115200;
编译下载复位运行后,串口约每 20 ms 打印一行计数值。
使用提示 系统时钟 64 MHz;
改节拍周期时同步调整分频系数,避免重装值超出计数器的位宽。
核心代码解读与移植

示例按系统主频算出一个 10 ms 的节拍周期并启动 SysTick,在中断里累加计数并按偶数条件打印。

数据路径: 宏声明分频系数并定义计数变量 → SystemCoreClockUpdate 更新主频 → SysTick_Config 按 10 ms 配置并启动节拍 → 主循环空转 → 每 10 ms 进入 SysTick_Handler 累加计数 → 偶数时打印

1. 分频系数与计数变量先定好。 分频系数决定节拍周期,计数变量用来反映中断触发次数,两者都在文件作用域声明。

src/main.csrc/main.c
#define SYSTICK_10MS           ((uint32_t)100)

uint32_t Tick_num = 0;

SYSTICK_10MS 取 100 表示把主频等分 100 份,得到 10 ms 的节拍周期;改周期时改这个宏即可。

2. 更新主频并按 10 ms 配置 SysTick。 先按寄存器重算当前主频,再用主频除以分频系数设置重装值并启动节拍中断。

src/main.csrc/main.c · main
int main(void)
{
    /* USART Init */
    log_init();
    log_info("Cortex-M0 SysTick \r\n");

    /* Get SystemCoreClock */
    SystemCoreClockUpdate();

    /* Config 10ms SysTick  */
    SysTick_Config(SystemCoreClock/SYSTICK_10MS);

    while (1)
    {
    }
}

主频必须先更新,否则重装值按旧主频计算,实际节拍周期会与预期不符。

3. 中断里累加计数并按条件打印。 每次节拍中断给计数变量加一,计数为偶数时打印一行,避免打印过于频繁。

src/n32g033_it.csrc/n32g033_it.c · SysTick_Handler
void SysTick_Handler(void)
{
    Tick_num++;
    if((Tick_num%2) == 0)
    {
        log_info("Cortex-M0 SysTick IRQ %d\r\n",Tick_num);
    }
}

中断里的打印会占用一定时间;节拍周期很短时,应把需要打印的信息先缓存再在主流程输出。

对应源码: src/main.c、src/n32g033_it.c、firmware/CMSIS/core/core_cm0.h

5.3.4 注意与排障

场景 检查与建议 来源
系统主频读取 用户手册系统章节写明主频由时钟配置决定,SystemCoreClockUpdate 按寄存器重算 SystemCoreClock;
改时钟树后先更新主频再计算节拍重装值。
用户手册
线程模式访问级别 用户手册内核相关章节写明线程模式可运行在特权级或非特权级;
切到非特权级前先把需要访问的外设配置好。
用户手册
异常上下文堆栈 用户手册内核相关章节写明进入异常后使用主栈、返回线程模式后恢复原有堆栈选择;
切换堆栈后按此验证行为。
用户手册
SysTick 重装值 用户手册内核相关章节写明 SysTick 为递减计数器且有位宽上限;
重装值不能超过计数器位宽。
用户手册

勘误手册 V1.0.0(2025.9.25)针对 A 版芯片,未列出 Cortex-M0 相关条目。

国民技术开发套件文档中心

按芯片系列在线查阅国民技术 MCU 开发套件的芯片资料、SDK、驱动 API 与 Demo 示例说明。

© 2026 国民技术股份有限公司 版权所有粤ICP备11069485号本站使用不含 Cookie 的匿名访问统计