跳到主要内容

HPM 代码生成

代码生成器的 libxr hpm setup 命令把 HPM Pinmux Tool 工程接入 LibXR:解析工程,生成 C++ 初始化代码。引脚复用和时钟由 Pinmux Tool 导出的代码配置,工程根目录的 main.c 调用其中的 pinmux 函数,main.c 由 BSP 维护;生成的代码是 User/ 下的 app_main,以 HPM SDK 的宏构造 LibXR 对象。


工程识别​

工程是根目录有 app.yaml、boards/<board>/ 下恰有一个 .hpmpc 文件的目录。选中的信号来自 .hpmpc 的 selectPins,以及 main.c 中 main 在预处理条件之外调用的 pinmux 函数;.hpmpc 中有而 main 没有调用的函数不参与。函数里的每个信号按 hpm_iomux.h 的宏名对照 SoC 封装的布局核对,封装没有的 pad 或 pad 不能承载的信号给出警告。

gpiom 设置把引脚分给 GPIO0 以外的控制器(gpioController 不为 1,未写时是 GPIO0)时,不为它生成对象,每个这样的引脚给出一条警告。

main 只在预处理条件里调用的 pinmux 函数同样不读(生成的对象不能依赖编译选项),每个这样的函数给出一条警告;调用全部在预处理条件里时读不到引脚。main 一次 pinmux 函数都没有调用时退回读 init_bsp_pins,工程没有这个函数时读不到引脚。


快速使用​

在工程根目录下执行:

libxr hpm setup -d .

该命令依次完成以下步骤:

  1. 工程目录还没有 .gitignore 时创建一个,忽略 build、.history、.cache、CMakeFiles 和 .config.yaml;已有的 .gitignore 保持不变;
  2. 解析工程,写出 .config.yaml;
  3. 在 User/ 下生成 app_main.cpp、app_main.h 和 libxr_config.yaml。

--xrobot 和 --no-xrobot 都不写时,沿用输出文件现在的选择,新文件不使用 XRobot;使用 XRobot 时生成 XR_REGISTER 注册和 XROBOT_MAIN();,命令结束前提示运行 xrobot gen 更新 User/xrobot_main.hpp。已有的 app_main.cpp 不是 libxr gen 生成时,setup 给出警告并列出将被替换的行,User Code 区域之外的内容全部重新生成。


示例输出​

以下是在 bsp-hpm5301evklite 的副本上删去 User/ 下的三个生成文件后的首次运行:

$ libxr hpm setup -d . --xrobot
[信息] 配置已导出到:.\.config.yaml
[信息] 系统:裸机
[信息] .\User\libxr_config.yaml 不存在,按默认设置新建
[信息] 已生成 User:已写入 app_main.cpp、app_main.h、libxr_config.yaml
[信息] 下一步:运行 `xrobot gen` 更新 User/xrobot_main.hpp;libxr 的 setup 不更新它

===== [配置摘要] =====

SoC:HPM5301(QFN48)

GPIO(2 个引脚):
pa10: PA10
pa3: PA03

已启用的外设:
I2C:1 个实例
I2C3
PWM:1 个实例
GPTMR0
Other:7 个实例
UART0
UART3
SPI1
ADC0
ACMP
USBPHY
USB0
代码块已折叠

之后再运行时,生成器只改写内容有变化的文件。配置摘要中的 Other 是 LibXR 还没有驱动的配置,见生成的对象与命名。


输出结构​

执行完成后,工程新增或修改这些文件:

.
├── .config.yaml # 解析生成的配置文件
├── app.yaml # HPM SDK 工程描述(BSP 提供)
├── main.c # BSP 提供:init_bsp_pins() 等之后调用 app_main()
├── boards/<board>/
│ └── tool_config.hpmpc # Pinmux Tool 工程(BSP 提供)
├── User/
│ ├── app_main.cpp # 入口源文件
│ ├── app_main.h # 入口源文件的头文件
│ └── libxr_config.yaml # LibXR 配置文件
└── libxr/ # Git 子模块:LibXR 本体

app_main.cpp​

app_main.cpp 包含 LibXR 初始化函数 app_main()。/* User Code Begin N */ 与 /* User Code End N */ 之间的代码在重新生成时保留,其余部分每次重新生成。生成的外设对象都是 static 对象,app_main() 返回后仍然有效;长期使用的局部对象声明为 static。外设对象可以以基类指针传给线程或模块使用,做法见与XRobot集成。

