☰
国民技术 N32 使用 Nimmake 编译指南
2026/10/2 2:29:35 网站建设 项目流程

国民技术 N32 使用 Nimmake 编译指南(详细版)

主线型号:N32G45x / Cortex-M4F
适用系列:N32G45x、N32G43x、N32L43x、N32L40x、N32G03x
构建方式:arm-none-eabi-gcc+ Nimmake(Python 构建脚本)


1. 可行性结论

国民技术 N32 系列基于 ARM Cortex-M 内核,官方 SDK 和 N32Cube 工具已提供完整的 GCC 支持,使用 Nimmake 进行裸机编译完全可行。

官方已提供的 GCC 素材:

素材位置说明
启动文件Libraries/CMSIS/Device/Nationstech/N32G45x/Source/GCC/startup_n32g45x_gcc.sARM 汇编语法,GCC 可用
链接脚本Libraries/CMSIS/Device/Nationstech/N32G45x/Source/GCC/n32g45x_flash.ldFlash + SRAM 布局
CMSIS 头文件Libraries/CMSIS/Device/Nationstech/N32G45x/Include/n32g45x.h寄存器定义
系统初始化Libraries/CMSIS/Device/Nationstech/N32G45x/Source/system_n32g45x.c时钟配置、SystemInit
标准外设库Libraries/N32G45x_StdPeriph_Driver/GPIO、USART、SPI、I2C、TIM 等
GCC 示例工程Projects/N32G45x_StdPeriph_Examples/部分示例含 GCC/MDK-ARM 双版本

N32Cube 支持:N32Cube 图形化工具可生成 Makefile 工程,包含启动文件、链接脚本、外设初始化代码,可作为 Nimmake 工程的素材来源。


2. 为什么用 Nimmake

痛点Makefile 写法Nimmake 写法
芯片参数手动写-mcpu=cortex-m4 -mfpu=fpv4-sp-d16 -mfloat-abi=hardCORTEX_M4F_CFG.clone()一行预设
HAL 源文件列表手动 glob 或逐行写,换库就改Parties自动扫描src/**/*.c
换型号改 CPU、FPU、链接脚本、启动文件、宏,多处同步只改Chip预设和Parties路径
头文件路径-I逐条写,漏一个就报错Parties自动收集inc/
多目标每个 target 写一套规则多个Program对象即可
缓存需要手动配ccache--cache内置支持
跳转补全无--compiledb生成compile_commands.json,VSCode/CLion 可用
烧录命令手写openocd -f ...Phony目标封装,nimmake flash
跨平台Windows 下路径分隔符、工具链路径问题Nimmake 内部处理路径

3. 环境准备

3.1 安装 Nimmake

pipinstallnimmake nimmake--version

3.2 安装 ARM GCC 工具链

推荐使用xPack ARM Embedded GCC:

# macOSbrewinstallxpack-arm-none-eabi-gcc# Windows (Scoop)scoopinstallxpack-arm-none-eabi-gcc# Linux:从 xpack-dev-tools 发布页下载解压

验证:

arm-none-eabi-gcc--versionarm-none-eabi-size--versionarm-none-eabi-objcopy--version

Nimmake 会自动检测工具链,无需手动配置 PATH(但建议加上方便调试)。

3.3 获取 N32 SDK 素材

方式一:从官方 SDK 获取

  1. 从国民技术官网或 GitHub 下载 N32G45x SDK
  2. 关注Libraries/CMSIS/、Libraries/N32G45x_StdPeriph_Driver/、Projects/

方式二:用 N32Cube 生成

  1. 选择 N32G45x 系列芯片,配置时钟/GPIO/外设
  2. 生成Makefile工程
  3. 提取startup_n32g45x_gcc.s、n32g45x_flash.ld、Core/Inc|Src、Drivers/

3.4 素材自检清单

  • startup_n32g45x_gcc.s— GCC 版启动文件(不是 Keil/IAR 版)
  • n32g45x_flash.ld— 链接脚本
  • n32g45x.h— 设备头文件
  • system_n32g45x.c— 系统初始化
  • 标准外设库.c文件(至少 GPIO + USART 用于测试)
  • arm-none-eabi-gcc可执行

4. 工程目录结构

推荐按重编频率分层:

