# ra-mcu-sdk
**Repository Path**: jasontek/ra-mcu-sdk
## Basic Information
- **Project Name**: ra-mcu-sdk
- **Description**: 瑞萨Arm核心的RA MCU产品系列,主要面向嵌入式AI应用的基础软件仓库,基于CMSIS编码规范,主要覆盖RA8P1等主打AI应用的芯片平台。
- **Primary Language**: C/C++
- **License**: MIT
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 4
- **Created**: 2026-09-20
- **Last Updated**: 2026-09-20
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# RA-MCU-SDK 发行与开发指南(Preliminary Version)
[TOC]
## 项目简介
`ra-mcu-sdk`专为瑞萨电子使用 Arm Cortex-M 内核的 RA 系列微控制器开发的基础驱动软件库,基于Arm CMSIS(Cortex-M Software Interface Standard)编码规范撰写,其中的内容包含 MCU 芯片的寄存器头文件、启动代码、外设HAL层驱动,以及一些基础驱动样例(driver_examples)和综合应用样例(demo_apps),供嵌入式及相关开发者基于样例工程进行二次开发,或者提取代码片段集成至自有工程。
`ra-mcu-sdk` 在架构定义阶段,特别顾及对嵌入式AI应用开发的支持:涉及底层硬件的源码简洁明了,封装的API见名知义,不同外设模块的调用逻辑遵循统一的流程规范,极大地降低了阅读和使用底层源码的复杂度,让AI开发者可以更专注于算法和上层应用的开发。
## 电路板
| 电路板 | 简介 |
| ------------- | ------------------------------------------------------------ |
| EK-RA8P1K | 瑞萨官方发布的 RA8P1 全功能开发板。适配瑞萨所有工具的软件,包括FSP的样例工程,可以运行众多的官方用例和第三方开发的演示用例。售价高昂,国内紧缺。 |
| CPKHMI-RA8P1K | 瑞萨中国官方开发和发布的早期 RA8P1 核心板以及扩展板。外扩存储、接口等同 EK-RA8P1 不兼容。其使用的微小型 B2B 插座比较小众,不便 DIY 玩家外接电路,但在推广 RA8P1 的早期阶段,在各渠道免费赠送了一些,有一定的用户量。 |
| ZFEVK-RA8P1K | 瑞萨中国官方合作伙伴,逐飞科技,开发的 RA8P1 核心板以及扩展板。外扩存储、接口等部分兼容 CPKHMI-RA8P1,使用尺寸较大且对齐的 B2B 插座同扩展板对接,通过扩展板外扩比较全面和丰富的接口,包括常用的 OV5640 摄像头、SPI/MIPI 接口的显示屏模组,以及麦克风电路,适合嵌入式 AI 开发者开发原型项目或评估。 |
## 工具链
目前主要支持 Arm Keil MDK 编译工具链。开发本项目使用的 Keil 版本号是`5.43.0.0 社区版`,其中C/C++编译器版本是`v6.24`。
在使用 Keil 开发 RA8P1 之前,需安装设备插件包:xxxx.pack(此处提供本仓库中pack文件的下载路径)。
。。。
有计划继续开发适配纯命令行的armgcc工具链版本,使用CMake作为工程管理工具。但是,似乎armgcc对RA8P1集成的Arm Ethos-U55 NPU执行代码优化效率不高,同时也在考虑能否将Keil内部的AC6 (Arm Compiler) 直接同CMake对接。使用命令行工具更容易同自动化运维系统对接,以及后续支持 LLM 的 RAG,向 AI 生成代码工具集成。
。。。
## 源码结构
用户视图的 SDK 源码在工程中分为不同的功能模块,如图x所示。
- 芯片抽象层。提供芯片头文件、片内存储的链接命令脚本文件、启动程序等。另外,芯片集成阶段构建的一些单元驱动也在其中。
- 外设驱动抽象层。通用的独立外设驱动源文件。
- 电路板配置。与电路板相关的配置源代码,包括:时钟时钟树(外设时钟源)、引脚复用功能、启用外设,电路与芯片内部外设的关联映射等。
- 与硬件无关的应用层顶级逻辑代码。可适用于不同芯片、电路板平台的业务逻辑。