下面是上例首次生成的 app_main.cpp:

// Generated by `libxr gen`; do not edit by hand.
// Generated from boards/hpm5301evklite/tool_config.hpmpc (HPM5301); the Pinmux Tool
// configures the pins via init_bsp_pins() in main.c and main.c stays BSP-owned. Put
// application code between the "User Code Begin" and "User Code End" markers.
#include "app_main.h"

#include "libxr.hpp"
#include "hpm_gpio.hpp"
#include "hpm_i2c.hpp"
#include "hpm_power.hpp"
#include "hpm_pwm.hpp"
#include "hpm_soc.h"
#include "hpm_timebase.hpp"
#include "xrobot_main.hpp"

using namespace LibXR;

/* User Code Begin 1 */
/* User Code End 1 */

extern "C" void app_main(void)
{
/* User Code Begin 2 */
/* User Code End 2 */

// Timebase and platform
static HPMTimebase timebase;
PlatformInit();
static HPMPowerManager power_manager;

// GPIO: init_bsp_pins() configured pa10 (PA10) as an output pin, pa3 (PA03) as an input
// pin with a pull-down. The interrupt edge comes from the pinmux and every object
// carries its port IRQ; RegisterCallback and EnableInterrupt belong to the consumer (a
// Module or the User Code).
static HPMGPIO pa10(HPM_GPIO0, GPIO_DI_GPIOA, 10, IRQn_GPIO0_A);
static HPMGPIO pa3(HPM_GPIO0, GPIO_DI_GPIOA, 3, IRQn_GPIO0_A);

// I2C: blocking transfers; the DMA-backed background path runs on dma_mgr.
static HPMI2C i2c3(HPM_I2C3, clock_i2c3, {100000U});

// PWM on a GPTMR comparator: the SoC has no PWM peripheral, so HPMPWM takes its GPTMR
// fallback path; the duty comparator is fixed at 0 and SetConfig is called when
// libxr_config sets a frequency.
static HPMPWM pwm_gptmr0_ch1(reinterpret_cast<LibXRHpmPwmType*>(HPM_GPTMR0),
clock_gptmr0, 1, 0, HPMPWM::Polarity::NORMAL);

// Hardware registration
XR_REGISTER(power_manager, LibXR::PowerManager);

XR_REGISTER(pa10, LibXR::GPIO);
XR_REGISTER(pa3, LibXR::GPIO);

XR_REGISTER(pwm_gptmr0_ch1, LibXR::PWM);

XR_REGISTER(i2c3, LibXR::I2C);

/* User Code Begin 3 */
/* User Code End 3 */
XROBOT_MAIN();
}
代码块已折叠

引脚和时钟在 main.c 调用的 pinmux 函数中配置完毕,构造参数引用 SDK 的宏(HPM_GPIO0、GPIO_DI_GPIOA、IRQn_GPIO0_A、HPM_I2C3、clock_i2c3)。GPIO 对象带所属端口的 IRQ,端口的中断服务程序由 LibXR 驱动提供,边沿来自 pinmux,回调的登记(RegisterCallback 和 EnableInterrupt)由使用方完成。LibXR 的 HPM 驱动依赖 SDK 的 dma_mgr 组件,工程的 CMakeLists.txt 设置 CONFIG_DMA_MGR,两个 BSP 都已设置。


生成的对象与命名​

生成的对象来自 LibXR 有驱动的配置:每个 GPIO 引脚一个对象,每个同时选了 SCL 和 SDA 的 I2C 一个对象;GPTMR 上的 PWM 见下文。其余配置(UART、SPI、MCAN、ADC、ACMP、USB、缺 SCL 或 SDA 的 I2C、只有捕获输入的 GPTMR 等)列入配置摘要的 Other,只作展示。

生成结果总是包含 static HPMPowerManager power_manager;,位置在 PlatformInit() 之后;使用 XRobot 时按 PowerManager 注册,且排在其他对象之前。power_manager 是保留名,GPIO 名字与它相同会使生成报错。生成的代码需要带 PowerManager 的 LibXR,即 v2.0.1 或更新版本。power reset|shutdown|bootloader 命令由用户代码调用 power_manager.RegisterCommand(ramfs) 注册,生成器不生成这条调用。

对象的命名:

配置对象名例子
GPIOp 加小写的端口字母和去掉前导零的线号,GPIO 段可改名pa10、pa3(PA03)
I2C外设名的小写i2c3
GPTMR 上的 PWMpwm_<实例小写>_ch<比较器号>pwm_gptmr0_ch1