n32-nimmake/ ├── Nimmake.py # 构建脚本(唯一需要维护的文件) ├── App/ # 应用代码(最常改) │ ├── inc/main.h │ └── src/main.c ├── Device/ # 芯片启动 + 系统初始化(几乎不改) │ ├── startup_n32g45x_gcc.s │ └── system_n32g45x.c ├── Libraries/ # 标准外设库(几乎不改) │ └── N32G45x_StdPeriph_Driver/ │ ├── inc/ (n32g45x.h, n32g45x_gpio.h, n32g45x_usart.h ...) │ └── src/ (n32g45x_gpio.c, n32g45x_usart.c ...) ├── ld/ # 链接脚本 │ └── n32g45x_flash.ld └── tools/ # 烧录脚本(可选) └── openocd_n32.cfg

分层理由:App/每次改代码只重编这一层;Device/和Libraries/作为静态库首次编译后缓存;ld/独立存放,换型号时只需替换链接脚本。


5. 完整 Nimmake.py

# Nimmake.pyfromnimmakeimport*# 1. 芯片预设Chip=CORTEX_M4F_CFG.clone()Chip.CPU="cortex-m4"Chip.FPU="fpv4-sp-d16"Chip.ABI="hard"Chip.MACROS=["N32G45X","USE_STDPERIPH_DRIVER"]# 2. 工具链Toolchain=Update(Chip)Refresh()# 3. 编译选项CFLAGS=["-O2","-g","-ffunction-sections","-fdata-sections","-Wall","-Wno-unused-function"]LDFLAGS=["-T"+ROOT+"/ld/n32g45x_flash.ld","-Wl,--gc-sections","-Wl,-Map="+BUILD+"/firmware.map","--specs=nano.specs","--specs=nosys.specs",]# 4. Party:标准外设库(静态库)StdLib=Party("StdLib")StdLib.Sources=Glob("Libraries/N32G45x_StdPeriph_Driver/src/*.c")StdLib.Includes=["Libraries/N32G45x_StdPeriph_Driver/inc","Libraries/CMSIS/Device/Nationstech/N32G45x/Include","Libraries/CMSIS/Core/Include",]StdLib.BuildType="staticlib"# 5. Party:Device(启动文件 + 系统初始化)Device=Party("Device")Device.Sources=["Device/startup_n32g45x_gcc.s","Device/system_n32g45x.c"]Device.Includes=["Libraries/CMSIS/Device/Nationstech/N32G45x/Include","Libraries/CMSIS/Core/Include","Libraries/N32G45x_StdPeriph_Driver/inc",]Device.Dependencies=[StdLib]# 6. Party:App(应用代码)App=Party("App")App.Sources=Glob("App/src/*.c")App.Includes=["App/inc","Libraries/N32G45x_StdPeriph_Driver/inc","Libraries/CMSIS/Device/Nationstech/N32G45x/Include","Libraries/CMSIS/Core/Include",]App.Dependencies=[Device]# 7. ProgramProgram=Prog("firmware")Program.Parties=[App]Program.CFLAGS=CFLAGS Program.LDFLAGS=LDFLAGS# 8. 后处理:生成 bin + 打印体积@AfterBuilddefpost_build(ctx):elf=ctx.Output Run("arm-none-eabi-objcopy -O binary "+elf+" "+elf[:-4]+".bin")Run("arm-none-eabi-objcopy -O ihex "+elf+" "+elf[:-4]+".hex")Run("arm-none-eabi-size "+elf)# 9. 烧录目标@Phonydefflash(ctx):elf=ctx.Program.Output cmd=('openocd -f interface/cmsis-dap.cfg -f target/stm32f4x.cfg ''-c "init" -c "reset halt" ''-c "flash write_image erase " + elf + " -c "reset run" -c "exit"')Run(cmd)@Phonydefgdb(ctx):Run("arm-none-eabi-gdb "+ctx.Program.Output)@Phonydeferase(ctx):cmd=('openocd -f interface/cmsis-dap.cfg -f target/stm32f4x.cfg ''-c "init" -c "reset halt" -c "stm32f4x mass_erase 0" -c "exit"')Run(cmd)DefaultTarget(Program)

6. 逐段解析

6.1 芯片预设

