#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_ */
#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.#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_ */
#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.#include <stdio.h>
#include <stdint.h>
// CONFIG_0 Register
#define TDC1000_CONFIG_0_TX_FREQ_DIV_Pos 5
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 union{
uint8_t TDC1000_CONFIG_0_Reg;
struct {
NUM_TX_TypeDef NUM_TX : 5;
TX_FREQ_DIV_TypeDef TX_FREQ_DIV : 3;
} __attribute__((__packed__));
} TDC1000_CONFIG_0_TypeDef;
volatile uint8_t CONFIG_0 = 0;
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;
printf("%x\r\n", mydef);
printf("%x\r\n", mydef.TDC1000_CONFIG_0_Reg);
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){
CONFIG_0 = value.TDC1000_CONFIG_0_Reg;
}
uint8_t TDC1000_getCONFIG_0_Reg(void){
return CONFIG_0;
}
c2
c2
c2
Que si es lo que debería obtener :D
Personalmente no lo haria con el struct por que es algo que ocupa espacio, si tenes pensado hacer algo asi para cada registro del TDC1000 estarias ocupando bastante memoria dentro del micro, ademas de miles de definiciones de struct, y no recuerdo si lei mal pero no hay forma de asgurarse el orden de los bitfield y es dependiende del compilador.