GPTMR 的每个比较器(COMP_n)生成一个 PWM 对象,走 HPMPWM 的 GPTMR 路径;对象只在 SoC 没有 PWM 外设时生成,判定依据是芯片的引脚数据里有没有 PWM 外设的信号:HPM5301 没有,HPM5361 有,后者的 GPTMR 列入 Other 只作展示。占空比比较器固定为 0,极性为 NORMAL;对象和注册无条件生成,包在预处理条件里会使 XRobot 的生成器拒绝该文件。


libxr_config.yaml​

User/libxr_config.yaml 保存代码生成的设置。文件不存在时按默认值新建;之后每次生成补上缺少的设置,已有的值保持不变。.hpmpc 只记录引脚,速率和频率以这里的设置为准。

段键与默认值说明
I2Cspeed: 100000总线速率,单位 Hz,段中每个实例一项,键为小写的实例名;取值须为 100000、400000 或 1000000(HPM 的 I2C 驱动支持的档位),其他值使生成报错
PWMfrequency(默认 null)每个对象一项;null 时不调用 SetConfig,设置了必须是正整数(Hz)
GPIO键为引脚名,值为新名字引脚改名;值为 null 时保留默认名。段中列出工程读到的每个引脚

设置值为 null 时取默认值;数值也可以写成字符串形式的整数字面量(如 '0x61A80')。设置键不区分大小写,文件中已有的写法保持不变。GPIO 的新名字必须是合法的 C++ 标识符,不能是 C++ 关键字或保留标识符,不能与 HPM SDK 的宏、生成代码中的名字或其他对象重名,不满足的名字一次全部列出后生成报错;引脚离开工程后,它值为 null 的改名项随之删除,写了名字的保留并给出警告。

HPM 的生成器不创建 UART 对象(LibXR 的 HPM UART 驱动还没有合入),所以不生成终端;文件中写了 terminal_source 时给出警告。

以下是 bsp-hpm5301evklite 首次生成的 User/libxr_config.yaml:

generator: 6.0.1
I2C:
i2c3:
speed: 100000
PWM:
pwm_gptmr0_ch1:
frequency:
GPIO:
pa10:
pa3:

把 GPIO 段中 pa10 的值改为 LED 后重新生成,对象即为 static HPMGPIO LED(HPM_GPIO0, GPIO_DI_GPIOA, 10, IRQn_GPIO0_A);,注册同步更新。


支持的 SoC 与封装​

当前数据覆盖 HPM5301(QFN48)和 HPM5361(LQFP100、LQFP64、QFN48);HPM53M1 暂不支持。引脚数据来自 HPM SDK 的 hpm_iomux.h、hpm_soc_ip.h 和 HPM5300 数据手册的引脚表。


重新生成代码​

修改 .hpmpc、main.c 或 User/libxr_config.yaml 后,在工程根目录重新运行 libxr hpm setup -d .,或分两步运行:

libxr parse -d .
libxr gen -i .config.yaml -o User/app_main.cpp

.config.yaml 由 libxr parse 写出,新克隆的工程里没有这个文件,先运行 libxr parse。不写 --xrobot 和 --no-xrobot 时,libxr gen 沿用输出文件现在的选择;生成后运行 xrobot gen 更新 User/xrobot_main.hpp。VS Code 扩展的 Generate LibXR Code 动作依次运行这两条命令,见 VS Code 扩展。


可选参数​

libxr hpm setup 的参数如下,libxr hpm setup --help 同样列出这些参数。

参数说明
-d, --directory工程目录:根目录有 app.yaml,boards/ 下有一个 .hpmpc(默认:当前目录)
-o, --output输出的 C++ 文件(默认:DIRECTORY 下的 User/app_main.cpp)
--xrobot / --no-xrobot生成或不生成 XR_REGISTER 注册与 XROBOT_MAIN();,见 与XRobot集成;都不写时沿用输出文件现在的选择,新文件不使用 XRobot
--libxr-configlibxr_config.yaml 的路径或 URL(默认:输出文件旁边的 libxr_config.yaml)
--verbose输出调试日志

相关命令​

命令功能说明
libxr parse解析工程,写出 .config.yaml
libxr gen由 .config.yaml 生成入口源文件和 app_main.h,并更新 libxr_config.yaml
libxr pins打印 SoC 的封装与引脚布局,见引脚布局

各命令的全部参数及默认值由 libxr <命令> --help 列出。

参考​