2025 / 02 / 12
RA8 Cortex-M85 Helium动手实践
本文将演示基于FSP来使用瑞萨RA8系列MCU的Helium基本功能,如果曾经学习和了解过先前发布的“RA8 Cortex M85 Helium入门指南”系列文章,学习效果会更佳。
工程代码链接:
https://gitee.com/recn-mcu-ae/ra8d1cpkcorheliumdemollvm
本实验选用CPKCOR-RA8D1,介绍如何使用J-Link OB CDC进行printf调试输出,以及Helium intrinsic技术的编程示例和调试。
开发环境:
-
FSP 5.1.0及其以上 (本文以FSP5.2.0为例)
-
e2 studio 2023-10及其以上 (本文以e2 studio 2024-01为例)
-
Renesas.RA_board_ra8d1_cpkcor.5.1.0.pack (该文件可以在上述工程代码gitee链接中找到)
-
Tera Term (其他的串口调试工具也可以)
-
开发板CPKCOR-RA8D1

CPKCOR-RA8D1总览图
1. 导入RA_board_ra8d1_cpkcor.5.1.0.pack文件
打开e2 studio,点击菜单中的 “File” → “import…”。

在弹出的“Import”对话框中,选择“General” →“CMSIS Pack”,点击“Next”。注意检查选中的工程描述是“Import a CMSIS Pack into e2 studio”。

在弹出的“Import CMSIS Pack”对话框中,点击“Specify pack file”右侧“...”,指定需要添加的Renesas.RA_board_ra8d1_cpkcor.5.1.0.pack文件。

导入完成后,点击“OK”。

2. 创建工程
打开e2 studio,点击菜单中的 “File” → “New” → “Renesas C/C++ Project” → “Renesas RA”。来创建新的C/C++工程。


填写创建的项目名称后,再按下图所示进行相关设置。
FSP选择"5.2.0",Board选择“CPK-RA8D1B Core Board”,Toolchains选择"LLVM Embedded Toolchain for Arm: 17.0.1"

本次示例中,我们建立基于RA8D1的裸机系统,选择Flat (Non-TrustZone) Project。

选择“No RTOS”,然后运行“Next”。

选择“Bare Metal – Minimal”,然后运行“Finish”。

工程创建后,默认会打开configuration.xml文件。切换到Stack选项卡后,选择New Stack → Connectivity>UART(r_sci_b_uart)。

此时会出现一个错误信息,是因为没有使能SCICLK造成的。

切换到Clocks选项卡并使能SCICLK。SCICLK时钟源是PLL1P。确保SCICLK为120MHz后,保存配置。

切换到Stack并确定之前错误已经被解决。

在“Properties”中选择 g_uart0 UART (r_sci_b_uart)。
General
Name: g_uart3
Channel: 3
Interrupts
Callback: user_uart3_callback
检查引脚配置:

在BSP设定中的"Cache settings→Data cache"选择"Enabled",由于该工程会对图片进行处理,所以会使用到比较大的RAM资源,需要事先进行堆栈设定,设置stack(栈)为"0x68000",heap(堆)为"0x1000"。

添加low_level.c,dwt.c,dwt.h,pic.h,hal_entry.c文件到工程中。提示冲突时,选择替换原来的hal_entry.c。

dwt.c和dwt.h是DWT相关的代码,DWT是(Data Watchpoint and Trace)的缩写,它是ARM cortex M中的一个32位的向上计数器,记录的是内核时钟运行个数,内核时钟跳动一次,该计数器就加1,精度非常高,用于系统调试和跟踪,也可以用来测试代码运行时间。
pic.h是一张240*160的32-bit RGBA8888图片数据。

在hal_entry.c中的第12~13行,有两个宏定义。

HELIUM_BASIC_DEMO是使能helium的基础例子的开关,它展示使用helium 原语函数(intrinsic)来进行加法,减法和乘法运算。
HELIUM_RGB_DEINTERLEAVE_DEMO 是使能helium 进行图像RGBA数据色彩数据分离例子的开关,它展示了在色彩数据分离中,普通c代码实现和通过使用helium 原语函数(intrinsic)的实现之间的效率对比。
注:HELIUM_BASIC_DEMO的优先级比HELIUM_RGB_DEINTERLEAVE_DEMO高,如果这两个宏同时定义,hal_entry.c中的第15~17行代码会自动进行 #undef HELIUM_RGB_DEINTERLEAVE_DEMO操作。


