#DEF CHANNEL_SELECT | 0<<25
#DEF MEMBURST_SINGLE_XFER | 0 << 23
.... etc.
Then you can do:
DMA1_Stream3->CR = CHANNEL_SELECT MEMBURST_SINGLE_XFER
and get a nice syntax error...
As you hint in the edit, using the preprocessor to change the language is not a good idea.
Magic numbers are not that magic if used once and commented, as in peter-h example.
The text in comments reflects the official reference manual names - so easy to find if a check is needed.
All the following is IMHO and for my personal code, when writing for work (very seldom, nowadays) I 100% abide by the agreed guidelines, like them or not (but I also contribute to them...):
If the same number is used repeatedly and has a meaning unrelated to its value, it might be worth a #define or an enum (for "families" like, say, error codes - this helps in code self-documentation e.g. as formal and actual parameters, and return values).
typedef enum SettingsUpdate_
{
/* modes for SDR settings updates */
NoUpdate = 0x00u,
PipelineUpdate = 0x01u,
VfoUpdate = 0x02u,
BfoUpdate = 0x04u,
FilterUpdate = 0x08u,
VolumeUpdate = 0x10u,
ZoomUpdate = 0x20u,
UpdateAll = 0x3Fu,
} SettingsUpdate;
Here something I wrote a couple of days ago, similar to peter-h's but using CMSIS defines:
#define CHAN_TCR (MDMA_CTCR_BWM /* FIXME: Bufferable? Tentative */ \
| MDMA_CTCR_SWRM /* Software request */ \
| MDMA_CTCR_TRGM /* Full transfer (list + block repeat) */ \
| 0x00000000u /* PAM: right aligned */ \
| 0x00000000u /* PKE: no packing */ \
| MDMA_CTCR_TLEN /* Maximum length: 128 bytes (16*64 bits) */ \
| MDMA_CTCR_DBURST_2 /* 2^4 beats for destination (maximum) */ \
| MDMA_CTCR_SBURST_2 /* Same for source */ \
| MDMA_CTCR_DINCOS /* Double word increment for destination */ \
| MDMA_CTCR_SINCOS /* Double word increment for source */ \
| MDMA_CTCR_DSIZE /* Double word size for destination */ \
| MDMA_CTCR_SSIZE /* Double word size for source */ \
| MDMA_CTCR_DINC_1 /* Increment destination */ \
| MDMA_CTCR_SINC_1) /* Increment source */
I normally use the vendor's definitions (CMSIS, mostly) for laziness: yes the RM is always open, but auto-completion is quick enough, and shifts are baked in.
The comments are there to tell my future self how the MDMA is setup.
If a set of number is interdependent then a set of related object like macros is useful.
#define FFT_DISPLAY_LEN 512 /* This is the actual size of the FFT */
#define FFT_DISPLAY_CENTER 256 /* Center of a full FFT display */
#define FFT_DISPLAY_BOTTOM 128
#define FFT_WATERFALL_HEIGHT 32
#define FFT_WATERFALL_TOP (FFT_DISPLAY_BOTTOM - FFT_WATERFALL_HEIGHT)
#define FFT_WINDOW_WIDTH (FFT_FRACTION(FFT_DISPLAY_LEN) | 1) /* Need an odd number */
#define FFT_WINDOW_HEIGHT 128
#define FFT_WINDOW_CENTER (FFT_WINDOW_WIDTH / 2)
#define FFT_WINDOW_X ((480 - FFT_WINDOW_WIDTH) / 2 + 2) /* Leave a little more space for dB labels */
#define FFT_WINDOW_Y (300 - FFT_WINDOW_HEIGHT)
0, 1, 2, 10, etc. are in general not in a define, never when used in their mathematical sense (see Siwastaja circumference example).
For some use, the preprocessor is absolutely needed, to reduce typing, and increase readability:
/* domain can be D1, D2 or D3 for AXI RAM, SRAM in domain 2 or SRAM in domain 3 */
#define DATA(domain) __attribute__((section(__STRING__(.data.$##domain))))
#define BSS(domain) __attribute__((section(__STRING__(.bss.$##domain))))
#define FAST_CODE __attribute__((section(".text.$ITCM")))
#define QSPI_CONST __attribute__((section(".rodata.$QSPI"))) const
#define DMA_SMALL __attribute__((section(".noinit.$D2_NCNI"))) alignas(max_align_t)
#define BDMA_SMALL __attribute__((section(".noinit.$D3_NCNI"))) alignas(max_align_t)
#define DMA_LARGE __attribute__((section(".noinit.$SDRAM_NCNI"))) alignas(max_align_t)
EtA: Ça va sans dire that the 2nd and 3rd examples are going to be replaced by constexpr when I switch to C23, further reducing preprocessor use. The last 4th example, dealing with things outside the C specification (but C23 introduces a standardized attribute syntax and some predefined attributes), will be more or less the only remaining use, with the exceptions of generics or maybe some function like macro when a static inline doesn't cut it.