Que tal, como van?
Estoy haciendo los archivos header y source para un integrado que se lee por SPI, el integrado es el TDC1000 de TI y tiene varios registros que se pueden leer y escribir, pero mientras me llega y le hago su PCB para hacer pruebas me puse a testear con la PC.
Mi duda es que implementación será más fácil de usar para un tercero o cual es la manera de hacer librerías que me recomiendan ustedes que seguro tienen mas practica que yo.
Por ejemplo, para el registro CONFIG_0:

En la primer implementación de la librería el header me quedó algo asi:
#ifndef TDC1000_H_
#define TDC1000_H_
/***** Register address *****/
#define TDC1000_CONFIG_0_ADDR 0x00
...
/***** CONFIG_0 Register *****/
#define TDC1000_CONFIG_0_NUM_TX_MASK 0x1F
#define TDC1000_CONFIG_0_TX_FREQ_DIV_MASK 0x07
#define TDC1000_CONFIG_0_TX_FREQ_DIV_Pos 5
#define TDC1000_CONFIG_0_TX_FREQ_DIV_BY2 0x00 << TDC1000_CONFIG_0_TX_FREQ_DIV_Pos)
#define TDC1000_CONFIG_0_TX_FREQ_DIV_BY4 0x01 << TDC1000_CONFIG_0_TX_FREQ_DIV_Pos)
#define TDC1000_CONFIG_0_TX_FREQ_DIV_BY8 0x02 << TDC1000_CONFIG_0_TX_FREQ_DIV_Pos)
#define TDC1000_CONFIG_0_TX_FREQ_DIV_BY16 0x03 << TDC1000_CONFIG_0_TX_FREQ_DIV_Pos)
#define TDC1000_CONFIG_0_TX_FREQ_DIV_BY32 0x04 << TDC1000_CONFIG_0_TX_FREQ_DIV_Pos)
#define TDC1000_CONFIG_0_TX_FREQ_DIV_BY64 0x05 << TDC1000_CONFIG_0_TX_FREQ_DIV_Pos)
#define TDC1000_CONFIG_0_TX_FREQ_DIV_BY128 0x06 << TDC1000_CONFIG_0_TX_FREQ_DIV_Pos)
#define TDC1000_CONFIG_0_TX_FREQ_DIV_BY256 0x07 << TDC1000_CONFIG_0_TX_FREQ_DIV_Pos)
...
#endif /* TDC1000_H_ */
Y en el main se usaría mas o menos así:
#include "TDC1000.h"
int main(){
SPI_Write(TDC1000_CONFIG_0_ADDR, TDC1000_CONFIG_0_TX_FREQ_DIV_BY4);
}
Esta implementación no me termina de convencer porque los #define quedaron muy largos xD, además que nada me impide escribirle a los registros valores que puedan llevar al IC a comportarse erróneamente, por ejemplo, escribiendole a bits reservados en los registros.
Asi que inicie una segunda implementación usando typedef enums y typedef structs:
#ifndef TDC1000_H_
#define TDC1000_H_
/***** Register address *****/
#define TDC1000_CONFIG_0_ADDR 0x00
/***** CONFIG_0 Register *****/
typedef enum{
NUM_TX_1_PULSE,
NUM_TX_2_PULSES,
NUM_TX_3_PULSES,
NUM_TX_4_PULSES,
NUM_TX_5_PULSES
} NUM_TX_TypeDef;
typedef enum {
TX_FREQ_DIV_BY2,
TX_FREQ_DIV_BY4,
TX_FREQ_DIV_BY8,
TX_FREQ_DIV_BY16,
TX_FREQ_DIV_BY32,
TX_FREQ_DIV_BY64,
TX_FREQ_DIV_BY128,
TX_FREQ_DIV_BY256
} TX_FREQ_DIV_TypeDef;
typedef struct {
NUM_TX_TypeDef NUM_TX : 5;
TX_FREQ_DIV_TypeDef TX_FREQ_DIV : 3;
} __attribute__((__packed__)) TDC1000_CONFIG_0_TypeDef;
#endif /* TDC1000_H_ */
Y se usaría mas o menos así:
#include <stdio.h>
#include <stdint.h>
#include "TDC1000.h"
#define TDC1000_CONFIG_0_TX_FREQ_DIV_Pos 5 // esto solo lo uso en la pc para crear un "registro falso"
volatile uint8_t CONFIG_0 = 0; // mi registro falso
void TDC1000_setCONFIG_0_Reg(TDC1000_CONFIG_0_TypeDef value);
uint8_t TDC1000_getCONFIG_0_Reg(void);
int main(int argc, char *argv[]){
TDC1000_CONFIG_0_TypeDef mydef;
uint8_t test;
mydef.NUM_TX = NUM_TX_3_PULSES;
mydef.TX_FREQ_DIV = TX_FREQ_DIV_BY128;
TDC1000_setCONFIG_0_Reg(mydef);
test = TDC1000_getCONFIG_0_Reg();
printf("%x\r\n", test);
return 0;
}
void TDC1000_setCONFIG_0_Reg(TDC1000_CONFIG_0_TypeDef value){
// SPI_Write(TDC1000_CONFIG_0_ADDR, value); // La linea de abajo hace lo mismo que haría esta esta linea ya teniendo el IC
/* Le escribo al registro CONFIG_0 el valor definido por TDC1000_CONFIG_0_TypeDef, recorro el campo TX_FREQ_DIV porque no puedo hacer un cast directamente de un TDC1000_CONFIG_0_TypeDef a un uint8_t, el compilador se queja :/ */
CONFIG_0 = (value.TX_FREQ_DIV << TDC1000_CONFIG_0_TX_FREQ_DIV_Pos) |
value.NUM_TX;
}
uint8_t TDC1000_getCONFIG_0_Reg(void){
return CONFIG_0;
}
Esta implementación es la que mas me convence, primero porque no quedaron #defines tan largos, segundo a la hora de crear el mydef obtengo "ayuda" extra porque el IDE me da las opciones que puedo escribir en los diferentes campos de los registros, tercero de alguna manera "protejo" las escrituras en los registros, porque ya tengo "controlados" los valores que puedo escribir en los registros, si por ejemplo escribo un TDC1000_CONFIG_2_TypeDef en una función que espera un TDC1000_CONFIG_0_TypeDef me marcará error el compilador.
Además de que es la que creo usaron para hacer las librerías de CMSIS a la hora de definir los registros de los ARM Cortex.
¿Qué implementación usarian ustedes?
¿Alguna mejora que le pueda hacer a la segunda implementación?
Saludos y de antemano gracias

PD: Soy lo que sigue de malo redactando xD, cualquier duda o cosa que no haya escrito bien háganmelo saber

.
PD2: No lo puse en el foro de C para PICs porque creo que no solo aplica para ese MCU, mas bien cualquier cosa que se programe en C.
PD3: el __attribute__ (__packed__) lo uso para forzar que el struct ocupe 8bits (1 byte).