[英]Does accessing an array of uint32_t with an uint16_t* lead to undefined behavior?
[英]Accessing a uint16_t array as a uint32_t pointer
我正在使用一个 ARM 处理器,它有 2 个 16 位 ADC 作为一个 32 位值(认为是读取立体声信号的两个通道)。 我想获取 uint16_t[1024][2] 的值,但系统模块需要使用 uint32_t 指针调用的 DMA。 如果我将它设为 uint32_t 数组,那么我必须执行 AND 和逻辑移位以将其转换回 2 个 uint16_t 值。 如果我可以让 DMA 将其视为 32 位 memory 但将数据作为 16 位 2 元素数组进行操作,则可以释放大部分 CPU 时间。 有什么建议么?
我正在使用 STM32CubeIDE,它是带有 GCC 编译器的 Eclipse 编辑器的插件。
我无法让 DMA 使用任何 16 位值,因此我设置了 32 位缓冲区,并正在寻找一种方法来将数学运算作为 2 个单独的 16 位值。 我的尝试是为缓冲区定义一个 32 位值,然后使用 16 位 int 2 元素数组取消引用它以进行数学运算:
...
static uint32_t ADC_Buff[SAMPLES*2];
static uint16_t *vasamples[][2];
*vasamples= &ADC_Buff;
...
// setup DMA
if (HAL_ADCEx_MultiModeStart_DMA(&hadc1, *(ADC_Buff), (uint32_t)(SAMPLES)) != HAL_OK)
{
errnum = 4;
Error_Handler();
}
将数组定义为联合:
static union {
uint32_t u32[SAMPLES*2];
uint16_t u16[SAMPLES*2][2];
} ADC_Buff;
将所需的uint32_t
传递给 DMA 例程:
HAL_ADCEx_MultiModeStart_DMA(&hadc1, ADC_Buff.u32, (uint32_t) SAMPLES)
使用uint16_t
数据:
for (size_t i = 0; i < SAMPLES*2; ++i)
printf("Sample[%zu] = %" PRIu16 ", %" PRIu16 ".\n",
i, ADC_Buff.u16[i][0], ADC_Buff.u16[i][1]);
C 标准定义了以这种方式在联合中重新解释数据的行为。 (C++ 没有。)
将ADC_Buff
定义为uint32_t
的数组(如题中当前代码所示),传递给DMA例程,然后使用memcpy
重新解释:
for (size_t i = 0; i < SAMPLES*2; ++i)
{
uint16_t t[2];
memcpy(t, &ADC_Buff[i], sizeof t);
printf("Sample[%zu] = %" PRIu16 ", %" PRIu16 ".\n",
i, t[0], t[1]);
}
编译器可能会对此进行优化以消除memcpy
,您可以通过检查生成的汇编语言来检查它。
根据uint32_t
的需要将缓冲区定义为uint16_t [2]
和 alignment 的数组,并在将其传递给 DMA 例程时强制地址:
static uint16_t _Alignas (uint32_t) ADC_Buff[SAMPLES*2][2];
…
HAL_ADCEx_MultiModeStart_DMA(&hadc1, (uint32_t *) ADC_Buff, (uint32_t) SAMPLES)
虽然 C 标准不会定义如果HAL_ADCEx_MultiModeStart_DMA
写在 C 代码中的行为,但显然不是,完全,因为 DMA 不可能严格符合 C 代码。 作为系统例程,它做自己的事情,编译器在编译这段源代码时对它不可见。 关于别名的规则是为了给编译器提供优化的机会,在这种情况下它们没有任何作用。
使用备选方案 2 并使用-fno-strict-aliasing
编译,它要求 GCC 和 Clang 支持超出 C 标准提供的别名。 如果源文件中的其他地方编译器会利用严格的别名规则,则此开关会阻止这些优化。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.