N32G45x 是 Cortex-M4F,带单精度 FPU。CORTEX_M4F_CFG已包含-mthumb -mcpu=cortex-m4 -mfpu=fpv4-sp-d16 -mfloat-abi=hard,无需手写。

6.2 编译宏

  • N32G45X:型号宏,决定头文件中寄存器定义
  • USE_STDPERIPH_DRIVER:使用标准外设库(若用 HAL 库改为USE_HAL_DRIVER)

6.3 链接脚本

  • --gc-sections:配合-ffunction-sections -fdata-sections裁剪未使用代码
  • nano.specs:使用 newlib-nano,显著减小体积
  • nosys.specs:提供_sbrk/_write等系统调用空实现

6.4 Party 分层

Party类型说明
StdLibstaticlib标准外设库,编译为.a,几乎不重编
Deviceobject启动文件 + system_n32g45x.c
Appobject用户代码,每次修改只重编这一层

6.5 N32 专属注意点

1. 启动文件必须用 GCC 版:SDK 中可能同时存在startup_n32g45x_keil.s(Keil 语法,GCC 不能用)与startup_n32g45x_gcc.s(GCC 兼容,选这个)。

2._estack与链接脚本:链接脚本中必须有_estack = ORIGIN(RAM) + LENGTH(RAM);,启动文件通过_estack初始化栈指针。RAM 大小与实际芯片不符会直接 HardFault。

3. VTOR 向量表偏移:system_n32g45x.c的SystemInit()会设置 VTOR;做 Bootloader + App 时 App 需重设SCB->VTOR = APP_FLASH_OFFSET;。


7. 常用命令

命令说明
nimmake编译默认目标
nimmake --dry-run预览命令,不实际执行
nimmake --compiledb生成compile_commands.json
nimmake --cache启用编译缓存
nimmake -n仅编译,不链接
nimmake -c清理构建目录
nimmake flash烧录固件
nimmake erase擦除芯片
nimmake gdb启动 GDB

8. 烧录方案

8.1 OpenOCD + CMSIS-DAP / NS-Link

N32G45x 与 STM32F407 兼容,多数可用stm32f4x.cfg:

openocd-finterface/cmsis-dap.cfg-ftarget/stm32f4x.cfg\-c"program firmware.elf verify reset exit"

若无 N32 专用 target,可用target/stm32f4x.cfg或参考 Flash 控制器寄存器自定义 cfg。

8.2 官方 NSING MCU Download Tool (UART/USB DFU)

NSLink_Download-pCOM3-b115200-ffirmware.bin

8.3 J-Link

JLinkExe-deviceN32G45x-ifSWD-speed4000-CommanderScriptflash.jlink

flash.jlink:h/loadbin firmware.bin 0x08000000/r/g/q


9. printf 重定向

使用nano.specs+nosys.specs时,需重写_write才能通过 USART 输出:

// App/src/uart_printf.c#include<stdio.h>#include<unistd.h>#include"n32g45x.h"#include"n32g45x_usart.h"int_write(intfd,char*ptr,intlen){if(fd==STDOUT_FILENO||fd==STDERR_FILENO){for(inti=0;i<len;i++){while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)==RESET);USART_SendData(USART1,ptr[i]);}returnlen;}return-1;}

若用nosys.specs且不重写_write,printf 会静默丢弃输出,不报错。


10. 型号迁移

型号内核预设宏FlashSRAM链接脚本
N32G45xM4FCORTEX_M4F_CFGN32G45X512K/1M144Kn32g45x_flash.ld
N32G43xM4FCORTEX_M4F_CFGN32G43X128K32Kn32g43x_flash.ld
N32L43xM4CORTEX_M4_CFGN32L43X256K48Kn32l43x_flash.ld
N32G03xM0CORTEX_M0_CFGN32G03X64K8Kn32g03x_flash.ld

迁移时只需修改Nimmake.py中的Chip预设、Chip.MACROS、LDFLAGS的-T链接脚本。


11. Bootloader + App

11.1 Flash 分区示例

区域地址大小用途
Bootloader0x0800000032 KB引导程序
App0x08008000剩余应用程序

11.2 App 链接脚本修改

/* n32g45x_app.ld */ MEMORY { FLASH (rx) : ORIGIN = 0x08008000, LENGTH = 480K RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 144K }

11.3 App 中设置 VTOR

