将 uint16_t 数组作为 uint32_t 指针访问

发布于 2025-01-10 21:42:10 字数 718 浏览 6 评论 0原文

我正在使用一种 ARM 处理器,该处理器具有 2 个 16 位 ADC 作为一个 32 位值(可以将其视为读取立体声信号的两个通道)。我想获取 uint16_t[1024][2] 形式的值,但系统模块需要使用 uint32_t 指针调用 DMA。如果我将其设为 uint32_t 数组,那么我必须执行 AND 和逻辑移位将其转换回 2 个 uint16_t 值。如果我可以让 DMA 将其视为 32 位内存,但将数据作为 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();
}

I am working with an ARM processor that has 2 16-bit ADC's as one 32-bit value (think of as reading both channels of a stereo signal). I want to get the values as uint16_t[1024][2], but the system modules require the DMA called with a uint32_t pointer. IF I make it a uint32_t array, then I have to do AND and Logical Shifts to convert it back into 2 uint16_t values. Most of the CPU time would be freed up if I could have the DMA see it as 32-bit memory but manipulate the data as a 16-bit 2 element array. Any suggestions?

I am using the STM32CubeIDE which is a plugin to the Eclipse editor with the GCC compiler.

I could not get the DMA to work with any 16-bit values so I set up 32-bit buffer and am looking for a way to do the math as 2 separate 16-bit values.
My attempt was to define a 32-bit value for the buffer and then dereference it for math operations with a 16-bit int 2 element array:

...
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();
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

土豪我们做朋友吧 2025-01-17 21:42:10

替代方案 0

将数组定义为联合:

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++ 没有。)

替代方案 1

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,您可以通过检查生成的汇编语言来检查它。

替代方案 2

将缓冲区定义为 uint16_t [2] 数组,并根据 uint32_t 需要进行对齐,并在将其传递到 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 代码的情况下是不可能的。作为一个系统例程,它做自己的事情,编译器在编译此源代码时无法看到它。关于别名的规则是为了给编译器提供优化的机会,在这种情况下它们没有任何作用。

替代方案 3

使用替代方案 2 并使用 -fno-strict-aliasing 进行编译,这要求 GCC 和 Clang 支持超出 C 标准提供的别名。如果源文件中的其他位置编译器会利用严格的别名规则,则此开关会阻止这些优化。

Alternative 0

Define the array as a union:

static union {
    uint32_t u32[SAMPLES*2];
    uint16_t u16[SAMPLES*2][2];
} ADC_Buff;

Pass the desired uint32_t to the DMA routine:

HAL_ADCEx_MultiModeStart_DMA(&hadc1, ADC_Buff.u32, (uint32_t) SAMPLES)

Use the uint16_t data:

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]);

The C standard defines the behavior of reinterpreting data in unions this way. (C++ does not.)

Alternative 1

Define ADC_Buff as an array of uint32_t (as shown in the code currently in the question), pass it to the DMA routine, and then use memcpy to reinterpret it:

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]);
}

The compiler may optimize this to eliminate the memcpy, which you can check by examining the generated assembly language.

Alternative 2

Define the buffer as an array of uint16_t [2] with alignment as needed for uint32_t and coerce the address when passing it to the DMA routine:

static uint16_t _Alignas (uint32_t) ADC_Buff[SAMPLES*2][2];

…
HAL_ADCEx_MultiModeStart_DMA(&hadc1, (uint32_t *) ADC_Buff, (uint32_t) SAMPLES)

While the C standard would not define the behavior if HAL_ADCEx_MultiModeStart_DMA were written in C code, it is clearly not, completely, as DMA is impossible in strictly conforming C code. As a system routine, it does its own thing, and the compiler has no visibility into it when compiling this source code. The rules about aliasing are to give the compiler opportunities to optimize, and they have no effect in this case.

Alternative 3

Use alternative 2 and compile with -fno-strict-aliasing, which asks GCC and Clang to support aliasing beyond what the C standard provides. If there are other places in the source file where the compiler would take advantage of the strict aliasing rules, this switch prevents those optimizations.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文