I am trying to adapt a 16bit audio recorder application for stm32fxxx to 24bit and I have stumbled across a #define
that is confusing me.
I have changed 16bit DMA to 32bit DMA along with some buffers uint16_t
to uint32_t
etc.. no idea why uint32_t
and not signed int but I will look at that later. There's no way to pass 24bits through the DMA so I will send 32bits and drop one byte later.
The 3rd input of HAL_SAI_Transmit_DMA()
actually expects a size value in uint16_t
.
#define AUDIO_OUT_BUFFER_SIZE 16384
#define AUDIODATA_SIZE 2
#define DMA_MAX_SZE 0xFFFF
#define DMA_MAX(x) (((x) <= DMA_MAX_SZE)? (x):DMA_MAX_SZE)
uint8_t BSP_AUDIO_OUT_Play(uint16_t* pBuffer, uint16_t Size)
{
// send audio samples over DMA from buffer to audio port
HAL_SAI_Transmit_DMA(&haudio_out_sai, (uint8_t*) pBuffer,
DMA_MAX(AUDIO_OUT_BUFFER_SIZE / AUDIODATA_SIZE));
return AUDIO_OK;
}
I'm guessing i need to change to
AUDIODATA_SIZE 4
#define DMA_MAX_SZE 0xFFFFFFFF // 32bit
But I'd like to know what #define DMA_MAX(x) (((x) <= DMA_MAX_SZE)? (x):DMA_MAX_SZE)
is supposed to be doing and how it even works! its almost written as if its a function? where x
is an IO value?
AUDIODATA_SIZE
is the number of bytes in each sample:
I apologize for being a beginner at C but I never saw anything like this and can only assume its masking the size of the buffer. But why?
pBuffer
is the pointer to the uint16_t*
pointer passed into the function and is cast to (uint8_t*)pBuffer
for the HAL_SAI_Transmit_DMA
as it requires it.
I've never seen pointers cast like that either but it works.
i'd like to know what this define is supposed to be doing and how it even works! its almost written as if its a function? where x is an IO value ?
I take it you're asking about the DMA_MAX
macro. Yes, it's written almost as if it were a function, and it conceptually works a bit like one, too. That's why macros like this are called "function-like macros". The other kind are "object-like macros".
A function-like macro is still a macro. Its appearances are replaced with the given replacement text, and all appearances within of the identifiers of any of its parameters are replaced by the (fully macro-expanded) macro arguments, with a couple of exceptions that I'll ignore here. Thus, this ...
DMA_MAX(BufferSize / AUDIODATA_SIZE)
... expands to this:
(((BufferSize / AUDIODATA_SIZE) <= DMA_MAX_SZE)? (BufferSize / AUDIODATA_SIZE):DMA_MAX_SZE)
That uses the ternary operator to evaluate to the lesser of BufferSize / AUDIODATA_SIZE
and DMA_MAX_SZE
. Why and how (and whether) that is the right thing to do is not evident from the code presented.
pBuffer is the pointer to the uint16_t* pointer passed into the function and is cast to (uint8_t*)pBuffer for the HAL_SAI_Transmit_DMA as it requires it. I've never seen pointers cast like that either but it works.
It is allowed to convert from one object pointer type to another, and sometimes it is even sensible to do so. In particular, casting to a character pointer such as uint8_t *
is a well-defined means to provide to access to the raw bytes of an object's representation.
0xFFFF doesn't appear to be a bit mask but a maximum size 65535.
The macro DMA_MAX(x)
simply checks if the passed parameter is less than a maximum, and if not caps the value to the maximum allowed. Some manner of overflow check, perhaps?
It is logically equivalent to this pseudo-code:
if( (BufferSize / AUDIODATA_SIZE) <= DMA_MAX_SZE)
{
HAL_SAI_Transmit_DMA(... , BufferSize / AUDIODATA_SIZE);
}
else
{
HAL_SAI_Transmit_DMA(... , DMA_MAX_SZE);
}
As a side-note, it would be better practice not to write icky macros like this but instead use a temporary variable:
uint16_t size;
if(BufferSize / AUDIODATA_SIZE <= DMA_MAX_SZE)
{
size = BufferSize / AUDIODATA_SIZE;
}
else
{
size = DMA_MAX_SZE;
}
HAL_SAI_Transmit_DMA(... , size);
This will compile to the same machine code as what you currently have, but is much more readable.
User contributions licensed under CC BY-SA 3.0