'Accessing a uint16_t array as a uint32_t pointer
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();
}
Solution 1:[1]
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.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 |