图x SDK的用户界面和层级结构
SDK的代码仓库中以不同于用户视图的开发者视图组织文件,关键组件的源码,仅保存唯一一份,多个工程引用组件源码而非复制工程,从而确保核心源码的一致性,并且在多个不同的工程中验证唯一代码的正确性,在任何工程中修改关键组件的源码,将自动同步到所有相关工程。SDK代码仓库中的开发者是图

图x SDK的代码结构一览
SDK中使用了基于Python脚本和YAML数据库的Generator软件工具,管理SDK众多工程的创建、打包等功能,并可实现基于脚本的自动化编译,为将SDK代码仓库接入 CI/CD 系统提供支持。

图x SDK的代码生成器文件组织结构一览
## 基础驱动的 API 命名规则
### 总体原则
<模块名>_<功能动作>(<外设结构体类型> * base, <外设配置结构体类型定义> * init)
- 模块名为英文简写,全部为大写字符。尽量用同一个单词,特殊情况可用下划线`_`分隔,但仍全为大写字符。
- 功能动作为驼峰标识法命名,并且遵守动词在前名词在后的语序表达。功能动作的动词,主要使用 `Init`(最初的基础配置), `Setup`(配置一个相对独立的功能组,涉及多个属性), `Set`(设置数据、状态), `Get`(获取数据、状态), `Claer`(清标志位), `Enable`(动作类开), `Disable`(动作类关)。嵌入式底层的功能仍是围绕硬件IP操作,`Write`,`Read`等综合动作,通常需要在具体应用中搭配缓冲区、中断机制、DMA机制以及同步机制完成,应放在 BSP / `board` 一级实现。
- API函数的命名,原则上以动词在前名词在后的方式表述,例如 `XXX_EnableOutput()`, `XXX_EnableDma`, `XXX_EnableInterrupts`。
- API 中的形式参数以小写字符和下划线"_"构成。
- 尽量用常用的缩写,避免超长单词。
- [可选] 考虑单词排列的审美:字符组合也是通过图像感知,感官舒适的函数名容易让人印象深刻,便于记忆。尽量用占位上格和中格的字母,尽量少用占位下格子的字符。整体坐落要稳重踏实。
- 枚举类型的命名格式和枚举元素的命令格式,见下文。
以UART驱动为例。
### 初始化函数
初始化外设的函数通常时首次配置外设模块处于一个确定的初始状态。
```c
void UART_Init(R_SCI_Type * base, UART_Init_Type * init);
```
`UART`为外设名称,全部大写字母。可以用常用的缩写,尽量合并多个单词。
对于名字本身由多个单词合并,但又不好缩写的,可以用下划线`_`作为单词的分隔符。例如,DMA_CHN_Xxxx。从芯片手册中可以看到,RA原有命名为“DMA”的模块中包含了对DMA通道的管辖,特指DMA的部分仅用作分配对各通道寄存器的可访问权限和仲裁优先级,因此DMA_CHN_Xxxx仍位于hal_dma.c文件中,但在官方原版的寄存器头文件中,不同的DMA通道又被写成独立的结构体,所以用DMA_CHN_Xxx这样的API命名表示对DMA通道结构体的操作,而DMA_Xxxx的命名仅仅处理以DMA结构体归拢起来的寄存器。
关于`Init`部分命名的解释。对于具有单一不重用功能的外设,仅使用Init命名即可。但有些IP设计了不同的影响到整个模块的工作模式,在不同工作模式下,大量寄存器的工作内容都对应不同,并且各自对应一套生命周期,此时,没必要设计很复杂的配置结构体去兼容不同的工作模式,而是直接做成初始化为特定工作模式的初始工作状态。例如,在NXP的Flexcomm模块中,UART、SPI、I2C甚至I2S都可以在同一个模块内配置,但每次仅以其中一种模式工作,此时,就可以对应设计 FLEXCOMM_InitUart,FLEXCOM_InitSpi等。甚至对于SPI这种外设模块有Master模式和Slave模式,也可以对应设计SPI_InitMaster()和SPI_InitSlave()。
这里还有一个命名要点,出现功能动作字段的命名,哪怕像SPI、DMA这种常用全大写的单词,仍要遵循首字母大写其余小写的规则,便于分词,例如XXX_EnableDma()。
### 配置结构体类型
当需要将多个配置参数同时传给一个函数时,可定义配置结构体类型,以其实例作为形参,将配置属性传递给函数。
```c
typedef struct
{
uint32_t ClkSrcHz;
uint32_t Baudrate;
/* ... */
UART_Parity_Type Parity;
UART_DataBits_Type DataBits;
UART_StopBits_Type StopBits;
} UART_Init_Type;
```
### 枚举类型
枚举类型的命名格式:`__Type`,对应地,枚举元素的命令格式:`__`。以见名知义为原则,也方便一些带有自动补全功能的编辑器引用。
~~~c
typedef enum
{
UART_Parity_None = 0u,
UART_Parity_Even = 1u,
UART_Parity_Odd = 3u,
} UART_Parity_Type;
typedef enum
{
UART_DataBits_7b = 3u,
UART_DataBits_8b = 2u,
UART_DataBits_9b = 0u,
} UART_DataBits_Type;
typedef enum
{
UART_StopBits_1b = 0u,
UART_StopBits_2b = 1u,
} UART_StopBits_Type;
~~~
使用下划线作为分隔符。
### 状态标志函数组
Flag的选项以宏定义,对应IP模块中的包含多个状态位的寄存器。通常的IP设计中,状态寄存器和中断开关寄存器是对映的。
- 如果状态寄存器同中断开关寄存器不对映,以状态标志位为主(整存整取),由软件完成对各中断控制位集中管理的工作。
- 如果状态寄存器也是散落的,则可在软件中自定义寄存器组,状态位和中断开关全部由软件集中管理。后续可以推动IP设计调整但不能改 API 接口。
```c
/* UART_STATUS_FLAG_xxx. */
#define UART_STATUS_FLAG_RX_BUF_AVAILABLE (1u << 31u)
#define UART_STATUS_FLAG_TX_DONE (1u << 30u)
#define UART_STATUS_FLAG_TX_BUF_AVAILABLE (1u << 29u)
uint32_t UART_GetStatusFlags(R_SCI_Type * base);
void UART_ClearStatusFlags(R_SCI_Type * base, uint32_t flags);
void UART_EnableInterrupts(R_SCI_Type * base uint32_t flags);
void UART_DisableInterrupts(R_SCI_Type * base, uint32_t flags);
```
这里的设计要点,是认为标志位是触发中断的源头(其实二者反馈的都是电路内部的事件),当标志位事件发生后,可以用CPU在软件中主动轮询进而处理,若启用对应的中断开关,也可以由硬件自动引导到特定的中断服务程序中处理。
### 发送/接收数据函数组
嵌入式底层的功能仍是围绕硬件IP操作,`Write`,`Read`等综合动作,通常需要在具体应用中搭配缓冲区、中断机制、DMA机制以及同步机制完成,应放在 BSP / `board` 一级实现。但部分通信类IP在设计时,不需要依赖于其他IP,其内部自行实现了这些机制,也可以酌情考虑使用。
~~~c
void UART_PutData(R_SCI_Type * base, uint8_t dat); /* put data into fifo. */
uint8_t UART_GetData(R_SCI_Type * base); /* get data from fifo. */
~~~
未完待续。。。
## 开发用例
### 新增一个用例
以为cpkhmi-ra8p1k电路板增加w25q256jv_qspi工程为例。
这个工程本身使用片内flash和片内sram运行,使用xspi接口的qspi工作模式对接w25q256jv存储器芯片,执行演示用例,为后续编写flashloader或者bootloader做准备。主要验证xspi驱动程序、w25q256jv的执行逻辑。
从SDK代码框架的角度上看,本例中除了演示基本的添加工程外,还需要注意w25q256jv在qspi下的操作函数:这不是通用的驱动,不能封装成组件(SDK中的components);不能完全脱离硬件成纯业务逻辑,不能放入应用层(SDK中的application),与具体电路板有关联,最后放到了board目录下。
本例中使用的代码都是在母仓库(arm-mcu-sdk)中调试完毕的,此处展现将相关文件搬运至本仓库的操作过程。
1. 复制board下的源文件目录
`ra-mcu-sdk\boards\cpkhmi-ra8p1k\demo_apps\basic\w25q256jv_qspi`
配置板子,以及同具体电路板相关的代码,对电路板资源的抽象。
2. 复制application下的源文件目录
`ra-mcu-sdk\applications\demo_apps\basic\w25q256jv_qspi`
应用层的源码,可以跨电路板、跨芯片使用。
3. 在generator中添加记录
在`ra-mcu-sdk\tools\generator\yaml\applications\demo_apps\basic`目录中创建`w25q256jv_qspi.yaml`文件,也可以复制同目录下的`hello_world.yaml`改。这个文件是描述应用层的文件,后续也可以给别的板子用,其中文件内容如下:
```yam
sourcefiles: # project dir
application: # group name
- main.c
- readme.md
defines:
- APP_W25Q256JV_QSPI
```
在`ra-mcu-sdk\tools\generator\yaml\boards`目录中的`cpkhmi-ra8p1k.yaml`文件中添加应用工程记录。如下所示:
```yaml
devices:
superset: R7FA8P1
partno: R7FA8P1KF
applications:
demo_apps:
basic:
hello_world:
blinky_led:
systick_basic:
w25q256jv_qspi:
driver_examples:
ra_ioport_0:
ioport_basic:
ra_uart_0:
uart_tx_rx_polling:
uart_tx_rx_interrupt:
ra_dma_1:
dma_normal_mode:
defines:
- BRD_CPKHMI_RA8P1K
```
4. 运行命令脚本,生成工程进行验证。
在Git Bash中切换至`ra-mcu-sdk/tools`目录,运行如下脚本。
~~~bash
AndrewSU@WorkStation-Andrew MINGW64 /d/_gitrepos/ra-mcu-sdk/tools (master)
$ python ./gen_create_one_prj.py --prjname demo_apps/basic/w25q256jv_qspi --board cpkhmi-ra8p1k
demo_apps\basic\w25q256jv_qspi
D:\_gitrepos\ra-mcu-sdk\boards\cpkhmi-ra8p1k\demo_apps\basic\w25q256jv_qspi\build\mdk\project.uvprojx
*** done ***
~~~
根据运行脚本输出log中的提示,使用Keil软件运行`ra-mcu-sdk\boards\cpkhmi-ra8p1k\demo_apps\basic\w25q256jv_qspi\build\mdk\project.uvprojx`文件即可打开工程。
5. 验证工程。
原则上,工程经过编译后需成功,不允许有警告。
可能会出现因为在新仓库更新驱动,导致原有调用不兼容的问题。需要手动修改调试board层的代码,后续合并到母仓库中。
特别看下引脚、时钟是否对应上,在实际电路板上运行后是否有预期的log输出。
本节完。
### 关于xip工程和2nd_bootloader工程
xip工程和普通工程的应用层源码没有任何区别,唯一的区别在linker file中,指定代码存放在xip的存储器上,以及把数据段映射到sdram中。但是特别要注意,不能碰xip存储器和sdram相关的引脚和时钟。下载程序需要同flashloader(Keil设备支持包中的下载算法文件)配合。
非标的linker file存放在board目录下各工程的项目目录下,例如`ra-mcu-sdk\boards\cpkhmi-ra8p1k\demo_apps\basic\hello_world_xip\linker\mdk`目录下存放的`r7ka8p1kf_flash.scf`文件。Generator生成工程时会自动使用定制的linker file取代默认的device目录下的linker file。
2nd_bootloader工程的程序是下载到片内flash中的,一次下载,终身受用,后面可以换不同的xip应用工程。
在有bootloader的情况下,也可以使用keil的调试功能,单步调试位于xip存储器上的代码。keil的调试器会等到main()函数的起始,然后开始单步调试,不关心之前在bootloader中经历的漫长过程。
### 使用Keil生成bin文件
在OTA升级的应用场景中,需要将固件文件转换成bin文件,才能通过bootloader中的YModem通信协议栈识别并下载。
在Keil中,可以通过添加命令`fromelf --bin -o "$L@L.bin" "#L`,实现在编译工程后自动输出bin文件。如图x所示。

图x 在Keil中添加后编译命令