I am trying to dereferencing a pointer, according to the data-type passed to the function, just to learn.
code 1: First Try. Oviously it didn't work.
typedef enum
{
bits_8,
bits_16,
bits_32
}bits_width;
inline int32_t write_data_spi1_enhanced_buffer_x_bits( void* data_address, uint32_t size, bits_width data_type )
{
switch( data_type )
{
case bits_8:
spi1_change_mode_8_bits();
uint8_t* dt = (uint8_t*)data_address;
break;
case bits_16:
spi1_change_mode_16_bits();
uint16_t* dt = (uint16_t*)data_address;
break;
case bits_32:
spi1_change_mode_32_bits();
uint32_t* dt = (uint32_t*)data_address;
break;
default:
return -1;
break;
}
for( uint32_t i = 0 ; i < size ; i )
{
while( SPI1STATbits.SPITBF );
SPI1BUF = *dt ; // Write the data out to the SPI1 peripheral. *dt = dt[i].
}
return 0;
}
code 2: Looks OK(compile). Not tested in hardware.
typedef enum
{
bits_8,
bits_16,
bits_32
}bits_width;
inline int32_t write_data_spi1_enhanced_buffer_x_bits( uint8_t* data_address, uint32_t size, bits_width data_type )
{
uint32_t x;
switch( data_type )
{
case bits_8:
spi1_change_mode_8_bits();
x = 1;
break;
case bits_16:
spi1_change_mode_16_bits();
x = 2;
break;
case bits_32:
//spi1_change_mode_32_bits();
x = 4;
break;
default:
return -1;
break;
}
for( uint32_t i = 0 ; i < size ; i )
{
while( SPI1STATbits.SPITBF );
SPI1BUF = *( data_address (i*x) ); // Write the data out to the SPI1 peripheral.
}
return 0;
}
code 3: I did not want to write the for loop 3 times(one for each data type).
typedef enum
{
bits_8,
bits_16,
bits_32
}bits_width;
inline int32_t write_data_spi1_enhanced_buffer_x_bits( void* data_address, uint32_t size, bits_width data_type )
{
switch( data_type )
{
case bits_8:
spi1_change_mode_8_bits();
uint8_t* dt = (uint8_t*)data_address;
for( uint32_t i = 0 ; i < size ; i )
{
while( SPI1STATbits.SPITBF );
SPI1BUF = *dt ; // Write the data out to the SPI1 peripheral. *dt = dt[i].
}
break;
case bits_16:
spi1_change_mode_16_bits();
uint16_t* dt = (uint16_t*)data_address;
for( uint32_t i = 0 ; i < size ; i )
{
while( SPI1STATbits.SPITBF );
SPI1BUF = *dt ; // Write the data out to the SPI1 peripheral. *dt = dt[i].
}
break;
case bits_32:
spi1_change_mode_32_bits();
uint32_t* dt = (uint32_t*)data_address;
for( uint32_t i = 0 ; i < size ; i )
{
while( SPI1STATbits.SPITBF );
SPI1BUF = *dt ; // Write the data out to the SPI1 peripheral. *dt = dt[i].
}
break;
default:
return -1;
break;
}
return 0;
}
How to make code 1 work ?
Or is there a better solution ?
Thank`s.
CodePudding user response:
You could create 3 separate functions using a macro:
#define TFUNC(X) \
inline void funcdef_##X(X* dt, uint32_t size) { \
spi1_change_mode_##X(); \
for (uint32_t i = 0; i < size; i ) { \
while (SPI1STATbits.SPITBF) \
; \
SPI1BUF = *dt ; \
} \
}
TFUNC(uint8_t)
TFUNC(uint16_t)
TFUNC(uint32_t)
Note: You'd have to rename spi1_change_mode_8_bits
into spi1_change_mode_uint8_t
etc. for the above to work.
Then use a _Generic
as a front-end:
#define write_data_spi1_enhanced_buffer_x_bits(X,S) \
_Generic((X), \
uint8_t* : funcdef_uint8_t, \
uint16_t* : funcdef_uint16_t, \
uint32_t* : funcdef_uint32_t) \
(X, S)
You'd then call it by a cast of data_address
to the proper type and it'll select the correct function to call. Example:
inline int32_t rite_data_spi1_enhanced_buffer(void* data_address, uint32_t size,
bits_width data_type) {
switch (data_type) {
case bits_8:
write_data_spi1_enhanced_buffer_x_bits((uint8_t*)data_address, size);
break;
case bits_16:
write_data_spi1_enhanced_buffer_x_bits((uint16_t*)data_address, size);
break;
case bits_32:
write_data_spi1_enhanced_buffer_x_bits((uint32_t*)data_address, size);
break;
default:
return 1;
}
return 0;
}
CodePudding user response:
I do not like this C "generic" syntax.
I would simple:
- Avoid pointer punning
- Set the register in the
switch() ... case
inline int32_t write_data_spi1_enhanced_buffer_x_bits( void* data_address, uint32_t size, bits_width data_type )
{
unsigned char *uda = data_address;
uint16_t u16;
uint32_t u32;
for( uint32_t i = 0 ; i < size ; i )
{
while( SPI1STATbits.SPITBF );
switch( data_type )
{
case bits_8:
SPI1BUF = uda[i];
break;
case bits_16:
memcpy(&u16, uda sizeof(u16) * i, sizeof(u16));
SPI1BUF = u16;
break;
case bits_32:
memcpy(&u32, uda sizeof(u32) * i, sizeof(u32));
SPI1BUF = u32;
break;
default:
return -1;
break;
}
}
}
memcpy
will be optimized out on targets not needing special alignment.