-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.

Ben Biles
  • 1
  • 2
  • 1
    Are you talking about `DMA_MAX(x)` and want us to explain what it does? – klutt Apr 16 '19 at 13:56
  • ... because if you're talking about `HAL_SAI_Transmit_DMA`, then your guess is better than ours, since you can, presumably, look at its definition, but you have not presented it to us. – John Bollinger Apr 16 '19 at 14:05
  • `DMA_MAX(x)` functions as a [conditional operator](http://www.trytoprogram.com/c-programming/c-conditional-operator/), returning `DMA_MAX_SIZE` if x is greater. Is this what you were confused with? – Patrick Apr 16 '19 at 14:07
  • yes i was talking about and should have made this more clear in the question. Thanks for your answers, I understand the macro now. #define DMA_MAX(x) (((x) <= DMA_MAX_SZE)? (x):DMA_MAX_SZE) – Ben Biles Apr 20 '19 at 09:22

2 Answers2

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.

John Bollinger
  • 160,171
  • 8
  • 81
  • 157
  • This is exactly what i was looking for. I was lacking understanding of "function-like macros" and ternary operator. I understand size value is being capped at max possible uint16_t value. I don't understand how a number larger than 65535 could arrive in the variable. maybe its checking for memory corruption? I guess its designed to stop HAL_SAI_Transmit_DMA function producing an error or perhaps other HAL layers. I'll just rewrite the code to be more readable as in comment below. Its only being used once in all code so not like this needs to be a reusable macro. Thanks for explanation – Ben Biles Apr 20 '19 at 09:56
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.

Lundin
  • 195,001
  • 40
  • 254
  • 396
  • Thank you Lundin. I'll do exactly that so its simple to read. in fact i'm not sure how a value larger than uint16_t could be written to the size variable so I wonder if the check is necessary. I will keep it in case it can protect against memory corruption or perhaps some other error. – Ben Biles Apr 20 '19 at 09:57