开发板上电连接与启动
打开PC设备管理器,在端口 (COM和LPT) 中会显示“Jlink CDC UART Port(COMxx)”,表示PC已经识别到CPKCOR-RA8D1板上的JLink CDC UART。在通用总线设备中会显示“BULK interface”。

打开Tera Term,Port设置为上个步骤中的设备管理器中的JLink CDC UART(COMXX)。将波特率设定为115200,点击“New setting”,完成设置。以下是Tera Term波特率的修改界面。







本示例中使用的原语函数(intrinsic)
uint8x16_t vld1q u8(uint8 t const*base)
从内存中加载数据到uint8x16_t (16个uint8数据)类型矢量寄存器中。
void vst1q_u8(uint8_t *base,uint8x16_t value)
将uint8x16_t (16个uint8数据)类型矢量寄存器中的数据写到内存中。
uint8x16_t vaddq_u8(uint8x16_t a,uint8x16_t b)
将两个uint8x16_t (16个uint8数据)类型矢量寄存器中的数据相加,并返回计算结果。
uint8x16_t vsubq_u8(uint8x16_t a,uint8x16_t b)
将两个uint8x16_t (16个uint8数据)类型矢量寄存器中的数据相减,并返回计算结果。
uint32x4 t vld1q u32(uint32 t const*base)
从内存中加载数据到uint32x4_t (4个uint32数据)类型矢量寄存器中。
uint32x4_t vmulq_n_u32(uint32x4_t a,uint32_t b)
将两个uint32x4_t (4个uint32_t数据)类型矢量寄存器中的数据相乘,并返回计算结果。
3. 调试Helium原语函数(intrinsic)对RGBA图像色彩数据解交织工程
点击菜单栏中的“Windows” → “Show View” → “Other...”,在弹出的窗口中输入“Memory”,选择“Open”。


Memory界面显示如下。


选择Raw Image。

再选择Raw Image Format。

填入图片的格式,宽为240,高为160,编码为RGB 32bpp(8:8:8:8)。点击“OK”。

设置完成后,会在gImage_dog_240_160:0x22000000处显示彩色图片。

使用同样的方法,添加rgba_b_mve到memory中,会发现显示的图片不正常。

在图片上鼠标右键选择“Format”填入参数宽为240,高为160,编码为Monochrome 8bpp。点击“OK”。

这个时候,图片就显示正常了。因为现在这个数据是单一色彩通道的数据,所以,就是一个8位的灰度图。

实验中的图片是RGBA格式,通过使用Helium的原语函数(intrinsic),实现RGBA色彩数据分离。
在做图片处理的时候,通过读取图片的数据,在内存上按照RGBA顺序排列,如下图所示:

一般来说,我们有时候需要将RGBA四个通道数据分开,进行分别处理,常用的代码可能是下面这样的。

通过遍历图片的每行和每列中的每个像素点中的RGBA四个通道数据,再逐一提取。
注:本程序仅提取了RGB数据
在这个例子中,我们主要使用了Helium的原语函数vld4q_u8和vst1q_u8。

vld4q_u8() 是ARM NEON/HELIUM SIMD(Single Instruction, Multiple Data)指令集中的一个原语函数。这个函数通常用于在四个连续内存位置上加载无符号8位整数,并将它们打包成一个128位的寄存器。具体来说,它的功能如下:
1.加载:vld4q_u8() 会从内存中加载四个连续的8位无符号整数值。
2.打包:这四个加载的整数值会被打包成一个128位的寄存器。在这个寄存器中,每个连续的32位子寄存器中包含一个8位整数值。
这种打包的方式对于同时处理多个数据元素非常有用,因为它可以减少内存访问次数和数据移动的开销,从而提高处理效率。
总的来说,vld4q_u8() 用于加载并打包四个连续的8位无符号整数值,以便在NEON/HELIUM SIMD指令集下进行并行处理。
vst1q_u8() 是ARM NEON/HELIUM SIMD指令集中的一个函数,用于将128位寄存器中的数据存储到内存中。具体来说,它的功能如下:
1.存储:vst1q_u8() 将一个128位的寄存器中的数据存储到内存中。
2.8位整数:这个函数是针对无符号8位整数的,即每次存储的是8位的数据。
因此,vst1q_u8() 通常用于将128位的寄存器中打包的多个数据元素写入内存,例如将处理后的图像数据或其他大规模数据集存储到内存中。