intmain(void){SCB->VTOR=0x08008000;// 偏移向量表SystemInit();// ...}

11.4 Nimmake 多目标

Bootloader=Prog("bootloader")Bootloader.Parties=[App_BL]Bootloader.LDFLAGS=["-T"+ROOT+"/ld/n32g45x_boot.ld","-Wl,--gc-sections","--specs=nano.specs","--specs=nosys.specs",]AppFw=Prog("app")AppFw.Parties=[App]AppFw.LDFLAGS=["-T"+ROOT+"/ld/n32g45x_app.ld","-Wl,--gc-sections","--specs=nano.specs","--specs=nosys.specs",]DefaultTargets=[Bootloader,AppFw]

12. FreeRTOS 集成

将 FreeRTOS 作为独立 Party,源码目录(croutine.c、event_groups.c、list.c、queue.c、stream_buffer.c、tasks.c、timers.c)+ 对应内核的portable/GCC/ARM_CM4F+portmacro。

FreeRTOS=Party("FreeRTOS")FreeRTOS.Sources=Glob("FreeRTOS/*.c")+\ Glob("FreeRTOS/portable/GCC/ARM_CM4F/*.c")FreeRTOS.Includes=["FreeRTOS/include","FreeRTOS/portable/GCC/ARM_CM4F","FreeRTOSConfig",# FreeRTOSConfig.h]FreeRTOS.Dependencies=[Device]App.Dependencies=[Device,FreeRTOS]

中断配合要点:在FreeRTOSConfig.h中把xPortPendSVHandler/xPortSysTickHandler/xPortSVCHandler映射到PendSV_Handler/SysTick_Handler/SV_Handler,并确保 N32 启动文件向量表中的这几个符号不与 FreeRTOS 端口冲突(N32 启动文件里的SysTick_Handler等弱符号会被 FreeRTOS 强符号覆盖)。


13. FAQ

  1. 启动文件报语法错:误用了 Keil/IAR 版.s,换成*_gcc.s。
  2. HAL 与标准库混用冲突:同一工程只保留一套驱动,宏也对应USE_HAL_DRIVER或USE_STDPERIPH_DRIVER。
  3. ABI mismatch(hard/soft float):Chip.ABI、-mfloat-abi与所有预编译库必须一致。
  4. OpenOCD 缺 flash 算法:改用stm32f4xtarget 或补 N32 flash 算法。
  5. -T没生效:链接脚本路径拼错或ROOT/BUILD变量未展开,用--dry-run看实际命令。
  6. 上电即 HardFault:_estack/RAM 大小与型号不符,或栈/堆区被链接脚本压缩。
  7. printf 无输出:未重写_write,或 USART 未初始化。
  8. Refresh()漏写:Update(Chip)后未Refresh(),工具链/选项不生效。
  9. 跳转/补全乱码:未生成compile_commands.json,用nimmake --compiledb。
  10. 静态库链接顺序错:用Dependencies声明顺序,不要手写.a顺序。
  11. 换型号后外设头找不到:同步换Includes里的 CMSIS Device 目录。

14. 体积分析

  • arm-none-eabi-size firmware.elf看 text/data/bss。
  • -Wl,-Map=firmware.map生成映射文件,按模块排查大对象。
  • --gc-sections+-ffunction-sections -fdata-sections裁剪未用代码。
  • --specs=nano.specs换 newlib-nano;去掉不用的printf浮点支持(_printf_float_support=0)。
  • 只启用需要的标准外设.c,避免整个 StdPeriph 全量链接。

15. 最小可跑 Checklist

  1. 装好arm-none-eabi-gcc与nimmake。
  2. 拿到 GCC 版启动文件startup_n32g45x_gcc.s。
  3. 拿到n32g45x_flash.ld且 RAM/FLASH 大小与型号一致。
  4. 放入n32g45x.h、system_n32g45x.c、GPIO 外设库。
  5. 写好Nimmake.py(芯片预设 + 工具链 + Party + Program)。
  6. nimmake编译出firmware.elf。
  7. nimmake flash或 OpenOCD 烧录。
  8. 上电点亮 LED。
  9. 串口printf重定向后确认日志输出。

全部通过后,即可在此基础上扩展 USART、SPI、FreeRTOS、Bootloader 等模块。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询