language - Paquetes de bits en una estructura c++/arduino
sintaxis del arduino (4)
Tengo una estructura
typedef struct {
uint8_t month; // 1..12 [4 bits]
uint8_t date; // 1..31 [5 bits]
uint8_t hour; // 00..23 [5 bits]
uint8_t minute; // 00..59 [6 bits]
uint8_t second; // 00..59 [6 bits]
} TimeStamp;
pero me gustaría empaquetarlo para que solo consuma 4 bytes en lugar de 5.
¿Hay una manera de cambiar los bits para crear una estructura más estrecha?
Puede que no parezca mucho, pero va a la EEPROM, por lo que 1 byte guardado es un extra de 512 bytes en una página de 4Kb (y puedo usar esos 6 bits adicionales que sobran para otra cosa también).
Lo que estás buscando son campos de bits.
Se ven así:
typedef struct {
uint32_t month : 4; // 1..12 [4 bits]
uint32_t date : 5; // 1..31 [5 bits]
uint32_t hour : 5; // 00..23 [5 bits]
uint32_t minute : 6; // 00..59 [6 bits]
uint32_t second : 6; // 00..59 [6 bits]
} TimeStamp;
Dependiendo de su compilador, para encajar en 4 bytes sin relleno, el tamaño de los miembros debe ser de 4 bytes (es decir, uint32_t
) en este caso. De lo contrario, los miembros de la estructura se rellenarán para no desbordarse en cada límite de byte, lo que dará como resultado una estructura de 5 bytes, si se utiliza uint8_t
. Usar esto como una regla general debería ayudar a prevenir discrepancias en el compilador.
Aquí hay un enlace de MSDN que profundiza un poco en los campos de bits:
Los campos de bits son una forma "correcta" de hacer esto en general, pero ¿por qué no almacenar segundos desde el comienzo del año? 4 bytes son suficientes para almacenarlos cómodamente; de hecho, 4 bytes son suficientes para almacenar los segundos entre 1970 y 2038. Obtener la otra información es un ejercicio simple, siempre y cuando se conozca el año actual (que podría almacenar junto con el resto de la información siempre que sea posible). ya que el intervalo de tiempo que le interesa abarca menos de 70 años (e incluso así podría simplemente agrupar las marcas de tiempo en rangos de 68 años y almacenar un desplazamiento para cada rango).
Otra solución es almacenar los valores en una variable de 32 bits y recuperar los elementos individuales con cambio de bits.
uint32_t timestamp = xxxx;
uint8_t month = timestamp & 0x0F;
uint8_t date = (timestamp & 0x1F0) >> 4;
uint8_t hour = (timestamp & 0x3E00) >> 9;
uint8_t minute = (timestamp & 0xFC000) >> 14;
uint8_t second = (timestamp & 0x3F00000) >> 20;
Si puede manejar una precisión de dos segundos, el formato de marca de hora de MS-DOS usó 16 bits para mantener la fecha (año 1980 como 7 bits, mes como 4, día como 5) y 16 bits para la hora (hora como cinco, minuto como seis, segundos como cinco). En un procesador como el Arduino, puede ser posible escribir código que divida valores en un límite de 16 bits, pero creo que el código será más eficiente si puede evitar tal división (como lo hizo MS-DOS al aceptar dos segundos). exactitud).
De lo contrario, como se señaló en otra respuesta, usar un número de segundos de 32 bits ya que un tiempo base a menudo será más eficiente que tratar de hacer un seguimiento de las cosas en "formato de calendario". Si todo lo que necesita hacer es avanzar de una fecha en formato de calendario a la siguiente, el código para hacerlo puede ser más simple que el código para convertir entre fechas de calendario y fechas lineales, pero si necesita hacer mucho más (incluso retroceda de una fecha a la anterior) es probable que sea mejor que convierta las fechas a / desde el formato lineal cuando se ingresen o se muestren, y de lo contrario, simplemente trabaje con números lineales de segundos.
Trabajar con números lineales de segundos puede ser más conveniente si elige como fecha de referencia el 1 de marzo de un año bisiesto. Luego, mientras la fecha excede de 1461, reste eso de la fecha y agregue 4 al año (la comparación y resta de 16 bits son eficientes en el Arduino, e incluso en 2040 el bucle aún puede tomar menos tiempo que una sola división de 16x16). Si la fecha excede de 364, reste 365 e incremente el año, e intente eso hasta dos veces más [si la fecha es 365 después de la tercera resta, déjela].
Se necesita algo de cuidado para garantizar que todos los casos de esquina funcionen correctamente, pero incluso en un micro de 8 bits o 16 bits, las conversiones pueden ser sorprendentemente eficientes.