trying to make sense of this #define

-1

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.

c
sample
stm32f4
stm32f7
asked on Stack Overflow Apr 16, 2019 by Ben Biles • edited Apr 20, 2019 by Ben Biles

2 Answers

2

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.

answered on Stack Overflow Apr 16, 2019 by John Bollinger
1

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.

answered on Stack Overflow Apr 16, 2019 by Lundin

User contributions licensed under CC BY-SA 3.0