前言
近日在调试一个 STM32H5 + TrustZone 项目时,碰到了两个小问题:
* ADC1 + DMA 搬运数据时报错,进入 HAL_DMA_ERROR_USE* sprintf("%.2f") 无法正常输出浮点数
本文把排查过程整理出来,供同仁参考。
项目背景
本次项目环境如下:
MCU:STM32H563
TrustZone:开启
ADC1:配置在
Non-secure域DMA:用于搬运 ADC 转换结果
开发环境:VSCode + STM32CubeIDE for VSCode 插件
利用STM32CubeMx进行配置并创建基于HAL库的代码串口打印:通过 Secure区的 UART 输出
目标功能很简单:
-
- ADC 在TIMER触发下持续采样;DMA 持续搬运数据到内存 buffer;在DMA完成中断的回调函数中计算并打印结果;
问题 一:ADC DMA 运行时报HAL_DMA_ERROR_USE
1.1 现象
在 DMA 中断中,程序进入了下面这段代码:
if (__HAL_DMA_GET_FLAG(hdma, DMA_FLAG_USE) != 0U){if (__HAL_DMA_GET_IT_SOURCE(hdma, DMA_IT_USE) != 0U){__HAL_DMA_CLEAR_FLAG(hdma, DMA_FLAG_USE);hdma->ErrorCode |= HAL_DMA_ERROR_USE;}}
这说明 DMA 触发了HAL_DMA_ERROR_USE错误。
这个错误通常表示出现了DMA用户配置错误,比方跟DMA有关的参数、对齐问题等。结合本人对STM32H5芯片DMA的使用经验,基本参数方面应该问题不大,也不难快速验证。倒是对开启trustzone条件下各种安全属性及权限的配置不是很熟悉,所以,很怀疑这方面的问题。
1.2 第一反应:怀疑 TrustZone 安全属性
因为工程启用了 TrustZone,所以一开始优先从这些方向排查:
ADC1 是否真的配置成了Non-secure;
DMA Channel 是否也是Non-secure;
ADC 结果 buffer 是否在Non-secure RAM;
DMA request 是否正确;DMA 方向、宽度、模式是否正确;
通过STM32CubeMx的DMA 配置界面里,看到的是:
-
- Channel = Non-secureSource = Non-secureDestination = Non-securePrivileged =
Non-privileged
看上去没什么问题,毕竟也是基于cubeMx的默认配置。
1.3 检查 ADC buffer 的地址
ADC buffer 的地址实际打印出来是:0x200501EC
NS 工程链接脚本中的 RAM 定义是:
MEMORY
{
RAM (xrw) : ORIGIN = 0x20050000, LENGTH = 320K
FLASH (rx) : ORIGIN = 0x08100000, LENGTH = 1024K
}
所以可以确认:
adc_buffer 落在 NS 工程的 RAM 区间;从链接地址角度看,变量放置没有问题;
这一步基本可以排除ADC buffer 被链接到了 Secure RAM。
1.4 继续看 GTZC / MPCBB 配置
查看 Secure 工程中的 MX_GTZC_S_Init(),可以看到的安全属性配置如下:
MPCBB_Area_Desc.AttributeConfig.MPCBB_SecConfig_array[0] = 0x00000000;MPCBB_Area_Desc.AttributeConfig.MPCBB_SecConfig_array[19] = 0x00000000;
这说明 SRAM3 对应区域被配置成了Non-secure。
所以从安全属性上看,内存区域的配置也是没问题的。
1.5 真正的问题:Privileged 属性冲突
继续往下看,同一段 GTZC 代码里还有这一组:
MPCBB_Area_Desc.AttributeConfig.MPCBB_PrivConfig_array[0] = 0xFFFFFFFF;...MPCBB_Area_Desc.AttributeConfig.MPCBB_PrivConfig_array[19] = 0xFFFFFFFF;
这意味着SRAM3 都被配置成了 Privileged,而 DMA Channel 的配置却是Non-privileged,于是出现了如下冲突:
DMA CH0 = Non-privileged
SRAM3 = Privileged
换句话说,DMA 正在尝试用非特权访问去写一个特权内存区域,这就解释了为什么 DMA 无法正常工作。
当然,这里真正的原因不是单纯的 Secure / Non-secure 配置错误,而是:
Privileged / Non-privileged 属性不匹配。
具体来说就是:
SRAM3 被配置成了 Privileged;DMA-CH0 却配置成了 Non-privileged;
导致DMA写 adc_buffer 时访问失败,最终触发 HAL_DMA_ERROR_USE。
1.6 解决办法
把 DMA Channel 的属性从NON PRIVILEGED 改成 PRIVILEGED。
修改后,DMA工作立刻恢复正常,ADC数据可以正常搬运。
问题 二:sprintf("%.2f")浮点输出异常
2.1 现象
DMA 正常后,在 ADC 回调中做了电压换算,并尝试打印带两位小数的结果:
float voltage;sprintf(buf, "ADC Voltage: %.2fVrn", (double)voltage);SECURE_UART_Print(buf);
结果输出异常,具体就是不能正确显示浮点数据。
2.2 原因分析
应该说,这个问题和 ADC/DMA以及UART都没关系,因为现在可以看到ADC结果,其它非浮点数字符打印都是正常的。所以这应是工具链配置问题。
在常用的GCC / newlib-nano 环境下, 默认不会启用 printf 的浮点支持。因此像这种格式 %.2f,如果没有额外配置,常常就会输出异常。
2.3 解决方法
在链接选项中加入:-u _printf_float
我这边使用的是 VSCode + STM32CubeIDE for VSCode 插件的开发环境,就在工程的 CMakeLists.txt 中加入了相应链接参数。
#Linker options
set(STM32_LINKER_SCRIPT STM32H563xx_FLASH_ns.ld)
set(STM32_LINKER_OPTION "-u _printf_float")
加上黄色高亮代码行后,浮点数打印输出恢复正常。
这个选项的作用是告诉链接器把 printf 相关的浮点格式化支持一起链接进来。如果使用STM32CubeIDE工具的话,在工程属性的相关配置里有专门的勾选项。参见下图示意:
三、小结
两个问题都不算复杂,但都挺“隐蔽”。第一个坑在 TrustZone 权限属性;第二个坑在 工具链 linker 配置。Ok,今天的分享就到这里,下次再聊。
145